Imported Upstream version 5.19 upstream upstream/5.19
authorSebastian Chlad <sebastian.chlad@tieto.com>
Tue, 27 May 2014 09:21:58 +0000 (11:21 +0200)
committerSebastian Chlad <sebastian.chlad@tieto.com>
Tue, 27 May 2014 09:21:58 +0000 (11:21 +0200)
448 files changed:
AUTHORS
ChangeLog
Makefile.am
Makefile.in
Makefile.plugins
Makefile.tools
TODO
android/Android.mk
android/Makefile.am
android/README
android/a2dp.c
android/a2dp.h
android/android-tester.c [new file with mode: 0644]
android/audio-ipc-api.txt [new file with mode: 0644]
android/audio-msg.h [new file with mode: 0644]
android/audio_utils/resampler.c [new file with mode: 0644]
android/audio_utils/resampler.h [new file with mode: 0644]
android/avctp.c [new file with mode: 0644]
android/avctp.h [new file with mode: 0644]
android/avdtp.c
android/avdtp.h
android/avrcp-lib.c [new file with mode: 0644]
android/avrcp-lib.h [new file with mode: 0644]
android/avrcp.c [new file with mode: 0644]
android/avrcp.h [new file with mode: 0644]
android/bite-rfcomm.txt [new file with mode: 0644]
android/bite-spp.txt [new file with mode: 0644]
android/bluetooth.c
android/bluetooth.h
android/bluetoothd-snoop.c [new file with mode: 0644]
android/client/haltest.c
android/client/history.c
android/client/hwmodule.c [deleted file]
android/client/if-audio.c [new file with mode: 0644]
android/client/if-av.c
android/client/if-bt.c
android/client/if-gatt.c
android/client/if-hh.c
android/client/if-hl.c [new file with mode: 0644]
android/client/if-main.h
android/client/if-pan.c
android/client/if-rc.c [new file with mode: 0644]
android/client/if-sco.c [new file with mode: 0644]
android/client/if-sock.c
android/client/tabcompletion.c
android/cutils/properties.h
android/gatt.c [new file with mode: 0644]
android/gatt.h [new file with mode: 0644]
android/hal-a2dp.c
android/hal-audio.c [new file with mode: 0644]
android/hal-avrcp.c [new file with mode: 0644]
android/hal-bluetooth.c
android/hal-gatt.c [new file with mode: 0644]
android/hal-handsfree.c [new file with mode: 0644]
android/hal-health.c [new file with mode: 0644]
android/hal-hidhost.c
android/hal-ipc-api.txt
android/hal-ipc.c
android/hal-log.h
android/hal-msg.h
android/hal-pan.c
android/hal-sco.c [new file with mode: 0644]
android/hal-socket.c [moved from android/hal-sock.c with 55% similarity]
android/hal-utils.c
android/hal-utils.h
android/hal.h
android/handsfree.c [new file with mode: 0644]
android/handsfree.h [new file with mode: 0644]
android/hardware/audio.h [new file with mode: 0644]
android/hardware/audio_effect.h [new file with mode: 0644]
android/hardware/hardware.c [new file with mode: 0644]
android/health.c [new file with mode: 0644]
android/health.h [new file with mode: 0644]
android/hidhost.c
android/hidhost.h
android/init.bluetooth.rc [new file with mode: 0644]
android/ipc-common.h [new file with mode: 0644]
android/ipc-tester.c [new file with mode: 0644]
android/ipc.c
android/ipc.h
android/main.c
android/mcap-lib.c [new file with mode: 0644]
android/mcap-lib.h [new file with mode: 0644]
android/pan.c
android/pan.h
android/pics-a2dp.txt [new file with mode: 0644]
android/pics-avctp.txt [new file with mode: 0644]
android/pics-avrcp.txt [new file with mode: 0644]
android/pics-did.txt
android/pics-gap.txt
android/pics-gatt.txt [new file with mode: 0644]
android/pics-hdp.txt [new file with mode: 0644]
android/pics-hfp.txt [new file with mode: 0644]
android/pics-hid.txt
android/pics-hsp.txt [new file with mode: 0644]
android/pics-iopt.txt [new file with mode: 0644]
android/pics-l2cap.txt [new file with mode: 0644]
android/pics-map.txt [new file with mode: 0644]
android/pics-mcap.txt [new file with mode: 0644]
android/pics-mps.txt [new file with mode: 0644]
android/pics-opp.txt
android/pics-pan.txt
android/pics-pbap.txt
android/pics-rfcomm.txt [new file with mode: 0644]
android/pics-sdp.txt [new file with mode: 0644]
android/pics-sm.txt [new file with mode: 0644]
android/pics-spp.txt [new file with mode: 0644]
android/pixit-a2dp.txt [new file with mode: 0644]
android/pixit-avctp.txt [new file with mode: 0644]
android/pixit-avrcp.txt [new file with mode: 0644]
android/pixit-did.txt [new file with mode: 0644]
android/pixit-gap.txt [new file with mode: 0644]
android/pixit-gatt.txt [new file with mode: 0644]
android/pixit-hdp.txt [new file with mode: 0644]
android/pixit-hfp.txt [new file with mode: 0644]
android/pixit-hid.txt [new file with mode: 0644]
android/pixit-hsp.txt [new file with mode: 0644]
android/pixit-iopt.txt [new file with mode: 0644]
android/pixit-l2cap.txt [new file with mode: 0644]
android/pixit-map.txt [new file with mode: 0644]
android/pixit-mcap.txt [new file with mode: 0644]
android/pixit-mps.txt [new file with mode: 0644]
android/pixit-opp.txt [new file with mode: 0644]
android/pixit-pan.txt [new file with mode: 0644]
android/pixit-pbap.txt [new file with mode: 0644]
android/pixit-sm.txt [new file with mode: 0644]
android/pts-a2dp.txt [new file with mode: 0644]
android/pts-avctp.txt [new file with mode: 0644]
android/pts-avrcp.txt [new file with mode: 0644]
android/pts-did.txt [new file with mode: 0644]
android/pts-gap.txt [new file with mode: 0644]
android/pts-gatt.txt [new file with mode: 0644]
android/pts-hdp.txt [new file with mode: 0644]
android/pts-hfp.txt [new file with mode: 0644]
android/pts-hid.txt [new file with mode: 0644]
android/pts-hsp.txt [new file with mode: 0644]
android/pts-iopt.txt [new file with mode: 0644]
android/pts-l2cap.txt [new file with mode: 0644]
android/pts-map.txt [new file with mode: 0644]
android/pts-mcap.txt [new file with mode: 0644]
android/pts-mps.txt [new file with mode: 0644]
android/pts-opp.txt [new file with mode: 0644]
android/pts-pan.txt [new file with mode: 0644]
android/pts-pbap.txt [new file with mode: 0644]
android/pts-sm.txt [new file with mode: 0644]
android/sco-msg.h [new file with mode: 0644]
android/socket.c
android/socket.h
android/system-emulator.c
android/system/audio.h [new file with mode: 0644]
android/test-ipc.c [new file with mode: 0644]
android/utils.h
attrib/att.c
attrib/att.h
attrib/gatt-service.c
attrib/gatt.c
attrib/gatt.h
attrib/gattrib.c
attrib/gatttool.c
attrib/interactive.c
attrib/utils.c
btio/btio.c
client/agent.c
client/main.c
config.h.in
configure
configure.ac
doc/media-api.txt
doc/mgmt-api.txt
doc/obex-api.txt
doc/settings-storage.txt [new file with mode: 0644]
doc/test-coverage.txt [new file with mode: 0644]
emulator/amp.c
emulator/btdev.c
emulator/bthost.c
emulator/bthost.h
emulator/hfp.c [new file with mode: 0644]
emulator/le.c [new file with mode: 0644]
emulator/le.h [new file with mode: 0644]
emulator/main.c
emulator/server.c
emulator/server.h
emulator/smp.c [new file with mode: 0644]
emulator/vhci.c
emulator/vhci.h
gdbus/client.c
gdbus/gdbus.h
gdbus/mainloop.c
gdbus/object.c
gobex/gobex-header.c
gobex/gobex-header.h
gobex/gobex-transfer.c
gobex/gobex.c
gobex/gobex.h
lib/bluetooth.c
lib/mgmt.h
lib/sdp.c
lib/sdp.h
lib/sdp_lib.h
lib/uuid.c
lib/uuid.h
monitor/analyze.c [new file with mode: 0644]
monitor/analyze.h [new file with mode: 0644]
monitor/bt.h
monitor/btsnoop.c [deleted file]
monitor/btsnoop.h [deleted file]
monitor/control.c
monitor/control.h
monitor/crc.c
monitor/crc.h
monitor/display.c
monitor/display.h
monitor/ellisys.c [new file with mode: 0644]
monitor/ellisys.h [new file with mode: 0644]
monitor/hcidump.c
monitor/hcidump.h
monitor/hwdb.c [new file with mode: 0644]
monitor/hwdb.h [new file with mode: 0644]
monitor/keys.c [new file with mode: 0644]
monitor/keys.h [new file with mode: 0644]
monitor/l2cap.c
monitor/l2cap.h
monitor/ll.c
monitor/ll.h
monitor/lmp.c
monitor/lmp.h
monitor/main.c
monitor/mainloop.c
monitor/mainloop.h
monitor/packet.c
monitor/packet.h
monitor/rfcomm.h [new file with mode: 0644]
monitor/sdp.c
monitor/sdp.h
monitor/uuid.c
monitor/uuid.h
monitor/vendor.c
monitor/vendor.h
obexd/client/bluetooth.c
obexd/client/session.c
obexd/client/transfer.c
obexd/plugins/bluetooth.c
obexd/plugins/pbap.c
obexd/plugins/phonebook.h
obexd/src/manager.c
obexd/src/obex.c
plugins/autopair.c
plugins/external-dummy.c
plugins/gatt-example.c
plugins/hostname.c
plugins/neard.c
plugins/policy.c
plugins/sixaxis.c
plugins/wiimote.c
profiles/alert/server.c
profiles/audio/a2dp.c
profiles/audio/avctp.c
profiles/audio/avctp.h
profiles/audio/avdtp.c
profiles/audio/avrcp.c
profiles/audio/avrcp.h
profiles/audio/control.c
profiles/audio/media.c
profiles/audio/player.c
profiles/audio/sink.c
profiles/audio/source.c
profiles/audio/transport.c
profiles/cyclingspeed/cyclingspeed.c
profiles/deviceinfo/deviceinfo.c
profiles/gatt/gas.c
profiles/health/hdp.c
profiles/health/hdp_main.c
profiles/health/hdp_manager.c
profiles/health/hdp_util.c
profiles/health/mcap.c
profiles/health/mcap_sync.c
profiles/heartrate/heartrate.c
profiles/input/device.c
profiles/input/device.h
profiles/input/hidp_defs.h [new file with mode: 0644]
profiles/input/hog.c
profiles/input/input.conf
profiles/input/manager.c
profiles/input/server.c
profiles/input/suspend-dummy.c
profiles/network/bnep.c
profiles/network/bnep.h
profiles/network/connection.c
profiles/network/manager.c
profiles/network/server.c
profiles/network/server.h
profiles/proximity/immalert.c
profiles/proximity/linkloss.c
profiles/proximity/main.c
profiles/proximity/manager.c
profiles/proximity/monitor.c
profiles/proximity/reporter.c
profiles/sap/main.c
profiles/sap/manager.c
profiles/sap/sap-dummy.c
profiles/sap/sap-u8500.c
profiles/sap/server.c
profiles/scanparam/scan.c
profiles/thermometer/thermometer.c
profiles/time/server.c
src/adapter.c
src/adapter.h
src/agent.c
src/attrib-server.c
src/attrib-server.h
src/bluetooth.conf
src/device.c
src/device.h
src/eir.c
src/eir.h
src/gatt-dbus.c [new file with mode: 0644]
src/gatt-dbus.h [new file with mode: 0644]
src/gatt.c [new file with mode: 0644]
src/gatt.h [new file with mode: 0644]
src/hcid.h
src/main.c
src/main.conf
src/plugin.c
src/profile.c
src/sdp-client.c
src/sdp-client.h
src/sdpd-request.c
src/sdpd-service.c
src/shared/btsnoop.c
src/shared/btsnoop.h
src/shared/crypto.c [new file with mode: 0644]
src/shared/crypto.h [new file with mode: 0644]
src/shared/gatt-db.c [new file with mode: 0644]
src/shared/gatt-db.h [new file with mode: 0644]
src/shared/hci.c [new file with mode: 0644]
src/shared/hci.h [new file with mode: 0644]
src/shared/hciemu.c
src/shared/hciemu.h
src/shared/hfp.c [new file with mode: 0644]
src/shared/hfp.h [new file with mode: 0644]
src/shared/io-glib.c [new file with mode: 0644]
src/shared/io-mainloop.c [new file with mode: 0644]
src/shared/io.h [new file with mode: 0644]
src/shared/mgmt.c
src/shared/mgmt.h
src/shared/pcap.c
src/shared/pcap.h
src/shared/queue.c [new file with mode: 0644]
src/shared/queue.h [new file with mode: 0644]
src/shared/ringbuf.c [new file with mode: 0644]
src/shared/ringbuf.h [new file with mode: 0644]
src/shared/tester.c
src/shared/tester.h
src/shared/timeout-glib.c [new file with mode: 0644]
src/shared/timeout-mainloop.c [new file with mode: 0644]
src/shared/timeout.h [new file with mode: 0644]
src/shared/util.c
src/shared/util.h
src/storage.c
src/storage.h
src/textfile.h
src/uuid-helper.c [moved from src/glib-helper.c with 87% similarity]
src/uuid-helper.h [moved from src/glib-helper.h with 100% similarity]
test/ftp-client [new file with mode: 0755]
test/map-client [new file with mode: 0755]
test/monitor-bluetooth
test/opp-client [new file with mode: 0755]
test/pbap-client [new file with mode: 0755]
test/simple-agent
test/simple-endpoint
test/simple-player
test/simple-service [deleted file]
test/test-adapter
test/test-alert
test/test-cyclingspeed
test/test-device
test/test-discovery
test/test-health
test/test-health-sink
test/test-heartrate
test/test-hfp
test/test-manager
test/test-nap
test/test-network
test/test-profile
test/test-proximity
test/test-thermometer
tools/3dsp.c [new file with mode: 0644]
tools/amptest.c
tools/avinfo.c
tools/bccmd.c
tools/bdaddr.c
tools/bluemoon.c [new file with mode: 0644]
tools/bluetooth-player.c
tools/btinfo.c
tools/btiotest.c
tools/btmgmt.c
tools/btproxy.c [new file with mode: 0644]
tools/btsnoop.c
tools/cltest.c
tools/csr_usb.c
tools/gatt-service.c [new file with mode: 0644]
tools/hci-tester.c [new file with mode: 0644]
tools/hciattach.c
tools/hciattach.h
tools/hciattach_bcm43xx.c [new file with mode: 0644]
tools/hciattach_qualcomm.c
tools/hciattach_tialt.c
tools/hciconfig.c
tools/hcidump.c
tools/hcitool.c
tools/hex2hcd.c [new file with mode: 0644]
tools/hid2hci.c
tools/ibeacon.c [new file with mode: 0644]
tools/l2cap-tester.c
tools/l2test.c
tools/mgmt-tester.c
tools/obex-client-tool.c
tools/obex-server-tool.c
tools/obexctl.c
tools/parser/avrcp.c
tools/parser/hci.c
tools/parser/l2cap.c
tools/parser/parser.h
tools/parser/ppp.c
tools/rctest.c
tools/rfcomm-tester.c [new file with mode: 0644]
tools/sco-tester.c
tools/scotest.c
tools/sdptool.c
tools/seq2bseq.c [new file with mode: 0644]
tools/smp-tester.c
unit/test-avctp.c [new file with mode: 0644]
unit/test-avdtp.c
unit/test-avrcp.c [new file with mode: 0644]
unit/test-eir.c
unit/test-gdbus-client.c
unit/test-gobex-apparam.c
unit/test-gobex-transfer.c
unit/test-gobex.c
unit/test-hfp.c [new file with mode: 0644]
unit/test-lib.c
unit/test-queue.c [new file with mode: 0644]
unit/test-ringbuf.c [new file with mode: 0644]
unit/test-sdp.c
unit/test-uuid.c
unit/util.c
unit/util.h

diff --git a/AUTHORS b/AUTHORS
index 4cb3d63..085e319 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -80,3 +80,10 @@ Takashi Sasai <sasai@sm.sony.co.jp>
 Andre Dieb Martins <andre.dieb@signove.com>
 Cristian Rodríguez <crrodriguez@opensuse.org>
 Alex Deymo <deymo@chromium.org>
+Petri Gynther <pgynther@google.com>
+Scott James Remnant <scott@netsplit.com>
+Jakub Tyszkowski <jakub.tyszkowski@tieto.com>
+Grzegorz Kołodziejczyk <grzegorz.kolodziejczyk@tieto.com>
+Marcin Krąglak <marcin.kraglak@tieto.com>
+Łukasz Rymanowski <lukasz.rymanowski@tieto.com>
+Jerzy Kasenberg <jerzy.kasenberg@tieto.com>
index 1db42c9..1c9fd1a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,65 @@
+ver 5.19:
+       Fix issue with OBEX Put-Delete and Create-Empty methods.
+       Fix issue with AVRCP browsable/searchable player properties.
+       Fix issue with handling multiple default agents.
+       Fix issue with handling unpair event per bearer.
+       Fix issue with HID over GATT report ID presence.
+       Add support for HID protocol handling in userspace.
+       Add support for Bluetooth reconnection policy framework.
+       Add support for Android Bluetooth SCO over HCI transport.
+       Add support for Android Bluetooth audio quality control.
+       Add support for Android Bluetooth Low Energy only mode.
+
+ver 5.18:
+       Fix issue with identifying LE single mode devices.
+       Fix issue with L2CAP and RFCOMM peer address lookup.
+       Add support for handling OBEX authentication procedure.
+       Add support for Android Bluetooth GATT client interface.
+
+ver 5.17:
+       Fix issue with not resetting OBEX SRM setup.
+       Fix issue with BR/EDR devices and auto-connect list.
+       Fix issue with bonding complete detection as peripheral.
+       Fix issue with not updating bearer timestamp of connections.
+       Fix issue with paired property for multiple bearers.
+       Add support for Android Bluetooth Handsfree interface.
+       Add support for Android Bluetooth Wideband speech.
+
+ver 5.16:
+       Fix issue with HID over GATT physical location.
+       Fix issue with HID over GATT unique identifier.
+       Fix issue with missing paired property notification.
+       Fix issue with endianess of long term key storage.
+       Add support for storing signature resolving keys.
+       Add support for Android Bluetooth AVRCP interface.
+
+ver 5.15:
+       Fix issue with LE enabling and background scanning.
+       Fix issue with HID over GATT input device name.
+       Fix issue with storage of slave long term keys.
+       Add support for handling identity resolving keys.
+       Add support for Android Bluetooth A2DP interface.
+       Add support for Android Bluetooth audio interface.
+
+ver 5.14:
+       Fix issue with marking PS3 controllers as trusted.
+       Fix issue with authorization of PS3 controllers.
+       Add support for DualShock 4 controller detection.
+       Add support for legacy pairing emulation.
+       Add support for secure simple pairing emulation.
+       Add support for automated pairing testing.
+       Add support for RFCOMM protocol testing.
+       Add support for HCI controller testing.
+
+ver 5.13:
+       Fix issue with PS3 controller detection.
+       Add support for data transfers to L2CAP testing tool.
+       Add support for delay reporting to AVDTP testing tool.
+       Add support for Android Bluetooth Core interface.
+       Add support for Android Bluetooth Socket interface.
+       Add support for Android Bluetooth HID Host interface.
+       Add support for Android Bluetooth PAN interface.
+
 ver 5.12:
        Fix issue with missing reply to DisconnectProfile.
        Fix issue with icon property and class of device changes.
@@ -8,7 +70,7 @@ ver 5.12:
        Add support for LE L2CAP CoC test capabilities.
        Add support for AVDTP qualification test cases.
        Add support for SMP cryptographic test cases.
-s
+
 ver 5.11:
        Fix issue with connection attempt when not powered.
        Fix issue with assigning player to AVRCP target role.
index 81a458b..14e735d 100644 (file)
@@ -80,7 +80,7 @@ include_HEADERS += $(lib_headers)
 lib_LTLIBRARIES += lib/libbluetooth.la
 
 lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:3:17
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:9:17
 lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 endif
 
@@ -137,7 +137,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
                        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/uuid-helper.h src/uuid-helper.c \
                        src/uinput.h \
                        src/plugin.h src/plugin.c \
                        src/storage.h src/storage.c \
@@ -146,9 +146,14 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
                        src/adapter.h src/adapter.c \
                        src/profile.h src/profile.c \
                        src/service.h src/service.c \
+                       src/gatt-dbus.h src/gatt-dbus.c \
+                       src/gatt.h src/gatt.c \
                        src/device.h src/device.c src/attio.h \
                        src/dbus-common.c src/dbus-common.h \
                        src/eir.h src/eir.c \
+                       src/shared/io.h src/shared/io-glib.c \
+                       src/shared/timeout.h src/shared/timeout-glib.c \
+                       src/shared/queue.h src/shared/queue.c \
                        src/shared/util.h src/shared/util.c \
                        src/shared/mgmt.h src/shared/mgmt.c
 src_bluetoothd_LDADD = lib/libbluetooth-internal.la gdbus/libgdbus-internal.la \
@@ -176,6 +181,7 @@ EXTRA_DIST += src/genbuiltin src/bluetooth.conf \
                        profiles/input/input.conf profiles/proximity/proximity.conf
 
 test_scripts =
+unit_tests =
 
 include Makefile.tools
 include Makefile.obexd
@@ -198,7 +204,8 @@ endif
 
 EXTRA_DIST += $(test_scripts)
 
-EXTRA_DIST += doc/assigned-numbers.txt doc/supported-features.txt
+EXTRA_DIST += doc/assigned-numbers.txt doc/supported-features.txt \
+                               doc/test-coverage.txt doc/settings-storage.txt
 
 EXTRA_DIST += doc/mgmt-api.txt \
                doc/adapter-api.txt doc/device-api.txt \
@@ -216,13 +223,12 @@ EXTRA_DIST += tools/magic.btsnoop
 
 AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
 
-AM_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
-                       -I$(srcdir)/gdbus -I$(srcdir)/btio
+AM_CPPFLAGS = -I$(builddir)/lib -I$(srcdir)/gdbus
 
 
-unit_tests = unit/test-eir unit/test-uuid unit/test-textfile unit/test-crc
+unit_tests += unit/test-eir unit/test-uuid unit/test-textfile unit/test-crc
 
-unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/glib-helper.c
+unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c
 unit_test_eir_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 
 unit_test_uuid_SOURCES = unit/test-uuid.c
@@ -234,9 +240,23 @@ unit_test_textfile_LDADD = @GLIB_LIBS@
 unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c
 unit_test_crc_LDADD = @GLIB_LIBS@
 
+unit_tests += unit/test-ringbuf unit/test-queue
+
+unit_test_ringbuf_SOURCES = unit/test-ringbuf.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c
+unit_test_ringbuf_LDADD = @GLIB_LIBS@
+
+unit_test_queue_SOURCES = unit/test-queue.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/queue.h src/shared/queue.c
+unit_test_queue_LDADD = @GLIB_LIBS@
+
 unit_tests += unit/test-mgmt
 
 unit_test_mgmt_SOURCES = unit/test-mgmt.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
                                src/shared/util.h src/shared/util.c \
                                src/shared/mgmt.h src/shared/mgmt.c
 unit_test_mgmt_LDADD = @GLIB_LIBS@
@@ -246,6 +266,7 @@ unit_tests += unit/test-sdp
 unit_test_sdp_SOURCES = unit/test-sdp.c \
                                src/shared/util.h src/shared/util.c \
                                src/sdpd.h src/sdpd-database.c \
+                               src/log.h src/log.c \
                                src/sdpd-service.c src/sdpd-request.c
 unit_test_sdp_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 
@@ -257,6 +278,35 @@ unit_test_avdtp_SOURCES = unit/test-avdtp.c \
                                android/avdtp.c android/avdtp.h
 unit_test_avdtp_LDADD = @GLIB_LIBS@
 
+unit_tests += unit/test-avctp
+
+unit_test_avctp_SOURCES = unit/test-avctp.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/log.h src/log.c \
+                               android/avctp.c android/avctp.h
+unit_test_avctp_LDADD = @GLIB_LIBS@
+
+unit_tests += unit/test-avrcp
+
+unit_test_avrcp_SOURCES = unit/test-avrcp.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/log.h src/log.c \
+                               android/avctp.c android/avctp.h \
+                               android/avrcp-lib.c android/avrcp-lib.h
+unit_test_avrcp_LDADD = @GLIB_LIBS@ lib/libbluetooth-internal.la
+
+unit_tests += unit/test-hfp
+
+unit_test_hfp_SOURCES = unit/test-hfp.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/mgmt.h src/shared/mgmt.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c \
+                               src/shared/hfp.h src/shared/hfp.c
+
+unit_test_hfp_LDADD = @GLIB_LIBS@
+
 unit_tests += unit/test-gdbus-client
 
 unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c
@@ -302,8 +352,7 @@ pkgconfig_DATA = lib/bluez.pc
 endif
 
 DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \
-                                       --disable-systemd --disable-udev \
-                                       --enable-android
+                                       --disable-systemd --disable-udev
 
 DISTCLEANFILES = $(pkgconfig_DATA)
 
index 9dfabfc..c0c179f 100644 (file)
@@ -58,7 +58,7 @@ build_triplet = @build@
 host_triplet = @host@
 bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3)
 noinst_PROGRAMS = $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \
-       $(am__EXEEXT_7) $(am__EXEEXT_8)
+       $(am__EXEEXT_7) $(am__EXEEXT_9)
 libexec_PROGRAMS = src/bluetoothd$(EXEEXT) obexd/src/obexd$(EXEEXT)
 @LIBRARY_TRUE@am__append_1 = $(lib_headers)
 @LIBRARY_TRUE@am__append_2 = lib/libbluetooth.la
@@ -113,18 +113,20 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \
 @CLIENT_TRUE@am__append_14 = client/bluetoothctl
 @MONITOR_TRUE@am__append_15 = monitor/btmon
 @EXPERIMENTAL_TRUE@am__append_16 = emulator/btvirt emulator/b1ee \
-@EXPERIMENTAL_TRUE@    tools/mgmt-tester tools/gap-tester \
-@EXPERIMENTAL_TRUE@    tools/l2cap-tester tools/sco-tester \
-@EXPERIMENTAL_TRUE@    tools/smp-tester tools/bdaddr tools/avinfo \
-@EXPERIMENTAL_TRUE@    tools/avtest tools/scotest tools/amptest \
-@EXPERIMENTAL_TRUE@    tools/hwdb tools/hcieventmask \
-@EXPERIMENTAL_TRUE@    tools/hcisecfilter tools/btmgmt \
-@EXPERIMENTAL_TRUE@    tools/btinfo tools/btattach tools/btsnoop \
-@EXPERIMENTAL_TRUE@    tools/btiotest tools/cltest \
-@EXPERIMENTAL_TRUE@    tools/mpris-player
+@EXPERIMENTAL_TRUE@    emulator/hfp tools/3dsp tools/mgmt-tester \
+@EXPERIMENTAL_TRUE@    tools/gap-tester tools/l2cap-tester \
+@EXPERIMENTAL_TRUE@    tools/sco-tester tools/smp-tester \
+@EXPERIMENTAL_TRUE@    tools/hci-tester tools/rfcomm-tester \
+@EXPERIMENTAL_TRUE@    tools/bdaddr tools/avinfo tools/avtest \
+@EXPERIMENTAL_TRUE@    tools/scotest tools/amptest tools/hwdb \
+@EXPERIMENTAL_TRUE@    tools/hcieventmask tools/hcisecfilter \
+@EXPERIMENTAL_TRUE@    tools/btmgmt tools/btinfo tools/btattach \
+@EXPERIMENTAL_TRUE@    tools/btsnoop tools/btproxy tools/btiotest \
+@EXPERIMENTAL_TRUE@    tools/mpris-player tools/cltest \
+@EXPERIMENTAL_TRUE@    tools/seq2bseq tools/hex2hcd tools/ibeacon
 @TOOLS_TRUE@am__append_17 = tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
 @TOOLS_TRUE@                   tools/rfcomm tools/rctest tools/l2test tools/l2ping \
-@TOOLS_TRUE@                   tools/sdptool tools/ciptool tools/bccmd
+@TOOLS_TRUE@                   tools/sdptool tools/ciptool tools/bccmd tools/bluemoon
 
 @TOOLS_TRUE@am__append_18 = tools/hciattach.1 tools/hciconfig.1 \
 @TOOLS_TRUE@                   tools/hcitool.1 tools/hcidump.1 \
@@ -144,7 +146,8 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \
 @READLINE_TRUE@                        tools/obex-client-tool tools/obex-server-tool \
 @READLINE_TRUE@                        tools/bluetooth-player tools/obexctl
 
-@EXPERIMENTAL_TRUE@am__append_24 = profiles/iap/iapd
+@EXPERIMENTAL_TRUE@am__append_24 = tools/gatt-service \
+@EXPERIMENTAL_TRUE@    profiles/iap/iapd
 @CUPS_TRUE@cups_PROGRAMS = profiles/cups/bluetooth$(EXEEXT)
 @EXPERIMENTAL_TRUE@am__append_25 = pcsuite
 @EXPERIMENTAL_TRUE@am__append_26 = obexd/plugins/pcsuite.c
@@ -154,10 +157,15 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \
 @OBEX_TRUE@    obexd/plugins/phonebook.h \
 @OBEX_TRUE@    obexd/plugins/phonebook-dummy.c
 @ANDROID_TRUE@am__append_29 = android/system-emulator \
-@ANDROID_TRUE@ android/bluetoothd android/haltest
-@ANDROID_TRUE@am__append_30 = android/libhal-internal.la
-@HID2HCI_TRUE@am__append_31 = $(rules_DATA)
-TESTS = $(am__EXEEXT_8)
+@ANDROID_TRUE@ android/bluetoothd-snoop android/bluetoothd \
+@ANDROID_TRUE@ android/haltest android/android-tester \
+@ANDROID_TRUE@ android/ipc-tester
+@ANDROID_TRUE@am__append_30 = android/bluetooth.default.la \
+@ANDROID_TRUE@ android/audio.sco.default.la \
+@ANDROID_TRUE@ android/audio.a2dp.default.la
+@ANDROID_TRUE@am__append_31 = android/test-ipc
+@HID2HCI_TRUE@am__append_32 = $(rules_DATA)
+TESTS = $(am__EXEEXT_9)
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
@@ -225,31 +233,75 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" \
        "$(DESTDIR)$(systemduserunitdir)" "$(DESTDIR)$(includedir)"
 LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) \
        $(plugin_LTLIBRARIES)
-android_libhal_internal_la_LIBADD =
-am__android_libhal_internal_la_SOURCES_DIST = android/hal.h \
-       android/hal-bluetooth.c android/hal-sock.c \
-       android/hal-hidhost.c android/hal-pan.c android/hal-a2dp.c \
-       android/hardware/bluetooth.h android/hardware/bt_av.h \
-       android/hardware/bt_gatt.h android/hardware/bt_gatt_client.h \
+android_audio_a2dp_default_la_DEPENDENCIES =
+am__android_audio_a2dp_default_la_SOURCES_DIST = android/audio-msg.h \
+       android/hal-msg.h android/hal-audio.c android/hardware/audio.h \
+       android/hardware/audio_effect.h android/hardware/hardware.h \
+       android/system/audio.h
+@ANDROID_TRUE@am_android_audio_a2dp_default_la_OBJECTS = android/android_audio_a2dp_default_la-hal-audio.lo
+android_audio_a2dp_default_la_OBJECTS =  \
+       $(am_android_audio_a2dp_default_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+android_audio_a2dp_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) \
+       $(android_audio_a2dp_default_la_LDFLAGS) $(LDFLAGS) -o $@
+@ANDROID_TRUE@am_android_audio_a2dp_default_la_rpath = -rpath \
+@ANDROID_TRUE@ $(plugindir)
+android_audio_sco_default_la_DEPENDENCIES =
+am__android_audio_sco_default_la_SOURCES_DIST = android/hal-log.h \
+       android/sco-msg.h android/hal-sco.c android/hardware/audio.h \
+       android/hardware/audio_effect.h android/hardware/hardware.h \
+       android/audio_utils/resampler.c \
+       android/audio_utils/resampler.h android/system/audio.h
+@ANDROID_TRUE@am_android_audio_sco_default_la_OBJECTS =  \
+@ANDROID_TRUE@ android/android_audio_sco_default_la-hal-sco.lo \
+@ANDROID_TRUE@ android/audio_utils/android_audio_sco_default_la-resampler.lo
+android_audio_sco_default_la_OBJECTS =  \
+       $(am_android_audio_sco_default_la_OBJECTS)
+android_audio_sco_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) \
+       $(android_audio_sco_default_la_LDFLAGS) $(LDFLAGS) -o $@
+@ANDROID_TRUE@am_android_audio_sco_default_la_rpath = -rpath \
+@ANDROID_TRUE@ $(plugindir)
+android_bluetooth_default_la_LIBADD =
+am__android_bluetooth_default_la_SOURCES_DIST = android/hal.h \
+       android/hal-bluetooth.c android/hal-socket.c \
+       android/hal-hidhost.c android/hal-health.c android/hal-pan.c \
+       android/hal-a2dp.c android/hal-avrcp.c android/hal-handsfree.c \
+       android/hal-gatt.c android/hardware/bluetooth.h \
+       android/hardware/bt_av.h android/hardware/bt_gatt.h \
+       android/hardware/bt_gatt_client.h \
        android/hardware/bt_gatt_server.h \
        android/hardware/bt_gatt_types.h android/hardware/bt_hf.h \
        android/hardware/bt_hh.h android/hardware/bt_hl.h \
        android/hardware/bt_pan.h android/hardware/bt_rc.h \
        android/hardware/bt_sock.h android/hardware/hardware.h \
-       android/cutils/properties.h android/hal-log.h \
-       android/hal-ipc.h android/hal-ipc.c
-@ANDROID_TRUE@am_android_libhal_internal_la_OBJECTS = android/android_libhal_internal_la-hal-bluetooth.lo \
-@ANDROID_TRUE@ android/android_libhal_internal_la-hal-sock.lo \
-@ANDROID_TRUE@ android/android_libhal_internal_la-hal-hidhost.lo \
-@ANDROID_TRUE@ android/android_libhal_internal_la-hal-pan.lo \
-@ANDROID_TRUE@ android/android_libhal_internal_la-hal-a2dp.lo \
-@ANDROID_TRUE@ android/android_libhal_internal_la-hal-ipc.lo
-android_libhal_internal_la_OBJECTS =  \
-       $(am_android_libhal_internal_la_OBJECTS)
-AM_V_lt = $(am__v_lt_@AM_V@)
-am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
-am__v_lt_0 = --silent
-@ANDROID_TRUE@am_android_libhal_internal_la_rpath =
+       android/cutils/properties.h android/ipc-common.h \
+       android/hal-log.h android/hal-ipc.h android/hal-ipc.c \
+       android/hal-utils.h android/hal-utils.c
+@ANDROID_TRUE@am_android_bluetooth_default_la_OBJECTS = android/android_bluetooth_default_la-hal-bluetooth.lo \
+@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-socket.lo \
+@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-hidhost.lo \
+@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-health.lo \
+@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-pan.lo \
+@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-a2dp.lo \
+@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-avrcp.lo \
+@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-handsfree.lo \
+@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-gatt.lo \
+@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-ipc.lo \
+@ANDROID_TRUE@ android/android_bluetooth_default_la-hal-utils.lo
+android_bluetooth_default_la_OBJECTS =  \
+       $(am_android_bluetooth_default_la_OBJECTS)
+android_bluetooth_default_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) \
+       $(android_bluetooth_default_la_LDFLAGS) $(LDFLAGS) -o $@
+@ANDROID_TRUE@am_android_bluetooth_default_la_rpath = -rpath \
+@ANDROID_TRUE@ $(plugindir)
 gdbus_libgdbus_internal_la_LIBADD =
 am_gdbus_libgdbus_internal_la_OBJECTS = gdbus/mainloop.lo \
        gdbus/watch.lo gdbus/object.lo gdbus/client.lo gdbus/polkit.lo
@@ -304,14 +356,18 @@ plugins_sixaxis_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
 @TOOLS_TRUE@   tools/hcidump$(EXEEXT) tools/rfcomm$(EXEEXT) \
 @TOOLS_TRUE@   tools/rctest$(EXEEXT) tools/l2test$(EXEEXT) \
 @TOOLS_TRUE@   tools/l2ping$(EXEEXT) tools/sdptool$(EXEEXT) \
-@TOOLS_TRUE@   tools/ciptool$(EXEEXT) tools/bccmd$(EXEEXT)
+@TOOLS_TRUE@   tools/ciptool$(EXEEXT) tools/bccmd$(EXEEXT) \
+@TOOLS_TRUE@   tools/bluemoon$(EXEEXT)
 @EXPERIMENTAL_TRUE@am__EXEEXT_4 = emulator/btvirt$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    emulator/b1ee$(EXEEXT) \
+@EXPERIMENTAL_TRUE@    emulator/hfp$(EXEEXT) tools/3dsp$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/mgmt-tester$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/gap-tester$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/l2cap-tester$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/sco-tester$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/smp-tester$(EXEEXT) \
+@EXPERIMENTAL_TRUE@    tools/hci-tester$(EXEEXT) \
+@EXPERIMENTAL_TRUE@    tools/rfcomm-tester$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/bdaddr$(EXEEXT) tools/avinfo$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/avtest$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/scotest$(EXEEXT) \
@@ -321,66 +377,142 @@ plugins_sixaxis_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
 @EXPERIMENTAL_TRUE@    tools/btmgmt$(EXEEXT) tools/btinfo$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/btattach$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/btsnoop$(EXEEXT) \
+@EXPERIMENTAL_TRUE@    tools/btproxy$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/btiotest$(EXEEXT) \
+@EXPERIMENTAL_TRUE@    tools/mpris-player$(EXEEXT) \
 @EXPERIMENTAL_TRUE@    tools/cltest$(EXEEXT) \
-@EXPERIMENTAL_TRUE@    tools/mpris-player$(EXEEXT)
+@EXPERIMENTAL_TRUE@    tools/seq2bseq$(EXEEXT) \
+@EXPERIMENTAL_TRUE@    tools/hex2hcd$(EXEEXT) \
+@EXPERIMENTAL_TRUE@    tools/ibeacon$(EXEEXT)
 @READLINE_TRUE@am__EXEEXT_5 = attrib/gatttool$(EXEEXT) \
 @READLINE_TRUE@        tools/obex-client-tool$(EXEEXT) \
 @READLINE_TRUE@        tools/obex-server-tool$(EXEEXT) \
 @READLINE_TRUE@        tools/bluetooth-player$(EXEEXT) \
 @READLINE_TRUE@        tools/obexctl$(EXEEXT)
-@EXPERIMENTAL_TRUE@am__EXEEXT_6 = profiles/iap/iapd$(EXEEXT)
+@EXPERIMENTAL_TRUE@am__EXEEXT_6 = tools/gatt-service$(EXEEXT) \
+@EXPERIMENTAL_TRUE@    profiles/iap/iapd$(EXEEXT)
 @ANDROID_TRUE@am__EXEEXT_7 = android/system-emulator$(EXEEXT) \
+@ANDROID_TRUE@ android/bluetoothd-snoop$(EXEEXT) \
 @ANDROID_TRUE@ android/bluetoothd$(EXEEXT) \
-@ANDROID_TRUE@ android/haltest$(EXEEXT)
-am__EXEEXT_8 = unit/test-eir$(EXEEXT) unit/test-uuid$(EXEEXT) \
-       unit/test-textfile$(EXEEXT) unit/test-crc$(EXEEXT) \
-       unit/test-mgmt$(EXEEXT) unit/test-sdp$(EXEEXT) \
-       unit/test-avdtp$(EXEEXT) unit/test-gdbus-client$(EXEEXT) \
+@ANDROID_TRUE@ android/haltest$(EXEEXT) \
+@ANDROID_TRUE@ android/android-tester$(EXEEXT) \
+@ANDROID_TRUE@ android/ipc-tester$(EXEEXT)
+@ANDROID_TRUE@am__EXEEXT_8 = android/test-ipc$(EXEEXT)
+am__EXEEXT_9 = $(am__EXEEXT_8) unit/test-eir$(EXEEXT) \
+       unit/test-uuid$(EXEEXT) unit/test-textfile$(EXEEXT) \
+       unit/test-crc$(EXEEXT) unit/test-ringbuf$(EXEEXT) \
+       unit/test-queue$(EXEEXT) unit/test-mgmt$(EXEEXT) \
+       unit/test-sdp$(EXEEXT) unit/test-avdtp$(EXEEXT) \
+       unit/test-avctp$(EXEEXT) unit/test-avrcp$(EXEEXT) \
+       unit/test-hfp$(EXEEXT) unit/test-gdbus-client$(EXEEXT) \
        unit/test-gobex-header$(EXEEXT) \
        unit/test-gobex-packet$(EXEEXT) unit/test-gobex$(EXEEXT) \
        unit/test-gobex-transfer$(EXEEXT) \
        unit/test-gobex-apparam$(EXEEXT) unit/test-lib$(EXEEXT)
 PROGRAMS = $(bin_PROGRAMS) $(cups_PROGRAMS) $(libexec_PROGRAMS) \
        $(noinst_PROGRAMS) $(udev_PROGRAMS)
+am__android_android_tester_SOURCES_DIST = emulator/btdev.h \
+       emulator/btdev.c emulator/bthost.h emulator/bthost.c \
+       emulator/smp.c src/shared/crypto.h src/shared/crypto.c \
+       src/shared/io.h src/shared/io-glib.c src/shared/queue.h \
+       src/shared/queue.c src/shared/util.h src/shared/util.c \
+       src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \
+       src/shared/hciemu.c src/shared/tester.h src/shared/tester.c \
+       src/shared/timeout.h src/shared/timeout-glib.c \
+       monitor/rfcomm.h android/hardware/hardware.c \
+       android/android-tester.c
+@ANDROID_TRUE@am_android_android_tester_OBJECTS =  \
+@ANDROID_TRUE@ emulator/android_android_tester-btdev.$(OBJEXT) \
+@ANDROID_TRUE@ emulator/android_android_tester-bthost.$(OBJEXT) \
+@ANDROID_TRUE@ emulator/android_android_tester-smp.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_android_tester-crypto.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_android_tester-io-glib.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_android_tester-queue.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_android_tester-util.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_android_tester-mgmt.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_android_tester-hciemu.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_android_tester-tester.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_android_tester-timeout-glib.$(OBJEXT) \
+@ANDROID_TRUE@ android/hardware/android_android_tester-hardware.$(OBJEXT) \
+@ANDROID_TRUE@ android/android_android_tester-android-tester.$(OBJEXT)
+android_android_tester_OBJECTS = $(am_android_android_tester_OBJECTS)
+@ANDROID_TRUE@android_android_tester_DEPENDENCIES =  \
+@ANDROID_TRUE@ lib/libbluetooth-internal.la
+android_android_tester_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(android_android_tester_CFLAGS) $(CFLAGS) \
+       $(android_android_tester_LDFLAGS) $(LDFLAGS) -o $@
 am__android_bluetoothd_SOURCES_DIST = android/main.c src/log.c \
-       android/hal-msg.h android/utils.h src/sdpd-database.c \
-       src/sdpd-server.c src/sdpd-service.c src/sdpd-request.c \
-       src/glib-helper.h src/glib-helper.c src/eir.h src/eir.c \
+       android/hal-msg.h android/audio-msg.h android/sco-msg.h \
+       android/utils.h src/sdpd-database.c src/sdpd-server.c \
+       src/sdpd-service.c src/sdpd-request.c src/uuid-helper.h \
+       src/uuid-helper.c src/eir.h src/eir.c src/shared/io.h \
+       src/shared/io-glib.c src/shared/queue.h src/shared/queue.c \
        src/shared/util.h src/shared/util.c src/shared/mgmt.h \
-       src/shared/mgmt.c android/bluetooth.h android/bluetooth.c \
-       android/hidhost.h android/hidhost.c android/ipc.h \
-       android/ipc.c android/avdtp.h android/avdtp.c android/a2dp.h \
-       android/a2dp.c android/socket.h android/socket.c android/pan.h \
-       android/pan.c btio/btio.h btio/btio.c src/sdp-client.h \
-       src/sdp-client.c profiles/network/bnep.h \
-       profiles/network/bnep.c
+       src/shared/mgmt.c src/shared/ringbuf.h src/shared/ringbuf.c \
+       src/shared/hfp.h src/shared/hfp.c src/shared/gatt-db.h \
+       src/shared/gatt-db.c android/bluetooth.h android/bluetooth.c \
+       android/hidhost.h android/hidhost.c android/ipc-common.h \
+       android/ipc.h android/ipc.c android/avdtp.h android/avdtp.c \
+       android/a2dp.h android/a2dp.c android/avctp.h android/avctp.c \
+       android/avrcp.h android/avrcp.c android/avrcp-lib.h \
+       android/avrcp-lib.c android/socket.h android/socket.c \
+       android/pan.h android/pan.c android/handsfree.h \
+       android/handsfree.c android/gatt.h android/gatt.c \
+       android/health.h android/health.c android/mcap-lib.h \
+       android/mcap-lib.c attrib/att.c attrib/att.h attrib/gatt.c \
+       attrib/gatt.h attrib/gattrib.c attrib/gattrib.h btio/btio.h \
+       btio/btio.c src/sdp-client.h src/sdp-client.c \
+       profiles/network/bnep.h profiles/network/bnep.c
 @ANDROID_TRUE@am_android_bluetoothd_OBJECTS = android/main.$(OBJEXT) \
 @ANDROID_TRUE@ src/log.$(OBJEXT) src/sdpd-database.$(OBJEXT) \
 @ANDROID_TRUE@ src/sdpd-server.$(OBJEXT) \
 @ANDROID_TRUE@ src/sdpd-service.$(OBJEXT) \
 @ANDROID_TRUE@ src/sdpd-request.$(OBJEXT) \
-@ANDROID_TRUE@ src/glib-helper.$(OBJEXT) src/eir.$(OBJEXT) \
+@ANDROID_TRUE@ src/uuid-helper.$(OBJEXT) src/eir.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/io-glib.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/queue.$(OBJEXT) \
 @ANDROID_TRUE@ src/shared/util.$(OBJEXT) \
 @ANDROID_TRUE@ src/shared/mgmt.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/ringbuf.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/hfp.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/gatt-db.$(OBJEXT) \
 @ANDROID_TRUE@ android/bluetooth.$(OBJEXT) \
 @ANDROID_TRUE@ android/hidhost.$(OBJEXT) android/ipc.$(OBJEXT) \
 @ANDROID_TRUE@ android/avdtp.$(OBJEXT) android/a2dp.$(OBJEXT) \
+@ANDROID_TRUE@ android/avctp.$(OBJEXT) android/avrcp.$(OBJEXT) \
+@ANDROID_TRUE@ android/avrcp-lib.$(OBJEXT) \
 @ANDROID_TRUE@ android/socket.$(OBJEXT) android/pan.$(OBJEXT) \
+@ANDROID_TRUE@ android/handsfree.$(OBJEXT) \
+@ANDROID_TRUE@ android/gatt.$(OBJEXT) android/health.$(OBJEXT) \
+@ANDROID_TRUE@ android/mcap-lib.$(OBJEXT) attrib/att.$(OBJEXT) \
+@ANDROID_TRUE@ attrib/gatt.$(OBJEXT) attrib/gattrib.$(OBJEXT) \
 @ANDROID_TRUE@ btio/btio.$(OBJEXT) src/sdp-client.$(OBJEXT) \
 @ANDROID_TRUE@ profiles/network/bnep.$(OBJEXT)
 android_bluetoothd_OBJECTS = $(am_android_bluetoothd_OBJECTS)
 @ANDROID_TRUE@android_bluetoothd_DEPENDENCIES =  \
 @ANDROID_TRUE@ lib/libbluetooth-internal.la
+am__android_bluetoothd_snoop_SOURCES_DIST =  \
+       android/bluetoothd-snoop.c monitor/mainloop.h \
+       monitor/mainloop.c src/shared/btsnoop.h src/shared/btsnoop.c
+@ANDROID_TRUE@am_android_bluetoothd_snoop_OBJECTS =  \
+@ANDROID_TRUE@ android/bluetoothd-snoop.$(OBJEXT) \
+@ANDROID_TRUE@ monitor/mainloop.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/btsnoop.$(OBJEXT)
+android_bluetoothd_snoop_OBJECTS =  \
+       $(am_android_bluetoothd_snoop_OBJECTS)
+android_bluetoothd_snoop_LDADD = $(LDADD)
 am__android_haltest_SOURCES_DIST = android/client/haltest.c \
        android/client/pollhandler.h android/client/pollhandler.c \
        android/client/terminal.h android/client/terminal.c \
        android/client/history.h android/client/history.c \
        android/client/tabcompletion.c android/client/if-main.h \
-       android/client/if-av.c android/client/if-bt.c \
-       android/client/if-gatt.c android/client/if-hf.c \
-       android/client/if-hh.c android/client/if-pan.c \
-       android/client/if-sock.c android/client/hwmodule.c \
+       android/client/if-av.c android/client/if-rc.c \
+       android/client/if-bt.c android/client/if-gatt.c \
+       android/client/if-hf.c android/client/if-hh.c \
+       android/client/if-pan.c android/client/if-hl.c \
+       android/client/if-sock.c android/client/if-audio.c \
+       android/client/if-sco.c android/hardware/hardware.c \
        android/hal-utils.h android/hal-utils.c
 @ANDROID_TRUE@am_android_haltest_OBJECTS = android/client/android_haltest-haltest.$(OBJEXT) \
 @ANDROID_TRUE@ android/client/android_haltest-pollhandler.$(OBJEXT) \
@@ -388,21 +520,55 @@ am__android_haltest_SOURCES_DIST = android/client/haltest.c \
 @ANDROID_TRUE@ android/client/android_haltest-history.$(OBJEXT) \
 @ANDROID_TRUE@ android/client/android_haltest-tabcompletion.$(OBJEXT) \
 @ANDROID_TRUE@ android/client/android_haltest-if-av.$(OBJEXT) \
+@ANDROID_TRUE@ android/client/android_haltest-if-rc.$(OBJEXT) \
 @ANDROID_TRUE@ android/client/android_haltest-if-bt.$(OBJEXT) \
 @ANDROID_TRUE@ android/client/android_haltest-if-gatt.$(OBJEXT) \
 @ANDROID_TRUE@ android/client/android_haltest-if-hf.$(OBJEXT) \
 @ANDROID_TRUE@ android/client/android_haltest-if-hh.$(OBJEXT) \
 @ANDROID_TRUE@ android/client/android_haltest-if-pan.$(OBJEXT) \
+@ANDROID_TRUE@ android/client/android_haltest-if-hl.$(OBJEXT) \
 @ANDROID_TRUE@ android/client/android_haltest-if-sock.$(OBJEXT) \
-@ANDROID_TRUE@ android/client/android_haltest-hwmodule.$(OBJEXT) \
+@ANDROID_TRUE@ android/client/android_haltest-if-audio.$(OBJEXT) \
+@ANDROID_TRUE@ android/client/android_haltest-if-sco.$(OBJEXT) \
+@ANDROID_TRUE@ android/hardware/android_haltest-hardware.$(OBJEXT) \
 @ANDROID_TRUE@ android/android_haltest-hal-utils.$(OBJEXT)
 android_haltest_OBJECTS = $(am_android_haltest_OBJECTS)
-@ANDROID_TRUE@android_haltest_DEPENDENCIES =  \
-@ANDROID_TRUE@ android/libhal-internal.la
+android_haltest_LDADD = $(LDADD)
 android_haltest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
        $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
        $(android_haltest_CFLAGS) $(CFLAGS) $(android_haltest_LDFLAGS) \
        $(LDFLAGS) -o $@
+am__android_ipc_tester_SOURCES_DIST = emulator/btdev.h \
+       emulator/btdev.c emulator/bthost.h emulator/bthost.c \
+       emulator/smp.c src/shared/crypto.h src/shared/crypto.c \
+       src/shared/io.h src/shared/io-glib.c src/shared/queue.h \
+       src/shared/queue.c src/shared/util.h src/shared/util.c \
+       src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \
+       src/shared/hciemu.c src/shared/tester.h src/shared/tester.c \
+       src/shared/timeout.h src/shared/timeout-glib.c \
+       android/hal-utils.h android/hal-utils.c android/ipc-common.h \
+       android/ipc-tester.c
+@ANDROID_TRUE@am_android_ipc_tester_OBJECTS =  \
+@ANDROID_TRUE@ emulator/android_ipc_tester-btdev.$(OBJEXT) \
+@ANDROID_TRUE@ emulator/android_ipc_tester-bthost.$(OBJEXT) \
+@ANDROID_TRUE@ emulator/android_ipc_tester-smp.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_ipc_tester-crypto.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_ipc_tester-io-glib.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_ipc_tester-queue.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_ipc_tester-util.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_ipc_tester-mgmt.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_ipc_tester-hciemu.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_ipc_tester-tester.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/android_ipc_tester-timeout-glib.$(OBJEXT) \
+@ANDROID_TRUE@ android/android_ipc_tester-hal-utils.$(OBJEXT) \
+@ANDROID_TRUE@ android/android_ipc_tester-ipc-tester.$(OBJEXT)
+android_ipc_tester_OBJECTS = $(am_android_ipc_tester_OBJECTS)
+@ANDROID_TRUE@android_ipc_tester_DEPENDENCIES =  \
+@ANDROID_TRUE@ lib/libbluetooth-internal.la
+android_ipc_tester_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(android_ipc_tester_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+       $(LDFLAGS) -o $@
 am__android_system_emulator_SOURCES_DIST = android/system-emulator.c \
        monitor/mainloop.h monitor/mainloop.c
 @ANDROID_TRUE@am_android_system_emulator_OBJECTS =  \
@@ -411,6 +577,15 @@ am__android_system_emulator_SOURCES_DIST = android/system-emulator.c \
 android_system_emulator_OBJECTS =  \
        $(am_android_system_emulator_OBJECTS)
 android_system_emulator_LDADD = $(LDADD)
+am__android_test_ipc_SOURCES_DIST = android/test-ipc.c \
+       src/shared/util.h src/shared/util.c src/log.h src/log.c \
+       android/ipc-common.h android/ipc.c android/ipc.h
+@ANDROID_TRUE@am_android_test_ipc_OBJECTS =  \
+@ANDROID_TRUE@ android/test-ipc.$(OBJEXT) \
+@ANDROID_TRUE@ src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \
+@ANDROID_TRUE@ android/ipc.$(OBJEXT)
+android_test_ipc_OBJECTS = $(am_android_test_ipc_OBJECTS)
+android_test_ipc_DEPENDENCIES =
 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 client/display.c \
@@ -440,40 +615,72 @@ am__emulator_b1ee_SOURCES_DIST = emulator/b1ee.c monitor/mainloop.h \
 emulator_b1ee_OBJECTS = $(am_emulator_b1ee_OBJECTS)
 emulator_b1ee_LDADD = $(LDADD)
 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 emulator/bthost.h \
-       emulator/bthost.c emulator/amp.h emulator/amp.c
+       monitor/mainloop.h monitor/mainloop.c src/shared/timeout.h \
+       src/shared/timeout-mainloop.c src/shared/util.h \
+       src/shared/util.c src/shared/crypto.h src/shared/crypto.c \
+       emulator/server.h emulator/server.c emulator/vhci.h \
+       emulator/vhci.c emulator/btdev.h emulator/btdev.c \
+       emulator/bthost.h emulator/bthost.c emulator/smp.c \
+       emulator/amp.h emulator/amp.c emulator/le.h emulator/le.c
 @EXPERIMENTAL_TRUE@am_emulator_btvirt_OBJECTS =  \
 @EXPERIMENTAL_TRUE@    emulator/main.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/timeout-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/crypto.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/server.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/vhci.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/btdev.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/bthost.$(OBJEXT) \
-@EXPERIMENTAL_TRUE@    emulator/amp.$(OBJEXT)
+@EXPERIMENTAL_TRUE@    emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    emulator/amp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    emulator/le.$(OBJEXT)
 emulator_btvirt_OBJECTS = $(am_emulator_btvirt_OBJECTS)
-emulator_btvirt_LDADD = $(LDADD)
+@EXPERIMENTAL_TRUE@emulator_btvirt_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
+am__emulator_hfp_SOURCES_DIST = emulator/hfp.c monitor/mainloop.h \
+       monitor/mainloop.c src/shared/io.h src/shared/io-mainloop.c \
+       src/shared/util.h src/shared/util.c src/shared/queue.h \
+       src/shared/queue.c src/shared/ringbuf.h src/shared/ringbuf.c \
+       src/shared/hfp.h src/shared/hfp.c
+@EXPERIMENTAL_TRUE@am_emulator_hfp_OBJECTS = emulator/hfp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/ringbuf.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/hfp.$(OBJEXT)
+emulator_hfp_OBJECTS = $(am_emulator_hfp_OBJECTS)
+emulator_hfp_LDADD = $(LDADD)
 am__monitor_btmon_SOURCES_DIST = monitor/main.c monitor/bt.h \
        monitor/mainloop.h monitor/mainloop.c monitor/display.h \
        monitor/display.c monitor/hcidump.h monitor/hcidump.c \
-       monitor/btsnoop.h monitor/btsnoop.c monitor/control.h \
+       monitor/ellisys.h monitor/ellisys.c monitor/control.h \
        monitor/control.c monitor/packet.h monitor/packet.c \
        monitor/vendor.h monitor/vendor.c monitor/lmp.h monitor/lmp.c \
-       monitor/l2cap.h monitor/l2cap.c monitor/uuid.h monitor/uuid.c \
-       monitor/sdp.h monitor/sdp.c monitor/crc.h monitor/crc.c \
-       monitor/ll.h monitor/ll.c
+       monitor/crc.h monitor/crc.c monitor/ll.h monitor/ll.c \
+       monitor/l2cap.h monitor/l2cap.c monitor/sdp.h monitor/sdp.c \
+       monitor/uuid.h monitor/uuid.c monitor/hwdb.h monitor/hwdb.c \
+       monitor/keys.h monitor/keys.c monitor/analyze.h \
+       monitor/analyze.c src/shared/util.h src/shared/util.c \
+       src/shared/queue.h src/shared/queue.c src/shared/crypto.h \
+       src/shared/crypto.c src/shared/btsnoop.h src/shared/btsnoop.c
 @MONITOR_TRUE@am_monitor_btmon_OBJECTS = monitor/main.$(OBJEXT) \
 @MONITOR_TRUE@ monitor/mainloop.$(OBJEXT) \
 @MONITOR_TRUE@ monitor/display.$(OBJEXT) \
 @MONITOR_TRUE@ monitor/hcidump.$(OBJEXT) \
-@MONITOR_TRUE@ monitor/btsnoop.$(OBJEXT) \
+@MONITOR_TRUE@ monitor/ellisys.$(OBJEXT) \
 @MONITOR_TRUE@ monitor/control.$(OBJEXT) \
 @MONITOR_TRUE@ monitor/packet.$(OBJEXT) \
 @MONITOR_TRUE@ monitor/vendor.$(OBJEXT) monitor/lmp.$(OBJEXT) \
-@MONITOR_TRUE@ monitor/l2cap.$(OBJEXT) monitor/uuid.$(OBJEXT) \
-@MONITOR_TRUE@ monitor/sdp.$(OBJEXT) monitor/crc.$(OBJEXT) \
-@MONITOR_TRUE@ monitor/ll.$(OBJEXT)
+@MONITOR_TRUE@ monitor/crc.$(OBJEXT) monitor/ll.$(OBJEXT) \
+@MONITOR_TRUE@ monitor/l2cap.$(OBJEXT) monitor/sdp.$(OBJEXT) \
+@MONITOR_TRUE@ monitor/uuid.$(OBJEXT) monitor/hwdb.$(OBJEXT) \
+@MONITOR_TRUE@ monitor/keys.$(OBJEXT) monitor/analyze.$(OBJEXT) \
+@MONITOR_TRUE@ src/shared/util.$(OBJEXT) \
+@MONITOR_TRUE@ src/shared/queue.$(OBJEXT) \
+@MONITOR_TRUE@ src/shared/crypto.$(OBJEXT) \
+@MONITOR_TRUE@ src/shared/btsnoop.$(OBJEXT)
 monitor_btmon_OBJECTS = $(am_monitor_btmon_OBJECTS)
 @MONITOR_TRUE@monitor_btmon_DEPENDENCIES =  \
 @MONITOR_TRUE@ lib/libbluetooth-internal.la
@@ -603,23 +810,23 @@ am__src_bluetoothd_SOURCES_DIST = plugins/hostname.c plugins/wiimote.c \
        profiles/network/connection.c profiles/input/manager.c \
        profiles/input/server.h profiles/input/server.c \
        profiles/input/device.h profiles/input/device.c \
-       profiles/input/hog.c profiles/input/uhid_copy.h \
-       profiles/input/suspend.h profiles/input/suspend-dummy.c \
-       profiles/health/mcap_lib.h profiles/health/mcap_internal.h \
-       profiles/health/mcap.h profiles/health/mcap.c \
-       profiles/health/mcap_sync.c profiles/health/hdp_main.c \
-       profiles/health/hdp_types.h profiles/health/hdp_manager.h \
-       profiles/health/hdp_manager.c profiles/health/hdp.h \
-       profiles/health/hdp.c profiles/health/hdp_util.h \
-       profiles/health/hdp_util.c profiles/gatt/gas.c \
-       profiles/scanparam/scan.c profiles/deviceinfo/deviceinfo.c \
-       profiles/alert/server.c profiles/time/server.c \
-       profiles/proximity/main.c profiles/proximity/manager.h \
-       profiles/proximity/manager.c profiles/proximity/monitor.h \
-       profiles/proximity/monitor.c profiles/proximity/reporter.h \
-       profiles/proximity/reporter.c profiles/proximity/linkloss.h \
-       profiles/proximity/linkloss.c profiles/proximity/immalert.h \
-       profiles/proximity/immalert.c \
+       profiles/input/uhid_copy.h profiles/input/hidp_defs.h \
+       profiles/input/hog.c profiles/input/suspend.h \
+       profiles/input/suspend-dummy.c profiles/health/mcap_lib.h \
+       profiles/health/mcap_internal.h profiles/health/mcap.h \
+       profiles/health/mcap.c profiles/health/mcap_sync.c \
+       profiles/health/hdp_main.c profiles/health/hdp_types.h \
+       profiles/health/hdp_manager.h profiles/health/hdp_manager.c \
+       profiles/health/hdp.h profiles/health/hdp.c \
+       profiles/health/hdp_util.h profiles/health/hdp_util.c \
+       profiles/gatt/gas.c profiles/scanparam/scan.c \
+       profiles/deviceinfo/deviceinfo.c profiles/alert/server.c \
+       profiles/time/server.c profiles/proximity/main.c \
+       profiles/proximity/manager.h profiles/proximity/manager.c \
+       profiles/proximity/monitor.h profiles/proximity/monitor.c \
+       profiles/proximity/reporter.h profiles/proximity/reporter.c \
+       profiles/proximity/linkloss.h profiles/proximity/linkloss.c \
+       profiles/proximity/immalert.h profiles/proximity/immalert.c \
        profiles/thermometer/thermometer.c \
        profiles/heartrate/heartrate.c \
        profiles/cyclingspeed/cyclingspeed.c attrib/att.h \
@@ -631,14 +838,18 @@ am__src_bluetoothd_SOURCES_DIST = plugins/hostname.c plugins/wiimote.c \
        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/uinput.h src/plugin.h src/plugin.c \
+       src/textfile.h src/textfile.c src/uuid-helper.h \
+       src/uuid-helper.c src/uinput.h src/plugin.h src/plugin.c \
        src/storage.h src/storage.c src/agent.h src/agent.c \
        src/error.h src/error.c src/adapter.h src/adapter.c \
        src/profile.h src/profile.c src/service.h src/service.c \
+       src/gatt-dbus.h src/gatt-dbus.c src/gatt.h src/gatt.c \
        src/device.h src/device.c src/attio.h src/dbus-common.c \
-       src/dbus-common.h src/eir.h src/eir.c src/shared/util.h \
-       src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c
+       src/dbus-common.h src/eir.h src/eir.c src/shared/io.h \
+       src/shared/io-glib.c src/shared/timeout.h \
+       src/shared/timeout-glib.c src/shared/queue.h \
+       src/shared/queue.c src/shared/util.h src/shared/util.c \
+       src/shared/mgmt.h src/shared/mgmt.c
 @MAINTAINER_MODE_TRUE@am__objects_10 = plugins/bluetoothd-gatt-example.$(OBJEXT)
 @EXPERIMENTAL_TRUE@am__objects_11 =  \
 @EXPERIMENTAL_TRUE@    plugins/bluetoothd-neard.$(OBJEXT) \
@@ -709,16 +920,20 @@ am_src_bluetoothd_OBJECTS = $(am__objects_14) $(am__objects_15) \
        src/bluetoothd-sdp-xml.$(OBJEXT) \
        src/bluetoothd-sdp-client.$(OBJEXT) \
        src/bluetoothd-textfile.$(OBJEXT) \
-       src/bluetoothd-glib-helper.$(OBJEXT) \
+       src/bluetoothd-uuid-helper.$(OBJEXT) \
        src/bluetoothd-plugin.$(OBJEXT) \
        src/bluetoothd-storage.$(OBJEXT) \
        src/bluetoothd-agent.$(OBJEXT) src/bluetoothd-error.$(OBJEXT) \
        src/bluetoothd-adapter.$(OBJEXT) \
        src/bluetoothd-profile.$(OBJEXT) \
        src/bluetoothd-service.$(OBJEXT) \
-       src/bluetoothd-device.$(OBJEXT) \
+       src/bluetoothd-gatt-dbus.$(OBJEXT) \
+       src/bluetoothd-gatt.$(OBJEXT) src/bluetoothd-device.$(OBJEXT) \
        src/bluetoothd-dbus-common.$(OBJEXT) \
        src/bluetoothd-eir.$(OBJEXT) \
+       src/shared/bluetoothd-io-glib.$(OBJEXT) \
+       src/shared/bluetoothd-timeout-glib.$(OBJEXT) \
+       src/shared/bluetoothd-queue.$(OBJEXT) \
        src/shared/bluetoothd-util.$(OBJEXT) \
        src/shared/bluetoothd-mgmt.$(OBJEXT)
 nodist_src_bluetoothd_OBJECTS = $(am__objects_9)
@@ -728,6 +943,23 @@ src_bluetoothd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
        $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
        $(src_bluetoothd_CFLAGS) $(CFLAGS) $(src_bluetoothd_LDFLAGS) \
        $(LDFLAGS) -o $@
+am__tools_3dsp_SOURCES_DIST = tools/3dsp.c monitor/bt.h \
+       monitor/mainloop.h monitor/mainloop.c src/shared/io.h \
+       src/shared/io-mainloop.c src/shared/timeout.h \
+       src/shared/timeout-mainloop.c src/shared/hci.h \
+       src/shared/hci.c src/shared/util.h src/shared/util.c \
+       src/shared/queue.h src/shared/queue.c src/shared/ringbuf.h \
+       src/shared/ringbuf.c
+@EXPERIMENTAL_TRUE@am_tools_3dsp_OBJECTS = tools/3dsp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/timeout-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/hci.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/ringbuf.$(OBJEXT)
+tools_3dsp_OBJECTS = $(am_tools_3dsp_OBJECTS)
+tools_3dsp_LDADD = $(LDADD)
 tools_amptest_SOURCES = tools/amptest.c
 tools_amptest_OBJECTS = tools/amptest.$(OBJEXT)
 @EXPERIMENTAL_TRUE@tools_amptest_DEPENDENCIES =  \
@@ -756,6 +988,19 @@ am__tools_bdaddr_SOURCES_DIST = tools/bdaddr.c src/oui.h src/oui.c
 tools_bdaddr_OBJECTS = $(am_tools_bdaddr_OBJECTS)
 @EXPERIMENTAL_TRUE@tools_bdaddr_DEPENDENCIES =  \
 @EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
+am__tools_bluemoon_SOURCES_DIST = tools/bluemoon.c monitor/bt.h \
+       monitor/mainloop.h monitor/mainloop.c src/shared/io.h \
+       src/shared/io-mainloop.c src/shared/hci.h src/shared/hci.c \
+       src/shared/util.h src/shared/util.c src/shared/queue.h \
+       src/shared/queue.c src/shared/ringbuf.h src/shared/ringbuf.c
+@TOOLS_TRUE@am_tools_bluemoon_OBJECTS = tools/bluemoon.$(OBJEXT) \
+@TOOLS_TRUE@   monitor/mainloop.$(OBJEXT) \
+@TOOLS_TRUE@   src/shared/io-mainloop.$(OBJEXT) \
+@TOOLS_TRUE@   src/shared/hci.$(OBJEXT) src/shared/util.$(OBJEXT) \
+@TOOLS_TRUE@   src/shared/queue.$(OBJEXT) \
+@TOOLS_TRUE@   src/shared/ringbuf.$(OBJEXT)
+tools_bluemoon_OBJECTS = $(am_tools_bluemoon_OBJECTS)
+tools_bluemoon_LDADD = $(LDADD)
 am__tools_bluetooth_player_SOURCES_DIST = tools/bluetooth-player.c \
        client/display.h client/display.c
 @READLINE_TRUE@am_tools_bluetooth_player_OBJECTS =  \
@@ -767,8 +1012,21 @@ tools_bluetooth_player_OBJECTS = $(am_tools_bluetooth_player_OBJECTS)
 tools_btattach_SOURCES = tools/btattach.c
 tools_btattach_OBJECTS = tools/btattach.$(OBJEXT)
 tools_btattach_LDADD = $(LDADD)
-am__tools_btinfo_SOURCES_DIST = tools/btinfo.c
-@EXPERIMENTAL_TRUE@am_tools_btinfo_OBJECTS = tools/btinfo.$(OBJEXT)
+am__tools_btinfo_SOURCES_DIST = tools/btinfo.c monitor/bt.h \
+       monitor/mainloop.h monitor/mainloop.c src/shared/io.h \
+       src/shared/io-mainloop.c src/shared/timeout.h \
+       src/shared/timeout-mainloop.c src/shared/hci.h \
+       src/shared/hci.c src/shared/util.h src/shared/util.c \
+       src/shared/queue.h src/shared/queue.c src/shared/ringbuf.h \
+       src/shared/ringbuf.c
+@EXPERIMENTAL_TRUE@am_tools_btinfo_OBJECTS = tools/btinfo.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/timeout-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/hci.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/ringbuf.$(OBJEXT)
 tools_btinfo_OBJECTS = $(am_tools_btinfo_OBJECTS)
 tools_btinfo_LDADD = $(LDADD)
 am__tools_btiotest_SOURCES_DIST = tools/btiotest.c btio/btio.h \
@@ -779,16 +1037,29 @@ am__tools_btiotest_SOURCES_DIST = tools/btiotest.c btio/btio.h \
 tools_btiotest_OBJECTS = $(am_tools_btiotest_OBJECTS)
 @EXPERIMENTAL_TRUE@tools_btiotest_DEPENDENCIES =  \
 @EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
-am__tools_btmgmt_SOURCES_DIST = tools/btmgmt.c src/glib-helper.c \
-       src/eir.c src/shared/util.h src/shared/util.c \
-       src/shared/mgmt.h src/shared/mgmt.c
+am__tools_btmgmt_SOURCES_DIST = tools/btmgmt.c src/uuid-helper.c \
+       monitor/mainloop.h monitor/mainloop.c src/shared/io.h \
+       src/shared/io-mainloop.c src/shared/queue.h src/shared/queue.c \
+       src/shared/util.h src/shared/util.c src/shared/mgmt.h \
+       src/shared/mgmt.c
 @EXPERIMENTAL_TRUE@am_tools_btmgmt_OBJECTS = tools/btmgmt.$(OBJEXT) \
-@EXPERIMENTAL_TRUE@    src/glib-helper.$(OBJEXT) src/eir.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/uuid-helper.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/mgmt.$(OBJEXT)
 tools_btmgmt_OBJECTS = $(am_tools_btmgmt_OBJECTS)
 @EXPERIMENTAL_TRUE@tools_btmgmt_DEPENDENCIES =  \
 @EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
+am__tools_btproxy_SOURCES_DIST = tools/btproxy.c monitor/bt.h \
+       monitor/mainloop.h monitor/mainloop.c src/shared/util.h \
+       src/shared/util.c
+@EXPERIMENTAL_TRUE@am_tools_btproxy_OBJECTS = tools/btproxy.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT)
+tools_btproxy_OBJECTS = $(am_tools_btproxy_OBJECTS)
+tools_btproxy_LDADD = $(LDADD)
 am__tools_btsnoop_SOURCES_DIST = tools/btsnoop.c src/shared/pcap.h \
        src/shared/pcap.c src/shared/btsnoop.h src/shared/btsnoop.c
 @EXPERIMENTAL_TRUE@am_tools_btsnoop_OBJECTS = tools/btsnoop.$(OBJEXT) \
@@ -808,28 +1079,60 @@ tools_cltest_OBJECTS = $(am_tools_cltest_OBJECTS)
 @EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
 am__tools_gap_tester_SOURCES_DIST = tools/gap-tester.c monitor/bt.h \
        emulator/btdev.h emulator/btdev.c emulator/bthost.h \
-       emulator/bthost.c src/shared/hciemu.h src/shared/hciemu.c \
-       src/shared/tester.h src/shared/tester.c
+       emulator/bthost.c emulator/smp.c src/shared/crypto.h \
+       src/shared/crypto.c src/shared/util.h src/shared/util.c \
+       src/shared/queue.h src/shared/queue.c src/shared/hciemu.h \
+       src/shared/hciemu.c src/shared/tester.h src/shared/tester.c \
+       src/shared/timeout.h src/shared/timeout-glib.c
 @EXPERIMENTAL_TRUE@am_tools_gap_tester_OBJECTS =  \
 @EXPERIMENTAL_TRUE@    tools/gap-tester.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/btdev.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/hciemu.$(OBJEXT) \
-@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT)
+@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/timeout-glib.$(OBJEXT)
 tools_gap_tester_OBJECTS = $(am_tools_gap_tester_OBJECTS)
 @EXPERIMENTAL_TRUE@tools_gap_tester_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la \
 @EXPERIMENTAL_TRUE@    gdbus/libgdbus-internal.la
+am__tools_gatt_service_SOURCES_DIST = tools/gatt-service.c
+@EXPERIMENTAL_TRUE@am_tools_gatt_service_OBJECTS =  \
+@EXPERIMENTAL_TRUE@    tools/gatt-service.$(OBJEXT)
+tools_gatt_service_OBJECTS = $(am_tools_gatt_service_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_gatt_service_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@    gdbus/libgdbus-internal.la
+am__tools_hci_tester_SOURCES_DIST = tools/hci-tester.c monitor/bt.h \
+       src/shared/io.h src/shared/io-glib.c src/shared/hci.h \
+       src/shared/hci.c src/shared/util.h src/shared/util.c \
+       src/shared/queue.h src/shared/queue.c src/shared/ringbuf.h \
+       src/shared/ringbuf.c src/shared/tester.h src/shared/tester.c
+@EXPERIMENTAL_TRUE@am_tools_hci_tester_OBJECTS =  \
+@EXPERIMENTAL_TRUE@    tools/hci-tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/hci.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/ringbuf.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT)
+tools_hci_tester_OBJECTS = $(am_tools_hci_tester_OBJECTS)
+tools_hci_tester_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/hciattach_qualcomm.c tools/hciattach_intel.c \
+       tools/hciattach_bcm43xx.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_TRUE@   tools/hciattach_intel.$(OBJEXT) \
+@TOOLS_TRUE@   tools/hciattach_bcm43xx.$(OBJEXT)
 tools_hciattach_OBJECTS = $(am_tools_hciattach_OBJECTS)
 @TOOLS_TRUE@tools_hciattach_DEPENDENCIES =  \
 @TOOLS_TRUE@   lib/libbluetooth-internal.la
@@ -889,6 +1192,10 @@ am__tools_hcitool_SOURCES_DIST = tools/hcitool.c src/oui.h src/oui.c
 @TOOLS_TRUE@   src/oui.$(OBJEXT)
 tools_hcitool_OBJECTS = $(am_tools_hcitool_OBJECTS)
 @TOOLS_TRUE@tools_hcitool_DEPENDENCIES = lib/libbluetooth-internal.la
+am__tools_hex2hcd_SOURCES_DIST = tools/hex2hcd.c
+@EXPERIMENTAL_TRUE@am_tools_hex2hcd_OBJECTS = tools/hex2hcd.$(OBJEXT)
+tools_hex2hcd_OBJECTS = $(am_tools_hex2hcd_OBJECTS)
+tools_hex2hcd_LDADD = $(LDADD)
 tools_hid2hci_SOURCES = tools/hid2hci.c
 tools_hid2hci_OBJECTS = tools/hid2hci.$(OBJEXT)
 tools_hid2hci_DEPENDENCIES =
@@ -896,20 +1203,45 @@ tools_hwdb_SOURCES = tools/hwdb.c
 tools_hwdb_OBJECTS = tools/hwdb.$(OBJEXT)
 @EXPERIMENTAL_TRUE@tools_hwdb_DEPENDENCIES =  \
 @EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
+am__tools_ibeacon_SOURCES_DIST = tools/ibeacon.c monitor/bt.h \
+       monitor/mainloop.h monitor/mainloop.c src/shared/io.h \
+       src/shared/io-mainloop.c src/shared/timeout.h \
+       src/shared/timeout-mainloop.c src/shared/hci.h \
+       src/shared/hci.c src/shared/util.h src/shared/util.c \
+       src/shared/queue.h src/shared/queue.c src/shared/ringbuf.h \
+       src/shared/ringbuf.c
+@EXPERIMENTAL_TRUE@am_tools_ibeacon_OBJECTS = tools/ibeacon.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    monitor/mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/timeout-mainloop.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/hci.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/ringbuf.$(OBJEXT)
+tools_ibeacon_OBJECTS = $(am_tools_ibeacon_OBJECTS)
+tools_ibeacon_LDADD = $(LDADD)
 am__tools_l2cap_tester_SOURCES_DIST = tools/l2cap-tester.c \
        monitor/bt.h emulator/btdev.h emulator/btdev.c \
-       emulator/bthost.h emulator/bthost.c src/shared/util.h \
-       src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c \
-       src/shared/hciemu.h src/shared/hciemu.c src/shared/tester.h \
-       src/shared/tester.c
+       emulator/bthost.h emulator/bthost.c emulator/smp.c \
+       src/shared/crypto.h src/shared/crypto.c src/shared/io.h \
+       src/shared/io-glib.c src/shared/queue.h src/shared/queue.c \
+       src/shared/util.h src/shared/util.c src/shared/mgmt.h \
+       src/shared/mgmt.c src/shared/hciemu.h src/shared/hciemu.c \
+       src/shared/tester.h src/shared/tester.c src/shared/timeout.h \
+       src/shared/timeout-glib.c
 @EXPERIMENTAL_TRUE@am_tools_l2cap_tester_OBJECTS =  \
 @EXPERIMENTAL_TRUE@    tools/l2cap-tester.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/btdev.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/mgmt.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/hciemu.$(OBJEXT) \
-@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT)
+@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/timeout-glib.$(OBJEXT)
 tools_l2cap_tester_OBJECTS = $(am_tools_l2cap_tester_OBJECTS)
 @EXPERIMENTAL_TRUE@tools_l2cap_tester_DEPENDENCIES =  \
 @EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
@@ -921,17 +1253,26 @@ tools_l2test_OBJECTS = tools/l2test.$(OBJEXT)
 @TOOLS_TRUE@tools_l2test_DEPENDENCIES = lib/libbluetooth-internal.la
 am__tools_mgmt_tester_SOURCES_DIST = tools/mgmt-tester.c monitor/bt.h \
        emulator/btdev.h emulator/btdev.c emulator/bthost.h \
-       emulator/bthost.c src/shared/util.h src/shared/util.c \
-       src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \
-       src/shared/hciemu.c src/shared/tester.h src/shared/tester.c
+       emulator/bthost.c emulator/smp.c src/shared/crypto.h \
+       src/shared/crypto.c src/shared/io.h src/shared/io-glib.c \
+       src/shared/queue.h src/shared/queue.c src/shared/util.h \
+       src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c \
+       src/shared/hciemu.h src/shared/hciemu.c src/shared/tester.h \
+       src/shared/tester.c src/shared/timeout.h \
+       src/shared/timeout-glib.c
 @EXPERIMENTAL_TRUE@am_tools_mgmt_tester_OBJECTS =  \
 @EXPERIMENTAL_TRUE@    tools/mgmt-tester.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/btdev.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/mgmt.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/hciemu.$(OBJEXT) \
-@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT)
+@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/timeout-glib.$(OBJEXT)
 tools_mgmt_tester_OBJECTS = $(am_tools_mgmt_tester_OBJECTS)
 @EXPERIMENTAL_TRUE@tools_mgmt_tester_DEPENDENCIES =  \
 @EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
@@ -982,19 +1323,53 @@ tools_rctest_OBJECTS = tools/rctest.$(OBJEXT)
 tools_rfcomm_SOURCES = tools/rfcomm.c
 tools_rfcomm_OBJECTS = tools/rfcomm.$(OBJEXT)
 @TOOLS_TRUE@tools_rfcomm_DEPENDENCIES = lib/libbluetooth-internal.la
+am__tools_rfcomm_tester_SOURCES_DIST = tools/rfcomm-tester.c \
+       monitor/bt.h emulator/btdev.h emulator/btdev.c \
+       emulator/bthost.h emulator/bthost.c emulator/smp.c \
+       src/shared/crypto.h src/shared/crypto.c src/shared/io.h \
+       src/shared/io-glib.c src/shared/queue.h src/shared/queue.c \
+       src/shared/util.h src/shared/util.c src/shared/mgmt.h \
+       src/shared/mgmt.c src/shared/hciemu.h src/shared/hciemu.c \
+       src/shared/tester.h src/shared/tester.c src/shared/timeout.h \
+       src/shared/timeout-glib.c
+@EXPERIMENTAL_TRUE@am_tools_rfcomm_tester_OBJECTS =  \
+@EXPERIMENTAL_TRUE@    tools/rfcomm-tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    emulator/btdev.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/mgmt.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/hciemu.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/timeout-glib.$(OBJEXT)
+tools_rfcomm_tester_OBJECTS = $(am_tools_rfcomm_tester_OBJECTS)
+@EXPERIMENTAL_TRUE@tools_rfcomm_tester_DEPENDENCIES =  \
+@EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
 am__tools_sco_tester_SOURCES_DIST = tools/sco-tester.c monitor/bt.h \
        emulator/btdev.h emulator/btdev.c emulator/bthost.h \
-       emulator/bthost.c src/shared/util.h src/shared/util.c \
-       src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \
-       src/shared/hciemu.c src/shared/tester.h src/shared/tester.c
+       emulator/bthost.c emulator/smp.c src/shared/crypto.h \
+       src/shared/crypto.c src/shared/io.h src/shared/io-glib.c \
+       src/shared/queue.h src/shared/queue.c src/shared/util.h \
+       src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c \
+       src/shared/hciemu.h src/shared/hciemu.c src/shared/tester.h \
+       src/shared/tester.c src/shared/timeout.h \
+       src/shared/timeout-glib.c
 @EXPERIMENTAL_TRUE@am_tools_sco_tester_OBJECTS =  \
 @EXPERIMENTAL_TRUE@    tools/sco-tester.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/btdev.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/mgmt.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/hciemu.$(OBJEXT) \
-@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT)
+@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/timeout-glib.$(OBJEXT)
 tools_sco_tester_OBJECTS = $(am_tools_sco_tester_OBJECTS)
 @EXPERIMENTAL_TRUE@tools_sco_tester_DEPENDENCIES =  \
 @EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
@@ -1008,33 +1383,57 @@ am__tools_sdptool_SOURCES_DIST = tools/sdptool.c src/sdp-xml.h \
 @TOOLS_TRUE@   src/sdp-xml.$(OBJEXT)
 tools_sdptool_OBJECTS = $(am_tools_sdptool_OBJECTS)
 @TOOLS_TRUE@tools_sdptool_DEPENDENCIES = lib/libbluetooth-internal.la
+am__tools_seq2bseq_SOURCES_DIST = tools/seq2bseq.c
+@EXPERIMENTAL_TRUE@am_tools_seq2bseq_OBJECTS =  \
+@EXPERIMENTAL_TRUE@    tools/seq2bseq.$(OBJEXT)
+tools_seq2bseq_OBJECTS = $(am_tools_seq2bseq_OBJECTS)
+tools_seq2bseq_LDADD = $(LDADD)
 am__tools_smp_tester_SOURCES_DIST = tools/smp-tester.c monitor/bt.h \
        emulator/btdev.h emulator/btdev.c emulator/bthost.h \
-       emulator/bthost.c src/shared/util.h src/shared/util.c \
-       src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \
-       src/shared/hciemu.c src/shared/tester.h src/shared/tester.c
+       emulator/bthost.c emulator/smp.c src/shared/crypto.h \
+       src/shared/crypto.c src/shared/io.h src/shared/io-glib.c \
+       src/shared/queue.h src/shared/queue.c src/shared/util.h \
+       src/shared/util.c src/shared/mgmt.h src/shared/mgmt.c \
+       src/shared/hciemu.h src/shared/hciemu.c src/shared/tester.h \
+       src/shared/tester.c src/shared/timeout.h \
+       src/shared/timeout-glib.c
 @EXPERIMENTAL_TRUE@am_tools_smp_tester_OBJECTS =  \
 @EXPERIMENTAL_TRUE@    tools/smp-tester.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/btdev.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    emulator/bthost.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    emulator/smp.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/crypto.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/io-glib.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/queue.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/util.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/mgmt.$(OBJEXT) \
 @EXPERIMENTAL_TRUE@    src/shared/hciemu.$(OBJEXT) \
-@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT)
+@EXPERIMENTAL_TRUE@    src/shared/tester.$(OBJEXT) \
+@EXPERIMENTAL_TRUE@    src/shared/timeout-glib.$(OBJEXT)
 tools_smp_tester_OBJECTS = $(am_tools_smp_tester_OBJECTS)
 @EXPERIMENTAL_TRUE@tools_smp_tester_DEPENDENCIES =  \
 @EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
+am_unit_test_avctp_OBJECTS = unit/test-avctp.$(OBJEXT) \
+       src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \
+       android/avctp.$(OBJEXT)
+unit_test_avctp_OBJECTS = $(am_unit_test_avctp_OBJECTS)
+unit_test_avctp_DEPENDENCIES =
 am_unit_test_avdtp_OBJECTS = unit/test-avdtp.$(OBJEXT) \
        src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \
        android/avdtp.$(OBJEXT)
 unit_test_avdtp_OBJECTS = $(am_unit_test_avdtp_OBJECTS)
 unit_test_avdtp_DEPENDENCIES =
+am_unit_test_avrcp_OBJECTS = unit/test-avrcp.$(OBJEXT) \
+       src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \
+       android/avctp.$(OBJEXT) android/avrcp-lib.$(OBJEXT)
+unit_test_avrcp_OBJECTS = $(am_unit_test_avrcp_OBJECTS)
+unit_test_avrcp_DEPENDENCIES = lib/libbluetooth-internal.la
 am_unit_test_crc_OBJECTS = unit/test-crc.$(OBJEXT) \
        monitor/crc.$(OBJEXT)
 unit_test_crc_OBJECTS = $(am_unit_test_crc_OBJECTS)
 unit_test_crc_DEPENDENCIES =
 am_unit_test_eir_OBJECTS = unit/test-eir.$(OBJEXT) src/eir.$(OBJEXT) \
-       src/glib-helper.$(OBJEXT)
+       src/uuid-helper.$(OBJEXT)
 unit_test_eir_OBJECTS = $(am_unit_test_eir_OBJECTS)
 unit_test_eir_DEPENDENCIES = lib/libbluetooth-internal.la
 am_unit_test_gdbus_client_OBJECTS = unit/test-gdbus-client.$(OBJEXT)
@@ -1062,16 +1461,32 @@ am_unit_test_gobex_transfer_OBJECTS = $(am__objects_17) \
 unit_test_gobex_transfer_OBJECTS =  \
        $(am_unit_test_gobex_transfer_OBJECTS)
 unit_test_gobex_transfer_DEPENDENCIES =
+am_unit_test_hfp_OBJECTS = unit/test-hfp.$(OBJEXT) \
+       src/shared/io-glib.$(OBJEXT) src/shared/queue.$(OBJEXT) \
+       src/shared/util.$(OBJEXT) src/shared/mgmt.$(OBJEXT) \
+       src/shared/ringbuf.$(OBJEXT) src/shared/hfp.$(OBJEXT)
+unit_test_hfp_OBJECTS = $(am_unit_test_hfp_OBJECTS)
+unit_test_hfp_DEPENDENCIES =
 am_unit_test_lib_OBJECTS = unit/test-lib.$(OBJEXT)
 unit_test_lib_OBJECTS = $(am_unit_test_lib_OBJECTS)
 unit_test_lib_DEPENDENCIES = lib/libbluetooth-internal.la
 am_unit_test_mgmt_OBJECTS = unit/test-mgmt.$(OBJEXT) \
+       src/shared/io-glib.$(OBJEXT) src/shared/queue.$(OBJEXT) \
        src/shared/util.$(OBJEXT) src/shared/mgmt.$(OBJEXT)
 unit_test_mgmt_OBJECTS = $(am_unit_test_mgmt_OBJECTS)
 unit_test_mgmt_DEPENDENCIES =
+am_unit_test_queue_OBJECTS = unit/test-queue.$(OBJEXT) \
+       src/shared/util.$(OBJEXT) src/shared/queue.$(OBJEXT)
+unit_test_queue_OBJECTS = $(am_unit_test_queue_OBJECTS)
+unit_test_queue_DEPENDENCIES =
+am_unit_test_ringbuf_OBJECTS = unit/test-ringbuf.$(OBJEXT) \
+       src/shared/util.$(OBJEXT) src/shared/ringbuf.$(OBJEXT)
+unit_test_ringbuf_OBJECTS = $(am_unit_test_ringbuf_OBJECTS)
+unit_test_ringbuf_DEPENDENCIES =
 am_unit_test_sdp_OBJECTS = unit/test-sdp.$(OBJEXT) \
        src/shared/util.$(OBJEXT) src/sdpd-database.$(OBJEXT) \
-       src/sdpd-service.$(OBJEXT) src/sdpd-request.$(OBJEXT)
+       src/log.$(OBJEXT) src/sdpd-service.$(OBJEXT) \
+       src/sdpd-request.$(OBJEXT)
 unit_test_sdp_OBJECTS = $(am_unit_test_sdp_OBJECTS)
 unit_test_sdp_DEPENDENCIES = lib/libbluetooth-internal.la
 am_unit_test_textfile_OBJECTS = unit/test-textfile.$(OBJEXT) \
@@ -1106,95 +1521,126 @@ AM_V_GEN = $(am__v_GEN_@AM_V@)
 am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
 am__v_GEN_0 = @echo "  GEN   " $@;
 SOURCES = $(profiles_sap_libsap_a_SOURCES) \
-       $(android_libhal_internal_la_SOURCES) \
+       $(android_audio_a2dp_default_la_SOURCES) \
+       $(android_audio_sco_default_la_SOURCES) \
+       $(android_bluetooth_default_la_SOURCES) \
        $(gdbus_libgdbus_internal_la_SOURCES) \
        $(lib_libbluetooth_internal_la_SOURCES) \
        $(lib_libbluetooth_la_SOURCES) \
        $(plugins_external_dummy_la_SOURCES) \
-       $(plugins_sixaxis_la_SOURCES) $(android_bluetoothd_SOURCES) \
-       $(android_haltest_SOURCES) $(android_system_emulator_SOURCES) \
+       $(plugins_sixaxis_la_SOURCES) \
+       $(android_android_tester_SOURCES) \
+       $(android_bluetoothd_SOURCES) \
+       $(android_bluetoothd_snoop_SOURCES) $(android_haltest_SOURCES) \
+       $(android_ipc_tester_SOURCES) \
+       $(android_system_emulator_SOURCES) $(android_test_ipc_SOURCES) \
        $(attrib_gatttool_SOURCES) $(client_bluetoothctl_SOURCES) \
        $(emulator_b1ee_SOURCES) $(emulator_btvirt_SOURCES) \
-       $(monitor_btmon_SOURCES) $(obexd_src_obexd_SOURCES) \
-       $(nodist_obexd_src_obexd_SOURCES) \
+       $(emulator_hfp_SOURCES) $(monitor_btmon_SOURCES) \
+       $(obexd_src_obexd_SOURCES) $(nodist_obexd_src_obexd_SOURCES) \
        $(profiles_cups_bluetooth_SOURCES) \
        $(profiles_iap_iapd_SOURCES) $(src_bluetoothd_SOURCES) \
-       $(nodist_src_bluetoothd_SOURCES) tools/amptest.c \
-       tools/avinfo.c tools/avtest.c $(tools_bccmd_SOURCES) \
-       $(tools_bdaddr_SOURCES) $(tools_bluetooth_player_SOURCES) \
+       $(nodist_src_bluetoothd_SOURCES) $(tools_3dsp_SOURCES) \
+       tools/amptest.c tools/avinfo.c tools/avtest.c \
+       $(tools_bccmd_SOURCES) $(tools_bdaddr_SOURCES) \
+       $(tools_bluemoon_SOURCES) $(tools_bluetooth_player_SOURCES) \
        tools/btattach.c $(tools_btinfo_SOURCES) \
        $(tools_btiotest_SOURCES) $(tools_btmgmt_SOURCES) \
-       $(tools_btsnoop_SOURCES) tools/ciptool.c \
-       $(tools_cltest_SOURCES) $(tools_gap_tester_SOURCES) \
-       $(tools_hciattach_SOURCES) $(tools_hciconfig_SOURCES) \
-       $(tools_hcidump_SOURCES) tools/hcieventmask.c \
-       tools/hcisecfilter.c $(tools_hcitool_SOURCES) tools/hid2hci.c \
-       tools/hwdb.c $(tools_l2cap_tester_SOURCES) tools/l2ping.c \
-       tools/l2test.c $(tools_mgmt_tester_SOURCES) \
-       $(tools_mpris_player_SOURCES) \
+       $(tools_btproxy_SOURCES) $(tools_btsnoop_SOURCES) \
+       tools/ciptool.c $(tools_cltest_SOURCES) \
+       $(tools_gap_tester_SOURCES) $(tools_gatt_service_SOURCES) \
+       $(tools_hci_tester_SOURCES) $(tools_hciattach_SOURCES) \
+       $(tools_hciconfig_SOURCES) $(tools_hcidump_SOURCES) \
+       tools/hcieventmask.c tools/hcisecfilter.c \
+       $(tools_hcitool_SOURCES) $(tools_hex2hcd_SOURCES) \
+       tools/hid2hci.c tools/hwdb.c $(tools_ibeacon_SOURCES) \
+       $(tools_l2cap_tester_SOURCES) tools/l2ping.c tools/l2test.c \
+       $(tools_mgmt_tester_SOURCES) $(tools_mpris_player_SOURCES) \
        $(tools_obex_client_tool_SOURCES) \
        $(tools_obex_server_tool_SOURCES) $(tools_obexctl_SOURCES) \
-       tools/rctest.c tools/rfcomm.c $(tools_sco_tester_SOURCES) \
-       tools/scotest.c $(tools_sdptool_SOURCES) \
-       $(tools_smp_tester_SOURCES) $(unit_test_avdtp_SOURCES) \
+       tools/rctest.c tools/rfcomm.c $(tools_rfcomm_tester_SOURCES) \
+       $(tools_sco_tester_SOURCES) tools/scotest.c \
+       $(tools_sdptool_SOURCES) $(tools_seq2bseq_SOURCES) \
+       $(tools_smp_tester_SOURCES) $(unit_test_avctp_SOURCES) \
+       $(unit_test_avdtp_SOURCES) $(unit_test_avrcp_SOURCES) \
        $(unit_test_crc_SOURCES) $(unit_test_eir_SOURCES) \
        $(unit_test_gdbus_client_SOURCES) $(unit_test_gobex_SOURCES) \
        $(unit_test_gobex_apparam_SOURCES) \
        $(unit_test_gobex_header_SOURCES) \
        $(unit_test_gobex_packet_SOURCES) \
-       $(unit_test_gobex_transfer_SOURCES) $(unit_test_lib_SOURCES) \
-       $(unit_test_mgmt_SOURCES) $(unit_test_sdp_SOURCES) \
-       $(unit_test_textfile_SOURCES) $(unit_test_uuid_SOURCES)
+       $(unit_test_gobex_transfer_SOURCES) $(unit_test_hfp_SOURCES) \
+       $(unit_test_lib_SOURCES) $(unit_test_mgmt_SOURCES) \
+       $(unit_test_queue_SOURCES) $(unit_test_ringbuf_SOURCES) \
+       $(unit_test_sdp_SOURCES) $(unit_test_textfile_SOURCES) \
+       $(unit_test_uuid_SOURCES)
 DIST_SOURCES = $(am__profiles_sap_libsap_a_SOURCES_DIST) \
-       $(am__android_libhal_internal_la_SOURCES_DIST) \
+       $(am__android_audio_a2dp_default_la_SOURCES_DIST) \
+       $(am__android_audio_sco_default_la_SOURCES_DIST) \
+       $(am__android_bluetooth_default_la_SOURCES_DIST) \
        $(gdbus_libgdbus_internal_la_SOURCES) \
        $(lib_libbluetooth_internal_la_SOURCES) \
        $(am__lib_libbluetooth_la_SOURCES_DIST) \
        $(am__plugins_external_dummy_la_SOURCES_DIST) \
        $(am__plugins_sixaxis_la_SOURCES_DIST) \
+       $(am__android_android_tester_SOURCES_DIST) \
        $(am__android_bluetoothd_SOURCES_DIST) \
+       $(am__android_bluetoothd_snoop_SOURCES_DIST) \
        $(am__android_haltest_SOURCES_DIST) \
+       $(am__android_ipc_tester_SOURCES_DIST) \
        $(am__android_system_emulator_SOURCES_DIST) \
+       $(am__android_test_ipc_SOURCES_DIST) \
        $(am__attrib_gatttool_SOURCES_DIST) \
        $(am__client_bluetoothctl_SOURCES_DIST) \
        $(am__emulator_b1ee_SOURCES_DIST) \
        $(am__emulator_btvirt_SOURCES_DIST) \
+       $(am__emulator_hfp_SOURCES_DIST) \
        $(am__monitor_btmon_SOURCES_DIST) \
        $(am__obexd_src_obexd_SOURCES_DIST) \
        $(am__profiles_cups_bluetooth_SOURCES_DIST) \
        $(am__profiles_iap_iapd_SOURCES_DIST) \
-       $(am__src_bluetoothd_SOURCES_DIST) tools/amptest.c \
-       tools/avinfo.c tools/avtest.c $(am__tools_bccmd_SOURCES_DIST) \
+       $(am__src_bluetoothd_SOURCES_DIST) \
+       $(am__tools_3dsp_SOURCES_DIST) tools/amptest.c tools/avinfo.c \
+       tools/avtest.c $(am__tools_bccmd_SOURCES_DIST) \
        $(am__tools_bdaddr_SOURCES_DIST) \
+       $(am__tools_bluemoon_SOURCES_DIST) \
        $(am__tools_bluetooth_player_SOURCES_DIST) tools/btattach.c \
        $(am__tools_btinfo_SOURCES_DIST) \
        $(am__tools_btiotest_SOURCES_DIST) \
        $(am__tools_btmgmt_SOURCES_DIST) \
+       $(am__tools_btproxy_SOURCES_DIST) \
        $(am__tools_btsnoop_SOURCES_DIST) tools/ciptool.c \
        $(am__tools_cltest_SOURCES_DIST) \
        $(am__tools_gap_tester_SOURCES_DIST) \
+       $(am__tools_gatt_service_SOURCES_DIST) \
+       $(am__tools_hci_tester_SOURCES_DIST) \
        $(am__tools_hciattach_SOURCES_DIST) \
        $(am__tools_hciconfig_SOURCES_DIST) \
        $(am__tools_hcidump_SOURCES_DIST) tools/hcieventmask.c \
        tools/hcisecfilter.c $(am__tools_hcitool_SOURCES_DIST) \
-       tools/hid2hci.c tools/hwdb.c \
+       $(am__tools_hex2hcd_SOURCES_DIST) tools/hid2hci.c tools/hwdb.c \
+       $(am__tools_ibeacon_SOURCES_DIST) \
        $(am__tools_l2cap_tester_SOURCES_DIST) tools/l2ping.c \
        tools/l2test.c $(am__tools_mgmt_tester_SOURCES_DIST) \
        $(am__tools_mpris_player_SOURCES_DIST) \
        $(am__tools_obex_client_tool_SOURCES_DIST) \
        $(am__tools_obex_server_tool_SOURCES_DIST) \
        $(am__tools_obexctl_SOURCES_DIST) tools/rctest.c \
-       tools/rfcomm.c $(am__tools_sco_tester_SOURCES_DIST) \
-       tools/scotest.c $(am__tools_sdptool_SOURCES_DIST) \
+       tools/rfcomm.c $(am__tools_rfcomm_tester_SOURCES_DIST) \
+       $(am__tools_sco_tester_SOURCES_DIST) tools/scotest.c \
+       $(am__tools_sdptool_SOURCES_DIST) \
+       $(am__tools_seq2bseq_SOURCES_DIST) \
        $(am__tools_smp_tester_SOURCES_DIST) \
-       $(unit_test_avdtp_SOURCES) $(unit_test_crc_SOURCES) \
+       $(unit_test_avctp_SOURCES) $(unit_test_avdtp_SOURCES) \
+       $(unit_test_avrcp_SOURCES) $(unit_test_crc_SOURCES) \
        $(unit_test_eir_SOURCES) $(unit_test_gdbus_client_SOURCES) \
        $(unit_test_gobex_SOURCES) $(unit_test_gobex_apparam_SOURCES) \
        $(unit_test_gobex_header_SOURCES) \
        $(unit_test_gobex_packet_SOURCES) \
-       $(unit_test_gobex_transfer_SOURCES) $(unit_test_lib_SOURCES) \
-       $(unit_test_mgmt_SOURCES) $(unit_test_sdp_SOURCES) \
-       $(unit_test_textfile_SOURCES) $(unit_test_uuid_SOURCES)
+       $(unit_test_gobex_transfer_SOURCES) $(unit_test_hfp_SOURCES) \
+       $(unit_test_lib_SOURCES) $(unit_test_mgmt_SOURCES) \
+       $(unit_test_queue_SOURCES) $(unit_test_ringbuf_SOURCES) \
+       $(unit_test_sdp_SOURCES) $(unit_test_textfile_SOURCES) \
+       $(unit_test_uuid_SOURCES)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -1319,9 +1765,13 @@ PKG_CONFIG = @PKG_CONFIG@
 PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
 PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
 RANLIB = @RANLIB@
+SBC_CFLAGS = @SBC_CFLAGS@
+SBC_LIBS = @SBC_LIBS@
 SED = @SED@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
+SPEEXDSP_CFLAGS = @SPEEXDSP_CFLAGS@
+SPEEXDSP_LIBS = @SPEEXDSP_LIBS@
 STRIP = @STRIP@
 SYSTEMD_SYSTEMUNITDIR = @SYSTEMD_SYSTEMUNITDIR@
 SYSTEMD_USERUNITDIR = @SYSTEMD_USERUNITDIR@
@@ -1386,23 +1836,49 @@ AM_MAKEFLAGS = --no-print-directory
 lib_LTLIBRARIES = $(am__append_2)
 noinst_LIBRARIES = $(am__append_7)
 noinst_LTLIBRARIES = lib/libbluetooth-internal.la \
-       gdbus/libgdbus-internal.la $(am__append_30)
+       gdbus/libgdbus-internal.la
 dist_man_MANS = $(am__append_18) $(am__append_20)
 dist_noinst_MANS = 
 CLEANFILES = $(builtin_files) src/bluetooth.service \
        obexd/src/builtin.h $(builtin_files) obexd/src/obex.service \
-       $(am__append_31)
+       $(am__append_32)
 EXTRA_DIST = src/bluetooth.service.in src/org.bluez.service \
        src/genbuiltin src/bluetooth.conf src/main.conf \
        profiles/network/network.conf profiles/input/input.conf \
        profiles/proximity/proximity.conf $(am__append_19) \
        $(am__append_21) $(am__append_22) obexd/src/obex.service.in \
        obexd/src/org.bluez.obex.service obexd/src/genbuiltin \
-       android/Android.mk android/hal-ipc-api.txt android/README \
-       android/pics-gap.txt android/pics-hid.txt android/pics-pan.txt \
-       android/pics-did.txt android/pics-opp.txt \
-       android/pics-pbap.txt tools/hid2hci.rules $(test_scripts) \
+       android/Android.mk android/README android/init.bluetooth.rc \
+       android/hal-ipc-api.txt android/audio-ipc-api.txt \
+       android/pics-rfcomm.txt android/pics-spp.txt \
+       android/pics-sdp.txt android/pics-l2cap.txt \
+       android/pics-gap.txt android/pics-did.txt android/pics-hid.txt \
+       android/pics-pan.txt android/pics-opp.txt android/pics-map.txt \
+       android/pics-pbap.txt android/pics-a2dp.txt \
+       android/pics-avctp.txt android/pics-avrcp.txt \
+       android/pics-hsp.txt android/pics-hfp.txt \
+       android/pics-gatt.txt android/pics-mcap.txt \
+       android/pics-hdp.txt android/pics-iopt.txt android/pics-sm.txt \
+       android/pics-mps.txt android/pixit-l2cap.txt \
+       android/pixit-gap.txt android/pixit-did.txt \
+       android/pixit-hid.txt android/pixit-pan.txt \
+       android/pixit-opp.txt android/pixit-map.txt \
+       android/pixit-pbap.txt android/pixit-a2dp.txt \
+       android/pixit-avctp.txt android/pixit-avrcp.txt \
+       android/pixit-hsp.txt android/pixit-hfp.txt \
+       android/pixit-gatt.txt android/pixit-mcap.txt \
+       android/pixit-hdp.txt android/pixit-iopt.txt \
+       android/pixit-sm.txt android/pixit-mps.txt \
+       android/bite-rfcomm.txt android/bite-spp.txt \
+       android/pts-l2cap.txt android/pts-gap.txt android/pts-did.txt \
+       android/pts-hid.txt android/pts-pan.txt android/pts-opp.txt \
+       android/pts-map.txt android/pts-a2dp.txt android/pts-avrcp.txt \
+       android/pts-avctp.txt android/pts-pbap.txt android/pts-hfp.txt \
+       android/pts-gatt.txt android/pts-hsp.txt android/pts-iopt.txt \
+       android/pts-hdp.txt android/pts-mcap.txt android/pts-mps.txt \
+       android/pts-sm.txt tools/hid2hci.rules $(test_scripts) \
        doc/assigned-numbers.txt doc/supported-features.txt \
+       doc/test-coverage.txt doc/settings-storage.txt \
        doc/mgmt-api.txt doc/adapter-api.txt doc/device-api.txt \
        doc/agent-api.txt doc/profile-api.txt doc/network-api.txt \
        doc/media-api.txt doc/health-api.txt doc/sap-api.txt \
@@ -1426,7 +1902,8 @@ AM_LDFLAGS = $(MISC_LDFLAGS)
 plugindir = $(libdir)/bluetooth/plugins
 @MAINTAINER_MODE_FALSE@build_plugindir = $(plugindir)
 @MAINTAINER_MODE_TRUE@build_plugindir = $(abs_top_srcdir)/plugins/.libs
-plugin_LTLIBRARIES = $(am__append_12) $(am__append_13)
+plugin_LTLIBRARIES = $(am__append_12) $(am__append_13) \
+       $(am__append_30)
 lib_sources = lib/bluetooth.c lib/hci.c lib/sdp.c
 lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h \
                lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h \
@@ -1437,7 +1914,7 @@ extra_sources = lib/uuid.c
 local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file)))
 BUILT_SOURCES = $(local_headers) src/builtin.h obexd/src/builtin.h
 @LIBRARY_TRUE@lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:3:17
+@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:9:17
 @LIBRARY_TRUE@lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \
                                        $(extra_headers) $(extra_sources)
@@ -1481,6 +1958,7 @@ builtin_sources = plugins/hostname.c plugins/wiimote.c \
        profiles/network/connection.c profiles/input/manager.c \
        profiles/input/server.h profiles/input/server.c \
        profiles/input/device.h profiles/input/device.c \
+       profiles/input/uhid_copy.h profiles/input/hidp_defs.h \
        profiles/input/hog.c profiles/input/uhid_copy.h \
        profiles/input/suspend.h profiles/input/suspend-dummy.c \
        $(am__append_9) profiles/gatt/gas.c profiles/scanparam/scan.c \
@@ -1509,7 +1987,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
                        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/uuid-helper.h src/uuid-helper.c \
                        src/uinput.h \
                        src/plugin.h src/plugin.c \
                        src/storage.h src/storage.c \
@@ -1518,9 +1996,14 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
                        src/adapter.h src/adapter.c \
                        src/profile.h src/profile.c \
                        src/service.h src/service.c \
+                       src/gatt-dbus.h src/gatt-dbus.c \
+                       src/gatt.h src/gatt.c \
                        src/device.h src/device.c src/attio.h \
                        src/dbus-common.c src/dbus-common.h \
                        src/eir.h src/eir.c \
+                       src/shared/io.h src/shared/io-glib.c \
+                       src/shared/timeout.h src/shared/timeout-glib.c \
+                       src/shared/queue.h src/shared/queue.c \
                        src/shared/util.h src/shared/util.c \
                        src/shared/mgmt.h src/shared/mgmt.c
 
@@ -1543,14 +2026,22 @@ man_MANS = src/bluetoothd.8
 test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \
        test/monitor-bluetooth test/list-devices test/test-discovery \
        test/test-manager test/test-adapter test/test-device \
-       test/simple-agent test/simple-service test/simple-endpoint \
-       test/test-sap-server test/test-proximity test/test-network \
-       test/test-thermometer test/test-profile test/test-health \
-       test/test-health-sink test/service-record.dtd \
-       test/service-did.xml test/service-spp.xml test/service-opp.xml \
-       test/service-ftp.xml test/simple-player test/test-nap \
-       test/test-heartrate test/test-alert test/test-hfp \
-       test/test-cyclingspeed
+       test/simple-agent test/simple-endpoint test/test-sap-server \
+       test/test-proximity test/test-network test/test-thermometer \
+       test/test-profile test/test-health test/test-health-sink \
+       test/service-record.dtd test/service-did.xml \
+       test/service-spp.xml test/service-opp.xml test/service-ftp.xml \
+       test/simple-player test/test-nap test/test-heartrate \
+       test/test-alert test/test-hfp test/test-cyclingspeed \
+       test/opp-client test/ftp-client test/pbap-client \
+       test/map-client
+unit_tests = $(am__append_31) unit/test-eir unit/test-uuid \
+       unit/test-textfile unit/test-crc unit/test-ringbuf \
+       unit/test-queue unit/test-mgmt unit/test-sdp unit/test-avdtp \
+       unit/test-avctp unit/test-avrcp unit/test-hfp \
+       unit/test-gdbus-client unit/test-gobex-header \
+       unit/test-gobex-packet unit/test-gobex \
+       unit/test-gobex-transfer unit/test-gobex-apparam unit/test-lib
 @CLIENT_TRUE@client_bluetoothctl_SOURCES = client/main.c \
 @CLIENT_TRUE@                                  client/display.h client/display.c \
 @CLIENT_TRUE@                                  client/agent.h client/agent.c \
@@ -1560,86 +2051,170 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \
 @CLIENT_TRUE@                          -lreadline
 
 @MONITOR_TRUE@monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
-@MONITOR_TRUE@                                 monitor/mainloop.h monitor/mainloop.c \
-@MONITOR_TRUE@                                 monitor/display.h monitor/display.c \
-@MONITOR_TRUE@                                 monitor/hcidump.h monitor/hcidump.c \
-@MONITOR_TRUE@                                 monitor/btsnoop.h monitor/btsnoop.c \
-@MONITOR_TRUE@                                 monitor/control.h monitor/control.c \
-@MONITOR_TRUE@                                 monitor/packet.h monitor/packet.c \
-@MONITOR_TRUE@                                 monitor/vendor.h monitor/vendor.c \
-@MONITOR_TRUE@                                 monitor/lmp.h monitor/lmp.c \
-@MONITOR_TRUE@                                 monitor/l2cap.h monitor/l2cap.c \
-@MONITOR_TRUE@                                 monitor/uuid.h monitor/uuid.c \
-@MONITOR_TRUE@                                 monitor/sdp.h monitor/sdp.c \
-@MONITOR_TRUE@                                 monitor/crc.h monitor/crc.c \
-@MONITOR_TRUE@                                 monitor/ll.h monitor/ll.c
-
-@MONITOR_TRUE@monitor_btmon_LDADD = lib/libbluetooth-internal.la
+@MONITOR_TRUE@                         monitor/mainloop.h monitor/mainloop.c \
+@MONITOR_TRUE@                         monitor/display.h monitor/display.c \
+@MONITOR_TRUE@                         monitor/hcidump.h monitor/hcidump.c \
+@MONITOR_TRUE@                         monitor/ellisys.h monitor/ellisys.c \
+@MONITOR_TRUE@                         monitor/control.h monitor/control.c \
+@MONITOR_TRUE@                         monitor/packet.h monitor/packet.c \
+@MONITOR_TRUE@                         monitor/vendor.h monitor/vendor.c \
+@MONITOR_TRUE@                         monitor/lmp.h monitor/lmp.c \
+@MONITOR_TRUE@                         monitor/crc.h monitor/crc.c \
+@MONITOR_TRUE@                         monitor/ll.h monitor/ll.c \
+@MONITOR_TRUE@                         monitor/l2cap.h monitor/l2cap.c \
+@MONITOR_TRUE@                         monitor/sdp.h monitor/sdp.c \
+@MONITOR_TRUE@                         monitor/uuid.h monitor/uuid.c \
+@MONITOR_TRUE@                         monitor/hwdb.h monitor/hwdb.c \
+@MONITOR_TRUE@                         monitor/keys.h monitor/keys.c \
+@MONITOR_TRUE@                         monitor/analyze.h monitor/analyze.c \
+@MONITOR_TRUE@                         src/shared/util.h src/shared/util.c \
+@MONITOR_TRUE@                         src/shared/queue.h src/shared/queue.c \
+@MONITOR_TRUE@                         src/shared/crypto.h src/shared/crypto.c \
+@MONITOR_TRUE@                         src/shared/btsnoop.h src/shared/btsnoop.c
+
+@MONITOR_TRUE@monitor_btmon_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
 @EXPERIMENTAL_TRUE@emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
-@EXPERIMENTAL_TRUE@                                    monitor/mainloop.h monitor/mainloop.c \
-@EXPERIMENTAL_TRUE@                                    emulator/server.h emulator/server.c \
-@EXPERIMENTAL_TRUE@                                    emulator/vhci.h emulator/vhci.c \
-@EXPERIMENTAL_TRUE@                                    emulator/btdev.h emulator/btdev.c \
-@EXPERIMENTAL_TRUE@                                    emulator/bthost.h emulator/bthost.c \
-@EXPERIMENTAL_TRUE@                                    emulator/amp.h emulator/amp.c
+@EXPERIMENTAL_TRUE@                            monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout.h \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout-mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@                            src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@                            emulator/server.h emulator/server.c \
+@EXPERIMENTAL_TRUE@                            emulator/vhci.h emulator/vhci.c \
+@EXPERIMENTAL_TRUE@                            emulator/btdev.h emulator/btdev.c \
+@EXPERIMENTAL_TRUE@                            emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@                            emulator/smp.c \
+@EXPERIMENTAL_TRUE@                            emulator/amp.h emulator/amp.c \
+@EXPERIMENTAL_TRUE@                            emulator/le.h emulator/le.c
 
+@EXPERIMENTAL_TRUE@emulator_btvirt_LDADD = lib/libbluetooth-internal.la
 @EXPERIMENTAL_TRUE@emulator_b1ee_SOURCES = emulator/b1ee.c monitor/mainloop.h monitor/mainloop.c
+@EXPERIMENTAL_TRUE@emulator_hfp_SOURCES = emulator/hfp.c \
+@EXPERIMENTAL_TRUE@                            monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@                            src/shared/ringbuf.h src/shared/ringbuf.c \
+@EXPERIMENTAL_TRUE@                            src/shared/hfp.h src/shared/hfp.c
+
+@EXPERIMENTAL_TRUE@tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@                            monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout.h \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout-mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/hci.h src/shared/hci.c \
+@EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@                            src/shared/ringbuf.h src/shared/ringbuf.c
+
 @EXPERIMENTAL_TRUE@tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \
 @EXPERIMENTAL_TRUE@                            emulator/btdev.h emulator/btdev.c \
 @EXPERIMENTAL_TRUE@                            emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@                            emulator/smp.c \
+@EXPERIMENTAL_TRUE@                            src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
 @EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
 @EXPERIMENTAL_TRUE@                            src/shared/mgmt.h src/shared/mgmt.c \
 @EXPERIMENTAL_TRUE@                            src/shared/hciemu.h src/shared/hciemu.c \
-@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c
+@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout.h src/shared/timeout-glib.c
 
 @EXPERIMENTAL_TRUE@tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 @EXPERIMENTAL_TRUE@tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \
 @EXPERIMENTAL_TRUE@                            emulator/btdev.h emulator/btdev.c \
 @EXPERIMENTAL_TRUE@                            emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@                            emulator/smp.c \
+@EXPERIMENTAL_TRUE@                            src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
 @EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
 @EXPERIMENTAL_TRUE@                            src/shared/mgmt.h src/shared/mgmt.c \
 @EXPERIMENTAL_TRUE@                            src/shared/hciemu.h src/shared/hciemu.c \
-@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c
+@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout.h src/shared/timeout-glib.c
 
 @EXPERIMENTAL_TRUE@tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@EXPERIMENTAL_TRUE@tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@                            emulator/btdev.h emulator/btdev.c \
+@EXPERIMENTAL_TRUE@                            emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@                            emulator/smp.c \
+@EXPERIMENTAL_TRUE@                            src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@                            src/shared/mgmt.h src/shared/mgmt.c \
+@EXPERIMENTAL_TRUE@                            src/shared/hciemu.h src/shared/hciemu.c \
+@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout.h src/shared/timeout-glib.c
+
+@EXPERIMENTAL_TRUE@tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 @EXPERIMENTAL_TRUE@tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \
 @EXPERIMENTAL_TRUE@                            emulator/btdev.h emulator/btdev.c \
 @EXPERIMENTAL_TRUE@                            emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@                            emulator/smp.c \
+@EXPERIMENTAL_TRUE@                            src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
 @EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
 @EXPERIMENTAL_TRUE@                            src/shared/mgmt.h src/shared/mgmt.c \
 @EXPERIMENTAL_TRUE@                            src/shared/hciemu.h src/shared/hciemu.c \
-@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c
+@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout.h src/shared/timeout-glib.c
 
 @EXPERIMENTAL_TRUE@tools_smp_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 @EXPERIMENTAL_TRUE@tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \
 @EXPERIMENTAL_TRUE@                            emulator/btdev.h emulator/btdev.c \
 @EXPERIMENTAL_TRUE@                            emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@                            emulator/smp.c \
+@EXPERIMENTAL_TRUE@                            src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
 @EXPERIMENTAL_TRUE@                            src/shared/hciemu.h src/shared/hciemu.c \
-@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c
+@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout.h src/shared/timeout-glib.c
+
+@EXPERIMENTAL_TRUE@tools_gap_tester_LDADD = lib/libbluetooth-internal.la \
+@EXPERIMENTAL_TRUE@                            gdbus/libgdbus-internal.la \
+@EXPERIMENTAL_TRUE@                            @GLIB_LIBS@ @DBUS_LIBS@
 
-@EXPERIMENTAL_TRUE@tools_gap_tester_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
 @EXPERIMENTAL_TRUE@tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \
 @EXPERIMENTAL_TRUE@                            emulator/btdev.h emulator/btdev.c \
 @EXPERIMENTAL_TRUE@                            emulator/bthost.h emulator/bthost.c \
+@EXPERIMENTAL_TRUE@                            emulator/smp.c \
+@EXPERIMENTAL_TRUE@                            src/shared/crypto.h src/shared/crypto.c \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
 @EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
 @EXPERIMENTAL_TRUE@                            src/shared/mgmt.h src/shared/mgmt.c \
 @EXPERIMENTAL_TRUE@                            src/shared/hciemu.h src/shared/hciemu.c \
-@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c
+@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout.h src/shared/timeout-glib.c
 
 @EXPERIMENTAL_TRUE@tools_sco_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@EXPERIMENTAL_TRUE@tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-glib.c \
+@EXPERIMENTAL_TRUE@                            src/shared/hci.h src/shared/hci.c \
+@EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@                            src/shared/ringbuf.h src/shared/ringbuf.c \
+@EXPERIMENTAL_TRUE@                            src/shared/tester.h src/shared/tester.c
+
+@EXPERIMENTAL_TRUE@tools_hci_tester_LDADD = @GLIB_LIBS@
 @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_intel.c \
+@TOOLS_TRUE@                                           tools/hciattach_bcm43xx.c
 
 @TOOLS_TRUE@tools_hciattach_LDADD = lib/libbluetooth-internal.la
 @TOOLS_TRUE@tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c
 @TOOLS_TRUE@tools_hciconfig_LDADD = lib/libbluetooth-internal.la
 @TOOLS_TRUE@tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c
-@TOOLS_TRUE@tools_hcitool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @UDEV_LIBS@
+@TOOLS_TRUE@tools_hcitool_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
 @TOOLS_TRUE@tools_hcidump_SOURCES = tools/hcidump.c \
 @TOOLS_TRUE@                           tools/parser/parser.h tools/parser/parser.c \
 @TOOLS_TRUE@                           tools/parser/lmp.c \
@@ -1680,6 +2255,14 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \
 @TOOLS_TRUE@                   tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c
 
 @TOOLS_TRUE@tools_bccmd_LDADD = lib/libbluetooth-internal.la
+@TOOLS_TRUE@tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h \
+@TOOLS_TRUE@                           monitor/mainloop.h monitor/mainloop.c \
+@TOOLS_TRUE@                           src/shared/io.h src/shared/io-mainloop.c \
+@TOOLS_TRUE@                           src/shared/hci.h src/shared/hci.c \
+@TOOLS_TRUE@                           src/shared/util.h src/shared/util.c \
+@TOOLS_TRUE@                           src/shared/queue.h src/shared/queue.c \
+@TOOLS_TRUE@                           src/shared/ringbuf.h src/shared/ringbuf.c
+
 @HID2HCI_TRUE@udevdir = @UDEV_DIR@
 @HID2HCI_TRUE@tools_hid2hci_LDADD = @UDEV_LIBS@
 @EXPERIMENTAL_TRUE@tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
@@ -1690,22 +2273,50 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \
 @EXPERIMENTAL_TRUE@tools_amptest_LDADD = lib/libbluetooth-internal.la
 @EXPERIMENTAL_TRUE@tools_hwdb_LDADD = lib/libbluetooth-internal.la
 @EXPERIMENTAL_TRUE@tools_hcieventmask_LDADD = lib/libbluetooth-internal.la
-@EXPERIMENTAL_TRUE@tools_btmgmt_SOURCES = tools/btmgmt.c src/glib-helper.c src/eir.c \
+@EXPERIMENTAL_TRUE@tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c \
+@EXPERIMENTAL_TRUE@                            monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
 @EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
 @EXPERIMENTAL_TRUE@                            src/shared/mgmt.h src/shared/mgmt.c
 
-@EXPERIMENTAL_TRUE@tools_btmgmt_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
-@EXPERIMENTAL_TRUE@tools_btinfo_SOURCES = tools/btinfo.c
+@EXPERIMENTAL_TRUE@tools_btmgmt_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@                            monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout.h \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout-mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/hci.h src/shared/hci.c \
+@EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@                            src/shared/ringbuf.h src/shared/ringbuf.c
+
 @EXPERIMENTAL_TRUE@tools_btsnoop_SOURCES = tools/btsnoop.c \
 @EXPERIMENTAL_TRUE@                            src/shared/pcap.h src/shared/pcap.c \
 @EXPERIMENTAL_TRUE@                            src/shared/btsnoop.h src/shared/btsnoop.c
 
+@EXPERIMENTAL_TRUE@tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@                            monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c
+
 @EXPERIMENTAL_TRUE@tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c
 @EXPERIMENTAL_TRUE@tools_btiotest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 @EXPERIMENTAL_TRUE@tools_mpris_player_SOURCES = tools/mpris-player.c
 @EXPERIMENTAL_TRUE@tools_mpris_player_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
 @EXPERIMENTAL_TRUE@tools_cltest_SOURCES = tools/cltest.c monitor/mainloop.h monitor/mainloop.c
 @EXPERIMENTAL_TRUE@tools_cltest_LDADD = lib/libbluetooth-internal.la
+@EXPERIMENTAL_TRUE@tools_seq2bseq_SOURCES = tools/seq2bseq.c
+@EXPERIMENTAL_TRUE@tools_hex2hcd_SOURCES = tools/hex2hcd.c
+@EXPERIMENTAL_TRUE@tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h \
+@EXPERIMENTAL_TRUE@                            monitor/mainloop.h monitor/mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/io.h src/shared/io-mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout.h \
+@EXPERIMENTAL_TRUE@                            src/shared/timeout-mainloop.c \
+@EXPERIMENTAL_TRUE@                            src/shared/hci.h src/shared/hci.c \
+@EXPERIMENTAL_TRUE@                            src/shared/util.h src/shared/util.c \
+@EXPERIMENTAL_TRUE@                            src/shared/queue.h src/shared/queue.c \
+@EXPERIMENTAL_TRUE@                            src/shared/ringbuf.h src/shared/ringbuf.c
+
 @READLINE_TRUE@attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
 @READLINE_TRUE@                                attrib/gattrib.c btio/btio.c \
 @READLINE_TRUE@                                attrib/gatttool.h attrib/interactive.c \
@@ -1735,6 +2346,8 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \
 @READLINE_TRUE@tools_obexctl_LDADD = gdbus/libgdbus-internal.la \
 @READLINE_TRUE@                                @GLIB_LIBS@ @DBUS_LIBS@ -lreadline
 
+@EXPERIMENTAL_TRUE@tools_gatt_service_SOURCES = tools/gatt-service.c
+@EXPERIMENTAL_TRUE@tools_gatt_service_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ gdbus/libgdbus-internal.la
 @EXPERIMENTAL_TRUE@profiles_iap_iapd_SOURCES = profiles/iap/main.c
 @EXPERIMENTAL_TRUE@profiles_iap_iapd_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
 @CUPS_TRUE@cupsdir = $(libdir)/cups/backend
@@ -1806,36 +2419,63 @@ obexd_src_obexd_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/obexd/src  \
 obexd_src_obexd_SHORTNAME = obexd
 obexd_builtin_files = obexd/src/builtin.h $(obexd_builtin_nodist)
 nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files)
+@ANDROID_TRUE@android_plugindir = $(abs_top_srcdir)/android/.libs
 @ANDROID_TRUE@android_system_emulator_SOURCES = android/system-emulator.c \
 @ANDROID_TRUE@                                 monitor/mainloop.h monitor/mainloop.c
 
+@ANDROID_TRUE@android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c \
+@ANDROID_TRUE@                         monitor/mainloop.h monitor/mainloop.c \
+@ANDROID_TRUE@                         src/shared/btsnoop.h src/shared/btsnoop.c
+
 @ANDROID_TRUE@android_bluetoothd_SOURCES = android/main.c \
 @ANDROID_TRUE@                         src/log.c \
 @ANDROID_TRUE@                         android/hal-msg.h \
+@ANDROID_TRUE@                         android/audio-msg.h \
+@ANDROID_TRUE@                         android/sco-msg.h \
 @ANDROID_TRUE@                         android/utils.h \
 @ANDROID_TRUE@                         src/sdpd-database.c src/sdpd-server.c \
 @ANDROID_TRUE@                         src/sdpd-service.c src/sdpd-request.c \
-@ANDROID_TRUE@                         src/glib-helper.h src/glib-helper.c \
+@ANDROID_TRUE@                         src/uuid-helper.h src/uuid-helper.c \
 @ANDROID_TRUE@                         src/eir.h src/eir.c \
+@ANDROID_TRUE@                         src/shared/io.h src/shared/io-glib.c \
+@ANDROID_TRUE@                         src/shared/queue.h src/shared/queue.c \
 @ANDROID_TRUE@                         src/shared/util.h src/shared/util.c \
 @ANDROID_TRUE@                         src/shared/mgmt.h src/shared/mgmt.c \
+@ANDROID_TRUE@                         src/shared/ringbuf.h src/shared/ringbuf.c \
+@ANDROID_TRUE@                         src/shared/hfp.h src/shared/hfp.c \
+@ANDROID_TRUE@                         src/shared/gatt-db.h src/shared/gatt-db.c \
 @ANDROID_TRUE@                         android/bluetooth.h android/bluetooth.c \
 @ANDROID_TRUE@                         android/hidhost.h android/hidhost.c \
+@ANDROID_TRUE@                         android/ipc-common.h \
 @ANDROID_TRUE@                         android/ipc.h android/ipc.c \
 @ANDROID_TRUE@                         android/avdtp.h android/avdtp.c \
 @ANDROID_TRUE@                         android/a2dp.h android/a2dp.c \
+@ANDROID_TRUE@                         android/avctp.h android/avctp.c \
+@ANDROID_TRUE@                         android/avrcp.h android/avrcp.c \
+@ANDROID_TRUE@                         android/avrcp-lib.h android/avrcp-lib.c \
 @ANDROID_TRUE@                         android/socket.h android/socket.c \
 @ANDROID_TRUE@                         android/pan.h android/pan.c \
+@ANDROID_TRUE@                         android/handsfree.h android/handsfree.c \
+@ANDROID_TRUE@                         android/gatt.h android/gatt.c \
+@ANDROID_TRUE@                         android/health.h android/health.c \
+@ANDROID_TRUE@                         android/mcap-lib.h android/mcap-lib.c \
+@ANDROID_TRUE@                         attrib/att.c attrib/att.h \
+@ANDROID_TRUE@                         attrib/gatt.c attrib/gatt.h \
+@ANDROID_TRUE@                         attrib/gattrib.c attrib/gattrib.h \
 @ANDROID_TRUE@                         btio/btio.h btio/btio.c \
 @ANDROID_TRUE@                         src/sdp-client.h src/sdp-client.c \
 @ANDROID_TRUE@                         profiles/network/bnep.h profiles/network/bnep.c
 
 @ANDROID_TRUE@android_bluetoothd_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
-@ANDROID_TRUE@android_libhal_internal_la_SOURCES = android/hal.h android/hal-bluetooth.c \
-@ANDROID_TRUE@                                 android/hal-sock.c \
+@ANDROID_TRUE@android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \
+@ANDROID_TRUE@                                 android/hal-socket.c \
 @ANDROID_TRUE@                                 android/hal-hidhost.c \
+@ANDROID_TRUE@                                 android/hal-health.c \
 @ANDROID_TRUE@                                 android/hal-pan.c \
 @ANDROID_TRUE@                                 android/hal-a2dp.c \
+@ANDROID_TRUE@                                 android/hal-avrcp.c \
+@ANDROID_TRUE@                                 android/hal-handsfree.c \
+@ANDROID_TRUE@                                 android/hal-gatt.c \
 @ANDROID_TRUE@                                 android/hardware/bluetooth.h \
 @ANDROID_TRUE@                                 android/hardware/bt_av.h \
 @ANDROID_TRUE@                                 android/hardware/bt_gatt.h \
@@ -1850,10 +2490,15 @@ nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files)
 @ANDROID_TRUE@                                 android/hardware/bt_sock.h \
 @ANDROID_TRUE@                                 android/hardware/hardware.h \
 @ANDROID_TRUE@                                 android/cutils/properties.h \
+@ANDROID_TRUE@                                 android/ipc-common.h \
 @ANDROID_TRUE@                                 android/hal-log.h \
-@ANDROID_TRUE@                                 android/hal-ipc.h android/hal-ipc.c
+@ANDROID_TRUE@                                 android/hal-ipc.h android/hal-ipc.c \
+@ANDROID_TRUE@                                 android/hal-utils.h android/hal-utils.c
+
+@ANDROID_TRUE@android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+@ANDROID_TRUE@android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+@ANDROID_TRUE@                                 -no-undefined
 
-@ANDROID_TRUE@android_libhal_internal_la_CPPFLAGS = -I$(srcdir)/android
 @ANDROID_TRUE@android_haltest_SOURCES = android/client/haltest.c \
 @ANDROID_TRUE@                         android/client/pollhandler.h \
 @ANDROID_TRUE@                         android/client/pollhandler.c \
@@ -1864,33 +2509,100 @@ nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files)
 @ANDROID_TRUE@                         android/client/tabcompletion.c \
 @ANDROID_TRUE@                         android/client/if-main.h \
 @ANDROID_TRUE@                         android/client/if-av.c \
+@ANDROID_TRUE@                         android/client/if-rc.c \
 @ANDROID_TRUE@                         android/client/if-bt.c \
 @ANDROID_TRUE@                         android/client/if-gatt.c \
 @ANDROID_TRUE@                         android/client/if-hf.c \
 @ANDROID_TRUE@                         android/client/if-hh.c \
 @ANDROID_TRUE@                         android/client/if-pan.c \
+@ANDROID_TRUE@                         android/client/if-hl.c \
 @ANDROID_TRUE@                         android/client/if-sock.c \
-@ANDROID_TRUE@                         android/client/hwmodule.c \
+@ANDROID_TRUE@                         android/client/if-audio.c \
+@ANDROID_TRUE@                         android/client/if-sco.c \
+@ANDROID_TRUE@                         android/hardware/hardware.c \
 @ANDROID_TRUE@                         android/hal-utils.h android/hal-utils.c
 
-@ANDROID_TRUE@android_haltest_LDADD = android/libhal-internal.la
 @ANDROID_TRUE@android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
-@ANDROID_TRUE@                         -DPLATFORM_SDK_VERSION=19
+@ANDROID_TRUE@                         -DPLUGINDIR=\""$(android_plugindir)"\"
+
+@ANDROID_TRUE@android_haltest_LDFLAGS = -pthread -ldl -lm
+@ANDROID_TRUE@android_android_tester_SOURCES = emulator/btdev.h emulator/btdev.c \
+@ANDROID_TRUE@                         emulator/bthost.h emulator/bthost.c \
+@ANDROID_TRUE@                         emulator/smp.c \
+@ANDROID_TRUE@                         src/shared/crypto.h src/shared/crypto.c \
+@ANDROID_TRUE@                         src/shared/io.h src/shared/io-glib.c \
+@ANDROID_TRUE@                         src/shared/queue.h src/shared/queue.c \
+@ANDROID_TRUE@                         src/shared/util.h src/shared/util.c \
+@ANDROID_TRUE@                         src/shared/mgmt.h src/shared/mgmt.c \
+@ANDROID_TRUE@                         src/shared/hciemu.h src/shared/hciemu.c \
+@ANDROID_TRUE@                         src/shared/tester.h src/shared/tester.c \
+@ANDROID_TRUE@                         src/shared/timeout.h src/shared/timeout-glib.c \
+@ANDROID_TRUE@                         monitor/rfcomm.h \
+@ANDROID_TRUE@                         android/hardware/hardware.c \
+@ANDROID_TRUE@                         android/android-tester.c
+
+@ANDROID_TRUE@android_android_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+@ANDROID_TRUE@                         -DPLUGINDIR=\""$(android_plugindir)"\"
+
+@ANDROID_TRUE@android_android_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@ANDROID_TRUE@android_android_tester_LDFLAGS = -pthread -ldl
+@ANDROID_TRUE@android_ipc_tester_SOURCES = emulator/btdev.h emulator/btdev.c \
+@ANDROID_TRUE@                         emulator/bthost.h emulator/bthost.c \
+@ANDROID_TRUE@                         emulator/smp.c \
+@ANDROID_TRUE@                         src/shared/crypto.h src/shared/crypto.c \
+@ANDROID_TRUE@                         src/shared/io.h src/shared/io-glib.c \
+@ANDROID_TRUE@                         src/shared/queue.h src/shared/queue.c \
+@ANDROID_TRUE@                         src/shared/util.h src/shared/util.c \
+@ANDROID_TRUE@                         src/shared/mgmt.h src/shared/mgmt.c \
+@ANDROID_TRUE@                         src/shared/hciemu.h src/shared/hciemu.c \
+@ANDROID_TRUE@                         src/shared/tester.h src/shared/tester.c \
+@ANDROID_TRUE@                         src/shared/timeout.h src/shared/timeout-glib.c \
+@ANDROID_TRUE@                         android/hal-utils.h android/hal-utils.c \
+@ANDROID_TRUE@                         android/ipc-common.h android/ipc-tester.c
+
+@ANDROID_TRUE@android_ipc_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+@ANDROID_TRUE@android_ipc_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+@ANDROID_TRUE@android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
+@ANDROID_TRUE@                                 android/hal-msg.h \
+@ANDROID_TRUE@                                 android/hal-audio.c \
+@ANDROID_TRUE@                                 android/hardware/audio.h \
+@ANDROID_TRUE@                                 android/hardware/audio_effect.h \
+@ANDROID_TRUE@                                 android/hardware/hardware.h \
+@ANDROID_TRUE@                                 android/system/audio.h
+
+@ANDROID_TRUE@android_audio_sco_default_la_SOURCES = android/hal-log.h \
+@ANDROID_TRUE@                                 android/sco-msg.h \
+@ANDROID_TRUE@                                 android/hal-sco.c \
+@ANDROID_TRUE@                                 android/hardware/audio.h \
+@ANDROID_TRUE@                                 android/hardware/audio_effect.h \
+@ANDROID_TRUE@                                 android/hardware/hardware.h \
+@ANDROID_TRUE@                                 android/audio_utils/resampler.c \
+@ANDROID_TRUE@                                 android/audio_utils/resampler.h \
+@ANDROID_TRUE@                                 android/system/audio.h
+
+@ANDROID_TRUE@android_audio_sco_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+@ANDROID_TRUE@android_audio_sco_default_la_LIBADD = @SPEEXDSP_LIBS@
+@ANDROID_TRUE@android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+@ANDROID_TRUE@                                 -no-undefined -lrt
+
+@ANDROID_TRUE@android_test_ipc_SOURCES = android/test-ipc.c \
+@ANDROID_TRUE@                         src/shared/util.h src/shared/util.c \
+@ANDROID_TRUE@                         src/log.h src/log.c \
+@ANDROID_TRUE@                         android/ipc-common.h \
+@ANDROID_TRUE@                         android/ipc.c android/ipc.h
+
+@ANDROID_TRUE@android_test_ipc_LDADD = @GLIB_LIBS@
+@ANDROID_TRUE@android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+@ANDROID_TRUE@android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@
+@ANDROID_TRUE@android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+@ANDROID_TRUE@                                 -no-undefined -pthread -lrt
 
-@ANDROID_TRUE@android_haltest_LDFLAGS = -pthread
 @HID2HCI_TRUE@rulesdir = @UDEV_DIR@/rules.d
 @HID2HCI_TRUE@rules_DATA = tools/97-hid2hci.rules
 @TEST_TRUE@testdir = $(pkglibdir)/test
 @TEST_TRUE@test_SCRIPTS = $(test_scripts)
-AM_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
-                       -I$(srcdir)/gdbus -I$(srcdir)/btio
-
-unit_tests = unit/test-eir unit/test-uuid unit/test-textfile \
-       unit/test-crc unit/test-mgmt unit/test-sdp unit/test-avdtp \
-       unit/test-gdbus-client unit/test-gobex-header \
-       unit/test-gobex-packet unit/test-gobex \
-       unit/test-gobex-transfer unit/test-gobex-apparam unit/test-lib
-unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/glib-helper.c
+AM_CPPFLAGS = -I$(builddir)/lib -I$(srcdir)/gdbus
+unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/uuid-helper.c
 unit_test_eir_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 unit_test_uuid_SOURCES = unit/test-uuid.c
 unit_test_uuid_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
@@ -1898,7 +2610,19 @@ unit_test_textfile_SOURCES = unit/test-textfile.c src/textfile.h src/textfile.c
 unit_test_textfile_LDADD = @GLIB_LIBS@
 unit_test_crc_SOURCES = unit/test-crc.c monitor/crc.h monitor/crc.c
 unit_test_crc_LDADD = @GLIB_LIBS@
+unit_test_ringbuf_SOURCES = unit/test-ringbuf.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c
+
+unit_test_ringbuf_LDADD = @GLIB_LIBS@
+unit_test_queue_SOURCES = unit/test-queue.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/queue.h src/shared/queue.c
+
+unit_test_queue_LDADD = @GLIB_LIBS@
 unit_test_mgmt_SOURCES = unit/test-mgmt.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
                                src/shared/util.h src/shared/util.c \
                                src/shared/mgmt.h src/shared/mgmt.c
 
@@ -1906,6 +2630,7 @@ unit_test_mgmt_LDADD = @GLIB_LIBS@
 unit_test_sdp_SOURCES = unit/test-sdp.c \
                                src/shared/util.h src/shared/util.c \
                                src/sdpd.h src/sdpd-database.c \
+                               src/log.h src/log.c \
                                src/sdpd-service.c src/sdpd-request.c
 
 unit_test_sdp_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
@@ -1915,6 +2640,28 @@ unit_test_avdtp_SOURCES = unit/test-avdtp.c \
                                android/avdtp.c android/avdtp.h
 
 unit_test_avdtp_LDADD = @GLIB_LIBS@
+unit_test_avctp_SOURCES = unit/test-avctp.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/log.h src/log.c \
+                               android/avctp.c android/avctp.h
+
+unit_test_avctp_LDADD = @GLIB_LIBS@
+unit_test_avrcp_SOURCES = unit/test-avrcp.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/log.h src/log.c \
+                               android/avctp.c android/avctp.h \
+                               android/avrcp-lib.c android/avrcp-lib.h
+
+unit_test_avrcp_LDADD = @GLIB_LIBS@ lib/libbluetooth-internal.la
+unit_test_hfp_SOURCES = unit/test-hfp.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/mgmt.h src/shared/mgmt.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c \
+                               src/shared/hfp.h src/shared/hfp.c
+
+unit_test_hfp_LDADD = @GLIB_LIBS@
 unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c
 unit_test_gdbus_client_LDADD = gdbus/libgdbus-internal.la \
                                @GLIB_LIBS@ @DBUS_LIBS@
@@ -1944,8 +2691,7 @@ unit_test_lib_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 pkgconfigdir = $(libdir)/pkgconfig
 @LIBRARY_TRUE@pkgconfig_DATA = lib/bluez.pc
 DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles --enable-library \
-                                       --disable-systemd --disable-udev \
-                                       --enable-android
+                                       --disable-systemd --disable-udev
 
 DISTCLEANFILES = $(pkgconfig_DATA)
 MAINTAINERCLEANFILES = Makefile.in \
@@ -2109,20 +2855,47 @@ android/$(am__dirstamp):
 android/$(DEPDIR)/$(am__dirstamp):
        @$(MKDIR_P) android/$(DEPDIR)
        @: > android/$(DEPDIR)/$(am__dirstamp)
-android/android_libhal_internal_la-hal-bluetooth.lo:  \
+android/android_audio_a2dp_default_la-hal-audio.lo:  \
+       android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/audio.a2dp.default.la: $(android_audio_a2dp_default_la_OBJECTS) $(android_audio_a2dp_default_la_DEPENDENCIES) $(EXTRA_android_audio_a2dp_default_la_DEPENDENCIES) android/$(am__dirstamp)
+       $(AM_V_CCLD)$(android_audio_a2dp_default_la_LINK) $(am_android_audio_a2dp_default_la_rpath) $(android_audio_a2dp_default_la_OBJECTS) $(android_audio_a2dp_default_la_LIBADD) $(LIBS)
+android/android_audio_sco_default_la-hal-sco.lo:  \
+       android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/audio_utils/$(am__dirstamp):
+       @$(MKDIR_P) android/audio_utils
+       @: > android/audio_utils/$(am__dirstamp)
+android/audio_utils/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) android/audio_utils/$(DEPDIR)
+       @: > android/audio_utils/$(DEPDIR)/$(am__dirstamp)
+android/audio_utils/android_audio_sco_default_la-resampler.lo:  \
+       android/audio_utils/$(am__dirstamp) \
+       android/audio_utils/$(DEPDIR)/$(am__dirstamp)
+android/audio.sco.default.la: $(android_audio_sco_default_la_OBJECTS) $(android_audio_sco_default_la_DEPENDENCIES) $(EXTRA_android_audio_sco_default_la_DEPENDENCIES) android/$(am__dirstamp)
+       $(AM_V_CCLD)$(android_audio_sco_default_la_LINK) $(am_android_audio_sco_default_la_rpath) $(android_audio_sco_default_la_OBJECTS) $(android_audio_sco_default_la_LIBADD) $(LIBS)
+android/android_bluetooth_default_la-hal-bluetooth.lo:  \
        android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
-android/android_libhal_internal_la-hal-sock.lo:  \
+android/android_bluetooth_default_la-hal-socket.lo:  \
        android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
-android/android_libhal_internal_la-hal-hidhost.lo:  \
+android/android_bluetooth_default_la-hal-hidhost.lo:  \
        android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
-android/android_libhal_internal_la-hal-pan.lo:  \
+android/android_bluetooth_default_la-hal-health.lo:  \
        android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
-android/android_libhal_internal_la-hal-a2dp.lo:  \
+android/android_bluetooth_default_la-hal-pan.lo:  \
        android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
-android/android_libhal_internal_la-hal-ipc.lo:  \
+android/android_bluetooth_default_la-hal-a2dp.lo:  \
        android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
-android/libhal-internal.la: $(android_libhal_internal_la_OBJECTS) $(android_libhal_internal_la_DEPENDENCIES) $(EXTRA_android_libhal_internal_la_DEPENDENCIES) android/$(am__dirstamp)
-       $(AM_V_CCLD)$(LINK) $(am_android_libhal_internal_la_rpath) $(android_libhal_internal_la_OBJECTS) $(android_libhal_internal_la_LIBADD) $(LIBS)
+android/android_bluetooth_default_la-hal-avrcp.lo:  \
+       android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-handsfree.lo:  \
+       android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-gatt.lo:  \
+       android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-ipc.lo:  \
+       android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_bluetooth_default_la-hal-utils.lo:  \
+       android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/bluetooth.default.la: $(android_bluetooth_default_la_OBJECTS) $(android_bluetooth_default_la_DEPENDENCIES) $(EXTRA_android_bluetooth_default_la_DEPENDENCIES) android/$(am__dirstamp)
+       $(AM_V_CCLD)$(android_bluetooth_default_la_LINK) $(am_android_bluetooth_default_la_rpath) $(android_bluetooth_default_la_OBJECTS) $(android_bluetooth_default_la_LIBADD) $(LIBS)
 gdbus/$(am__dirstamp):
        @$(MKDIR_P) gdbus
        @: > gdbus/$(am__dirstamp)
@@ -2358,6 +3131,62 @@ clean-udevPROGRAMS:
        list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
        echo " rm -f" $$list; \
        rm -f $$list
+emulator/$(am__dirstamp):
+       @$(MKDIR_P) emulator
+       @: > emulator/$(am__dirstamp)
+emulator/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) emulator/$(DEPDIR)
+       @: > emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/android_android_tester-btdev.$(OBJEXT):  \
+       emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/android_android_tester-bthost.$(OBJEXT):  \
+       emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/android_android_tester-smp.$(OBJEXT):  \
+       emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp)
+src/shared/$(am__dirstamp):
+       @$(MKDIR_P) src/shared
+       @: > src/shared/$(am__dirstamp)
+src/shared/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) src/shared/$(DEPDIR)
+       @: > src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-crypto.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-io-glib.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-queue.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-util.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-mgmt.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-hciemu.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-tester.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_android_tester-timeout-glib.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+android/hardware/$(am__dirstamp):
+       @$(MKDIR_P) android/hardware
+       @: > android/hardware/$(am__dirstamp)
+android/hardware/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) android/hardware/$(DEPDIR)
+       @: > android/hardware/$(DEPDIR)/$(am__dirstamp)
+android/hardware/android_android_tester-hardware.$(OBJEXT):  \
+       android/hardware/$(am__dirstamp) \
+       android/hardware/$(DEPDIR)/$(am__dirstamp)
+android/android_android_tester-android-tester.$(OBJEXT):  \
+       android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android-tester$(EXEEXT): $(android_android_tester_OBJECTS) $(android_android_tester_DEPENDENCIES) $(EXTRA_android_android_tester_DEPENDENCIES) android/$(am__dirstamp)
+       @rm -f android/android-tester$(EXEEXT)
+       $(AM_V_CCLD)$(android_android_tester_LINK) $(android_android_tester_OBJECTS) $(android_android_tester_LDADD) $(LIBS)
 android/main.$(OBJEXT): android/$(am__dirstamp) \
        android/$(DEPDIR)/$(am__dirstamp)
 src/$(am__dirstamp):
@@ -2375,19 +3204,23 @@ src/sdpd-service.$(OBJEXT): src/$(am__dirstamp) \
        src/$(DEPDIR)/$(am__dirstamp)
 src/sdpd-request.$(OBJEXT): src/$(am__dirstamp) \
        src/$(DEPDIR)/$(am__dirstamp)
-src/glib-helper.$(OBJEXT): src/$(am__dirstamp) \
+src/uuid-helper.$(OBJEXT): src/$(am__dirstamp) \
        src/$(DEPDIR)/$(am__dirstamp)
 src/eir.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
-src/shared/$(am__dirstamp):
-       @$(MKDIR_P) src/shared
-       @: > src/shared/$(am__dirstamp)
-src/shared/$(DEPDIR)/$(am__dirstamp):
-       @$(MKDIR_P) src/shared/$(DEPDIR)
-       @: > src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/io-glib.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/queue.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
 src/shared/util.$(OBJEXT): src/shared/$(am__dirstamp) \
        src/shared/$(DEPDIR)/$(am__dirstamp)
 src/shared/mgmt.$(OBJEXT): src/shared/$(am__dirstamp) \
        src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/ringbuf.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/hfp.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/gatt-db.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
 android/bluetooth.$(OBJEXT): android/$(am__dirstamp) \
        android/$(DEPDIR)/$(am__dirstamp)
 android/hidhost.$(OBJEXT): android/$(am__dirstamp) \
@@ -2398,10 +3231,36 @@ android/avdtp.$(OBJEXT): android/$(am__dirstamp) \
        android/$(DEPDIR)/$(am__dirstamp)
 android/a2dp.$(OBJEXT): android/$(am__dirstamp) \
        android/$(DEPDIR)/$(am__dirstamp)
+android/avctp.$(OBJEXT): android/$(am__dirstamp) \
+       android/$(DEPDIR)/$(am__dirstamp)
+android/avrcp.$(OBJEXT): android/$(am__dirstamp) \
+       android/$(DEPDIR)/$(am__dirstamp)
+android/avrcp-lib.$(OBJEXT): android/$(am__dirstamp) \
+       android/$(DEPDIR)/$(am__dirstamp)
 android/socket.$(OBJEXT): android/$(am__dirstamp) \
        android/$(DEPDIR)/$(am__dirstamp)
 android/pan.$(OBJEXT): android/$(am__dirstamp) \
        android/$(DEPDIR)/$(am__dirstamp)
+android/handsfree.$(OBJEXT): android/$(am__dirstamp) \
+       android/$(DEPDIR)/$(am__dirstamp)
+android/gatt.$(OBJEXT): android/$(am__dirstamp) \
+       android/$(DEPDIR)/$(am__dirstamp)
+android/health.$(OBJEXT): android/$(am__dirstamp) \
+       android/$(DEPDIR)/$(am__dirstamp)
+android/mcap-lib.$(OBJEXT): android/$(am__dirstamp) \
+       android/$(DEPDIR)/$(am__dirstamp)
+attrib/$(am__dirstamp):
+       @$(MKDIR_P) attrib
+       @: > attrib/$(am__dirstamp)
+attrib/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) attrib/$(DEPDIR)
+       @: > 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)
@@ -2423,6 +3282,21 @@ profiles/network/bnep.$(OBJEXT): profiles/network/$(am__dirstamp) \
 android/bluetoothd$(EXEEXT): $(android_bluetoothd_OBJECTS) $(android_bluetoothd_DEPENDENCIES) $(EXTRA_android_bluetoothd_DEPENDENCIES) android/$(am__dirstamp)
        @rm -f android/bluetoothd$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(android_bluetoothd_OBJECTS) $(android_bluetoothd_LDADD) $(LIBS)
+android/bluetoothd-snoop.$(OBJEXT): android/$(am__dirstamp) \
+       android/$(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)
+src/shared/btsnoop.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+android/bluetoothd-snoop$(EXEEXT): $(android_bluetoothd_snoop_OBJECTS) $(android_bluetoothd_snoop_DEPENDENCIES) $(EXTRA_android_bluetoothd_snoop_DEPENDENCIES) android/$(am__dirstamp)
+       @rm -f android/bluetoothd-snoop$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(android_bluetoothd_snoop_OBJECTS) $(android_bluetoothd_snoop_LDADD) $(LIBS)
 android/client/$(am__dirstamp):
        @$(MKDIR_P) android/client
        @: > android/client/$(am__dirstamp)
@@ -2447,6 +3321,9 @@ android/client/android_haltest-tabcompletion.$(OBJEXT):  \
 android/client/android_haltest-if-av.$(OBJEXT):  \
        android/client/$(am__dirstamp) \
        android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-rc.$(OBJEXT):  \
+       android/client/$(am__dirstamp) \
+       android/client/$(DEPDIR)/$(am__dirstamp)
 android/client/android_haltest-if-bt.$(OBJEXT):  \
        android/client/$(am__dirstamp) \
        android/client/$(DEPDIR)/$(am__dirstamp)
@@ -2462,44 +3339,75 @@ android/client/android_haltest-if-hh.$(OBJEXT):  \
 android/client/android_haltest-if-pan.$(OBJEXT):  \
        android/client/$(am__dirstamp) \
        android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-hl.$(OBJEXT):  \
+       android/client/$(am__dirstamp) \
+       android/client/$(DEPDIR)/$(am__dirstamp)
 android/client/android_haltest-if-sock.$(OBJEXT):  \
        android/client/$(am__dirstamp) \
        android/client/$(DEPDIR)/$(am__dirstamp)
-android/client/android_haltest-hwmodule.$(OBJEXT):  \
+android/client/android_haltest-if-audio.$(OBJEXT):  \
        android/client/$(am__dirstamp) \
        android/client/$(DEPDIR)/$(am__dirstamp)
+android/client/android_haltest-if-sco.$(OBJEXT):  \
+       android/client/$(am__dirstamp) \
+       android/client/$(DEPDIR)/$(am__dirstamp)
+android/hardware/android_haltest-hardware.$(OBJEXT):  \
+       android/hardware/$(am__dirstamp) \
+       android/hardware/$(DEPDIR)/$(am__dirstamp)
 android/android_haltest-hal-utils.$(OBJEXT): android/$(am__dirstamp) \
        android/$(DEPDIR)/$(am__dirstamp)
 android/haltest$(EXEEXT): $(android_haltest_OBJECTS) $(android_haltest_DEPENDENCIES) $(EXTRA_android_haltest_DEPENDENCIES) android/$(am__dirstamp)
        @rm -f android/haltest$(EXEEXT)
        $(AM_V_CCLD)$(android_haltest_LINK) $(android_haltest_OBJECTS) $(android_haltest_LDADD) $(LIBS)
+emulator/android_ipc_tester-btdev.$(OBJEXT): emulator/$(am__dirstamp) \
+       emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/android_ipc_tester-bthost.$(OBJEXT):  \
+       emulator/$(am__dirstamp) emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/android_ipc_tester-smp.$(OBJEXT): emulator/$(am__dirstamp) \
+       emulator/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-crypto.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-io-glib.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-queue.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-util.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-mgmt.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-hciemu.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-tester.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/android_ipc_tester-timeout-glib.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+android/android_ipc_tester-hal-utils.$(OBJEXT):  \
+       android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/android_ipc_tester-ipc-tester.$(OBJEXT):  \
+       android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp)
+android/ipc-tester$(EXEEXT): $(android_ipc_tester_OBJECTS) $(android_ipc_tester_DEPENDENCIES) $(EXTRA_android_ipc_tester_DEPENDENCIES) android/$(am__dirstamp)
+       @rm -f android/ipc-tester$(EXEEXT)
+       $(AM_V_CCLD)$(android_ipc_tester_LINK) $(android_ipc_tester_OBJECTS) $(android_ipc_tester_LDADD) $(LIBS)
 android/system-emulator.$(OBJEXT): android/$(am__dirstamp) \
        android/$(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)
 android/system-emulator$(EXEEXT): $(android_system_emulator_OBJECTS) $(android_system_emulator_DEPENDENCIES) $(EXTRA_android_system_emulator_DEPENDENCIES) android/$(am__dirstamp)
        @rm -f android/system-emulator$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(android_system_emulator_OBJECTS) $(android_system_emulator_LDADD) $(LIBS)
-attrib/$(am__dirstamp):
-       @$(MKDIR_P) attrib
-       @: > attrib/$(am__dirstamp)
-attrib/$(DEPDIR)/$(am__dirstamp):
-       @$(MKDIR_P) attrib/$(DEPDIR)
-       @: > attrib/$(DEPDIR)/$(am__dirstamp)
+android/test-ipc.$(OBJEXT): android/$(am__dirstamp) \
+       android/$(DEPDIR)/$(am__dirstamp)
+android/test-ipc$(EXEEXT): $(android_test_ipc_OBJECTS) $(android_test_ipc_DEPENDENCIES) $(EXTRA_android_test_ipc_DEPENDENCIES) android/$(am__dirstamp)
+       @rm -f android/test-ipc$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(android_test_ipc_OBJECTS) $(android_test_ipc_LDADD) $(LIBS)
 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)
 attrib/interactive.$(OBJEXT): attrib/$(am__dirstamp) \
        attrib/$(DEPDIR)/$(am__dirstamp)
 attrib/utils.$(OBJEXT): attrib/$(am__dirstamp) \
@@ -2524,12 +3432,6 @@ monitor/uuid.$(OBJEXT): monitor/$(am__dirstamp) \
 client/bluetoothctl$(EXEEXT): $(client_bluetoothctl_OBJECTS) $(client_bluetoothctl_DEPENDENCIES) $(EXTRA_client_bluetoothctl_DEPENDENCIES) client/$(am__dirstamp)
        @rm -f client/bluetoothctl$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(client_bluetoothctl_OBJECTS) $(client_bluetoothctl_LDADD) $(LIBS)
-emulator/$(am__dirstamp):
-       @$(MKDIR_P) emulator
-       @: > emulator/$(am__dirstamp)
-emulator/$(DEPDIR)/$(am__dirstamp):
-       @$(MKDIR_P) emulator/$(DEPDIR)
-       @: > emulator/$(DEPDIR)/$(am__dirstamp)
 emulator/b1ee.$(OBJEXT): emulator/$(am__dirstamp) \
        emulator/$(DEPDIR)/$(am__dirstamp)
 emulator/b1ee$(EXEEXT): $(emulator_b1ee_OBJECTS) $(emulator_b1ee_DEPENDENCIES) $(EXTRA_emulator_b1ee_DEPENDENCIES) emulator/$(am__dirstamp)
@@ -2537,6 +3439,10 @@ emulator/b1ee$(EXEEXT): $(emulator_b1ee_OBJECTS) $(emulator_b1ee_DEPENDENCIES) $
        $(AM_V_CCLD)$(LINK) $(emulator_b1ee_OBJECTS) $(emulator_b1ee_LDADD) $(LIBS)
 emulator/main.$(OBJEXT): emulator/$(am__dirstamp) \
        emulator/$(DEPDIR)/$(am__dirstamp)
+src/shared/timeout-mainloop.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/crypto.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
 emulator/server.$(OBJEXT): emulator/$(am__dirstamp) \
        emulator/$(DEPDIR)/$(am__dirstamp)
 emulator/vhci.$(OBJEXT): emulator/$(am__dirstamp) \
@@ -2545,18 +3451,29 @@ emulator/btdev.$(OBJEXT): emulator/$(am__dirstamp) \
        emulator/$(DEPDIR)/$(am__dirstamp)
 emulator/bthost.$(OBJEXT): emulator/$(am__dirstamp) \
        emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/smp.$(OBJEXT): emulator/$(am__dirstamp) \
+       emulator/$(DEPDIR)/$(am__dirstamp)
 emulator/amp.$(OBJEXT): emulator/$(am__dirstamp) \
        emulator/$(DEPDIR)/$(am__dirstamp)
+emulator/le.$(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)
+emulator/hfp.$(OBJEXT): emulator/$(am__dirstamp) \
+       emulator/$(DEPDIR)/$(am__dirstamp)
+src/shared/io-mainloop.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+emulator/hfp$(EXEEXT): $(emulator_hfp_OBJECTS) $(emulator_hfp_DEPENDENCIES) $(EXTRA_emulator_hfp_DEPENDENCIES) emulator/$(am__dirstamp)
+       @rm -f emulator/hfp$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(emulator_hfp_OBJECTS) $(emulator_hfp_LDADD) $(LIBS)
 monitor/main.$(OBJEXT): monitor/$(am__dirstamp) \
        monitor/$(DEPDIR)/$(am__dirstamp)
 monitor/display.$(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/ellisys.$(OBJEXT): monitor/$(am__dirstamp) \
        monitor/$(DEPDIR)/$(am__dirstamp)
 monitor/control.$(OBJEXT): monitor/$(am__dirstamp) \
        monitor/$(DEPDIR)/$(am__dirstamp)
@@ -2566,13 +3483,19 @@ monitor/vendor.$(OBJEXT): monitor/$(am__dirstamp) \
        monitor/$(DEPDIR)/$(am__dirstamp)
 monitor/lmp.$(OBJEXT): monitor/$(am__dirstamp) \
        monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/crc.$(OBJEXT): monitor/$(am__dirstamp) \
+       monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/ll.$(OBJEXT): monitor/$(am__dirstamp) \
+       monitor/$(DEPDIR)/$(am__dirstamp)
 monitor/l2cap.$(OBJEXT): monitor/$(am__dirstamp) \
        monitor/$(DEPDIR)/$(am__dirstamp)
 monitor/sdp.$(OBJEXT): monitor/$(am__dirstamp) \
        monitor/$(DEPDIR)/$(am__dirstamp)
-monitor/crc.$(OBJEXT): monitor/$(am__dirstamp) \
+monitor/hwdb.$(OBJEXT): monitor/$(am__dirstamp) \
        monitor/$(DEPDIR)/$(am__dirstamp)
-monitor/ll.$(OBJEXT): monitor/$(am__dirstamp) \
+monitor/keys.$(OBJEXT): monitor/$(am__dirstamp) \
+       monitor/$(DEPDIR)/$(am__dirstamp)
+monitor/analyze.$(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)
@@ -2963,7 +3886,7 @@ 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/bluetoothd-uuid-helper.$(OBJEXT): src/$(am__dirstamp) \
        src/$(DEPDIR)/$(am__dirstamp)
 src/bluetoothd-plugin.$(OBJEXT): src/$(am__dirstamp) \
        src/$(DEPDIR)/$(am__dirstamp)
@@ -2979,12 +3902,23 @@ src/bluetoothd-profile.$(OBJEXT): src/$(am__dirstamp) \
        src/$(DEPDIR)/$(am__dirstamp)
 src/bluetoothd-service.$(OBJEXT): src/$(am__dirstamp) \
        src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-gatt-dbus.$(OBJEXT): src/$(am__dirstamp) \
+       src/$(DEPDIR)/$(am__dirstamp)
+src/bluetoothd-gatt.$(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-eir.$(OBJEXT): src/$(am__dirstamp) \
        src/$(DEPDIR)/$(am__dirstamp)
+src/shared/bluetoothd-io-glib.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/bluetoothd-timeout-glib.$(OBJEXT):  \
+       src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/bluetoothd-queue.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
 src/shared/bluetoothd-util.$(OBJEXT): src/shared/$(am__dirstamp) \
        src/shared/$(DEPDIR)/$(am__dirstamp)
 src/shared/bluetoothd-mgmt.$(OBJEXT): src/shared/$(am__dirstamp) \
@@ -2998,6 +3932,13 @@ tools/$(am__dirstamp):
 tools/$(DEPDIR)/$(am__dirstamp):
        @$(MKDIR_P) tools/$(DEPDIR)
        @: > tools/$(DEPDIR)/$(am__dirstamp)
+tools/3dsp.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+src/shared/hci.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
+tools/3dsp$(EXEEXT): $(tools_3dsp_OBJECTS) $(tools_3dsp_DEPENDENCIES) $(EXTRA_tools_3dsp_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/3dsp$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_3dsp_OBJECTS) $(tools_3dsp_LDADD) $(LIBS)
 tools/amptest.$(OBJEXT): tools/$(am__dirstamp) \
        tools/$(DEPDIR)/$(am__dirstamp)
 tools/amptest$(EXEEXT): $(tools_amptest_OBJECTS) $(tools_amptest_DEPENDENCIES) $(EXTRA_tools_amptest_DEPENDENCIES) tools/$(am__dirstamp)
@@ -3038,6 +3979,11 @@ src/oui.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp)
 tools/bdaddr$(EXEEXT): $(tools_bdaddr_OBJECTS) $(tools_bdaddr_DEPENDENCIES) $(EXTRA_tools_bdaddr_DEPENDENCIES) tools/$(am__dirstamp)
        @rm -f tools/bdaddr$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(tools_bdaddr_OBJECTS) $(tools_bdaddr_LDADD) $(LIBS)
+tools/bluemoon.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/bluemoon$(EXEEXT): $(tools_bluemoon_OBJECTS) $(tools_bluemoon_DEPENDENCIES) $(EXTRA_tools_bluemoon_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/bluemoon$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_bluemoon_OBJECTS) $(tools_bluemoon_LDADD) $(LIBS)
 tools/bluetooth-player.$(OBJEXT): tools/$(am__dirstamp) \
        tools/$(DEPDIR)/$(am__dirstamp)
 tools/bluetooth-player$(EXEEXT): $(tools_bluetooth_player_OBJECTS) $(tools_bluetooth_player_DEPENDENCIES) $(EXTRA_tools_bluetooth_player_DEPENDENCIES) tools/$(am__dirstamp)
@@ -3063,12 +4009,15 @@ tools/btmgmt.$(OBJEXT): tools/$(am__dirstamp) \
 tools/btmgmt$(EXEEXT): $(tools_btmgmt_OBJECTS) $(tools_btmgmt_DEPENDENCIES) $(EXTRA_tools_btmgmt_DEPENDENCIES) tools/$(am__dirstamp)
        @rm -f tools/btmgmt$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(tools_btmgmt_OBJECTS) $(tools_btmgmt_LDADD) $(LIBS)
+tools/btproxy.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/btproxy$(EXEEXT): $(tools_btproxy_OBJECTS) $(tools_btproxy_DEPENDENCIES) $(EXTRA_tools_btproxy_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/btproxy$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_btproxy_OBJECTS) $(tools_btproxy_LDADD) $(LIBS)
 tools/btsnoop.$(OBJEXT): tools/$(am__dirstamp) \
        tools/$(DEPDIR)/$(am__dirstamp)
 src/shared/pcap.$(OBJEXT): src/shared/$(am__dirstamp) \
        src/shared/$(DEPDIR)/$(am__dirstamp)
-src/shared/btsnoop.$(OBJEXT): src/shared/$(am__dirstamp) \
-       src/shared/$(DEPDIR)/$(am__dirstamp)
 tools/btsnoop$(EXEEXT): $(tools_btsnoop_OBJECTS) $(tools_btsnoop_DEPENDENCIES) $(EXTRA_tools_btsnoop_DEPENDENCIES) tools/$(am__dirstamp)
        @rm -f tools/btsnoop$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(tools_btsnoop_OBJECTS) $(tools_btsnoop_LDADD) $(LIBS)
@@ -3088,9 +4037,21 @@ src/shared/hciemu.$(OBJEXT): src/shared/$(am__dirstamp) \
        src/shared/$(DEPDIR)/$(am__dirstamp)
 src/shared/tester.$(OBJEXT): src/shared/$(am__dirstamp) \
        src/shared/$(DEPDIR)/$(am__dirstamp)
+src/shared/timeout-glib.$(OBJEXT): src/shared/$(am__dirstamp) \
+       src/shared/$(DEPDIR)/$(am__dirstamp)
 tools/gap-tester$(EXEEXT): $(tools_gap_tester_OBJECTS) $(tools_gap_tester_DEPENDENCIES) $(EXTRA_tools_gap_tester_DEPENDENCIES) tools/$(am__dirstamp)
        @rm -f tools/gap-tester$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(tools_gap_tester_OBJECTS) $(tools_gap_tester_LDADD) $(LIBS)
+tools/gatt-service.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/gatt-service$(EXEEXT): $(tools_gatt_service_OBJECTS) $(tools_gatt_service_DEPENDENCIES) $(EXTRA_tools_gatt_service_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/gatt-service$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_gatt_service_OBJECTS) $(tools_gatt_service_LDADD) $(LIBS)
+tools/hci-tester.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hci-tester$(EXEEXT): $(tools_hci_tester_OBJECTS) $(tools_hci_tester_DEPENDENCIES) $(EXTRA_tools_hci_tester_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/hci-tester$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_hci_tester_OBJECTS) $(tools_hci_tester_LDADD) $(LIBS)
 tools/hciattach.$(OBJEXT): tools/$(am__dirstamp) \
        tools/$(DEPDIR)/$(am__dirstamp)
 tools/hciattach_st.$(OBJEXT): tools/$(am__dirstamp) \
@@ -3105,6 +4066,8 @@ tools/hciattach_qualcomm.$(OBJEXT): tools/$(am__dirstamp) \
        tools/$(DEPDIR)/$(am__dirstamp)
 tools/hciattach_intel.$(OBJEXT): tools/$(am__dirstamp) \
        tools/$(DEPDIR)/$(am__dirstamp)
+tools/hciattach_bcm43xx.$(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)
@@ -3187,6 +4150,11 @@ tools/hcitool.$(OBJEXT): tools/$(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/hex2hcd.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/hex2hcd$(EXEEXT): $(tools_hex2hcd_OBJECTS) $(tools_hex2hcd_DEPENDENCIES) $(EXTRA_tools_hex2hcd_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/hex2hcd$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_hex2hcd_OBJECTS) $(tools_hex2hcd_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)
@@ -3197,6 +4165,11 @@ tools/hwdb.$(OBJEXT): tools/$(am__dirstamp) \
 tools/hwdb$(EXEEXT): $(tools_hwdb_OBJECTS) $(tools_hwdb_DEPENDENCIES) $(EXTRA_tools_hwdb_DEPENDENCIES) tools/$(am__dirstamp)
        @rm -f tools/hwdb$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(tools_hwdb_OBJECTS) $(tools_hwdb_LDADD) $(LIBS)
+tools/ibeacon.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/ibeacon$(EXEEXT): $(tools_ibeacon_OBJECTS) $(tools_ibeacon_DEPENDENCIES) $(EXTRA_tools_ibeacon_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/ibeacon$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_ibeacon_OBJECTS) $(tools_ibeacon_LDADD) $(LIBS)
 tools/l2cap-tester.$(OBJEXT): tools/$(am__dirstamp) \
        tools/$(DEPDIR)/$(am__dirstamp)
 tools/l2cap-tester$(EXEEXT): $(tools_l2cap_tester_OBJECTS) $(tools_l2cap_tester_DEPENDENCIES) $(EXTRA_tools_l2cap_tester_DEPENDENCIES) tools/$(am__dirstamp)
@@ -3259,6 +4232,11 @@ tools/rfcomm.$(OBJEXT): tools/$(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/rfcomm-tester.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/rfcomm-tester$(EXEEXT): $(tools_rfcomm_tester_OBJECTS) $(tools_rfcomm_tester_DEPENDENCIES) $(EXTRA_tools_rfcomm_tester_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/rfcomm-tester$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_rfcomm_tester_OBJECTS) $(tools_rfcomm_tester_LDADD) $(LIBS)
 tools/sco-tester.$(OBJEXT): tools/$(am__dirstamp) \
        tools/$(DEPDIR)/$(am__dirstamp)
 tools/sco-tester$(EXEEXT): $(tools_sco_tester_OBJECTS) $(tools_sco_tester_DEPENDENCIES) $(EXTRA_tools_sco_tester_DEPENDENCIES) tools/$(am__dirstamp)
@@ -3276,6 +4254,11 @@ src/sdp-xml.$(OBJEXT): src/$(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)
+tools/seq2bseq.$(OBJEXT): tools/$(am__dirstamp) \
+       tools/$(DEPDIR)/$(am__dirstamp)
+tools/seq2bseq$(EXEEXT): $(tools_seq2bseq_OBJECTS) $(tools_seq2bseq_DEPENDENCIES) $(EXTRA_tools_seq2bseq_DEPENDENCIES) tools/$(am__dirstamp)
+       @rm -f tools/seq2bseq$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(tools_seq2bseq_OBJECTS) $(tools_seq2bseq_LDADD) $(LIBS)
 tools/smp-tester.$(OBJEXT): tools/$(am__dirstamp) \
        tools/$(DEPDIR)/$(am__dirstamp)
 tools/smp-tester$(EXEEXT): $(tools_smp_tester_OBJECTS) $(tools_smp_tester_DEPENDENCIES) $(EXTRA_tools_smp_tester_DEPENDENCIES) tools/$(am__dirstamp)
@@ -3287,11 +4270,21 @@ unit/$(am__dirstamp):
 unit/$(DEPDIR)/$(am__dirstamp):
        @$(MKDIR_P) unit/$(DEPDIR)
        @: > unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-avctp.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-avctp$(EXEEXT): $(unit_test_avctp_OBJECTS) $(unit_test_avctp_DEPENDENCIES) $(EXTRA_unit_test_avctp_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-avctp$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_avctp_OBJECTS) $(unit_test_avctp_LDADD) $(LIBS)
 unit/test-avdtp.$(OBJEXT): unit/$(am__dirstamp) \
        unit/$(DEPDIR)/$(am__dirstamp)
 unit/test-avdtp$(EXEEXT): $(unit_test_avdtp_OBJECTS) $(unit_test_avdtp_DEPENDENCIES) $(EXTRA_unit_test_avdtp_DEPENDENCIES) unit/$(am__dirstamp)
        @rm -f unit/test-avdtp$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(unit_test_avdtp_OBJECTS) $(unit_test_avdtp_LDADD) $(LIBS)
+unit/test-avrcp.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-avrcp$(EXEEXT): $(unit_test_avrcp_OBJECTS) $(unit_test_avrcp_DEPENDENCIES) $(EXTRA_unit_test_avrcp_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-avrcp$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_avrcp_OBJECTS) $(unit_test_avrcp_LDADD) $(LIBS)
 unit/test-crc.$(OBJEXT): unit/$(am__dirstamp) \
        unit/$(DEPDIR)/$(am__dirstamp)
 unit/test-crc$(EXEEXT): $(unit_test_crc_OBJECTS) $(unit_test_crc_DEPENDENCIES) $(EXTRA_unit_test_crc_DEPENDENCIES) unit/$(am__dirstamp)
@@ -3334,6 +4327,11 @@ unit/test-gobex-transfer.$(OBJEXT): unit/$(am__dirstamp) \
 unit/test-gobex-transfer$(EXEEXT): $(unit_test_gobex_transfer_OBJECTS) $(unit_test_gobex_transfer_DEPENDENCIES) $(EXTRA_unit_test_gobex_transfer_DEPENDENCIES) unit/$(am__dirstamp)
        @rm -f unit/test-gobex-transfer$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(unit_test_gobex_transfer_OBJECTS) $(unit_test_gobex_transfer_LDADD) $(LIBS)
+unit/test-hfp.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-hfp$(EXEEXT): $(unit_test_hfp_OBJECTS) $(unit_test_hfp_DEPENDENCIES) $(EXTRA_unit_test_hfp_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-hfp$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_hfp_OBJECTS) $(unit_test_hfp_LDADD) $(LIBS)
 unit/test-lib.$(OBJEXT): unit/$(am__dirstamp) \
        unit/$(DEPDIR)/$(am__dirstamp)
 unit/test-lib$(EXEEXT): $(unit_test_lib_OBJECTS) $(unit_test_lib_DEPENDENCIES) $(EXTRA_unit_test_lib_DEPENDENCIES) unit/$(am__dirstamp)
@@ -3344,6 +4342,16 @@ unit/test-mgmt.$(OBJEXT): unit/$(am__dirstamp) \
 unit/test-mgmt$(EXEEXT): $(unit_test_mgmt_OBJECTS) $(unit_test_mgmt_DEPENDENCIES) $(EXTRA_unit_test_mgmt_DEPENDENCIES) unit/$(am__dirstamp)
        @rm -f unit/test-mgmt$(EXEEXT)
        $(AM_V_CCLD)$(LINK) $(unit_test_mgmt_OBJECTS) $(unit_test_mgmt_LDADD) $(LIBS)
+unit/test-queue.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-queue$(EXEEXT): $(unit_test_queue_OBJECTS) $(unit_test_queue_DEPENDENCIES) $(EXTRA_unit_test_queue_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-queue$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_queue_OBJECTS) $(unit_test_queue_LDADD) $(LIBS)
+unit/test-ringbuf.$(OBJEXT): unit/$(am__dirstamp) \
+       unit/$(DEPDIR)/$(am__dirstamp)
+unit/test-ringbuf$(EXEEXT): $(unit_test_ringbuf_OBJECTS) $(unit_test_ringbuf_DEPENDENCIES) $(EXTRA_unit_test_ringbuf_DEPENDENCIES) unit/$(am__dirstamp)
+       @rm -f unit/test-ringbuf$(EXEEXT)
+       $(AM_V_CCLD)$(LINK) $(unit_test_ringbuf_OBJECTS) $(unit_test_ringbuf_LDADD) $(LIBS)
 unit/test-sdp.$(OBJEXT): unit/$(am__dirstamp) \
        unit/$(DEPDIR)/$(am__dirstamp)
 unit/test-sdp$(EXEEXT): $(unit_test_sdp_OBJECTS) $(unit_test_sdp_DEPENDENCIES) $(EXTRA_unit_test_sdp_DEPENDENCIES) unit/$(am__dirstamp)
@@ -3400,40 +4408,73 @@ uninstall-testSCRIPTS:
 mostlyclean-compile:
        -rm -f *.$(OBJEXT)
        -rm -f android/a2dp.$(OBJEXT)
+       -rm -f android/android_android_tester-android-tester.$(OBJEXT)
+       -rm -f android/android_audio_a2dp_default_la-hal-audio.$(OBJEXT)
+       -rm -f android/android_audio_a2dp_default_la-hal-audio.lo
+       -rm -f android/android_audio_sco_default_la-hal-sco.$(OBJEXT)
+       -rm -f android/android_audio_sco_default_la-hal-sco.lo
+       -rm -f android/android_bluetooth_default_la-hal-a2dp.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-a2dp.lo
+       -rm -f android/android_bluetooth_default_la-hal-avrcp.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-avrcp.lo
+       -rm -f android/android_bluetooth_default_la-hal-bluetooth.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-bluetooth.lo
+       -rm -f android/android_bluetooth_default_la-hal-gatt.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-gatt.lo
+       -rm -f android/android_bluetooth_default_la-hal-handsfree.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-handsfree.lo
+       -rm -f android/android_bluetooth_default_la-hal-health.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-health.lo
+       -rm -f android/android_bluetooth_default_la-hal-hidhost.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-hidhost.lo
+       -rm -f android/android_bluetooth_default_la-hal-ipc.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-ipc.lo
+       -rm -f android/android_bluetooth_default_la-hal-pan.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-pan.lo
+       -rm -f android/android_bluetooth_default_la-hal-socket.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-socket.lo
+       -rm -f android/android_bluetooth_default_la-hal-utils.$(OBJEXT)
+       -rm -f android/android_bluetooth_default_la-hal-utils.lo
        -rm -f android/android_haltest-hal-utils.$(OBJEXT)
-       -rm -f android/android_libhal_internal_la-hal-a2dp.$(OBJEXT)
-       -rm -f android/android_libhal_internal_la-hal-a2dp.lo
-       -rm -f android/android_libhal_internal_la-hal-bluetooth.$(OBJEXT)
-       -rm -f android/android_libhal_internal_la-hal-bluetooth.lo
-       -rm -f android/android_libhal_internal_la-hal-hidhost.$(OBJEXT)
-       -rm -f android/android_libhal_internal_la-hal-hidhost.lo
-       -rm -f android/android_libhal_internal_la-hal-ipc.$(OBJEXT)
-       -rm -f android/android_libhal_internal_la-hal-ipc.lo
-       -rm -f android/android_libhal_internal_la-hal-pan.$(OBJEXT)
-       -rm -f android/android_libhal_internal_la-hal-pan.lo
-       -rm -f android/android_libhal_internal_la-hal-sock.$(OBJEXT)
-       -rm -f android/android_libhal_internal_la-hal-sock.lo
+       -rm -f android/android_ipc_tester-hal-utils.$(OBJEXT)
+       -rm -f android/android_ipc_tester-ipc-tester.$(OBJEXT)
+       -rm -f android/audio_utils/android_audio_sco_default_la-resampler.$(OBJEXT)
+       -rm -f android/audio_utils/android_audio_sco_default_la-resampler.lo
+       -rm -f android/avctp.$(OBJEXT)
        -rm -f android/avdtp.$(OBJEXT)
+       -rm -f android/avrcp-lib.$(OBJEXT)
+       -rm -f android/avrcp.$(OBJEXT)
        -rm -f android/bluetooth.$(OBJEXT)
+       -rm -f android/bluetoothd-snoop.$(OBJEXT)
        -rm -f android/client/android_haltest-haltest.$(OBJEXT)
        -rm -f android/client/android_haltest-history.$(OBJEXT)
-       -rm -f android/client/android_haltest-hwmodule.$(OBJEXT)
+       -rm -f android/client/android_haltest-if-audio.$(OBJEXT)
        -rm -f android/client/android_haltest-if-av.$(OBJEXT)
        -rm -f android/client/android_haltest-if-bt.$(OBJEXT)
        -rm -f android/client/android_haltest-if-gatt.$(OBJEXT)
        -rm -f android/client/android_haltest-if-hf.$(OBJEXT)
        -rm -f android/client/android_haltest-if-hh.$(OBJEXT)
+       -rm -f android/client/android_haltest-if-hl.$(OBJEXT)
        -rm -f android/client/android_haltest-if-pan.$(OBJEXT)
+       -rm -f android/client/android_haltest-if-rc.$(OBJEXT)
+       -rm -f android/client/android_haltest-if-sco.$(OBJEXT)
        -rm -f android/client/android_haltest-if-sock.$(OBJEXT)
        -rm -f android/client/android_haltest-pollhandler.$(OBJEXT)
        -rm -f android/client/android_haltest-tabcompletion.$(OBJEXT)
        -rm -f android/client/android_haltest-terminal.$(OBJEXT)
+       -rm -f android/gatt.$(OBJEXT)
+       -rm -f android/handsfree.$(OBJEXT)
+       -rm -f android/hardware/android_android_tester-hardware.$(OBJEXT)
+       -rm -f android/hardware/android_haltest-hardware.$(OBJEXT)
+       -rm -f android/health.$(OBJEXT)
        -rm -f android/hidhost.$(OBJEXT)
        -rm -f android/ipc.$(OBJEXT)
        -rm -f android/main.$(OBJEXT)
+       -rm -f android/mcap-lib.$(OBJEXT)
        -rm -f android/pan.$(OBJEXT)
        -rm -f android/socket.$(OBJEXT)
        -rm -f android/system-emulator.$(OBJEXT)
+       -rm -f android/test-ipc.$(OBJEXT)
        -rm -f attrib/att.$(OBJEXT)
        -rm -f attrib/bluetoothd-att.$(OBJEXT)
        -rm -f attrib/bluetoothd-gatt-service.$(OBJEXT)
@@ -3451,11 +4492,20 @@ mostlyclean-compile:
        -rm -f client/display.$(OBJEXT)
        -rm -f client/main.$(OBJEXT)
        -rm -f emulator/amp.$(OBJEXT)
+       -rm -f emulator/android_android_tester-btdev.$(OBJEXT)
+       -rm -f emulator/android_android_tester-bthost.$(OBJEXT)
+       -rm -f emulator/android_android_tester-smp.$(OBJEXT)
+       -rm -f emulator/android_ipc_tester-btdev.$(OBJEXT)
+       -rm -f emulator/android_ipc_tester-bthost.$(OBJEXT)
+       -rm -f emulator/android_ipc_tester-smp.$(OBJEXT)
        -rm -f emulator/b1ee.$(OBJEXT)
        -rm -f emulator/btdev.$(OBJEXT)
        -rm -f emulator/bthost.$(OBJEXT)
+       -rm -f emulator/hfp.$(OBJEXT)
+       -rm -f emulator/le.$(OBJEXT)
        -rm -f emulator/main.$(OBJEXT)
        -rm -f emulator/server.$(OBJEXT)
+       -rm -f emulator/smp.$(OBJEXT)
        -rm -f emulator/vhci.$(OBJEXT)
        -rm -f gdbus/client.$(OBJEXT)
        -rm -f gdbus/client.lo
@@ -3487,11 +4537,14 @@ mostlyclean-compile:
        -rm -f lib/sdp.lo
        -rm -f lib/uuid.$(OBJEXT)
        -rm -f lib/uuid.lo
-       -rm -f monitor/btsnoop.$(OBJEXT)
+       -rm -f monitor/analyze.$(OBJEXT)
        -rm -f monitor/control.$(OBJEXT)
        -rm -f monitor/crc.$(OBJEXT)
        -rm -f monitor/display.$(OBJEXT)
+       -rm -f monitor/ellisys.$(OBJEXT)
        -rm -f monitor/hcidump.$(OBJEXT)
+       -rm -f monitor/hwdb.$(OBJEXT)
+       -rm -f monitor/keys.$(OBJEXT)
        -rm -f monitor/l2cap.$(OBJEXT)
        -rm -f monitor/ll.$(OBJEXT)
        -rm -f monitor/lmp.$(OBJEXT)
@@ -3602,7 +4655,8 @@ mostlyclean-compile:
        -rm -f src/bluetoothd-device.$(OBJEXT)
        -rm -f src/bluetoothd-eir.$(OBJEXT)
        -rm -f src/bluetoothd-error.$(OBJEXT)
-       -rm -f src/bluetoothd-glib-helper.$(OBJEXT)
+       -rm -f src/bluetoothd-gatt-dbus.$(OBJEXT)
+       -rm -f src/bluetoothd-gatt.$(OBJEXT)
        -rm -f src/bluetoothd-log.$(OBJEXT)
        -rm -f src/bluetoothd-main.$(OBJEXT)
        -rm -f src/bluetoothd-plugin.$(OBJEXT)
@@ -3618,8 +4672,8 @@ mostlyclean-compile:
        -rm -f src/bluetoothd-storage.$(OBJEXT)
        -rm -f src/bluetoothd-systemd.$(OBJEXT)
        -rm -f src/bluetoothd-textfile.$(OBJEXT)
+       -rm -f src/bluetoothd-uuid-helper.$(OBJEXT)
        -rm -f src/eir.$(OBJEXT)
-       -rm -f src/glib-helper.$(OBJEXT)
        -rm -f src/log.$(OBJEXT)
        -rm -f src/oui.$(OBJEXT)
        -rm -f src/sdp-client.$(OBJEXT)
@@ -3628,25 +4682,58 @@ mostlyclean-compile:
        -rm -f src/sdpd-request.$(OBJEXT)
        -rm -f src/sdpd-server.$(OBJEXT)
        -rm -f src/sdpd-service.$(OBJEXT)
+       -rm -f src/shared/android_android_tester-crypto.$(OBJEXT)
+       -rm -f src/shared/android_android_tester-hciemu.$(OBJEXT)
+       -rm -f src/shared/android_android_tester-io-glib.$(OBJEXT)
+       -rm -f src/shared/android_android_tester-mgmt.$(OBJEXT)
+       -rm -f src/shared/android_android_tester-queue.$(OBJEXT)
+       -rm -f src/shared/android_android_tester-tester.$(OBJEXT)
+       -rm -f src/shared/android_android_tester-timeout-glib.$(OBJEXT)
+       -rm -f src/shared/android_android_tester-util.$(OBJEXT)
+       -rm -f src/shared/android_ipc_tester-crypto.$(OBJEXT)
+       -rm -f src/shared/android_ipc_tester-hciemu.$(OBJEXT)
+       -rm -f src/shared/android_ipc_tester-io-glib.$(OBJEXT)
+       -rm -f src/shared/android_ipc_tester-mgmt.$(OBJEXT)
+       -rm -f src/shared/android_ipc_tester-queue.$(OBJEXT)
+       -rm -f src/shared/android_ipc_tester-tester.$(OBJEXT)
+       -rm -f src/shared/android_ipc_tester-timeout-glib.$(OBJEXT)
+       -rm -f src/shared/android_ipc_tester-util.$(OBJEXT)
+       -rm -f src/shared/bluetoothd-io-glib.$(OBJEXT)
        -rm -f src/shared/bluetoothd-mgmt.$(OBJEXT)
+       -rm -f src/shared/bluetoothd-queue.$(OBJEXT)
+       -rm -f src/shared/bluetoothd-timeout-glib.$(OBJEXT)
        -rm -f src/shared/bluetoothd-util.$(OBJEXT)
        -rm -f src/shared/btsnoop.$(OBJEXT)
+       -rm -f src/shared/crypto.$(OBJEXT)
+       -rm -f src/shared/gatt-db.$(OBJEXT)
+       -rm -f src/shared/hci.$(OBJEXT)
        -rm -f src/shared/hciemu.$(OBJEXT)
+       -rm -f src/shared/hfp.$(OBJEXT)
+       -rm -f src/shared/io-glib.$(OBJEXT)
+       -rm -f src/shared/io-mainloop.$(OBJEXT)
        -rm -f src/shared/mgmt.$(OBJEXT)
        -rm -f src/shared/pcap.$(OBJEXT)
+       -rm -f src/shared/queue.$(OBJEXT)
+       -rm -f src/shared/ringbuf.$(OBJEXT)
        -rm -f src/shared/tester.$(OBJEXT)
+       -rm -f src/shared/timeout-glib.$(OBJEXT)
+       -rm -f src/shared/timeout-mainloop.$(OBJEXT)
        -rm -f src/shared/util.$(OBJEXT)
        -rm -f src/textfile.$(OBJEXT)
+       -rm -f src/uuid-helper.$(OBJEXT)
+       -rm -f tools/3dsp.$(OBJEXT)
        -rm -f tools/amptest.$(OBJEXT)
        -rm -f tools/avinfo.$(OBJEXT)
        -rm -f tools/avtest.$(OBJEXT)
        -rm -f tools/bccmd.$(OBJEXT)
        -rm -f tools/bdaddr.$(OBJEXT)
+       -rm -f tools/bluemoon.$(OBJEXT)
        -rm -f tools/bluetooth-player.$(OBJEXT)
        -rm -f tools/btattach.$(OBJEXT)
        -rm -f tools/btinfo.$(OBJEXT)
        -rm -f tools/btiotest.$(OBJEXT)
        -rm -f tools/btmgmt.$(OBJEXT)
+       -rm -f tools/btproxy.$(OBJEXT)
        -rm -f tools/btsnoop.$(OBJEXT)
        -rm -f tools/ciptool.$(OBJEXT)
        -rm -f tools/cltest.$(OBJEXT)
@@ -3657,8 +4744,11 @@ mostlyclean-compile:
        -rm -f tools/csr_hci.$(OBJEXT)
        -rm -f tools/csr_usb.$(OBJEXT)
        -rm -f tools/gap-tester.$(OBJEXT)
+       -rm -f tools/gatt-service.$(OBJEXT)
+       -rm -f tools/hci-tester.$(OBJEXT)
        -rm -f tools/hciattach.$(OBJEXT)
        -rm -f tools/hciattach_ath3k.$(OBJEXT)
+       -rm -f tools/hciattach_bcm43xx.$(OBJEXT)
        -rm -f tools/hciattach_intel.$(OBJEXT)
        -rm -f tools/hciattach_qualcomm.$(OBJEXT)
        -rm -f tools/hciattach_st.$(OBJEXT)
@@ -3669,8 +4759,10 @@ mostlyclean-compile:
        -rm -f tools/hcieventmask.$(OBJEXT)
        -rm -f tools/hcisecfilter.$(OBJEXT)
        -rm -f tools/hcitool.$(OBJEXT)
+       -rm -f tools/hex2hcd.$(OBJEXT)
        -rm -f tools/hid2hci.$(OBJEXT)
        -rm -f tools/hwdb.$(OBJEXT)
+       -rm -f tools/ibeacon.$(OBJEXT)
        -rm -f tools/l2cap-tester.$(OBJEXT)
        -rm -f tools/l2ping.$(OBJEXT)
        -rm -f tools/l2test.$(OBJEXT)
@@ -3704,13 +4796,17 @@ mostlyclean-compile:
        -rm -f tools/parser/smp.$(OBJEXT)
        -rm -f tools/parser/tcpip.$(OBJEXT)
        -rm -f tools/rctest.$(OBJEXT)
+       -rm -f tools/rfcomm-tester.$(OBJEXT)
        -rm -f tools/rfcomm.$(OBJEXT)
        -rm -f tools/sco-tester.$(OBJEXT)
        -rm -f tools/scotest.$(OBJEXT)
        -rm -f tools/sdptool.$(OBJEXT)
+       -rm -f tools/seq2bseq.$(OBJEXT)
        -rm -f tools/smp-tester.$(OBJEXT)
        -rm -f tools/ubcsp.$(OBJEXT)
+       -rm -f unit/test-avctp.$(OBJEXT)
        -rm -f unit/test-avdtp.$(OBJEXT)
+       -rm -f unit/test-avrcp.$(OBJEXT)
        -rm -f unit/test-crc.$(OBJEXT)
        -rm -f unit/test-eir.$(OBJEXT)
        -rm -f unit/test-gdbus-client.$(OBJEXT)
@@ -3719,8 +4815,11 @@ mostlyclean-compile:
        -rm -f unit/test-gobex-packet.$(OBJEXT)
        -rm -f unit/test-gobex-transfer.$(OBJEXT)
        -rm -f unit/test-gobex.$(OBJEXT)
+       -rm -f unit/test-hfp.$(OBJEXT)
        -rm -f unit/test-lib.$(OBJEXT)
        -rm -f unit/test-mgmt.$(OBJEXT)
+       -rm -f unit/test-queue.$(OBJEXT)
+       -rm -f unit/test-ringbuf.$(OBJEXT)
        -rm -f unit/test-sdp.$(OBJEXT)
        -rm -f unit/test-textfile.$(OBJEXT)
        -rm -f unit/test-uuid.$(OBJEXT)
@@ -3730,34 +4829,59 @@ distclean-compile:
        -rm -f *.tab.c
 
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/a2dp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_android_tester-android-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_audio_sco_default_la-hal-sco.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_haltest-hal-utils.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_ipc_tester-hal-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_ipc_tester-ipc-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avctp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avdtp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avrcp-lib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avrcp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetoothd-snoop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/gatt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/handsfree.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/health.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/hidhost.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/ipc.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/mcap-lib.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/pan.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/socket.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/system-emulator.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/test-ipc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/audio_utils/$(DEPDIR)/android_audio_sco_default_la-resampler.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-haltest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-history.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-hwmodule.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-audio.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-av.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-bt.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-gatt.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-hf.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-hh.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-hl.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-pan.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-rc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-sco.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-sock.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-pollhandler.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-tabcompletion.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-terminal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/hardware/$(DEPDIR)/android_android_tester-hardware.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@android/hardware/$(DEPDIR)/android_haltest-hardware.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-gatt-service.Po@am__quote@
@@ -3775,11 +4899,20 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/display.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@client/$(DEPDIR)/main.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/amp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-btdev.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-bthost.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_android_tester-smp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-btdev.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-bthost.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/android_ipc_tester-smp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/b1ee.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/btdev.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/bthost.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/hfp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/le.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)/smp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@emulator/$(DEPDIR)/vhci.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/client.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@gdbus/$(DEPDIR)/mainloop.Plo@am__quote@
@@ -3802,11 +4935,14 @@ distclean-compile:
 @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@monitor/$(DEPDIR)/btsnoop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/analyze.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/control.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/crc.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/display.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/ellisys.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/hcidump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/hwdb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/keys.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/l2cap.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/ll.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@monitor/$(DEPDIR)/lmp.Po@am__quote@
@@ -3915,7 +5051,8 @@ distclean-compile:
 @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-glib-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-gatt-dbus.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-gatt.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-plugin.Po@am__quote@
@@ -3931,8 +5068,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-storage.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-systemd.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-textfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/bluetoothd-uuid-helper.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/eir.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-client.Po@am__quote@
@@ -3942,24 +5079,57 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-server.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-service.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/textfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/uuid-helper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-crypto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-hciemu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-io-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-mgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_android_tester-util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-crypto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/android_ipc_tester-util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-io-glib.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-mgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-util.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/btsnoop.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/crypto.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/gatt-db.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/hci.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/hciemu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/hfp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/io-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/io-mainloop.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/mgmt.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/pcap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/ringbuf.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/timeout-glib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/timeout-mainloop.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/3dsp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/amptest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avinfo.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/avtest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bccmd.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bdaddr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bluemoon.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/bluetooth-player.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btattach.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btinfo.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btiotest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btmgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btproxy.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/btsnoop.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ciptool.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/cltest.Po@am__quote@
@@ -3970,8 +5140,11 @@ distclean-compile:
 @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)/gap-tester.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/gatt-service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hci-tester.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_bcm43xx.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@
@@ -3982,8 +5155,10 @@ distclean-compile:
 @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)/hex2hcd.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hid2hci.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/hwdb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ibeacon.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2cap-tester.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2ping.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/l2test.Po@am__quote@
@@ -3993,10 +5168,12 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/obex-server-tool.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/obexctl.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rctest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rfcomm-tester.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/rfcomm.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sco-tester.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/scotest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sdptool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/seq2bseq.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/smp-tester.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ubcsp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/amp.Po@am__quote@
@@ -4023,7 +5200,9 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/sdp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/smp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/tcpip.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avctp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avdtp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avrcp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-crc.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-eir.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gdbus-client.Po@am__quote@
@@ -4032,8 +5211,11 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-packet.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex-transfer.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gobex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-hfp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-lib.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-mgmt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-queue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-ringbuf.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-sdp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-textfile.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-uuid.Po@am__quote@
@@ -4063,47 +5245,103 @@ distclean-compile:
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
 
-android/android_libhal_internal_la-hal-bluetooth.lo: android/hal-bluetooth.c
-@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-bluetooth.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Tpo -c -o android/android_libhal_internal_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c
-@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-bluetooth.c' object='android/android_libhal_internal_la-hal-bluetooth.lo' libtool=yes @AMDEPBACKSLASH@
+android/android_audio_a2dp_default_la-hal-audio.lo: android/hal-audio.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) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -MT android/android_audio_a2dp_default_la-hal-audio.lo -MD -MP -MF android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Tpo -c -o android/android_audio_a2dp_default_la-hal-audio.lo `test -f 'android/hal-audio.c' || echo '$(srcdir)/'`android/hal-audio.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Tpo android/$(DEPDIR)/android_audio_a2dp_default_la-hal-audio.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-audio.c' object='android/android_audio_a2dp_default_la-hal-audio.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) $(android_audio_a2dp_default_la_CFLAGS) $(CFLAGS) -c -o android/android_audio_a2dp_default_la-hal-audio.lo `test -f 'android/hal-audio.c' || echo '$(srcdir)/'`android/hal-audio.c
+
+android/android_audio_sco_default_la-hal-sco.lo: android/hal-sco.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) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -MT android/android_audio_sco_default_la-hal-sco.lo -MD -MP -MF android/$(DEPDIR)/android_audio_sco_default_la-hal-sco.Tpo -c -o android/android_audio_sco_default_la-hal-sco.lo `test -f 'android/hal-sco.c' || echo '$(srcdir)/'`android/hal-sco.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_audio_sco_default_la-hal-sco.Tpo android/$(DEPDIR)/android_audio_sco_default_la-hal-sco.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-sco.c' object='android/android_audio_sco_default_la-hal-sco.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) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -c -o android/android_audio_sco_default_la-hal-sco.lo `test -f 'android/hal-sco.c' || echo '$(srcdir)/'`android/hal-sco.c
+
+android/audio_utils/android_audio_sco_default_la-resampler.lo: android/audio_utils/resampler.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) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -MT android/audio_utils/android_audio_sco_default_la-resampler.lo -MD -MP -MF android/audio_utils/$(DEPDIR)/android_audio_sco_default_la-resampler.Tpo -c -o android/audio_utils/android_audio_sco_default_la-resampler.lo `test -f 'android/audio_utils/resampler.c' || echo '$(srcdir)/'`android/audio_utils/resampler.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/audio_utils/$(DEPDIR)/android_audio_sco_default_la-resampler.Tpo android/audio_utils/$(DEPDIR)/android_audio_sco_default_la-resampler.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/audio_utils/resampler.c' object='android/audio_utils/android_audio_sco_default_la-resampler.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) $(android_audio_sco_default_la_CFLAGS) $(CFLAGS) -c -o android/audio_utils/android_audio_sco_default_la-resampler.lo `test -f 'android/audio_utils/resampler.c' || echo '$(srcdir)/'`android/audio_utils/resampler.c
+
+android/android_bluetooth_default_la-hal-bluetooth.lo: android/hal-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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-bluetooth.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Tpo -c -o android/android_bluetooth_default_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-bluetooth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-bluetooth.c' object='android/android_bluetooth_default_la-hal-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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c
+
+android/android_bluetooth_default_la-hal-socket.lo: android/hal-socket.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-socket.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Tpo -c -o android/android_bluetooth_default_la-hal-socket.lo `test -f 'android/hal-socket.c' || echo '$(srcdir)/'`android/hal-socket.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-socket.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-socket.c' object='android/android_bluetooth_default_la-hal-socket.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) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c
+@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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-socket.lo `test -f 'android/hal-socket.c' || echo '$(srcdir)/'`android/hal-socket.c
 
-android/android_libhal_internal_la-hal-sock.lo: android/hal-sock.c
-@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-sock.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Tpo -c -o android/android_libhal_internal_la-hal-sock.lo `test -f 'android/hal-sock.c' || echo '$(srcdir)/'`android/hal-sock.c
-@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-sock.c' object='android/android_libhal_internal_la-hal-sock.lo' libtool=yes @AMDEPBACKSLASH@
+android/android_bluetooth_default_la-hal-hidhost.lo: android/hal-hidhost.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-hidhost.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Tpo -c -o android/android_bluetooth_default_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-hidhost.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-hidhost.c' object='android/android_bluetooth_default_la-hal-hidhost.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) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-sock.lo `test -f 'android/hal-sock.c' || echo '$(srcdir)/'`android/hal-sock.c
+@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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c
 
-android/android_libhal_internal_la-hal-hidhost.lo: android/hal-hidhost.c
-@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-hidhost.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Tpo -c -o android/android_libhal_internal_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c
-@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-hidhost.c' object='android/android_libhal_internal_la-hal-hidhost.lo' libtool=yes @AMDEPBACKSLASH@
+android/android_bluetooth_default_la-hal-health.lo: android/hal-health.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-health.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Tpo -c -o android/android_bluetooth_default_la-hal-health.lo `test -f 'android/hal-health.c' || echo '$(srcdir)/'`android/hal-health.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-health.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-health.c' object='android/android_bluetooth_default_la-hal-health.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) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c
+@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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-health.lo `test -f 'android/hal-health.c' || echo '$(srcdir)/'`android/hal-health.c
 
-android/android_libhal_internal_la-hal-pan.lo: android/hal-pan.c
-@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-pan.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Tpo -c -o android/android_libhal_internal_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c
-@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-pan.c' object='android/android_libhal_internal_la-hal-pan.lo' libtool=yes @AMDEPBACKSLASH@
+android/android_bluetooth_default_la-hal-pan.lo: android/hal-pan.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-pan.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Tpo -c -o android/android_bluetooth_default_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-pan.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-pan.c' object='android/android_bluetooth_default_la-hal-pan.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) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c
+@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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c
 
-android/android_libhal_internal_la-hal-a2dp.lo: android/hal-a2dp.c
-@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-a2dp.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Tpo -c -o android/android_libhal_internal_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c
-@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-a2dp.c' object='android/android_libhal_internal_la-hal-a2dp.lo' libtool=yes @AMDEPBACKSLASH@
+android/android_bluetooth_default_la-hal-a2dp.lo: android/hal-a2dp.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-a2dp.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Tpo -c -o android/android_bluetooth_default_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-a2dp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-a2dp.c' object='android/android_bluetooth_default_la-hal-a2dp.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) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c
+@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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c
 
-android/android_libhal_internal_la-hal-ipc.lo: android/hal-ipc.c
-@am__fastdepCC_TRUE@   $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-ipc.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Tpo -c -o android/android_libhal_internal_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c
-@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-ipc.c' object='android/android_libhal_internal_la-hal-ipc.lo' libtool=yes @AMDEPBACKSLASH@
+android/android_bluetooth_default_la-hal-avrcp.lo: android/hal-avrcp.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-avrcp.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Tpo -c -o android/android_bluetooth_default_la-hal-avrcp.lo `test -f 'android/hal-avrcp.c' || echo '$(srcdir)/'`android/hal-avrcp.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-avrcp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-avrcp.c' object='android/android_bluetooth_default_la-hal-avrcp.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) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c
+@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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-avrcp.lo `test -f 'android/hal-avrcp.c' || echo '$(srcdir)/'`android/hal-avrcp.c
+
+android/android_bluetooth_default_la-hal-handsfree.lo: android/hal-handsfree.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-handsfree.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Tpo -c -o android/android_bluetooth_default_la-hal-handsfree.lo `test -f 'android/hal-handsfree.c' || echo '$(srcdir)/'`android/hal-handsfree.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-handsfree.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-handsfree.c' object='android/android_bluetooth_default_la-hal-handsfree.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-handsfree.lo `test -f 'android/hal-handsfree.c' || echo '$(srcdir)/'`android/hal-handsfree.c
+
+android/android_bluetooth_default_la-hal-gatt.lo: android/hal-gatt.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-gatt.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Tpo -c -o android/android_bluetooth_default_la-hal-gatt.lo `test -f 'android/hal-gatt.c' || echo '$(srcdir)/'`android/hal-gatt.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-gatt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-gatt.c' object='android/android_bluetooth_default_la-hal-gatt.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-gatt.lo `test -f 'android/hal-gatt.c' || echo '$(srcdir)/'`android/hal-gatt.c
+
+android/android_bluetooth_default_la-hal-ipc.lo: android/hal-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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-ipc.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Tpo -c -o android/android_bluetooth_default_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-ipc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-ipc.c' object='android/android_bluetooth_default_la-hal-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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c
+
+android/android_bluetooth_default_la-hal-utils.lo: android/hal-utils.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -MT android/android_bluetooth_default_la-hal-utils.lo -MD -MP -MF android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Tpo -c -o android/android_bluetooth_default_la-hal-utils.lo `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Tpo android/$(DEPDIR)/android_bluetooth_default_la-hal-utils.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-utils.c' object='android/android_bluetooth_default_la-hal-utils.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) $(android_bluetooth_default_la_CFLAGS) $(CFLAGS) -c -o android/android_bluetooth_default_la-hal-utils.lo `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.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
@@ -4119,6 +5357,188 @@ plugins/plugins_sixaxis_la-sixaxis.lo: plugins/sixaxis.c
 @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_sixaxis_la_CFLAGS) $(CFLAGS) -c -o plugins/plugins_sixaxis_la-sixaxis.lo `test -f 'plugins/sixaxis.c' || echo '$(srcdir)/'`plugins/sixaxis.c
 
+emulator/android_android_tester-btdev.o: emulator/btdev.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-btdev.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-btdev.Tpo -c -o emulator/android_android_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-btdev.Tpo emulator/$(DEPDIR)/android_android_tester-btdev.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_android_tester-btdev.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c
+
+emulator/android_android_tester-btdev.obj: emulator/btdev.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-btdev.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-btdev.Tpo -c -o emulator/android_android_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-btdev.Tpo emulator/$(DEPDIR)/android_android_tester-btdev.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_android_tester-btdev.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi`
+
+emulator/android_android_tester-bthost.o: emulator/bthost.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-bthost.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-bthost.Tpo -c -o emulator/android_android_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-bthost.Tpo emulator/$(DEPDIR)/android_android_tester-bthost.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_android_tester-bthost.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c
+
+emulator/android_android_tester-bthost.obj: emulator/bthost.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-bthost.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-bthost.Tpo -c -o emulator/android_android_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-bthost.Tpo emulator/$(DEPDIR)/android_android_tester-bthost.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_android_tester-bthost.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi`
+
+emulator/android_android_tester-smp.o: emulator/smp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-smp.o -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-smp.Tpo -c -o emulator/android_android_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-smp.Tpo emulator/$(DEPDIR)/android_android_tester-smp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/smp.c' object='emulator/android_android_tester-smp.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c
+
+emulator/android_android_tester-smp.obj: emulator/smp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT emulator/android_android_tester-smp.obj -MD -MP -MF emulator/$(DEPDIR)/android_android_tester-smp.Tpo -c -o emulator/android_android_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_android_tester-smp.Tpo emulator/$(DEPDIR)/android_android_tester-smp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/smp.c' object='emulator/android_android_tester-smp.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_android_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi`
+
+src/shared/android_android_tester-crypto.o: src/shared/crypto.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-crypto.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo -c -o src/shared/android_android_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo src/shared/$(DEPDIR)/android_android_tester-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_android_tester-crypto.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c
+
+src/shared/android_android_tester-crypto.obj: src/shared/crypto.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-crypto.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo -c -o src/shared/android_android_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-crypto.Tpo src/shared/$(DEPDIR)/android_android_tester-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_android_tester-crypto.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi`
+
+src/shared/android_android_tester-io-glib.o: src/shared/io-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-io-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo -c -o src/shared/android_android_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_android_tester-io-glib.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+
+src/shared/android_android_tester-io-glib.obj: src/shared/io-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-io-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo -c -o src/shared/android_android_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_android_tester-io-glib.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+
+src/shared/android_android_tester-queue.o: src/shared/queue.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-queue.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-queue.Tpo -c -o src/shared/android_android_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-queue.Tpo src/shared/$(DEPDIR)/android_android_tester-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_android_tester-queue.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+
+src/shared/android_android_tester-queue.obj: src/shared/queue.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-queue.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-queue.Tpo -c -o src/shared/android_android_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-queue.Tpo src/shared/$(DEPDIR)/android_android_tester-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_android_tester-queue.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+
+src/shared/android_android_tester-util.o: src/shared/util.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-util.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-util.Tpo -c -o src/shared/android_android_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-util.Tpo src/shared/$(DEPDIR)/android_android_tester-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/util.c' object='src/shared/android_android_tester-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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
+
+src/shared/android_android_tester-util.obj: src/shared/util.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-util.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-util.Tpo -c -o src/shared/android_android_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-util.Tpo src/shared/$(DEPDIR)/android_android_tester-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/util.c' object='src/shared/android_android_tester-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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi`
+
+src/shared/android_android_tester-mgmt.o: src/shared/mgmt.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-mgmt.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo -c -o src/shared/android_android_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_android_tester-mgmt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_android_tester-mgmt.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c
+
+src/shared/android_android_tester-mgmt.obj: src/shared/mgmt.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-mgmt.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo -c -o src/shared/android_android_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_android_tester-mgmt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_android_tester-mgmt.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi`
+
+src/shared/android_android_tester-hciemu.o: src/shared/hciemu.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-hciemu.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo -c -o src/shared/android_android_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_android_tester-hciemu.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_android_tester-hciemu.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c
+
+src/shared/android_android_tester-hciemu.obj: src/shared/hciemu.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-hciemu.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo -c -o src/shared/android_android_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_android_tester-hciemu.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_android_tester-hciemu.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi`
+
+src/shared/android_android_tester-tester.o: src/shared/tester.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-tester.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-tester.Tpo -c -o src/shared/android_android_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-tester.Tpo src/shared/$(DEPDIR)/android_android_tester-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_android_tester-tester.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c
+
+src/shared/android_android_tester-tester.obj: src/shared/tester.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-tester.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-tester.Tpo -c -o src/shared/android_android_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-tester.Tpo src/shared/$(DEPDIR)/android_android_tester-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_android_tester-tester.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi`
+
+src/shared/android_android_tester-timeout-glib.o: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-timeout-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo -c -o src/shared/android_android_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_android_tester-timeout-glib.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+
+src/shared/android_android_tester-timeout-glib.obj: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_android_tester-timeout-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo -c -o src/shared/android_android_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_android_tester-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_android_tester-timeout-glib.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_android_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+
+android/hardware/android_android_tester-hardware.o: android/hardware/hardware.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/hardware/android_android_tester-hardware.o -MD -MP -MF android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo -c -o android/hardware/android_android_tester-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo android/hardware/$(DEPDIR)/android_android_tester-hardware.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_android_tester-hardware.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/hardware/android_android_tester-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c
+
+android/hardware/android_android_tester-hardware.obj: android/hardware/hardware.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/hardware/android_android_tester-hardware.obj -MD -MP -MF android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo -c -o android/hardware/android_android_tester-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_android_tester-hardware.Tpo android/hardware/$(DEPDIR)/android_android_tester-hardware.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_android_tester-hardware.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/hardware/android_android_tester-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi`
+
+android/android_android_tester-android-tester.o: android/android-tester.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/android_android_tester-android-tester.o -MD -MP -MF android/$(DEPDIR)/android_android_tester-android-tester.Tpo -c -o android/android_android_tester-android-tester.o `test -f 'android/android-tester.c' || echo '$(srcdir)/'`android/android-tester.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_android_tester-android-tester.Tpo android/$(DEPDIR)/android_android_tester-android-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/android-tester.c' object='android/android_android_tester-android-tester.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/android_android_tester-android-tester.o `test -f 'android/android-tester.c' || echo '$(srcdir)/'`android/android-tester.c
+
+android/android_android_tester-android-tester.obj: android/android-tester.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_android_tester_CFLAGS) $(CFLAGS) -MT android/android_android_tester-android-tester.obj -MD -MP -MF android/$(DEPDIR)/android_android_tester-android-tester.Tpo -c -o android/android_android_tester-android-tester.obj `if test -f 'android/android-tester.c'; then $(CYGPATH_W) 'android/android-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/android-tester.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_android_tester-android-tester.Tpo android/$(DEPDIR)/android_android_tester-android-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/android-tester.c' object='android/android_android_tester-android-tester.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) $(android_android_tester_CFLAGS) $(CFLAGS) -c -o android/android_android_tester-android-tester.obj `if test -f 'android/android-tester.c'; then $(CYGPATH_W) 'android/android-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/android-tester.c'; fi`
+
 android/client/android_haltest-haltest.o: android/client/haltest.c
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-haltest.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-haltest.Tpo -c -o android/client/android_haltest-haltest.o `test -f 'android/client/haltest.c' || echo '$(srcdir)/'`android/client/haltest.c
 @am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-haltest.Tpo android/client/$(DEPDIR)/android_haltest-haltest.Po
@@ -4203,6 +5623,20 @@ android/client/android_haltest-if-av.obj: android/client/if-av.c
 @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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-av.obj `if test -f 'android/client/if-av.c'; then $(CYGPATH_W) 'android/client/if-av.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-av.c'; fi`
 
+android/client/android_haltest-if-rc.o: android/client/if-rc.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-rc.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-rc.Tpo -c -o android/client/android_haltest-if-rc.o `test -f 'android/client/if-rc.c' || echo '$(srcdir)/'`android/client/if-rc.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-rc.Tpo android/client/$(DEPDIR)/android_haltest-if-rc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/client/if-rc.c' object='android/client/android_haltest-if-rc.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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-rc.o `test -f 'android/client/if-rc.c' || echo '$(srcdir)/'`android/client/if-rc.c
+
+android/client/android_haltest-if-rc.obj: android/client/if-rc.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-rc.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-rc.Tpo -c -o android/client/android_haltest-if-rc.obj `if test -f 'android/client/if-rc.c'; then $(CYGPATH_W) 'android/client/if-rc.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-rc.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-rc.Tpo android/client/$(DEPDIR)/android_haltest-if-rc.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/client/if-rc.c' object='android/client/android_haltest-if-rc.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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-rc.obj `if test -f 'android/client/if-rc.c'; then $(CYGPATH_W) 'android/client/if-rc.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-rc.c'; fi`
+
 android/client/android_haltest-if-bt.o: android/client/if-bt.c
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-bt.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-bt.Tpo -c -o android/client/android_haltest-if-bt.o `test -f 'android/client/if-bt.c' || echo '$(srcdir)/'`android/client/if-bt.c
 @am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-bt.Tpo android/client/$(DEPDIR)/android_haltest-if-bt.Po
@@ -4273,6 +5707,20 @@ android/client/android_haltest-if-pan.obj: android/client/if-pan.c
 @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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-pan.obj `if test -f 'android/client/if-pan.c'; then $(CYGPATH_W) 'android/client/if-pan.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-pan.c'; fi`
 
+android/client/android_haltest-if-hl.o: android/client/if-hl.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hl.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hl.Tpo -c -o android/client/android_haltest-if-hl.o `test -f 'android/client/if-hl.c' || echo '$(srcdir)/'`android/client/if-hl.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hl.Tpo android/client/$(DEPDIR)/android_haltest-if-hl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/client/if-hl.c' object='android/client/android_haltest-if-hl.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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hl.o `test -f 'android/client/if-hl.c' || echo '$(srcdir)/'`android/client/if-hl.c
+
+android/client/android_haltest-if-hl.obj: android/client/if-hl.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hl.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hl.Tpo -c -o android/client/android_haltest-if-hl.obj `if test -f 'android/client/if-hl.c'; then $(CYGPATH_W) 'android/client/if-hl.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hl.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hl.Tpo android/client/$(DEPDIR)/android_haltest-if-hl.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/client/if-hl.c' object='android/client/android_haltest-if-hl.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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hl.obj `if test -f 'android/client/if-hl.c'; then $(CYGPATH_W) 'android/client/if-hl.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hl.c'; fi`
+
 android/client/android_haltest-if-sock.o: android/client/if-sock.c
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-sock.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-sock.Tpo -c -o android/client/android_haltest-if-sock.o `test -f 'android/client/if-sock.c' || echo '$(srcdir)/'`android/client/if-sock.c
 @am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-sock.Tpo android/client/$(DEPDIR)/android_haltest-if-sock.Po
@@ -4287,19 +5735,47 @@ android/client/android_haltest-if-sock.obj: android/client/if-sock.c
 @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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-sock.obj `if test -f 'android/client/if-sock.c'; then $(CYGPATH_W) 'android/client/if-sock.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sock.c'; fi`
 
-android/client/android_haltest-hwmodule.o: android/client/hwmodule.c
-@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-hwmodule.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo -c -o android/client/android_haltest-hwmodule.o `test -f 'android/client/hwmodule.c' || echo '$(srcdir)/'`android/client/hwmodule.c
-@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo android/client/$(DEPDIR)/android_haltest-hwmodule.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/client/hwmodule.c' object='android/client/android_haltest-hwmodule.o' libtool=no @AMDEPBACKSLASH@
+android/client/android_haltest-if-audio.o: android/client/if-audio.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-audio.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-audio.Tpo -c -o android/client/android_haltest-if-audio.o `test -f 'android/client/if-audio.c' || echo '$(srcdir)/'`android/client/if-audio.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-audio.Tpo android/client/$(DEPDIR)/android_haltest-if-audio.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/client/if-audio.c' object='android/client/android_haltest-if-audio.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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-audio.o `test -f 'android/client/if-audio.c' || echo '$(srcdir)/'`android/client/if-audio.c
+
+android/client/android_haltest-if-audio.obj: android/client/if-audio.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-audio.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-audio.Tpo -c -o android/client/android_haltest-if-audio.obj `if test -f 'android/client/if-audio.c'; then $(CYGPATH_W) 'android/client/if-audio.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-audio.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-audio.Tpo android/client/$(DEPDIR)/android_haltest-if-audio.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/client/if-audio.c' object='android/client/android_haltest-if-audio.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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-audio.obj `if test -f 'android/client/if-audio.c'; then $(CYGPATH_W) 'android/client/if-audio.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-audio.c'; fi`
+
+android/client/android_haltest-if-sco.o: android/client/if-sco.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-sco.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-sco.Tpo -c -o android/client/android_haltest-if-sco.o `test -f 'android/client/if-sco.c' || echo '$(srcdir)/'`android/client/if-sco.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-sco.Tpo android/client/$(DEPDIR)/android_haltest-if-sco.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/client/if-sco.c' object='android/client/android_haltest-if-sco.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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-sco.o `test -f 'android/client/if-sco.c' || echo '$(srcdir)/'`android/client/if-sco.c
+
+android/client/android_haltest-if-sco.obj: android/client/if-sco.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-sco.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-sco.Tpo -c -o android/client/android_haltest-if-sco.obj `if test -f 'android/client/if-sco.c'; then $(CYGPATH_W) 'android/client/if-sco.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sco.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-sco.Tpo android/client/$(DEPDIR)/android_haltest-if-sco.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/client/if-sco.c' object='android/client/android_haltest-if-sco.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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-hwmodule.o `test -f 'android/client/hwmodule.c' || echo '$(srcdir)/'`android/client/hwmodule.c
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-sco.obj `if test -f 'android/client/if-sco.c'; then $(CYGPATH_W) 'android/client/if-sco.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sco.c'; fi`
 
-android/client/android_haltest-hwmodule.obj: android/client/hwmodule.c
-@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-hwmodule.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo -c -o android/client/android_haltest-hwmodule.obj `if test -f 'android/client/hwmodule.c'; then $(CYGPATH_W) 'android/client/hwmodule.c'; else $(CYGPATH_W) '$(srcdir)/android/client/hwmodule.c'; fi`
-@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo android/client/$(DEPDIR)/android_haltest-hwmodule.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/client/hwmodule.c' object='android/client/android_haltest-hwmodule.obj' libtool=no @AMDEPBACKSLASH@
+android/hardware/android_haltest-hardware.o: android/hardware/hardware.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/hardware/android_haltest-hardware.o -MD -MP -MF android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo -c -o android/hardware/android_haltest-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo android/hardware/$(DEPDIR)/android_haltest-hardware.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_haltest-hardware.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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-hwmodule.obj `if test -f 'android/client/hwmodule.c'; then $(CYGPATH_W) 'android/client/hwmodule.c'; else $(CYGPATH_W) '$(srcdir)/android/client/hwmodule.c'; fi`
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/hardware/android_haltest-hardware.o `test -f 'android/hardware/hardware.c' || echo '$(srcdir)/'`android/hardware/hardware.c
+
+android/hardware/android_haltest-hardware.obj: android/hardware/hardware.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/hardware/android_haltest-hardware.obj -MD -MP -MF android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo -c -o android/hardware/android_haltest-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/hardware/$(DEPDIR)/android_haltest-hardware.Tpo android/hardware/$(DEPDIR)/android_haltest-hardware.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hardware/hardware.c' object='android/hardware/android_haltest-hardware.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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/hardware/android_haltest-hardware.obj `if test -f 'android/hardware/hardware.c'; then $(CYGPATH_W) 'android/hardware/hardware.c'; else $(CYGPATH_W) '$(srcdir)/android/hardware/hardware.c'; fi`
 
 android/android_haltest-hal-utils.o: android/hal-utils.c
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/android_haltest-hal-utils.o -MD -MP -MF android/$(DEPDIR)/android_haltest-hal-utils.Tpo -c -o android/android_haltest-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c
@@ -4315,6 +5791,188 @@ android/android_haltest-hal-utils.obj: android/hal-utils.c
 @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) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/android_haltest-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi`
 
+emulator/android_ipc_tester-btdev.o: emulator/btdev.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-btdev.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo -c -o emulator/android_ipc_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo emulator/$(DEPDIR)/android_ipc_tester-btdev.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_ipc_tester-btdev.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-btdev.o `test -f 'emulator/btdev.c' || echo '$(srcdir)/'`emulator/btdev.c
+
+emulator/android_ipc_tester-btdev.obj: emulator/btdev.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-btdev.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo -c -o emulator/android_ipc_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-btdev.Tpo emulator/$(DEPDIR)/android_ipc_tester-btdev.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/btdev.c' object='emulator/android_ipc_tester-btdev.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-btdev.obj `if test -f 'emulator/btdev.c'; then $(CYGPATH_W) 'emulator/btdev.c'; else $(CYGPATH_W) '$(srcdir)/emulator/btdev.c'; fi`
+
+emulator/android_ipc_tester-bthost.o: emulator/bthost.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-bthost.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo -c -o emulator/android_ipc_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo emulator/$(DEPDIR)/android_ipc_tester-bthost.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_ipc_tester-bthost.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-bthost.o `test -f 'emulator/bthost.c' || echo '$(srcdir)/'`emulator/bthost.c
+
+emulator/android_ipc_tester-bthost.obj: emulator/bthost.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-bthost.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo -c -o emulator/android_ipc_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-bthost.Tpo emulator/$(DEPDIR)/android_ipc_tester-bthost.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/bthost.c' object='emulator/android_ipc_tester-bthost.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-bthost.obj `if test -f 'emulator/bthost.c'; then $(CYGPATH_W) 'emulator/bthost.c'; else $(CYGPATH_W) '$(srcdir)/emulator/bthost.c'; fi`
+
+emulator/android_ipc_tester-smp.o: emulator/smp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-smp.o -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo -c -o emulator/android_ipc_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo emulator/$(DEPDIR)/android_ipc_tester-smp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/smp.c' object='emulator/android_ipc_tester-smp.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-smp.o `test -f 'emulator/smp.c' || echo '$(srcdir)/'`emulator/smp.c
+
+emulator/android_ipc_tester-smp.obj: emulator/smp.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT emulator/android_ipc_tester-smp.obj -MD -MP -MF emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo -c -o emulator/android_ipc_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) emulator/$(DEPDIR)/android_ipc_tester-smp.Tpo emulator/$(DEPDIR)/android_ipc_tester-smp.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='emulator/smp.c' object='emulator/android_ipc_tester-smp.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o emulator/android_ipc_tester-smp.obj `if test -f 'emulator/smp.c'; then $(CYGPATH_W) 'emulator/smp.c'; else $(CYGPATH_W) '$(srcdir)/emulator/smp.c'; fi`
+
+src/shared/android_ipc_tester-crypto.o: src/shared/crypto.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-crypto.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo -c -o src/shared/android_ipc_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo src/shared/$(DEPDIR)/android_ipc_tester-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_ipc_tester-crypto.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-crypto.o `test -f 'src/shared/crypto.c' || echo '$(srcdir)/'`src/shared/crypto.c
+
+src/shared/android_ipc_tester-crypto.obj: src/shared/crypto.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-crypto.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo -c -o src/shared/android_ipc_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-crypto.Tpo src/shared/$(DEPDIR)/android_ipc_tester-crypto.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/crypto.c' object='src/shared/android_ipc_tester-crypto.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-crypto.obj `if test -f 'src/shared/crypto.c'; then $(CYGPATH_W) 'src/shared/crypto.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/crypto.c'; fi`
+
+src/shared/android_ipc_tester-io-glib.o: src/shared/io-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-io-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo -c -o src/shared/android_ipc_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_ipc_tester-io-glib.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+
+src/shared/android_ipc_tester-io-glib.obj: src/shared/io-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-io-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo -c -o src/shared/android_ipc_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/android_ipc_tester-io-glib.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+
+src/shared/android_ipc_tester-queue.o: src/shared/queue.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-queue.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo -c -o src/shared/android_ipc_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo src/shared/$(DEPDIR)/android_ipc_tester-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_ipc_tester-queue.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+
+src/shared/android_ipc_tester-queue.obj: src/shared/queue.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-queue.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo -c -o src/shared/android_ipc_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-queue.Tpo src/shared/$(DEPDIR)/android_ipc_tester-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/queue.c' object='src/shared/android_ipc_tester-queue.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+
+src/shared/android_ipc_tester-util.o: src/shared/util.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-util.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo -c -o src/shared/android_ipc_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo src/shared/$(DEPDIR)/android_ipc_tester-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/util.c' object='src/shared/android_ipc_tester-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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
+
+src/shared/android_ipc_tester-util.obj: src/shared/util.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-util.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo -c -o src/shared/android_ipc_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-util.Tpo src/shared/$(DEPDIR)/android_ipc_tester-util.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/util.c' object='src/shared/android_ipc_tester-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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-util.obj `if test -f 'src/shared/util.c'; then $(CYGPATH_W) 'src/shared/util.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/util.c'; fi`
+
+src/shared/android_ipc_tester-mgmt.o: src/shared/mgmt.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-mgmt.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo -c -o src/shared/android_ipc_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_ipc_tester-mgmt.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-mgmt.o `test -f 'src/shared/mgmt.c' || echo '$(srcdir)/'`src/shared/mgmt.c
+
+src/shared/android_ipc_tester-mgmt.obj: src/shared/mgmt.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-mgmt.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo -c -o src/shared/android_ipc_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Tpo src/shared/$(DEPDIR)/android_ipc_tester-mgmt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/mgmt.c' object='src/shared/android_ipc_tester-mgmt.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-mgmt.obj `if test -f 'src/shared/mgmt.c'; then $(CYGPATH_W) 'src/shared/mgmt.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/mgmt.c'; fi`
+
+src/shared/android_ipc_tester-hciemu.o: src/shared/hciemu.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-hciemu.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo -c -o src/shared/android_ipc_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_ipc_tester-hciemu.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-hciemu.o `test -f 'src/shared/hciemu.c' || echo '$(srcdir)/'`src/shared/hciemu.c
+
+src/shared/android_ipc_tester-hciemu.obj: src/shared/hciemu.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-hciemu.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo -c -o src/shared/android_ipc_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Tpo src/shared/$(DEPDIR)/android_ipc_tester-hciemu.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/hciemu.c' object='src/shared/android_ipc_tester-hciemu.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-hciemu.obj `if test -f 'src/shared/hciemu.c'; then $(CYGPATH_W) 'src/shared/hciemu.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/hciemu.c'; fi`
+
+src/shared/android_ipc_tester-tester.o: src/shared/tester.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-tester.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo -c -o src/shared/android_ipc_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo src/shared/$(DEPDIR)/android_ipc_tester-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_ipc_tester-tester.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-tester.o `test -f 'src/shared/tester.c' || echo '$(srcdir)/'`src/shared/tester.c
+
+src/shared/android_ipc_tester-tester.obj: src/shared/tester.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-tester.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo -c -o src/shared/android_ipc_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-tester.Tpo src/shared/$(DEPDIR)/android_ipc_tester-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/tester.c' object='src/shared/android_ipc_tester-tester.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-tester.obj `if test -f 'src/shared/tester.c'; then $(CYGPATH_W) 'src/shared/tester.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/tester.c'; fi`
+
+src/shared/android_ipc_tester-timeout-glib.o: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-timeout-glib.o -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo -c -o src/shared/android_ipc_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_ipc_tester-timeout-glib.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+
+src/shared/android_ipc_tester-timeout-glib.obj: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT src/shared/android_ipc_tester-timeout-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo -c -o src/shared/android_ipc_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Tpo src/shared/$(DEPDIR)/android_ipc_tester-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/android_ipc_tester-timeout-glib.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o src/shared/android_ipc_tester-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+
+android/android_ipc_tester-hal-utils.o: android/hal-utils.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-hal-utils.o -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo -c -o android/android_ipc_tester-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo android/$(DEPDIR)/android_ipc_tester-hal-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-utils.c' object='android/android_ipc_tester-hal-utils.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c
+
+android/android_ipc_tester-hal-utils.obj: android/hal-utils.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-hal-utils.obj -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo -c -o android/android_ipc_tester-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-hal-utils.Tpo android/$(DEPDIR)/android_ipc_tester-hal-utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/hal-utils.c' object='android/android_ipc_tester-hal-utils.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi`
+
+android/android_ipc_tester-ipc-tester.o: android/ipc-tester.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-ipc-tester.o -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo -c -o android/android_ipc_tester-ipc-tester.o `test -f 'android/ipc-tester.c' || echo '$(srcdir)/'`android/ipc-tester.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo android/$(DEPDIR)/android_ipc_tester-ipc-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/ipc-tester.c' object='android/android_ipc_tester-ipc-tester.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-ipc-tester.o `test -f 'android/ipc-tester.c' || echo '$(srcdir)/'`android/ipc-tester.c
+
+android/android_ipc_tester-ipc-tester.obj: android/ipc-tester.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_ipc_tester_CFLAGS) $(CFLAGS) -MT android/android_ipc_tester-ipc-tester.obj -MD -MP -MF android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo -c -o android/android_ipc_tester-ipc-tester.obj `if test -f 'android/ipc-tester.c'; then $(CYGPATH_W) 'android/ipc-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/ipc-tester.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_ipc_tester-ipc-tester.Tpo android/$(DEPDIR)/android_ipc_tester-ipc-tester.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='android/ipc-tester.c' object='android/android_ipc_tester-ipc-tester.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) $(android_ipc_tester_CFLAGS) $(CFLAGS) -c -o android/android_ipc_tester-ipc-tester.obj `if test -f 'android/ipc-tester.c'; then $(CYGPATH_W) 'android/ipc-tester.c'; else $(CYGPATH_W) '$(srcdir)/android/ipc-tester.c'; fi`
+
 btio/obexd-btio.o: btio/btio.c
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT btio/obexd-btio.o -MD -MP -MF btio/$(DEPDIR)/obexd-btio.Tpo -c -o btio/obexd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c
 @am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) btio/$(DEPDIR)/obexd-btio.Tpo btio/$(DEPDIR)/obexd-btio.Po
@@ -5813,19 +7471,19 @@ src/bluetoothd-textfile.obj: src/textfile.c
 @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@
+src/bluetoothd-uuid-helper.o: src/uuid-helper.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-uuid-helper.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo -c -o src/bluetoothd-uuid-helper.o `test -f 'src/uuid-helper.c' || echo '$(srcdir)/'`src/uuid-helper.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo src/$(DEPDIR)/bluetoothd-uuid-helper.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/uuid-helper.c' object='src/bluetoothd-uuid-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
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-uuid-helper.o `test -f 'src/uuid-helper.c' || echo '$(srcdir)/'`src/uuid-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@
+src/bluetoothd-uuid-helper.obj: src/uuid-helper.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-uuid-helper.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo -c -o src/bluetoothd-uuid-helper.obj `if test -f 'src/uuid-helper.c'; then $(CYGPATH_W) 'src/uuid-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/uuid-helper.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-uuid-helper.Tpo src/$(DEPDIR)/bluetoothd-uuid-helper.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/uuid-helper.c' object='src/bluetoothd-uuid-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`
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o src/bluetoothd-uuid-helper.obj `if test -f 'src/uuid-helper.c'; then $(CYGPATH_W) 'src/uuid-helper.c'; else $(CYGPATH_W) '$(srcdir)/src/uuid-helper.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
@@ -5925,6 +7583,34 @@ src/bluetoothd-service.obj: src/service.c
 @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-service.obj `if test -f 'src/service.c'; then $(CYGPATH_W) 'src/service.c'; else $(CYGPATH_W) '$(srcdir)/src/service.c'; fi`
 
+src/bluetoothd-gatt-dbus.o: src/gatt-dbus.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt-dbus.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo -c -o src/bluetoothd-gatt-dbus.o `test -f 'src/gatt-dbus.c' || echo '$(srcdir)/'`src/gatt-dbus.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo src/$(DEPDIR)/bluetoothd-gatt-dbus.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/gatt-dbus.c' object='src/bluetoothd-gatt-dbus.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-gatt-dbus.o `test -f 'src/gatt-dbus.c' || echo '$(srcdir)/'`src/gatt-dbus.c
+
+src/bluetoothd-gatt-dbus.obj: src/gatt-dbus.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt-dbus.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo -c -o src/bluetoothd-gatt-dbus.obj `if test -f 'src/gatt-dbus.c'; then $(CYGPATH_W) 'src/gatt-dbus.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt-dbus.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt-dbus.Tpo src/$(DEPDIR)/bluetoothd-gatt-dbus.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/gatt-dbus.c' object='src/bluetoothd-gatt-dbus.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-gatt-dbus.obj `if test -f 'src/gatt-dbus.c'; then $(CYGPATH_W) 'src/gatt-dbus.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt-dbus.c'; fi`
+
+src/bluetoothd-gatt.o: src/gatt.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt.o -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o src/bluetoothd-gatt.o `test -f 'src/gatt.c' || echo '$(srcdir)/'`src/gatt.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt.Tpo src/$(DEPDIR)/bluetoothd-gatt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/gatt.c' object='src/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 src/bluetoothd-gatt.o `test -f 'src/gatt.c' || echo '$(srcdir)/'`src/gatt.c
+
+src/bluetoothd-gatt.obj: src/gatt.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/bluetoothd-gatt.obj -MD -MP -MF src/$(DEPDIR)/bluetoothd-gatt.Tpo -c -o src/bluetoothd-gatt.obj `if test -f 'src/gatt.c'; then $(CYGPATH_W) 'src/gatt.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/$(DEPDIR)/bluetoothd-gatt.Tpo src/$(DEPDIR)/bluetoothd-gatt.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/gatt.c' object='src/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 src/bluetoothd-gatt.obj `if test -f 'src/gatt.c'; then $(CYGPATH_W) 'src/gatt.c'; else $(CYGPATH_W) '$(srcdir)/src/gatt.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
@@ -5967,6 +7653,48 @@ src/bluetoothd-eir.obj: src/eir.c
 @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`
 
+src/shared/bluetoothd-io-glib.o: src/shared/io-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-io-glib.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo -c -o src/shared/bluetoothd-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/bluetoothd-io-glib.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/shared/bluetoothd-io-glib.o `test -f 'src/shared/io-glib.c' || echo '$(srcdir)/'`src/shared/io-glib.c
+
+src/shared/bluetoothd-io-glib.obj: src/shared/io-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-io-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo -c -o src/shared/bluetoothd-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-io-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-io-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/io-glib.c' object='src/shared/bluetoothd-io-glib.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/shared/bluetoothd-io-glib.obj `if test -f 'src/shared/io-glib.c'; then $(CYGPATH_W) 'src/shared/io-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/io-glib.c'; fi`
+
+src/shared/bluetoothd-timeout-glib.o: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-timeout-glib.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo -c -o src/shared/bluetoothd-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/bluetoothd-timeout-glib.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/shared/bluetoothd-timeout-glib.o `test -f 'src/shared/timeout-glib.c' || echo '$(srcdir)/'`src/shared/timeout-glib.c
+
+src/shared/bluetoothd-timeout-glib.obj: src/shared/timeout-glib.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-timeout-glib.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo -c -o src/shared/bluetoothd-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Tpo src/shared/$(DEPDIR)/bluetoothd-timeout-glib.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/timeout-glib.c' object='src/shared/bluetoothd-timeout-glib.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/shared/bluetoothd-timeout-glib.obj `if test -f 'src/shared/timeout-glib.c'; then $(CYGPATH_W) 'src/shared/timeout-glib.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/timeout-glib.c'; fi`
+
+src/shared/bluetoothd-queue.o: src/shared/queue.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-queue.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-queue.Tpo -c -o src/shared/bluetoothd-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-queue.Tpo src/shared/$(DEPDIR)/bluetoothd-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/queue.c' object='src/shared/bluetoothd-queue.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/shared/bluetoothd-queue.o `test -f 'src/shared/queue.c' || echo '$(srcdir)/'`src/shared/queue.c
+
+src/shared/bluetoothd-queue.obj: src/shared/queue.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-queue.obj -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-queue.Tpo -c -o src/shared/bluetoothd-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-queue.Tpo src/shared/$(DEPDIR)/bluetoothd-queue.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='src/shared/queue.c' object='src/shared/bluetoothd-queue.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/shared/bluetoothd-queue.obj `if test -f 'src/shared/queue.c'; then $(CYGPATH_W) 'src/shared/queue.c'; else $(CYGPATH_W) '$(srcdir)/src/shared/queue.c'; fi`
+
 src/shared/bluetoothd-util.o: src/shared/util.c
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT src/shared/bluetoothd-util.o -MD -MP -MF src/shared/$(DEPDIR)/bluetoothd-util.Tpo -c -o src/shared/bluetoothd-util.o `test -f 'src/shared/util.c' || echo '$(srcdir)/'`src/shared/util.c
 @am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) src/shared/$(DEPDIR)/bluetoothd-util.Tpo src/shared/$(DEPDIR)/bluetoothd-util.Po
@@ -6001,6 +7729,7 @@ mostlyclean-libtool:
 clean-libtool:
        -rm -rf .libs _libs
        -rm -rf android/.libs android/_libs
+       -rm -rf android/audio_utils/.libs android/audio_utils/_libs
        -rm -rf attrib/.libs attrib/_libs
        -rm -rf client/.libs client/_libs
        -rm -rf emulator/.libs emulator/_libs
@@ -6675,8 +8404,12 @@ distclean-generic:
        -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
        -rm -f android/$(DEPDIR)/$(am__dirstamp)
        -rm -f android/$(am__dirstamp)
+       -rm -f android/audio_utils/$(DEPDIR)/$(am__dirstamp)
+       -rm -f android/audio_utils/$(am__dirstamp)
        -rm -f android/client/$(DEPDIR)/$(am__dirstamp)
        -rm -f android/client/$(am__dirstamp)
+       -rm -f android/hardware/$(DEPDIR)/$(am__dirstamp)
+       -rm -f android/hardware/$(am__dirstamp)
        -rm -f attrib/$(DEPDIR)/$(am__dirstamp)
        -rm -f attrib/$(am__dirstamp)
        -rm -f btio/$(DEPDIR)/$(am__dirstamp)
@@ -6760,7 +8493,7 @@ clean-am: clean-binPROGRAMS clean-cupsPROGRAMS clean-generic \
 
 distclean: distclean-am
        -rm -f $(am__CONFIG_DISTCLEAN_FILES)
-       -rm -rf android/$(DEPDIR) android/client/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR)
+       -rm -rf android/$(DEPDIR) android/audio_utils/$(DEPDIR) android/client/$(DEPDIR) android/hardware/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR)
        -rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
        distclean-hdr distclean-libtool distclean-tags
@@ -6815,7 +8548,7 @@ installcheck-am:
 maintainer-clean: maintainer-clean-am
        -rm -f $(am__CONFIG_DISTCLEAN_FILES)
        -rm -rf $(top_srcdir)/autom4te.cache
-       -rm -rf android/$(DEPDIR) android/client/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR)
+       -rm -rf android/$(DEPDIR) android/audio_utils/$(DEPDIR) android/client/$(DEPDIR) android/hardware/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR)
        -rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
index 6a1ddbf..f0eada9 100644 (file)
@@ -55,7 +55,8 @@ builtin_sources += profiles/network/manager.c \
 builtin_modules += input
 builtin_sources += profiles/input/manager.c \
                        profiles/input/server.h profiles/input/server.c \
-                       profiles/input/device.h profiles/input/device.c
+                       profiles/input/device.h profiles/input/device.c \
+                       profiles/input/uhid_copy.h profiles/input/hidp_defs.h
 
 builtin_modules += hog
 builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
index 78034f5..412a998 100644 (file)
@@ -14,86 +14,169 @@ if MONITOR
 bin_PROGRAMS += monitor/btmon
 
 monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
-                                       monitor/mainloop.h monitor/mainloop.c \
-                                       monitor/display.h monitor/display.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/vendor.h monitor/vendor.c \
-                                       monitor/lmp.h monitor/lmp.c \
-                                       monitor/l2cap.h monitor/l2cap.c \
-                                       monitor/uuid.h monitor/uuid.c \
-                                       monitor/sdp.h monitor/sdp.c \
-                                       monitor/crc.h monitor/crc.c \
-                                       monitor/ll.h monitor/ll.c
-monitor_btmon_LDADD = lib/libbluetooth-internal.la
+                               monitor/mainloop.h monitor/mainloop.c \
+                               monitor/display.h monitor/display.c \
+                               monitor/hcidump.h monitor/hcidump.c \
+                               monitor/ellisys.h monitor/ellisys.c \
+                               monitor/control.h monitor/control.c \
+                               monitor/packet.h monitor/packet.c \
+                               monitor/vendor.h monitor/vendor.c \
+                               monitor/lmp.h monitor/lmp.c \
+                               monitor/crc.h monitor/crc.c \
+                               monitor/ll.h monitor/ll.c \
+                               monitor/l2cap.h monitor/l2cap.c \
+                               monitor/sdp.h monitor/sdp.c \
+                               monitor/uuid.h monitor/uuid.c \
+                               monitor/hwdb.h monitor/hwdb.c \
+                               monitor/keys.h monitor/keys.c \
+                               monitor/analyze.h monitor/analyze.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/crypto.h src/shared/crypto.c \
+                               src/shared/btsnoop.h src/shared/btsnoop.c
+monitor_btmon_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
 endif
 
 if EXPERIMENTAL
-noinst_PROGRAMS += emulator/btvirt emulator/b1ee \
+noinst_PROGRAMS += emulator/btvirt emulator/b1ee emulator/hfp tools/3dsp \
                                        tools/mgmt-tester tools/gap-tester \
                                        tools/l2cap-tester tools/sco-tester \
-                                       tools/smp-tester
+                                       tools/smp-tester tools/hci-tester \
+                                       tools/rfcomm-tester
 
 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 \
-                                       emulator/bthost.h emulator/bthost.c \
-                                       emulator/amp.h emulator/amp.c
+                               monitor/mainloop.h monitor/mainloop.c \
+                               src/shared/timeout.h \
+                               src/shared/timeout-mainloop.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/crypto.h src/shared/crypto.c \
+                               emulator/server.h emulator/server.c \
+                               emulator/vhci.h emulator/vhci.c \
+                               emulator/btdev.h emulator/btdev.c \
+                               emulator/bthost.h emulator/bthost.c \
+                               emulator/smp.c \
+                               emulator/amp.h emulator/amp.c \
+                               emulator/le.h emulator/le.c
+emulator_btvirt_LDADD = lib/libbluetooth-internal.la
 
 emulator_b1ee_SOURCES = emulator/b1ee.c monitor/mainloop.h monitor/mainloop.c
 
+emulator_hfp_SOURCES = emulator/hfp.c \
+                               monitor/mainloop.h monitor/mainloop.c \
+                               src/shared/io.h src/shared/io-mainloop.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c \
+                               src/shared/hfp.h src/shared/hfp.c
+
+tools_3dsp_SOURCES = tools/3dsp.c monitor/bt.h \
+                               monitor/mainloop.h monitor/mainloop.c \
+                               src/shared/io.h src/shared/io-mainloop.c \
+                               src/shared/timeout.h \
+                               src/shared/timeout-mainloop.c \
+                               src/shared/hci.h src/shared/hci.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c
+
 tools_mgmt_tester_SOURCES = tools/mgmt-tester.c monitor/bt.h \
                                emulator/btdev.h emulator/btdev.c \
                                emulator/bthost.h emulator/bthost.c \
+                               emulator/smp.c \
+                               src/shared/crypto.h src/shared/crypto.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
                                src/shared/util.h src/shared/util.c \
                                src/shared/mgmt.h src/shared/mgmt.c \
                                src/shared/hciemu.h src/shared/hciemu.c \
-                               src/shared/tester.h src/shared/tester.c
+                               src/shared/tester.h src/shared/tester.c \
+                               src/shared/timeout.h src/shared/timeout-glib.c
 tools_mgmt_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 
 tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \
                                emulator/btdev.h emulator/btdev.c \
                                emulator/bthost.h emulator/bthost.c \
+                               emulator/smp.c \
+                               src/shared/crypto.h src/shared/crypto.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
                                src/shared/util.h src/shared/util.c \
                                src/shared/mgmt.h src/shared/mgmt.c \
                                src/shared/hciemu.h src/shared/hciemu.c \
-                               src/shared/tester.h src/shared/tester.c
+                               src/shared/tester.h src/shared/tester.c \
+                               src/shared/timeout.h src/shared/timeout-glib.c
 tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 
+tools_rfcomm_tester_SOURCES = tools/rfcomm-tester.c monitor/bt.h \
+                               emulator/btdev.h emulator/btdev.c \
+                               emulator/bthost.h emulator/bthost.c \
+                               emulator/smp.c \
+                               src/shared/crypto.h src/shared/crypto.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/mgmt.h src/shared/mgmt.c \
+                               src/shared/hciemu.h src/shared/hciemu.c \
+                               src/shared/tester.h src/shared/tester.c \
+                               src/shared/timeout.h src/shared/timeout-glib.c
+tools_rfcomm_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
 tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \
                                emulator/btdev.h emulator/btdev.c \
                                emulator/bthost.h emulator/bthost.c \
+                               emulator/smp.c \
+                               src/shared/crypto.h src/shared/crypto.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
                                src/shared/util.h src/shared/util.c \
                                src/shared/mgmt.h src/shared/mgmt.c \
                                src/shared/hciemu.h src/shared/hciemu.c \
-                               src/shared/tester.h src/shared/tester.c
+                               src/shared/tester.h src/shared/tester.c \
+                               src/shared/timeout.h src/shared/timeout-glib.c
 tools_smp_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 
 tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \
                                emulator/btdev.h emulator/btdev.c \
                                emulator/bthost.h emulator/bthost.c \
+                               emulator/smp.c \
+                               src/shared/crypto.h src/shared/crypto.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/queue.h src/shared/queue.c \
                                src/shared/hciemu.h src/shared/hciemu.c \
-                               src/shared/tester.h src/shared/tester.c
-tools_gap_tester_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+                               src/shared/tester.h src/shared/tester.c \
+                               src/shared/timeout.h src/shared/timeout-glib.c
+tools_gap_tester_LDADD =  lib/libbluetooth-internal.la \
+                               gdbus/libgdbus-internal.la \
+                               @GLIB_LIBS@ @DBUS_LIBS@
 
 tools_sco_tester_SOURCES = tools/sco-tester.c monitor/bt.h \
                                emulator/btdev.h emulator/btdev.c \
                                emulator/bthost.h emulator/bthost.c \
+                               emulator/smp.c \
+                               src/shared/crypto.h src/shared/crypto.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
                                src/shared/util.h src/shared/util.c \
                                src/shared/mgmt.h src/shared/mgmt.c \
                                src/shared/hciemu.h src/shared/hciemu.c \
-                               src/shared/tester.h src/shared/tester.c
+                               src/shared/tester.h src/shared/tester.c \
+                               src/shared/timeout.h src/shared/timeout-glib.c
 tools_sco_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+tools_hci_tester_SOURCES = tools/hci-tester.c monitor/bt.h \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/hci.h src/shared/hci.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c \
+                               src/shared/tester.h src/shared/tester.c
+tools_hci_tester_LDADD = @GLIB_LIBS@
 endif
 
 if TOOLS
 bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
                        tools/rfcomm tools/rctest tools/l2test tools/l2ping \
-                       tools/sdptool tools/ciptool tools/bccmd
+                       tools/sdptool tools/ciptool tools/bccmd tools/bluemoon
 
 tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
                                                tools/hciattach_st.c \
@@ -101,14 +184,15 @@ tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
                                                tools/hciattach_tialt.c \
                                                tools/hciattach_ath3k.c \
                                                tools/hciattach_qualcomm.c \
-                                               tools/hciattach_intel.c
+                                               tools/hciattach_intel.c \
+                                               tools/hciattach_bcm43xx.c
 tools_hciattach_LDADD = lib/libbluetooth-internal.la
 
 tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c
 tools_hciconfig_LDADD = lib/libbluetooth-internal.la
 
 tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c
-tools_hcitool_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @UDEV_LIBS@
+tools_hcitool_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
 
 tools_hcidump_SOURCES = tools/hcidump.c \
                                tools/parser/parser.h tools/parser/parser.c \
@@ -156,6 +240,14 @@ tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h tools/csr.c \
                        tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c
 tools_bccmd_LDADD = lib/libbluetooth-internal.la
 
+tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h \
+                               monitor/mainloop.h monitor/mainloop.c \
+                               src/shared/io.h src/shared/io-mainloop.c \
+                               src/shared/hci.h src/shared/hci.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c
+
 dist_man_MANS += tools/hciattach.1 tools/hciconfig.1 \
                        tools/hcitool.1 tools/hcidump.1 \
                        tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
@@ -184,8 +276,9 @@ noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \
                        tools/scotest tools/amptest tools/hwdb \
                        tools/hcieventmask tools/hcisecfilter \
                        tools/btmgmt tools/btinfo tools/btattach \
-                       tools/btsnoop tools/btiotest tools/cltest \
-                       tools/mpris-player
+                       tools/btsnoop tools/btproxy tools/btiotest \
+                       tools/mpris-player tools/cltest tools/seq2bseq \
+                       tools/hex2hcd tools/ibeacon
 
 tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
 tools_bdaddr_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
@@ -202,17 +295,32 @@ tools_hwdb_LDADD = lib/libbluetooth-internal.la
 
 tools_hcieventmask_LDADD = lib/libbluetooth-internal.la
 
-tools_btmgmt_SOURCES = tools/btmgmt.c src/glib-helper.c src/eir.c \
+tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c \
+                               monitor/mainloop.h monitor/mainloop.c \
+                               src/shared/io.h src/shared/io-mainloop.c \
+                               src/shared/queue.h src/shared/queue.c \
                                src/shared/util.h src/shared/util.c \
                                src/shared/mgmt.h src/shared/mgmt.c
-tools_btmgmt_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
-
-tools_btinfo_SOURCES = tools/btinfo.c
+tools_btmgmt_LDADD = lib/libbluetooth-internal.la
+
+tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h \
+                               monitor/mainloop.h monitor/mainloop.c \
+                               src/shared/io.h src/shared/io-mainloop.c \
+                               src/shared/timeout.h \
+                               src/shared/timeout-mainloop.c \
+                               src/shared/hci.h src/shared/hci.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c
 
 tools_btsnoop_SOURCES = tools/btsnoop.c \
                                src/shared/pcap.h src/shared/pcap.c \
                                src/shared/btsnoop.h src/shared/btsnoop.c
 
+tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h \
+                               monitor/mainloop.h monitor/mainloop.c \
+                               src/shared/util.h src/shared/util.c
+
 tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c
 tools_btiotest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 
@@ -222,6 +330,20 @@ tools_mpris_player_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
 tools_cltest_SOURCES = tools/cltest.c monitor/mainloop.h monitor/mainloop.c
 tools_cltest_LDADD = lib/libbluetooth-internal.la
 
+tools_seq2bseq_SOURCES = tools/seq2bseq.c
+
+tools_hex2hcd_SOURCES = tools/hex2hcd.c
+
+tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h \
+                               monitor/mainloop.h monitor/mainloop.c \
+                               src/shared/io.h src/shared/io-mainloop.c \
+                               src/shared/timeout.h \
+                               src/shared/timeout-mainloop.c \
+                               src/shared/hci.h src/shared/hci.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c
+
 EXTRA_DIST += tools/bdaddr.1
 endif
 
@@ -258,6 +380,11 @@ tools_obexctl_LDADD = gdbus/libgdbus-internal.la \
 endif
 
 if EXPERIMENTAL
+noinst_PROGRAMS += tools/gatt-service
+
+tools_gatt_service_SOURCES = tools/gatt-service.c
+tools_gatt_service_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ gdbus/libgdbus-internal.la
+
 noinst_PROGRAMS += profiles/iap/iapd
 
 profiles_iap_iapd_SOURCES = profiles/iap/main.c
@@ -284,11 +411,12 @@ test_scripts += test/sap_client.py test/bluezutils.py \
                test/dbusdef.py test/monitor-bluetooth test/list-devices \
                test/test-discovery test/test-manager test/test-adapter \
                test/test-device test/simple-agent \
-               test/simple-service test/simple-endpoint test/test-sap-server \
+               test/simple-endpoint test/test-sap-server \
                test/test-proximity test/test-network \
                test/test-thermometer test/test-profile test/test-health \
                test/test-health-sink test/service-record.dtd \
                test/service-did.xml test/service-spp.xml test/service-opp.xml \
                test/service-ftp.xml test/simple-player test/test-nap \
                test/test-heartrate test/test-alert test/test-hfp \
-               test/test-cyclingspeed
+               test/test-cyclingspeed test/opp-client test/ftp-client \
+               test/pbap-client test/map-client
diff --git a/TODO b/TODO
index 7bb100b..50678d3 100644 (file)
--- a/TODO
+++ b/TODO
@@ -24,11 +24,18 @@ General
   Priority: high
   Complexity: C4
 
-- Rename glib-helper file to a more convenient name. The idea is try to keep
-  only sdp helpers functions. bt_* prefix shall be also changed.
+- Update PBAP client/server implementation to 1.2 and create necessary APIs for
+  new features it introduces.
 
-  Priority: Low
-  Complexity: C1
+  Priority: Medium
+  Complexity: C4
+
+- Create GOEP unit tests based on its test specification:
+
+  https://www.bluetooth.org/docman/handlers/DownloadDoc.ashx?doc_id=230559
+
+  Priority: Medium
+  Complexity: C2
 
 - Function in src/adapter.c to convert old storage files to new ini-file format
   should be removed 6-8 months after first BlueZ 5 release.
@@ -96,12 +103,6 @@ Low Energy
   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.
@@ -147,12 +148,6 @@ ATT/GATT
   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.
@@ -160,11 +155,6 @@ ATT/GATT
   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().
@@ -185,17 +175,6 @@ ATT/GATT
   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
@@ -215,11 +194,6 @@ ATT/GATT
   Priority: Low
   Complecity: C1
 
-- Add sdp discovery support to gatttool 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.
@@ -234,15 +208,6 @@ ATT/GATT
   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
 ====================
 
index 549613c..4235a7c 100644 (file)
@@ -1,17 +1,27 @@
-LOCAL_PATH := $(call my-dir)
+LOCAL_PATH := external/bluetooth
 
 # Retrieve BlueZ version from configure.ac file
-BLUEZ_VERSION := $(shell grep ^AC_INIT $(LOCAL_PATH)/../configure.ac | cpp -P -D'AC_INIT(_,v)=v')
+BLUEZ_VERSION := `grep "^AC_INIT" $(LOCAL_PATH)/bluez/configure.ac | sed -e "s/.*,.\(.*\))/\1/"`
 
-# Specify pathmap for glib
-pathmap_INCL += glib:external/bluetooth/glib
+# Specify pathmap for glib and sbc
+pathmap_INCL += glib:external/bluetooth/glib \
+               sbc:external/bluetooth/sbc \
 
 # Specify common compiler flags
 BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \
-       -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)
+                       -DANDROID_STORAGEDIR=\"/data/misc/bluetooth\" \
+
+# Enable warnings enabled in autotools build
+BLUEZ_COMMON_CFLAGS += -Wall -Wextra \
+                       -Wdeclaration-after-statement \
+                       -Wmissing-declarations \
+                       -Wredundant-decls \
+                       -Wcast-align \
 
 # Disable warnings enabled by Android but not enabled in autotools build
-BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith -Wno-missing-field-initializers
+BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith \
+                       -Wno-missing-field-initializers \
+                       -Wno-unused-parameter \
 
 #
 # Android BlueZ daemon (bluetoothd)
@@ -20,60 +30,73 @@ BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith -Wno-missing-field-initializers
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-       main.c \
-       bluetooth.c \
-       hidhost.c \
-       socket.c \
-       ipc.c ipc.h \
-       avdtp.c \
-       a2dp.c \
-       pan.c \
-       ../src/log.c \
-       ../src/shared/mgmt.c \
-       ../src/shared/util.c \
-       ../src/sdpd-database.c \
-       ../src/sdpd-service.c \
-       ../src/sdpd-request.c \
-       ../src/sdpd-server.c \
-       ../src/glib-helper.c \
-       ../src/eir.c \
-       ../lib/sdp.c \
-       ../lib/bluetooth.c \
-       ../lib/hci.c \
-       ../btio/btio.c \
-       ../src/sdp-client.c \
-       ../profiles/network/bnep.c \
+       bluez/android/main.c \
+       bluez/android/bluetooth.c \
+       bluez/android/hidhost.c \
+       bluez/android/socket.c \
+       bluez/android/ipc.c \
+       bluez/android/avdtp.c \
+       bluez/android/a2dp.c \
+       bluez/android/avctp.c \
+       bluez/android/avrcp.c \
+       bluez/android/avrcp-lib.c \
+       bluez/android/pan.c \
+       bluez/android/handsfree.c \
+       bluez/android/gatt.c \
+       bluez/android/health.c \
+       bluez/android/mcap-lib.c \
+       bluez/src/log.c \
+       bluez/src/shared/mgmt.c \
+       bluez/src/shared/util.c \
+       bluez/src/shared/queue.c \
+       bluez/src/shared/ringbuf.c \
+       bluez/src/shared/hfp.c \
+       bluez/src/shared/gatt-db.c \
+       bluez/src/shared/io-glib.c \
+       bluez/src/sdpd-database.c \
+       bluez/src/sdpd-service.c \
+       bluez/src/sdpd-request.c \
+       bluez/src/sdpd-server.c \
+       bluez/src/uuid-helper.c \
+       bluez/src/eir.c \
+       bluez/lib/sdp.c \
+       bluez/lib/bluetooth.c \
+       bluez/lib/hci.c \
+       bluez/lib/uuid.c \
+       bluez/btio/btio.c \
+       bluez/src/sdp-client.c \
+       bluez/profiles/network/bnep.c \
+       bluez/attrib/gattrib.c \
+       bluez/attrib/gatt.c \
+       bluez/attrib/att.c
 
 LOCAL_C_INCLUDES := \
        $(call include-path-for, glib) \
        $(call include-path-for, glib)/glib \
 
 LOCAL_C_INCLUDES += \
-       $(LOCAL_PATH)/../ \
-       $(LOCAL_PATH)/../src \
-       $(LOCAL_PATH)/../lib \
+       $(LOCAL_PATH)/bluez \
 
 LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
 
 LOCAL_SHARED_LIBRARIES := \
        libglib \
 
-lib_headers := \
-       bluetooth.h \
-       hci.h \
-       hci_lib.h \
-       l2cap.h \
-       sdp_lib.h \
-       sdp.h \
-       rfcomm.h \
-       sco.h \
-       bnep.h \
-
-$(shell mkdir -p $(LOCAL_PATH)/../lib/bluetooth)
+LOCAL_STATIC_LIBRARIES := \
+       bluetooth-headers \
 
-$(foreach file,$(lib_headers), $(shell ln -sf ../$(file) $(LOCAL_PATH)/../lib/bluetooth/$(file)))
+LOCAL_MODULE_TAGS := optional
 
+# for userdebug/eng this module is bluetoothd-main since bluetoothd is used as
+# wrapper to launch bluetooth with Valgrind
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+LOCAL_MODULE := bluetoothd-main
+LOCAL_STRIP_MODULE := false
+else
 LOCAL_MODULE := bluetoothd
+endif
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
 
 include $(BUILD_EXECUTABLE)
 
@@ -84,13 +107,17 @@ include $(BUILD_EXECUTABLE)
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-       hal-ipc.c \
-       hal-bluetooth.c \
-       hal-sock.c \
-       hal-hidhost.c \
-       hal-pan.c \
-       hal-a2dp.c \
-       hal-utils.c \
+       bluez/android/hal-ipc.c \
+       bluez/android/hal-bluetooth.c \
+       bluez/android/hal-socket.c \
+       bluez/android/hal-hidhost.c \
+       bluez/android/hal-pan.c \
+       bluez/android/hal-a2dp.c \
+       bluez/android/hal-avrcp.c \
+       bluez/android/hal-handsfree.c \
+       bluez/android/hal-gatt.c \
+       bluez/android/hal-utils.c \
+       bluez/android/hal-health.c \
 
 LOCAL_C_INCLUDES += \
        $(call include-path-for, system-core) \
@@ -99,13 +126,13 @@ LOCAL_C_INCLUDES += \
 LOCAL_SHARED_LIBRARIES := \
        libcutils \
 
-LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) \
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
 
 LOCAL_MODULE := bluetooth.default
 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-LOCAL_REQUIRED_MODULES := haltest bluetoothd
+LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
 
 include $(BUILD_SHARED_LIBRARY)
 
@@ -116,25 +143,23 @@ include $(BUILD_SHARED_LIBRARY)
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-       client/haltest.c \
-       client/pollhandler.c \
-       client/terminal.c \
-       client/history.c \
-       client/tabcompletion.c \
-       client/if-av.c \
-       client/if-bt.c \
-       client/if-hf.c \
-       client/if-hh.c \
-       client/if-pan.c \
-       client/if-sock.c \
-       hal-utils.c \
-
-ANDROID_4_3_OR_ABOVE := $(shell echo 0 | awk -v v=$(PLATFORM_SDK_VERSION) 'END {print (v > 17) ? 1 : 0}')
-
-ifeq ($(ANDROID_4_3_OR_ABOVE), 1)
-LOCAL_SRC_FILES += \
-       client/if-gatt.c
-endif
+       bluez/android/client/haltest.c \
+       bluez/android/client/pollhandler.c \
+       bluez/android/client/terminal.c \
+       bluez/android/client/history.c \
+       bluez/android/client/tabcompletion.c \
+       bluez/android/client/if-audio.c \
+       bluez/android/client/if-sco.c \
+       bluez/android/client/if-av.c \
+       bluez/android/client/if-rc.c \
+       bluez/android/client/if-bt.c \
+       bluez/android/client/if-hf.c \
+       bluez/android/client/if-hh.c \
+       bluez/android/client/if-pan.c \
+       bluez/android/client/if-hl.c \
+       bluez/android/client/if-sock.c \
+       bluez/android/client/if-gatt.c \
+       bluez/android/hal-utils.c \
 
 LOCAL_C_INCLUDES += \
        $(call include-path-for, system-core) \
@@ -144,7 +169,8 @@ LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
 
 LOCAL_SHARED_LIBRARIES := libhardware
 
-LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE := haltest
 
 include $(BUILD_EXECUTABLE)
@@ -156,50 +182,402 @@ include $(BUILD_EXECUTABLE)
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
-       ../monitor/main.c \
-       ../monitor/bt.h \
-       ../monitor/mainloop.h \
-       ../monitor/mainloop.c \
-       ../monitor/display.h \
-       ../monitor/display.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/l2cap.h \
-       ../monitor/l2cap.c \
-       ../monitor/uuid.h \
-       ../monitor/uuid.c \
-       ../monitor/sdp.h \
-       ../monitor/sdp.c \
-       ../monitor/vendor.h \
-       ../monitor/vendor.c \
-       ../monitor/lmp.h \
-       ../monitor/lmp.c \
-       ../monitor/crc.h \
-       ../monitor/crc.c \
-       ../monitor/ll.h \
-       ../monitor/ll.c \
-       ../lib/hci.c \
-       ../lib/bluetooth.c \
+       bluez/monitor/main.c \
+       bluez/monitor/mainloop.c \
+       bluez/monitor/display.c \
+       bluez/monitor/hcidump.c \
+       bluez/monitor/control.c \
+       bluez/monitor/packet.c \
+       bluez/monitor/l2cap.c \
+       bluez/monitor/uuid.c \
+       bluez/monitor/sdp.c \
+       bluez/monitor/vendor.c \
+       bluez/monitor/lmp.c \
+       bluez/monitor/crc.c \
+       bluez/monitor/ll.c \
+       bluez/monitor/hwdb.c \
+       bluez/monitor/keys.c \
+       bluez/monitor/ellisys.c \
+       bluez/monitor/analyze.c \
+       bluez/src/shared/util.c \
+       bluez/src/shared/queue.c \
+       bluez/src/shared/crypto.c \
+       bluez/src/shared/btsnoop.c \
+       bluez/lib/hci.c \
+       bluez/lib/bluetooth.c \
 
 LOCAL_C_INCLUDES := \
-       $(LOCAL_PATH)/.. \
-       $(LOCAL_PATH)/../lib \
-       $(LOCAL_PATH)/../src/shared \
-
-LOCAL_C_INCLUDES += \
-       $(call include-path-for, glib) \
-       $(call include-path-for, glib)/glib \
+       $(LOCAL_PATH)/bluez \
 
 LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
 
+LOCAL_STATIC_LIBRARIES := \
+       bluetooth-headers \
+
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
-LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE_TAGS := debug
 LOCAL_MODULE := btmon
 
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# btproxy
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       bluez/tools/btproxy.c \
+       bluez/monitor/mainloop.c \
+       bluez/src/shared/util.c \
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btproxy
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# A2DP audio
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := bluez/android/hal-audio.c
+
+LOCAL_C_INCLUDES = \
+       $(call include-path-for, system-core) \
+       $(call include-path-for, libhardware) \
+       $(call include-path-for, sbc) \
+
+LOCAL_SHARED_LIBRARIES := \
+       libcutils \
+       libsbc \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := audio.a2dp.default
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# SCO audio
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := bluez/android/hal-sco.c
+
+LOCAL_C_INCLUDES = \
+       $(call include-path-for, system-core) \
+       $(call include-path-for, libhardware) \
+       $(call include-path-for, audio-utils) \
+
+LOCAL_SHARED_LIBRARIES := \
+       libcutils \
+       libaudioutils \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := audio.sco.default
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# l2cap-test
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       bluez/tools/l2test.c \
+       bluez/lib/bluetooth.c \
+       bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+       bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := l2test
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# bluetoothd-snoop
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       bluez/android/bluetoothd-snoop.c \
+       bluez/monitor/mainloop.c \
+       bluez/src/shared/btsnoop.c \
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/bluez \
+       $(LOCAL_PATH)/bluez/lib \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := bluetoothd-snoop
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# init.bluetooth.rc
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init.bluetooth.rc
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := bluez/android/$(LOCAL_MODULE)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+include $(BUILD_PREBUILT)
+
+#
+# btmgmt
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       bluez/tools/btmgmt.c \
+       bluez/lib/bluetooth.c \
+       bluez/lib/sdp.c \
+       bluez/monitor/mainloop.c \
+       bluez/src/shared/io-mainloop.c \
+       bluez/src/shared/mgmt.c \
+       bluez/src/shared/queue.c \
+       bluez/src/shared/util.c \
+       bluez/src/uuid-helper.c \
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+       bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := btmgmt
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hcitool
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       bluez/tools/hcitool.c \
+       bluez/src/oui.c \
+       bluez/lib/bluetooth.c \
+       bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+       $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+       bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := hcitool
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
 include $(BUILD_EXECUTABLE)
+
+#
+# l2ping
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       bluez/tools/l2ping.c \
+       bluez/lib/bluetooth.c \
+       bluez/lib/hci.c \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+       bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := l2ping
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# avtest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       bluez/tools/avtest.c \
+       bluez/lib/bluetooth.c \
+       bluez/lib/hci.c \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+       bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := avtest
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# hciattach
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       bluez/tools/hciattach.c \
+       bluez/tools/hciattach_st.c \
+       bluez/tools/hciattach_ti.c \
+       bluez/tools/hciattach_tialt.c \
+       bluez/tools/hciattach_ath3k.c \
+       bluez/tools/hciattach_qualcomm.c \
+       bluez/tools/hciattach_intel.c \
+       bluez/tools/hciattach_bcm43xx.c \
+       bluez/lib/bluetooth.c \
+       bluez/lib/hci.c \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+       bluetooth-headers \
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := hciattach
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
+# libsbc
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+       sbc/sbc/sbc.c \
+       sbc/sbc/sbc_primitives.c \
+       sbc/sbc/sbc_primitives_mmx.c \
+       sbc/sbc/sbc_primitives_neon.c \
+       sbc/sbc/sbc_primitives_armv6.c \
+       sbc/sbc/sbc_primitives_iwmmxt.c \
+
+LOCAL_C_INCLUDES:= \
+       $(LOCAL_PATH)/sbc \
+
+LOCAL_CFLAGS:= \
+       -Os \
+       -Wno-sign-compare \
+       -Wno-missing-field-initializers \
+       -Wno-unused-parameter \
+       -Wno-type-limits \
+       -Wno-empty-body \
+
+LOCAL_MODULE := libsbc
+
+include $(BUILD_SHARED_LIBRARY)
+
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+
+#
+# bluetoothd (debug)
+# this is just a wrapper used in userdebug/eng to launch bluetoothd-main
+# with/without Valgrind
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+       bluez/android/bluetoothd-wrapper.c
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+       libcutils \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := bluetoothd
+
+LOCAL_REQUIRED_MODULES := \
+       bluetoothd-main \
+       valgrind \
+       memcheck-$(TARGET_ARCH)-linux \
+       vgpreload_core-$(TARGET_ARCH)-linux \
+       vgpreload_memcheck-$(TARGET_ARCH)-linux \
+       default.supp
+
+include $(BUILD_EXECUTABLE)
+
+endif
+
+#
+# bluetooth-headers
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bluetooth-headers
+LOCAL_NODULE_TAGS := optional
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+
+include_path := $(local-intermediates-dir)/include
+include_files := $(wildcard $(LOCAL_PATH)/bluez/lib/*.h)
+$(shell mkdir -p $(include_path)/bluetooth)
+$(foreach file,$(include_files),$(shell cp -u $(file) $(include_path)/bluetooth))
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_path)
+
+include $(BUILD_STATIC_LIBRARY)
index df04762..e663790 100644 (file)
@@ -1,41 +1,71 @@
 if ANDROID
+android_plugindir = $(abs_top_srcdir)/android/.libs
+
 noinst_PROGRAMS += android/system-emulator
 
 android_system_emulator_SOURCES = android/system-emulator.c \
                                        monitor/mainloop.h monitor/mainloop.c
 
+noinst_PROGRAMS += android/bluetoothd-snoop
+
+android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c \
+                               monitor/mainloop.h monitor/mainloop.c \
+                               src/shared/btsnoop.h src/shared/btsnoop.c
+
 noinst_PROGRAMS += android/bluetoothd
 
 android_bluetoothd_SOURCES = android/main.c \
                                src/log.c \
                                android/hal-msg.h \
+                               android/audio-msg.h \
+                               android/sco-msg.h \
                                android/utils.h \
                                src/sdpd-database.c src/sdpd-server.c \
                                src/sdpd-service.c src/sdpd-request.c \
-                               src/glib-helper.h src/glib-helper.c \
+                               src/uuid-helper.h src/uuid-helper.c \
                                src/eir.h src/eir.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
                                src/shared/util.h src/shared/util.c \
                                src/shared/mgmt.h src/shared/mgmt.c \
+                               src/shared/ringbuf.h src/shared/ringbuf.c \
+                               src/shared/hfp.h src/shared/hfp.c \
+                               src/shared/gatt-db.h src/shared/gatt-db.c \
                                android/bluetooth.h android/bluetooth.c \
                                android/hidhost.h android/hidhost.c \
+                               android/ipc-common.h \
                                android/ipc.h android/ipc.c \
                                android/avdtp.h android/avdtp.c \
                                android/a2dp.h android/a2dp.c \
+                               android/avctp.h android/avctp.c \
+                               android/avrcp.h android/avrcp.c \
+                               android/avrcp-lib.h android/avrcp-lib.c \
                                android/socket.h android/socket.c \
                                android/pan.h android/pan.c \
+                               android/handsfree.h android/handsfree.c \
+                               android/gatt.h android/gatt.c \
+                               android/health.h android/health.c \
+                               android/mcap-lib.h android/mcap-lib.c \
+                               attrib/att.c attrib/att.h \
+                               attrib/gatt.c attrib/gatt.h \
+                               attrib/gattrib.c attrib/gattrib.h \
                                btio/btio.h btio/btio.c \
                                src/sdp-client.h src/sdp-client.c \
                                profiles/network/bnep.h profiles/network/bnep.c
 
 android_bluetoothd_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 
-noinst_LTLIBRARIES += android/libhal-internal.la
+plugin_LTLIBRARIES += android/bluetooth.default.la
 
-android_libhal_internal_la_SOURCES = android/hal.h android/hal-bluetooth.c \
-                                       android/hal-sock.c \
+android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \
+                                       android/hal-socket.c \
                                        android/hal-hidhost.c \
+                                       android/hal-health.c \
                                        android/hal-pan.c \
                                        android/hal-a2dp.c \
+                                       android/hal-avrcp.c \
+                                       android/hal-handsfree.c \
+                                       android/hal-gatt.c \
                                        android/hardware/bluetooth.h \
                                        android/hardware/bt_av.h \
                                        android/hardware/bt_gatt.h \
@@ -50,10 +80,14 @@ android_libhal_internal_la_SOURCES = android/hal.h android/hal-bluetooth.c \
                                        android/hardware/bt_sock.h \
                                        android/hardware/hardware.h \
                                        android/cutils/properties.h \
+                                       android/ipc-common.h \
                                        android/hal-log.h \
-                                       android/hal-ipc.h android/hal-ipc.c
+                                       android/hal-ipc.h android/hal-ipc.c \
+                                       android/hal-utils.h android/hal-utils.c
 
-android_libhal_internal_la_CPPFLAGS = -I$(srcdir)/android
+android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+                                       -no-undefined
 
 noinst_PROGRAMS += android/haltest
 
@@ -67,25 +101,178 @@ android_haltest_SOURCES = android/client/haltest.c \
                                android/client/tabcompletion.c \
                                android/client/if-main.h \
                                android/client/if-av.c \
+                               android/client/if-rc.c \
                                android/client/if-bt.c \
                                android/client/if-gatt.c \
                                android/client/if-hf.c \
                                android/client/if-hh.c \
                                android/client/if-pan.c \
+                               android/client/if-hl.c \
                                android/client/if-sock.c \
-                               android/client/hwmodule.c \
+                               android/client/if-audio.c \
+                               android/client/if-sco.c \
+                               android/hardware/hardware.c \
                                android/hal-utils.h android/hal-utils.c
 
-android_haltest_LDADD = android/libhal-internal.la
-
 android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
-                               -DPLATFORM_SDK_VERSION=19
+                               -DPLUGINDIR=\""$(android_plugindir)"\"
+
+android_haltest_LDFLAGS = -pthread -ldl -lm
+
+noinst_PROGRAMS += android/android-tester
+
+android_android_tester_SOURCES = emulator/btdev.h emulator/btdev.c \
+                               emulator/bthost.h emulator/bthost.c \
+                               emulator/smp.c \
+                               src/shared/crypto.h src/shared/crypto.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/mgmt.h src/shared/mgmt.c \
+                               src/shared/hciemu.h src/shared/hciemu.c \
+                               src/shared/tester.h src/shared/tester.c \
+                               src/shared/timeout.h src/shared/timeout-glib.c \
+                               monitor/rfcomm.h \
+                               android/hardware/hardware.c \
+                               android/android-tester.c
+
+android_android_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \
+                               -DPLUGINDIR=\""$(android_plugindir)"\"
+
+android_android_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+android_android_tester_LDFLAGS = -pthread -ldl
+
+noinst_PROGRAMS += android/ipc-tester
+
+android_ipc_tester_SOURCES = emulator/btdev.h emulator/btdev.c \
+                               emulator/bthost.h emulator/bthost.c \
+                               emulator/smp.c \
+                               src/shared/crypto.h src/shared/crypto.c \
+                               src/shared/io.h src/shared/io-glib.c \
+                               src/shared/queue.h src/shared/queue.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/shared/mgmt.h src/shared/mgmt.c \
+                               src/shared/hciemu.h src/shared/hciemu.c \
+                               src/shared/tester.h src/shared/tester.c \
+                               src/shared/timeout.h src/shared/timeout-glib.c \
+                               android/hal-utils.h android/hal-utils.c \
+                               android/ipc-common.h android/ipc-tester.c
+
+android_ipc_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+
+android_ipc_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
+android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \
+                                       android/hal-msg.h \
+                                       android/hal-audio.c \
+                                       android/hardware/audio.h \
+                                       android/hardware/audio_effect.h \
+                                       android/hardware/hardware.h \
+                                       android/system/audio.h
+
+android_audio_sco_default_la_SOURCES = android/hal-log.h \
+                                       android/sco-msg.h \
+                                       android/hal-sco.c \
+                                       android/hardware/audio.h \
+                                       android/hardware/audio_effect.h \
+                                       android/hardware/hardware.h \
+                                       android/audio_utils/resampler.c \
+                                       android/audio_utils/resampler.h \
+                                       android/system/audio.h
+
+android_audio_sco_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+
+android_audio_sco_default_la_LIBADD = @SPEEXDSP_LIBS@
+
+android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+                                       -no-undefined -lrt
+
+plugin_LTLIBRARIES += android/audio.sco.default.la
+
+unit_tests += android/test-ipc
+
+android_test_ipc_SOURCES = android/test-ipc.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/log.h src/log.c \
+                               android/ipc-common.h \
+                               android/ipc.c android/ipc.h
+android_test_ipc_LDADD = @GLIB_LIBS@
+
+plugin_LTLIBRARIES += android/audio.a2dp.default.la
+
+android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android
+
+android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@
 
-android_haltest_LDFLAGS = -pthread
+android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+                                       -no-undefined -pthread -lrt
 
 endif
 
-EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \
-               android/pics-gap.txt android/pics-hid.txt \
-               android/pics-pan.txt android/pics-did.txt \
-               android/pics-opp.txt android/pics-pbap.txt
+EXTRA_DIST += android/Android.mk android/README \
+                               android/init.bluetooth.rc \
+                               android/hal-ipc-api.txt \
+                               android/audio-ipc-api.txt \
+                               android/pics-rfcomm.txt \
+                               android/pics-spp.txt \
+                               android/pics-sdp.txt \
+                               android/pics-l2cap.txt \
+                               android/pics-gap.txt \
+                               android/pics-did.txt \
+                               android/pics-hid.txt \
+                               android/pics-pan.txt \
+                               android/pics-opp.txt \
+                               android/pics-map.txt \
+                               android/pics-pbap.txt \
+                               android/pics-a2dp.txt \
+                               android/pics-avctp.txt \
+                               android/pics-avrcp.txt \
+                               android/pics-hsp.txt \
+                               android/pics-hfp.txt \
+                               android/pics-gatt.txt \
+                               android/pics-mcap.txt \
+                               android/pics-hdp.txt \
+                               android/pics-iopt.txt \
+                               android/pics-sm.txt \
+                               android/pics-mps.txt \
+                               android/pixit-l2cap.txt \
+                               android/pixit-gap.txt \
+                               android/pixit-did.txt \
+                               android/pixit-hid.txt \
+                               android/pixit-pan.txt \
+                               android/pixit-opp.txt \
+                               android/pixit-map.txt \
+                               android/pixit-pbap.txt \
+                               android/pixit-a2dp.txt \
+                               android/pixit-avctp.txt \
+                               android/pixit-avrcp.txt \
+                               android/pixit-hsp.txt \
+                               android/pixit-hfp.txt \
+                               android/pixit-gatt.txt \
+                               android/pixit-mcap.txt \
+                               android/pixit-hdp.txt \
+                               android/pixit-iopt.txt \
+                               android/pixit-sm.txt \
+                               android/pixit-mps.txt \
+                               android/bite-rfcomm.txt \
+                               android/bite-spp.txt \
+                               android/pts-l2cap.txt \
+                               android/pts-gap.txt \
+                               android/pts-did.txt \
+                               android/pts-hid.txt \
+                               android/pts-pan.txt \
+                               android/pts-opp.txt \
+                               android/pts-map.txt \
+                               android/pts-a2dp.txt \
+                               android/pts-avrcp.txt \
+                               android/pts-avctp.txt \
+                               android/pts-pbap.txt \
+                               android/pts-hfp.txt \
+                               android/pts-gatt.txt \
+                               android/pts-hsp.txt \
+                               android/pts-iopt.txt \
+                               android/pts-hdp.txt \
+                               android/pts-mcap.txt \
+                               android/pts-mps.txt \
+                               android/pts-sm.txt
index 68c3e9f..0895b71 100644 (file)
@@ -7,79 +7,187 @@ of the stack of choice on Android. Android BlueZ is intended as a drop-in
 replacement to Android provided Bluetooth stack.
 
 More details about BlueZ for Android architecture and components can be found
-in android/hal-apc-api.txt file.
+in android/hal-ipc-api.txt file.
+
+Supported Android version: 4.4
+
 
-===============================
 Building and running on Android
 ===============================
 
+Steps needed to build and run Android Open Source Project 4.4.2 with
+integrated BlueZ.
+
+
 Build requirements
-==================
+------------------
 
 - GLib - Android 4.2 or later don't provide GLib and one must provide it in
 'external/bluetooth/glib' folder of Android tree. Sample Android GLib port
-is available at https://code.google.com/p/android-bluez.glib/
+is available at https://code.google.com/p/aosp-bluez.glib/
+
+- SBC - A2DP code requires SBC library (version 1.2 or higher) present in
+'external/bluetooth/sbc' directory. Library is build from Android.mk provided
+by BlueZ. SBC code is available at git://git.kernel.org/pub/scm/bluetooth/sbc
 
-- Bionic support - BlueZ requires signalfd and timerfd APIs to be provided
-by libc library. Currently only 'master' branch available at
+- Bionic support - Currently only 'master' branch available at
 https://android.googlesource.com/platform/bionic provides all required
-functionality and running BlueZ on older branch requires backporting missing
-features. Sample Bionic for Android on Intel Architecture (Android-IA) with all
-required features backported is available at
-https://code.google.com/p/android-bluez.bionic/
+functionality and running BlueZ on release branch requires backporting missing
+features (currently epoll_create1 and ppoll calls for Android 4.4.2). Sample
+Bionic for Android 4.4.2 with all required features backported is available at
+https://code.google.com/p/aosp-bluez.platform-bionic/
+
 
 Runtime requirements
-====================
+--------------------
 
-BlueZ HAL library requires 'bluetoothd' service to be available on Android
-system. This can be done by defining service in init.rc file of targeted board:
+BlueZ HAL library requires 'bluetoothd' and 'bluetoothd-snoop' services to be
+available on Android system. Some permissions settings are also required.
 
-service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
-  class main
-  group bluetooth net_admin
-  disabled
-  oneshot
+This can be done by importing init.bluetooth.rc file in init.rc file of targeted
+board:
+import init.bluetooth.rc
 
-It is required that bluetooth user could start and stop bluetoothd service by
-setting 'ctl.start' or 'ctl.stop' property. This can be achieved by
-whitelisting bluetooth user and bluetoothd service in init source code.
+For convenience examples are provided at:
+https://code.google.com/p/aosp-bluez.device-lge-mako/    (Nexus 4)
+https://code.google.com/p/aosp-bluez.device-asus-flo/    (Nexus 7 2013)
 
-Required Android init system modifications can be found at
-https://code.google.com/p/android-bluez.system-core/
 
 Downloading and building
-========================
+------------------------
 
-Building for Android requires full Android AOSP source tree. Sample Android-IA
-tree with all required components present is available at
-http://code.google.com/p/android-bluez/
+Building for Android requires full Android AOSP source tree. Sample Android
+4.4.2 tree with all required components present is available at
+http://code.google.com/p/aosp-bluez/
+
+This tree provides support for Nexus4 (target aosp_mako-userdebug) and
+Nexus 7 2013 (target aosp_flo-userdebug). Tree does not provide binary blobs
+needed to run Android on supported devices. Those can be obtained from
+https://developers.google.com/android/nexus/drivers. Binary blobs needs to be
+unpacked (EULA acceptance required) into 'vendor' directory of Android tree.
 
 Downloading:
-repo init -u https://code.google.com/p/android-bluez.manifest/ -m topics/bluez
+repo init -u https://code.google.com/p/aosp-bluez.platform-manifest -b kitkat
 repo sync
 
-Build for Intel ultrabook:
-'source build/envsetup.sh'
-'lunch core_mesa-eng'
-'make allimages -j8'
+Building:
+source build/envsetup.sh
+lunch aosp_mako-userdebug    or    lunch aosp_flo-userdebug
+make -j8
+
+Flashing:
+adb reboot bootloader
+fastboot flashall -w
 
 After full build is done it is possible to rebuild only BlueZ:
 'cd external/bluetooth/bluez/android/'
 'mm' (or 'mm -B' to force rebuilding of all files)
 'adb sync' to update target device.
 
-=============================
+
+Linux Kernel requirements
+-------------------------
+
+BlueZ for Android uses Linux Bluetooth subsystem and it must be enabled in
+kernel. Minimal required version of management interface is 1.3. This
+corresponds to Linux 3.9 but latest available version is recommended. Other
+requirements include UHID and network bridge support.
+
+Following kernel options should be enabled:
+CONFIG_BT
+CONFIG_BT_RFCOMM
+CONFIG_BT_RFCOMM_TTY
+CONFIG_BT_BNEP
+CONFIG_BT_BNEP_MC_FILTER
+CONFIG_BT_BNEP_PROTO_FILTER
+CONFIG_BRIDGE
+CONFIG_UHID
+
+Also BT chip driver needs to be enabled e.g:
+CONFIG_BT_HCIBTUSB
+
+If it is not possible to use new enough Linux kernel one can use updated
+bluetooth subsytem from Backports project. More information about Backports can
+be found at https://backports.wiki.kernel.org. Sample kernels using backports
+for running BlueZ on Android are available at
+https://code.google.com/p/aosp-bluez.
+
+
+Running with Valgrind
+---------------------
+
+BlueZ for Android is preconfigured to be easily run under Valgrind memcheck.
+Appropriate configuration and required modules are automatically included when
+building either userdebug or eng variant of Android platform.
+
+Valgrind can be enabled in runtime by setting "persist.sys.bluetooth.valgrind"
+property to either literal "true" or any numeric value >0. For example:
+adb root
+adb shell setprop persist.sys.bluetooth.valgrind true
+
+After changing property value Bluetooth need to be restarted to apply changes
+(this can be done using UI, just disable and enable it again). Property is
+persistent, i.e. there's no need to enable Valgrind again after reboot.
+
+It's recommended to have unstripped libglib.so installed which will enable
+complete backtraces in Valgrind output. Otherwise, in many cases backtrace
+will break at e.g. g_free() function without prior callers. It's possible to
+have proper library installed automatically by appropriate entry in Android.mk,
+see https://code.google.com/p/aosp-bluez.glib/ for an example.
+
+
+Enabling BlueZ debugs
+---------------------
+
+BlueZ debug logs can be enabled in runtime by setting "persist.sys.bluetooth.debug"
+property to either literal "true" or any numeric value >0. For example:
+adb root
+adb shell setprop persist.sys.bluetooth.debug 1
+
+After changing property value Bluetooth needs to be restarted to apply changes.
+
+There is also a possibility to enable mgmt debug logs which also enables debugs
+as above. To enable it procced in the same way as described above but use
+system properties called: persist.sys.bluetooth.mgmtdbg
+
+Note: Debugs are only available on NON USER build variants
+
+
+Customization
+-------------
+
+It is possible to customize BlueZ for Android through Android system properties.
+This may include enabling extra profiles or features inside HALs implementation
+These properties are read on Bluetooth stack startup only and require stack
+restart if changed. All customization properties names start with
+"persist.sys.bluetooth." followed by specific HAL name e.g.
+"persist.sys.bluetooth.handsfree". This section list available customization
+options.
+
+Property       Value           Description
+-------------------------------------------
+mode           bredr           Enable BlueZ in BR/EDR mode
+               le              Enable BlueZ in LE mode
+               <none>          Enable BlueZ in default mode - enable BR/EDR/LE
+                               if available.
+handsfree      hfp             Enable Handsfree Profile (HFP) with narrowband
+                               speech only
+               hfp_wbs         Enable Handsfree Profile (HFP) with narrowband
+                               and wideband speech support
+               <none>          Don't enable Handsfree Profile (HFP)
+
+
 Building and running on Linux
-=============================
+-----------------------------
 
 It is possible to build and test BlueZ for Android daemon on Linux (eg. PC).
 Simply follow instructions available at README file in BlueZ top directory.
 Android daemon binary is located at android/bluetoothd. See next section on
 how to test Android daemon on Linux.
 
-============
+
 Testing tool
-============
+------------
 
 BT HAL test tools located in android/haltest is provided for HAL level testing
 of both Android daemon and HAL library. Start it with '-n' parameter and type
@@ -91,3 +199,124 @@ use provided android/system-emulator, which takes care of launching daemon
 automatically on HAL library initialization. To deinitialize HAL library and
 stop daemon type 'bluetooth cleanup'. Type 'help' for more information. Tab
 completion is also supported.
+
+
+Implementation status
+=====================
+
+Summary of HALs implementation status.
+
+complete    - implementation is feature complete and Android Framework is able
+              to use it normally
+partial     - implementation is in progress and not all required features are
+              present, Android Framework is able to use some of features
+initial     - only initial implementations is present, Android Framework is
+              able to initialize but most likely not able to use it
+not started - no implementation, Android Framework is not able to initialize it
+
+Profile ID    HAL header         Status
+---------------------------------------
+core          bluetooth.h        complete
+a2dp          bt_av.h            complete
+gatt          bt_gatt.h          partial
+              bt_gatt_client.h   partial
+              bt_gatt_server.h   initial
+handsfree     bt_hf.h            complete
+hidhost       bt_hh.h            complete
+health        bt_hl.h            initial
+pan           bt_pan.h           complete
+avrcp         bt_rc.h            complete
+socket        bt_sock.h          complete
+
+
+Implementation shortcomings
+===========================
+
+It is possible that some of HAL functionality (although being marked as
+complete) is missing implementation due to reasons like feature feasibility or
+necessity for latest Android Framework. This sections provides list of such
+deficiencies. Note that HAL library is always expected to fully implement HAL
+API so missing implementation might happen only in daemon.
+
+
+HAL Bluetooth
+-------------
+
+methods:
+dut_mode_send                      never called from Android Framework
+le_test_mode                       never called from Android Framework
+get_remote_service_record          never called from Android Framework
+
+callbacks:
+dut_mode_recv_cb                   empty JNI implementation
+le_test_mode_cb                    empty JNI implementation
+
+properties:
+BT_PROPERTY_SERVICE_RECORD         not supported for adapter and device, for
+                                   device this property is to be returned as
+                                   response to get_remote_service_record,
+                                   not sure what to return on get_property
+                                   calls (records of all services?)
+
+BT_PROPERTY_REMOTE_VERSION_INFO    information required by this property (LMP
+                                   information) are not accessible from mgmt
+                                   interface, also marking this property as
+                                   settable is probably a typo in HAL header
+
+HAL Socket
+----------
+
+Support only for BTSOCK_RFCOMM socket type.
+
+
+HAL AVRCP
+---------
+
+methods:
+list_player_app_attr_rsp           never called from Android Framework
+list_player_app_value_rsp          never called from Android Framework
+get_player_app_value_rsp           never called from Android Framework
+get_player_app_attr_text_rsp       never called from Android Framework
+get_player_app_value_text_rsp      never called from Android Framework
+set_player_app_value_rsp           never called from Android Framework
+
+callbacks:
+list_player_app_attr_cb            NULL JNI implementation
+list_player_app_values_cb          NULL JNI implementation
+get_player_app_value_cb            NULL JNI implementation
+get_player_app_attrs_text_cb       NULL JNI implementation
+get_player_app_values_text_cb      NULL JNI implementation
+set_player_app_value_cb            NULL JNI implementation
+
+
+Known Android issues
+====================
+
+It is possible that BlueZ is triggering bugs on Android Framework that could
+affect qualification or user experience. This section provides list of
+recommended Android fixes that are not part of latest AOSP release supported by
+BlueZ.
+
+https://android-review.googlesource.com/82757
+https://android-review.googlesource.com/87670
+https://android-review.googlesource.com/88384
+
+
+Unimplemented Bluetooth features
+================================
+
+Some Bluetooth functionality require support from outside of BT stack
+eg. telephony stack. This sections describes profiles optional features not
+implemented due to lack of support in other Android subsystems or missing API
+in respective BT HALs.
+
+Profile                Feature                         Comments
+--------------------------------------------------------
+HFP            Attach a phone number to        AT+BINP=1
+               a voice tag
+HFP            Enhanced Call Control           AT+CHLD={1x,2x}
+HFP            Explicit Call Transfer          AT+CHLD=4
+HFP            Response and Hold               AT+BTRH, +BTRH
+HFP            In-band Ring Tone               +BSIR
+AVRCP          Player Settings                 HAL API present but not used
+AVRCP          Browsing                        No HAL API
index 107cbb8..452fdab 100644 (file)
@@ -2,21 +2,21 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 "lib/bluetooth.h"
 #include "lib/sdp.h"
 #include "lib/sdp_lib.h"
-#include "log.h"
-#include "a2dp.h"
+#include "profiles/audio/a2dp-codecs.h"
+#include "src/log.h"
 #include "hal-msg.h"
+#include "ipc-common.h"
 #include "ipc.h"
+#include "a2dp.h"
 #include "utils.h"
 #include "bluetooth.h"
 #include "avdtp.h"
+#include "avrcp.h"
+#include "audio-msg.h"
 
 #define L2CAP_PSM_AVDTP 0x19
 #define SVC_HINT_CAPTURING 0x08
+#define IDLE_TIMEOUT 1
+#define AUDIO_RETRY_TIMEOUT 2
 
 static GIOChannel *server = NULL;
 static GSList *devices = NULL;
+static GSList *endpoints = NULL;
+static GSList *setups = NULL;
 static bdaddr_t adapter_addr;
 static uint32_t record_id = 0;
+static guint audio_retry_id = 0;
+static bool audio_retrying = false;
+
+static struct ipc *hal_ipc = NULL;
+static struct ipc *audio_ipc = NULL;
+
+struct a2dp_preset {
+       void *data;
+       int8_t len;
+};
+
+struct a2dp_endpoint {
+       uint8_t id;
+       uint8_t codec;
+       struct avdtp_local_sep *sep;
+       struct a2dp_preset *caps;
+       GSList *presets;
+};
 
 struct a2dp_device {
        bdaddr_t        dst;
        uint8_t         state;
        GIOChannel      *io;
-       guint           watch;
        struct avdtp    *session;
+       guint           idle_id;
+};
+
+struct a2dp_setup {
+       struct a2dp_device *dev;
+       struct a2dp_endpoint *endpoint;
+       struct a2dp_preset *preset;
+       struct avdtp_stream *stream;
+       uint8_t state;
 };
 
 static int device_cmp(gconstpointer s, gconstpointer user_data)
@@ -69,21 +103,86 @@ static int device_cmp(gconstpointer s, gconstpointer user_data)
        return bacmp(&dev->dst, dst);
 }
 
-static void a2dp_device_free(struct a2dp_device *dev)
+static void preset_free(void *data)
+{
+       struct a2dp_preset *preset = data;
+
+       g_free(preset->data);
+       g_free(preset);
+}
+
+static void unregister_endpoint(void *data)
+{
+       struct a2dp_endpoint *endpoint = data;
+
+       if (endpoint->sep)
+               avdtp_unregister_sep(endpoint->sep);
+
+       if (endpoint->caps)
+               preset_free(endpoint->caps);
+
+       g_slist_free_full(endpoint->presets, preset_free);
+
+       g_free(endpoint);
+}
+
+static void setup_free(void *data)
+{
+       struct a2dp_setup *setup = data;
+
+       if (!g_slist_find(setup->endpoint->presets, setup->preset))
+               preset_free(setup->preset);
+
+       g_free(setup);
+}
+
+static void setup_remove(struct a2dp_setup *setup)
+{
+       setups = g_slist_remove(setups, setup);
+       setup_free(setup);
+}
+
+static void setup_remove_all_by_dev(struct a2dp_device *dev)
+{
+       GSList *l = setups;
+
+       while (l) {
+               struct a2dp_setup *setup = l->data;
+               GSList *next = g_slist_next(l);
+
+               if (setup->dev == dev)
+                       setup_remove(setup);
+
+               l = next;
+       }
+}
+
+static void a2dp_device_free(void *data)
 {
+       struct a2dp_device *dev = data;
+
+       if (dev->idle_id > 0)
+               g_source_remove(dev->idle_id);
+
        if (dev->session)
                avdtp_unref(dev->session);
 
-       if (dev->watch > 0)
-               g_source_remove(dev->watch);
-
-       if (dev->io)
+       if (dev->io) {
+               g_io_channel_shutdown(dev->io, FALSE, NULL);
                g_io_channel_unref(dev->io);
+       }
+
+       setup_remove_all_by_dev(dev);
 
-       devices = g_slist_remove(devices, dev);
        g_free(dev);
 }
 
+static void a2dp_device_remove(struct a2dp_device *dev)
+{
+       devices = g_slist_remove(devices, dev);
+       a2dp_device_free(dev);
+}
+
 static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst)
 {
        struct a2dp_device *dev;
@@ -95,6 +194,25 @@ static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst)
        return dev;
 }
 
+static bool a2dp_device_connect(struct a2dp_device *dev, BtIOConnect cb)
+{
+       GError *err = NULL;
+
+       dev->io = bt_io_connect(cb, dev, NULL, &err,
+                                       BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                                       BT_IO_OPT_DEST_BDADDR, &dev->dst,
+                                       BT_IO_OPT_PSM, L2CAP_PSM_AVDTP,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               return false;
+       }
+
+       return true;
+}
+
 static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state)
 {
        struct hal_ev_a2dp_conn_state ev;
@@ -111,19 +229,280 @@ static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state)
        bdaddr2android(&dev->dst, ev.bdaddr);
        ev.state = state;
 
-       ipc_send_notif(HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE, sizeof(ev),
-                                                                       &ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE,
+                                                       sizeof(ev), &ev);
 
        if (state != HAL_A2DP_STATE_DISCONNECTED)
                return;
 
-       a2dp_device_free(dev);
+       bt_avrcp_disconnect(&dev->dst);
+
+       a2dp_device_remove(dev);
 }
 
-static gboolean watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+static void bt_audio_notify_state(struct a2dp_setup *setup, uint8_t state)
 {
-       struct a2dp_device *dev = data;
+       struct hal_ev_a2dp_audio_state ev;
+       char address[18];
+
+       if (setup->state == state)
+               return;
+
+       setup->state = state;
+
+       ba2str(&setup->dev->dst, address);
+       DBG("device %s state %u", address, state);
+
+       bdaddr2android(&setup->dev->dst, ev.bdaddr);
+       ev.state = state;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_AUDIO_STATE,
+                                                       sizeof(ev), &ev);
+}
+
+static void disconnect_cb(void *user_data)
+{
+       struct a2dp_device *dev = user_data;
+
+       bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+}
+
+static int sbc_check_config(void *caps, uint8_t caps_len, void *conf,
+                                                       uint8_t conf_len)
+{
+       a2dp_sbc_t *cap, *config;
+
+       if (conf_len != caps_len || conf_len != sizeof(a2dp_sbc_t)) {
+               error("SBC: Invalid configuration size (%u)", conf_len);
+               return -EINVAL;
+       }
+
+       cap = caps;
+       config = conf;
+
+       if (!(cap->frequency & config->frequency)) {
+               error("SBC: Unsupported frequency (%u) by endpoint",
+                                                       config->frequency);
+               return -EINVAL;
+       }
+
+       if (!(cap->channel_mode & config->channel_mode)) {
+               error("SBC: Unsupported channel mode (%u) by endpoint",
+                                                       config->channel_mode);
+               return -EINVAL;
+       }
+
+       if (!(cap->block_length & config->block_length)) {
+               error("SBC: Unsupported block length (%u) by endpoint",
+                                                       config->block_length);
+               return -EINVAL;
+       }
+
+       if (!(cap->allocation_method & config->allocation_method)) {
+               error("SBC: Unsupported allocation method (%u) by endpoint",
+                                                       config->block_length);
+               return -EINVAL;
+       }
+
+       if (config->max_bitpool < cap->min_bitpool) {
+               error("SBC: Invalid maximun bitpool (%u < %u)",
+                                       config->max_bitpool, cap->min_bitpool);
+               return -EINVAL;
+       }
+
+       if (config->min_bitpool > cap->max_bitpool) {
+               error("SBC: Invalid minimun bitpool (%u > %u)",
+                                       config->min_bitpool, cap->min_bitpool);
+               return -EINVAL;
+       }
+
+       if (config->max_bitpool > cap->max_bitpool)
+               return -ERANGE;
+
+       if (config->min_bitpool < cap->min_bitpool)
+               return -ERANGE;
+
+       return 0;
+}
+
+static int check_capabilities(struct a2dp_preset *preset,
+                               struct avdtp_media_codec_capability *codec,
+                               uint8_t codec_len)
+{
+       /* Codec specific */
+       switch (codec->media_codec_type) {
+       case A2DP_CODEC_SBC:
+               return sbc_check_config(codec->data, codec_len, preset->data,
+                                                               preset->len);
+       default:
+               return -EINVAL;
+       }
+}
+
+static struct a2dp_preset *sbc_select_range(void *caps, uint8_t caps_len,
+                                               void *conf, uint8_t conf_len)
+{
+       struct a2dp_preset *p;
+       a2dp_sbc_t *cap, *config;
+
+       cap = caps;
+       config = conf;
+
+       config->min_bitpool = MAX(config->min_bitpool, cap->min_bitpool);
+       config->max_bitpool = MIN(config->max_bitpool, cap->max_bitpool);
+
+       p = g_new0(struct a2dp_preset, 1);
+       p->len = conf_len;
+       p->data = g_memdup(conf, p->len);
+
+       return p;
+}
+
+static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset,
+                               struct avdtp_media_codec_capability *codec,
+                               uint8_t codec_len)
+{
+       /* Codec specific */
+       switch (codec->media_codec_type) {
+       case A2DP_CODEC_SBC:
+               return sbc_select_range(codec->data, codec_len, preset->data,
+                                                               preset->len);
+       default:
+               return NULL;
+       }
+}
+
+static struct a2dp_preset *select_preset(struct a2dp_endpoint *endpoint,
+                                               struct avdtp_remote_sep *rsep)
+{
+       struct avdtp_service_capability *service;
+       struct avdtp_media_codec_capability *codec;
+       GSList *l;
+       uint8_t codec_len;
+
+       service = avdtp_get_codec(rsep);
+       codec = (struct avdtp_media_codec_capability *) service->data;
+       codec_len = service->length - sizeof(*codec);
+
+       for (l = endpoint->presets; l; l = g_slist_next(l)) {
+               struct a2dp_preset *preset = l->data;
+               int err;
+
+               err = check_capabilities(preset, codec, codec_len);
+               if (err == 0)
+                       return preset;
+
+               if (err == -ERANGE)
+                       return select_preset_range(preset, codec, codec_len);
+       }
+
+       return NULL;
+}
+
+static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint,
+                       struct a2dp_preset *preset, struct avdtp_stream *stream)
+{
+       struct a2dp_setup *setup;
+
+       setup = g_new0(struct a2dp_setup, 1);
+       setup->dev = dev;
+       setup->endpoint = endpoint;
+       setup->preset = preset;
+       setup->stream = stream;
+       setups = g_slist_append(setups, setup);
+
+       if (dev->idle_id > 0) {
+               g_source_remove(dev->idle_id);
+               dev->idle_id = 0;
+       }
+}
+
+static int select_configuration(struct a2dp_device *dev,
+                               struct a2dp_endpoint *endpoint,
+                               struct avdtp_remote_sep *rsep)
+{
+       struct a2dp_preset *preset;
+       struct avdtp_stream *stream;
+       struct avdtp_service_capability *service;
+       struct avdtp_media_codec_capability *codec;
+       GSList *caps;
+       int err;
+
+       preset = select_preset(endpoint, rsep);
+       if (!preset) {
+               error("Unable to select codec preset");
+               return -EINVAL;
+       }
+
+       service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+       caps = g_slist_append(NULL, service);
+
+       codec = g_malloc0(sizeof(*codec) + preset->len);
+       codec->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+       codec->media_codec_type = endpoint->codec;
+       memcpy(codec->data, preset->data, preset->len);
+
+       service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec,
+                                               sizeof(*codec) + preset->len);
+       caps = g_slist_append(caps, service);
+
+       g_free(codec);
+
+       err = avdtp_set_configuration(dev->session, rsep, endpoint->sep, caps,
+                                                               &stream);
+       g_slist_free_full(caps, g_free);
+       if (err < 0) {
+               error("avdtp_set_configuration: %s", strerror(-err));
+               return err;
+       }
+
+       setup_add(dev, endpoint, preset, stream);
+
+       return 0;
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct a2dp_device *dev = user_data;
+       struct a2dp_endpoint *endpoint = NULL;
+       struct avdtp_remote_sep *rsep = NULL;
+       GSList *l;
+
+       for (l = endpoints; l; l = g_slist_next(l)) {
+               endpoint = l->data;
+
+               rsep = avdtp_find_remote_sep(session, endpoint->sep);
+               if (rsep)
+                       break;
+       }
+
+       if (!rsep) {
+               error("Unable to find matching endpoint");
+               goto failed;
+       }
+
+       if (select_configuration(dev, endpoint, rsep) < 0)
+               goto failed;
+
+       return;
 
+failed:
+       avdtp_shutdown(session);
+}
+
+static gboolean idle_timeout(gpointer user_data)
+{
+       struct a2dp_device *dev = user_data;
+       int err;
+
+       dev->idle_id = 0;
+
+       err = avdtp_discover(dev->session, discover_cb, dev);
+       if (err == 0)
+               return FALSE;
+
+       error("avdtp_discover: %s", strerror(-err));
        bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
 
        return FALSE;
@@ -148,20 +527,43 @@ static void signaling_connect_cb(GIOChannel *chan, GError *err,
                        BT_IO_OPT_OMTU, &omtu,
                        BT_IO_OPT_INVALID);
        if (gerr) {
-               bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
                error("%s", gerr->message);
                g_error_free(gerr);
-               return;
+               goto failed;
        }
 
-       /* FIXME: Add proper version */
        fd = g_io_channel_unix_get_fd(chan);
+
+       /* FIXME: Add proper version */
        dev->session = avdtp_new(fd, imtu, omtu, 0x0100);
+       if (!dev->session)
+               goto failed;
 
-       dev->watch = g_io_add_watch(dev->io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                                                               watch_cb, dev);
+       avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev);
 
-       bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED);
+       if (dev->io) {
+               g_io_channel_unref(dev->io);
+               dev->io = NULL;
+       }
+
+       /* Proceed to stream setup if initiator */
+       if (dev->state == HAL_A2DP_STATE_CONNECTING) {
+               int perr;
+
+               perr = avdtp_discover(dev->session, discover_cb, dev);
+               if (perr < 0) {
+                       error("avdtp_discover: %s", strerror(-perr));
+                       goto failed;
+               }
+               bt_avrcp_connect(&dev->dst);
+       } else /* Init idle timeout to discover */
+               dev->idle_id = g_timeout_add_seconds(IDLE_TIMEOUT, idle_timeout,
+                                                                       dev);
+
+       return;
+
+failed:
+       bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
 }
 
 static void bt_a2dp_connect(const void *buf, uint16_t len)
@@ -172,7 +574,6 @@ static void bt_a2dp_connect(const void *buf, uint16_t len)
        char addr[18];
        bdaddr_t dst;
        GSList *l;
-       GError *err = NULL;
 
        DBG("");
 
@@ -185,16 +586,8 @@ static void bt_a2dp_connect(const void *buf, uint16_t len)
        }
 
        dev = a2dp_device_new(&dst);
-       dev->io = bt_io_connect(signaling_connect_cb, dev, NULL, &err,
-                                       BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
-                                       BT_IO_OPT_DEST_BDADDR, &dev->dst,
-                                       BT_IO_OPT_PSM, L2CAP_PSM_AVDTP,
-                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
-                                       BT_IO_OPT_INVALID);
-       if (err) {
-               error("%s", err->message);
-               g_error_free(err);
-               a2dp_device_free(dev);
+       if (!a2dp_device_connect(dev, signaling_connect_cb)) {
+               a2dp_device_remove(dev);
                status = HAL_STATUS_FAILED;
                goto failed;
        }
@@ -207,7 +600,7 @@ static void bt_a2dp_connect(const void *buf, uint16_t len)
        status = HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status);
 }
 
 static void bt_a2dp_disconnect(const void *buf, uint16_t len)
@@ -229,17 +622,20 @@ static void bt_a2dp_disconnect(const void *buf, uint16_t len)
        }
 
        dev = l->data;
+       status = HAL_STATUS_SUCCESS;
 
-       /* Wait signaling channel to HUP */
-       if (dev->io)
-               g_io_channel_shutdown(dev->io, TRUE, NULL);
+       if (dev->io) {
+               bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
+               goto failed;
+       }
 
+       /* Wait AVDTP session to shutdown */
+       avdtp_shutdown(dev->session);
        bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTING);
 
-       status = HAL_STATUS_SUCCESS;
-
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT,
+                                                                       status);
 }
 
 static const struct ipc_handler cmd_handlers[] = {
@@ -249,10 +645,71 @@ static const struct ipc_handler cmd_handlers[] = {
        { bt_a2dp_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) },
 };
 
+static struct a2dp_setup *find_setup_by_device(struct a2dp_device *dev)
+{
+       GSList *l;
+
+       for (l = setups; l; l = g_slist_next(l)) {
+               struct a2dp_setup *setup = l->data;
+
+               if (setup->dev == dev)
+                       return setup;
+       }
+
+       return NULL;
+}
+
+static void transport_connect_cb(GIOChannel *chan, GError *err,
+                                                       gpointer user_data)
+{
+       struct a2dp_device *dev = user_data;
+       struct a2dp_setup *setup;
+       uint16_t imtu, omtu;
+       GError *gerr = NULL;
+       int fd;
+
+       if (err) {
+               error("%s", err->message);
+               return;
+       }
+
+       setup = find_setup_by_device(dev);
+       if (!setup) {
+               error("Unable to find stream setup");
+               return;
+       }
+
+       bt_io_get(chan, &gerr,
+                       BT_IO_OPT_IMTU, &imtu,
+                       BT_IO_OPT_OMTU, &omtu,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               return;
+       }
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       if (!avdtp_stream_set_transport(setup->stream, fd, imtu, omtu)) {
+               error("avdtp_stream_set_transport: failed");
+               return;
+       }
+
+       g_io_channel_set_close_on_unref(chan, FALSE);
+
+       if (dev->io) {
+               g_io_channel_unref(dev->io);
+               dev->io = NULL;
+       }
+
+       bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED);
+}
+
 static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 {
        struct a2dp_device *dev;
-       bdaddr_t src, dst;
+       bdaddr_t dst;
        char address[18];
        GError *gerr = NULL;
        GSList *l;
@@ -263,7 +720,6 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
        }
 
        bt_io_get(chan, &gerr,
-                       BT_IO_OPT_SOURCE_BDADDR, &src,
                        BT_IO_OPT_DEST_BDADDR, &dst,
                        BT_IO_OPT_INVALID);
        if (gerr) {
@@ -273,13 +729,15 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
                return;
        }
 
-       l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (l)
-               return;
-
        ba2str(&dst, address);
        DBG("Incoming connection from %s", address);
 
+       l = g_slist_find_custom(devices, &dst, device_cmp);
+       if (l) {
+               transport_connect_cb(chan, err, l->data);
+               return;
+       }
+
        dev = a2dp_device_new(&dst);
        signaling_connect_cb(chan, err, dev);
 }
@@ -345,12 +803,772 @@ static sdp_record_t *a2dp_record(void)
        return record;
 }
 
-bool bt_a2dp_register(const bdaddr_t *addr)
+static gboolean sep_getcap_ind(struct avdtp *session,
+                                       struct avdtp_local_sep *sep,
+                                       GSList **caps, uint8_t *err,
+                                       void *user_data)
 {
-       GError *err = NULL;
-       sdp_record_t *rec;
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_preset *cap = endpoint->caps;
+       struct avdtp_service_capability *service;
+       struct avdtp_media_codec_capability *codec;
 
-       DBG("");
+       *caps = NULL;
+
+       service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0);
+       *caps = g_slist_append(*caps, service);
+
+       codec = g_malloc0(sizeof(*codec) + cap->len);
+       codec->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+       codec->media_codec_type = endpoint->codec;
+       memcpy(codec->data, cap->data, cap->len);
+
+       service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec,
+                                               sizeof(*codec) + cap->len);
+       *caps = g_slist_append(*caps, service);
+       g_free(codec);
+
+       return TRUE;
+}
+
+static int check_config(struct a2dp_endpoint *endpoint,
+                                               struct a2dp_preset *config)
+{
+       GSList *l;
+       struct a2dp_preset *caps;
+
+       for (l = endpoint->presets; l; l = g_slist_next(l)) {
+               struct a2dp_preset *preset = l->data;
+
+               if (preset->len != config->len)
+                       continue;
+
+               if (memcmp(preset->data, config->data, preset->len) == 0)
+                       return 0;
+       }
+
+       caps = endpoint->caps;
+
+       /* Codec specific */
+       switch (endpoint->codec) {
+       case A2DP_CODEC_SBC:
+               return sbc_check_config(caps->data, caps->len, config->data,
+                                                               config->len);
+       default:
+               return -EINVAL;
+       }
+}
+
+static struct a2dp_device *find_device_by_session(struct avdtp *session)
+{
+       GSList *l;
+
+       for (l = devices; l; l = g_slist_next(l)) {
+               struct a2dp_device *dev = l->data;
+
+               if (dev->session == session)
+                       return dev;
+       }
+
+       return NULL;
+}
+
+static struct a2dp_setup *find_setup(uint8_t id)
+{
+       GSList *l;
+
+       for (l = setups; l; l = g_slist_next(l)) {
+               struct a2dp_setup *setup = l->data;
+
+               if (setup->endpoint->id == id)
+                       return setup;
+       }
+
+       return NULL;
+}
+
+static void setup_remove_by_id(uint8_t id)
+{
+       struct a2dp_setup *setup;
+
+       setup = find_setup(id);
+       if (!setup) {
+               error("Unable to find stream setup for endpoint %u", id);
+               return;
+       }
+
+       setup_remove(setup);
+}
+
+static gboolean sep_setconf_ind(struct avdtp *session,
+                                               struct avdtp_local_sep *sep,
+                                               struct avdtp_stream *stream,
+                                               GSList *caps,
+                                               avdtp_set_configuration_cb cb,
+                                               void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_device *dev;
+       struct a2dp_preset *preset = NULL;
+
+       DBG("");
+
+       dev = find_device_by_session(session);
+       if (!dev) {
+               error("Unable to find device for session %p", session);
+               return FALSE;
+       }
+
+       for (; caps != NULL; caps = g_slist_next(caps)) {
+               struct avdtp_service_capability *cap = caps->data;
+               struct avdtp_media_codec_capability *codec;
+
+               if (cap->category == AVDTP_DELAY_REPORTING)
+                       return FALSE;
+
+               if (cap->category != AVDTP_MEDIA_CODEC)
+                       continue;
+
+               codec = (struct avdtp_media_codec_capability *) cap->data;
+
+               if (codec->media_codec_type != endpoint->codec)
+                       return FALSE;
+
+               preset = g_new0(struct a2dp_preset, 1);
+               preset->len = cap->length - sizeof(*codec);
+               preset->data = g_memdup(codec->data, preset->len);
+
+               if (check_config(endpoint, preset) < 0) {
+                       preset_free(preset);
+                       return FALSE;
+               }
+       }
+
+       if (!preset)
+               return FALSE;
+
+       setup_add(dev, endpoint, preset, stream);
+
+       cb(session, stream, NULL);
+
+       return TRUE;
+}
+
+static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_setup *setup;
+
+       DBG("");
+
+       setup = find_setup(endpoint->id);
+       if (!setup) {
+               error("Unable to find stream setup for endpoint %u",
+                                                               endpoint->id);
+               *err = AVDTP_SEP_NOT_IN_USE;
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean sep_close_ind(struct avdtp *session,
+                                               struct avdtp_local_sep *sep,
+                                               struct avdtp_stream *stream,
+                                               uint8_t *err,
+                                               void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_setup *setup;
+
+       DBG("");
+
+       setup = find_setup(endpoint->id);
+       if (!setup) {
+               error("Unable to find stream setup for endpoint %u",
+                                                               endpoint->id);
+               *err = AVDTP_SEP_NOT_IN_USE;
+               return FALSE;
+       }
+
+       bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+
+       setup_remove(setup);
+
+       return TRUE;
+}
+
+static gboolean sep_start_ind(struct avdtp *session,
+                                               struct avdtp_local_sep *sep,
+                                               struct avdtp_stream *stream,
+                                               uint8_t *err,
+                                               void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_setup *setup;
+
+       DBG("");
+
+       setup = find_setup(endpoint->id);
+       if (!setup) {
+               error("Unable to find stream setup for endpoint %u",
+                                                               endpoint->id);
+               *err = AVDTP_SEP_NOT_IN_USE;
+               return FALSE;
+       }
+
+       bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
+
+       return TRUE;
+}
+
+static gboolean sep_suspend_ind(struct avdtp *session,
+                                               struct avdtp_local_sep *sep,
+                                               struct avdtp_stream *stream,
+                                               uint8_t *err,
+                                               void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_setup *setup;
+
+       DBG("");
+
+       setup = find_setup(endpoint->id);
+       if (!setup) {
+               error("Unable to find stream setup for endpoint %u",
+                                                               endpoint->id);
+               *err = AVDTP_SEP_NOT_IN_USE;
+               return FALSE;
+       }
+
+       bt_audio_notify_state(setup, HAL_AUDIO_SUSPEND);
+
+       return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind = {
+       .get_capability         = sep_getcap_ind,
+       .set_configuration      = sep_setconf_ind,
+       .open                   = sep_open_ind,
+       .close                  = sep_close_ind,
+       .start                  = sep_start_ind,
+       .suspend                = sep_suspend_ind,
+};
+
+static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_setup *setup;
+       int ret;
+
+       DBG("");
+
+       setup = find_setup(endpoint->id);
+       if (!setup) {
+               error("Unable to find stream setup for endpoint %u",
+                                                               endpoint->id);
+               return;
+       }
+
+       if (err)
+               goto failed;
+
+       ret = avdtp_open(session, stream);
+       if (ret < 0) {
+               error("avdtp_open: %s", strerror(-ret));
+               goto failed;
+       }
+
+       return;
+
+failed:
+       setup_remove(setup);
+}
+
+static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_device *dev;
+
+       DBG("");
+
+       if (err)
+               goto failed;
+
+       dev = find_device_by_session(session);
+       if (!dev) {
+               error("Unable to find device for session");
+               goto failed;
+       }
+
+       a2dp_device_connect(dev, transport_connect_cb);
+
+       return;
+
+failed:
+       setup_remove_by_id(endpoint->id);
+}
+
+static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_setup *setup;
+
+       DBG("");
+
+       if (err) {
+               setup_remove_by_id(endpoint->id);
+               return;
+       }
+
+       setup = find_setup(endpoint->id);
+       if (!setup) {
+               error("Unable to find stream setup for %u endpoint",
+                                                               endpoint->id);
+               return;
+       }
+
+       bt_audio_notify_state(setup, HAL_AUDIO_STARTED);
+}
+
+static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_setup *setup;
+
+       DBG("");
+
+       if (err) {
+               setup_remove_by_id(endpoint->id);
+               return;
+       }
+
+       setup = find_setup(endpoint->id);
+       if (!setup) {
+               error("Unable to find stream setup for %u endpoint",
+                                                               endpoint->id);
+               return;
+       }
+
+       bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+}
+
+static void sep_close_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+       struct a2dp_setup *setup;
+
+       DBG("");
+
+       if (err)
+               return;
+
+       setup = find_setup(endpoint->id);
+       if (!setup) {
+               error("Unable to find stream setup for %u endpoint",
+                                                               endpoint->id);
+               return;
+       }
+
+       bt_audio_notify_state(setup, HAL_AUDIO_STOPPED);
+
+       setup_remove(setup);
+}
+
+static void sep_abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       struct a2dp_endpoint *endpoint = user_data;
+
+       DBG("");
+
+       if (err)
+               return;
+
+       setup_remove_by_id(endpoint->id);
+}
+
+static struct avdtp_sep_cfm sep_cfm = {
+       .set_configuration      = sep_setconf_cfm,
+       .open                   = sep_open_cfm,
+       .start                  = sep_start_cfm,
+       .suspend                = sep_suspend_cfm,
+       .close                  = sep_close_cfm,
+       .abort                  = sep_abort_cfm,
+};
+
+static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec,
+                                                       GSList *presets)
+{
+       struct a2dp_endpoint *endpoint;
+
+       /* FIXME: Add proper check for uuid */
+
+       endpoint = g_new0(struct a2dp_endpoint, 1);
+       endpoint->id = g_slist_length(endpoints) + 1;
+       endpoint->codec = codec;
+       endpoint->sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE,
+                                               AVDTP_MEDIA_TYPE_AUDIO,
+                                               codec, FALSE, &sep_ind,
+                                               &sep_cfm, endpoint);
+       endpoint->caps = presets->data;
+       endpoint->presets = g_slist_copy(g_slist_nth(presets, 1));
+
+       endpoints = g_slist_append(endpoints, endpoint);
+
+       return endpoint->id;
+}
+
+static GSList *parse_presets(const struct audio_preset *p, uint8_t count,
+                                                               uint16_t len)
+{
+       GSList *l = NULL;
+       uint8_t i;
+
+       for (i = 0; count > i; i++) {
+               const uint8_t *ptr = (const uint8_t *) p;
+               struct a2dp_preset *preset;
+
+               if (len < sizeof(struct audio_preset)) {
+                       DBG("Invalid preset index %u", i);
+                       g_slist_free_full(l, preset_free);
+                       return NULL;
+               }
+
+               len -= sizeof(struct audio_preset);
+               if (len == 0 || len < p->len) {
+                       DBG("Invalid preset size of %u for index %u", len, i);
+                       g_slist_free_full(l, preset_free);
+                       return NULL;
+               }
+
+               preset = g_new0(struct a2dp_preset, 1);
+               preset->len = p->len;
+               preset->data = g_memdup(p->data, preset->len);
+               l = g_slist_append(l, preset);
+
+               len -= preset->len;
+               ptr += sizeof(*p) + preset->len;
+               p = (const struct audio_preset *) ptr;
+       }
+
+       return l;
+}
+
+static void bt_audio_open(const void *buf, uint16_t len)
+{
+       const struct audio_cmd_open *cmd = buf;
+       struct audio_rsp_open rsp;
+       GSList *presets;
+
+       DBG("");
+
+       audio_retrying = false;
+
+       if (cmd->presets == 0) {
+               error("No audio presets found");
+               goto failed;
+       }
+
+       presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd));
+       if (!presets) {
+               error("No audio presets found");
+               goto failed;
+       }
+
+       rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets);
+       if (rsp.id == 0) {
+               g_slist_free_full(presets, preset_free);
+               error("Unable to register endpoint");
+               goto failed;
+       }
+
+       g_slist_free(presets);
+
+       ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN,
+                                                       sizeof(rsp), &rsp, -1);
+
+       return;
+
+failed:
+       ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN,
+                                                       AUDIO_STATUS_FAILED);
+}
+
+static struct a2dp_endpoint *find_endpoint(uint8_t id)
+{
+       GSList *l;
+
+       for (l = endpoints; l; l = g_slist_next(l)) {
+               struct a2dp_endpoint *endpoint = l->data;
+
+               if (endpoint->id == id)
+                       return endpoint;
+       }
+
+       return NULL;
+}
+
+static void bt_audio_close(const void *buf, uint16_t len)
+{
+       const struct audio_cmd_close *cmd = buf;
+       struct a2dp_endpoint *endpoint;
+
+       DBG("");
+
+       endpoint = find_endpoint(cmd->id);
+       if (!endpoint) {
+               error("Unable to find endpoint %u", cmd->id);
+               ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+                                                       AUDIO_STATUS_FAILED);
+               return;
+       }
+
+       endpoints = g_slist_remove(endpoints, endpoint);
+       unregister_endpoint(endpoint);
+
+       ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+                                                       AUDIO_STATUS_SUCCESS);
+}
+
+static void bt_stream_open(const void *buf, uint16_t len)
+{
+       const struct audio_cmd_open_stream *cmd = buf;
+       struct audio_rsp_open_stream *rsp;
+       struct a2dp_setup *setup;
+       int fd;
+       uint16_t omtu;
+
+       DBG("");
+
+       setup = find_setup(cmd->id);
+       if (!setup) {
+               error("Unable to find stream for endpoint %u", cmd->id);
+               ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+                                                       AUDIO_STATUS_FAILED);
+               return;
+       }
+
+       if (!avdtp_stream_get_transport(setup->stream, &fd, NULL, &omtu,
+                                                               NULL)) {
+               error("avdtp_stream_get_transport: failed");
+               ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+                                                       AUDIO_STATUS_FAILED);
+               return;
+       }
+
+       len = sizeof(struct audio_rsp_open_stream) +
+                       sizeof(struct audio_preset) + setup->preset->len;
+       rsp = g_malloc0(len);
+       rsp->mtu = omtu;
+       rsp->preset->len = setup->preset->len;
+       memcpy(rsp->preset->data, setup->preset->data, setup->preset->len);
+
+       ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+                                                               len, rsp, fd);
+
+       g_free(rsp);
+}
+
+static void bt_stream_close(const void *buf, uint16_t len)
+{
+       const struct audio_cmd_close_stream *cmd = buf;
+       struct a2dp_setup *setup;
+       int err;
+
+       DBG("");
+
+       setup = find_setup(cmd->id);
+       if (!setup) {
+               error("Unable to find stream for endpoint %u", cmd->id);
+               goto failed;
+       }
+
+       err = avdtp_close(setup->dev->session, setup->stream, FALSE);
+       if (err < 0) {
+               error("avdtp_close: %s", strerror(-err));
+               goto failed;
+       }
+
+       ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+                                                       AUDIO_STATUS_SUCCESS);
+
+       return;
+
+failed:
+       ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+                                                       AUDIO_STATUS_FAILED);
+}
+
+static void bt_stream_resume(const void *buf, uint16_t len)
+{
+       const struct audio_cmd_resume_stream *cmd = buf;
+       struct a2dp_setup *setup;
+       int err;
+
+       DBG("");
+
+       setup = find_setup(cmd->id);
+       if (!setup) {
+               error("Unable to find stream for endpoint %u", cmd->id);
+               goto failed;
+       }
+
+       if (setup->state != HAL_AUDIO_STARTED) {
+               err = avdtp_start(setup->dev->session, setup->stream);
+               if (err < 0) {
+                       error("avdtp_start: %s", strerror(-err));
+                       goto failed;
+               }
+       }
+
+       ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+                                                       AUDIO_STATUS_SUCCESS);
+
+       return;
+
+failed:
+       ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+                                                       AUDIO_STATUS_FAILED);
+}
+
+static void bt_stream_suspend(const void *buf, uint16_t len)
+{
+       const struct audio_cmd_suspend_stream *cmd = buf;
+       struct a2dp_setup *setup;
+       int err;
+
+       DBG("");
+
+       setup = find_setup(cmd->id);
+       if (!setup) {
+               error("Unable to find stream for endpoint %u", cmd->id);
+               goto failed;
+       }
+
+       err = avdtp_suspend(setup->dev->session, setup->stream);
+       if (err < 0) {
+               error("avdtp_suspend: %s", strerror(-err));
+               goto failed;
+       }
+
+       ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+                                                       AUDIO_STATUS_SUCCESS);
+
+       return;
+
+failed:
+       ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+                                                       AUDIO_STATUS_FAILED);
+}
+
+static const struct ipc_handler audio_handlers[] = {
+       /* AUDIO_OP_OPEN */
+       { bt_audio_open, true, sizeof(struct audio_cmd_open) },
+       /* AUDIO_OP_CLOSE */
+       { bt_audio_close, false, sizeof(struct audio_cmd_close) },
+       /* AUDIO_OP_OPEN_STREAM */
+       { bt_stream_open, false, sizeof(struct audio_cmd_open_stream) },
+       /* AUDIO_OP_CLOSE_STREAM */
+       { bt_stream_close, false, sizeof(struct audio_cmd_close_stream) },
+       /* AUDIO_OP_RESUME_STREAM */
+       { bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) },
+       /* AUDIO_OP_SUSPEND_STREAM */
+       { bt_stream_suspend, false, sizeof(struct audio_cmd_suspend_stream) },
+};
+
+static void bt_audio_unregister(void)
+{
+       DBG("");
+
+       if (audio_retry_id > 0)
+               g_source_remove(audio_retry_id);
+
+       g_slist_free_full(endpoints, unregister_endpoint);
+       endpoints = NULL;
+
+       g_slist_free_full(setups, setup_free);
+       setups = NULL;
+
+       ipc_cleanup(audio_ipc);
+       audio_ipc = NULL;
+}
+
+static bool bt_audio_register(ipc_disconnect_cb disconnect)
+{
+       DBG("");
+
+       audio_ipc = ipc_init(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH),
+                               AUDIO_SERVICE_ID_MAX, false, disconnect, NULL);
+       if (!audio_ipc)
+               return false;
+
+       ipc_register(audio_ipc, AUDIO_SERVICE_ID, audio_handlers,
+                                               G_N_ELEMENTS(audio_handlers));
+
+       return true;
+}
+
+static gboolean audio_retry_register(void *data)
+{
+       ipc_disconnect_cb cb = data;
+
+       audio_retry_id = 0;
+       audio_retrying = true;
+
+       bt_audio_register(cb);
+
+       return FALSE;
+}
+
+static void audio_disconnected(void *data)
+{
+       GSList *l;
+       bool restart;
+
+       DBG("");
+
+       if (audio_retrying)
+               goto retry;
+
+       restart = endpoints != NULL ? true : false;
+
+       bt_audio_unregister();
+
+       for (l = devices; l; l = g_slist_next(l)) {
+               struct a2dp_device *dev = l->data;
+
+               avdtp_shutdown(dev->session);
+       }
+
+       if (!restart)
+               return;
+
+retry:
+       audio_retry_id = g_timeout_add_seconds(AUDIO_RETRY_TIMEOUT,
+                                               audio_retry_register,
+                                               audio_disconnected);
+}
+
+bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+       GError *err = NULL;
+       sdp_record_t *rec;
+
+       DBG("");
 
        bacpy(&adapter_addr, addr);
 
@@ -358,6 +1576,7 @@ bool bt_a2dp_register(const bdaddr_t *addr)
                                BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
                                BT_IO_OPT_PSM, L2CAP_PSM_AVDTP,
                                BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_MASTER, true,
                                BT_IO_OPT_INVALID);
        if (!server) {
                error("Failed to listen on AVDTP channel: %s", err->message);
@@ -378,10 +1597,13 @@ bool bt_a2dp_register(const bdaddr_t *addr)
        }
        record_id = rec->handle;
 
-       ipc_register(HAL_SERVICE_ID_A2DP, cmd_handlers,
+       hal_ipc = ipc;
+
+       ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP, cmd_handlers,
                                                G_N_ELEMENTS(cmd_handlers));
 
-       return true;
+       if (bt_audio_register(audio_disconnected))
+               return true;
 
 fail:
        g_io_channel_shutdown(server, TRUE, NULL);
@@ -390,22 +1612,22 @@ fail:
        return false;
 }
 
-static void a2dp_device_disconnected(gpointer data, gpointer user_data)
-{
-       struct a2dp_device *dev = data;
-
-       bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
-}
-
 void bt_a2dp_unregister(void)
 {
        DBG("");
 
-       g_slist_foreach(devices, a2dp_device_disconnected, NULL);
+       g_slist_free_full(setups, setup_free);
+       setups = NULL;
+
+       g_slist_free_full(endpoints, unregister_endpoint);
+       endpoints = NULL;
+
+       g_slist_free_full(devices, a2dp_device_free);
        devices = NULL;
 
+       ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP);
+       hal_ipc = NULL;
 
-       ipc_unregister(HAL_SERVICE_ID_A2DP);
        bt_adapter_remove_record(record_id);
        record_id = 0;
 
@@ -414,4 +1636,10 @@ void bt_a2dp_unregister(void)
                g_io_channel_unref(server);
                server = NULL;
        }
+
+       if (audio_ipc) {
+               ipc_unregister(audio_ipc, AUDIO_SERVICE_ID);
+               ipc_cleanup(audio_ipc);
+               audio_ipc = NULL;
+       }
 }
index 7e9b2f6..8a70407 100644 (file)
@@ -2,24 +2,24 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
-bool bt_a2dp_register(const bdaddr_t *addr);
+bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
 void bt_a2dp_unregister(void);
diff --git a/android/android-tester.c b/android/android-tester.c
new file mode 100644 (file)
index 0000000..f04f8ac
--- /dev/null
@@ -0,0 +1,4827 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <glib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <sys/signalfd.h>
+#include <libgen.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hciemu.h"
+
+#include "emulator/bthost.h"
+#include "monitor/bt.h"
+
+#include <hardware/hardware.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
+#include <hardware/bt_hh.h>
+
+#include "utils.h"
+
+struct priority_property {
+       bt_property_t prop;
+       int prio;
+};
+
+struct generic_data {
+       int expected_adapter_status;
+       uint32_t expect_settings_set;
+       int expected_cb_count;
+       bt_property_t set_property;
+       bt_callbacks_t expected_hal_cb;
+       struct priority_property *expected_properties;
+       uint8_t expected_properties_num;
+};
+
+struct socket_data {
+       btsock_type_t sock_type;
+       const char *service_name;
+       const uint8_t *service_uuid;
+       const bt_bdaddr_t *bdaddr;
+       int channel;
+       int flags;
+       bt_status_t expected_status;
+       bool test_channel;
+};
+
+struct hidhost_generic_data {
+       bthh_status_t expected_status;
+       int expected_conn_state;
+       int expected_cb_count;
+       bthh_protocol_mode_t expected_protocol_mode;
+       int expected_report;
+       bthh_callbacks_t expected_hal_cb;
+       int expected_report_size;
+};
+
+#define WAIT_FOR_SIGNAL_TIME 2 /* in seconds */
+#define EMULATOR_SIGNAL "emulator_started"
+
+#define BT_STATUS_NOT_EXPECTED -1
+
+struct test_data {
+       struct mgmt *mgmt;
+       uint16_t mgmt_index;
+       unsigned int mgmt_settings_id;
+       struct hciemu *hciemu;
+       enum hciemu_type hciemu_type;
+       const void *test_data;
+       pid_t bluetoothd_pid;
+       guint signalfd;
+
+       struct hw_device_t *device;
+       const bt_interface_t *if_bluetooth;
+       const btsock_interface_t *if_sock;
+       const bthh_interface_t *if_hid;
+
+       int conditions_left;
+
+       /* Set to true if test conditions should be verified */
+       bool test_checks_valid;
+
+       bool test_result_set;
+
+       int cb_count;
+       GSList *expected_properties_list;
+
+       /* hidhost */
+       uint16_t sdp_handle;
+       uint16_t sdp_cid;
+       uint16_t ctrl_handle;
+       uint16_t ctrl_cid;
+       uint16_t intr_handle;
+       uint16_t intr_cid;
+};
+
+struct bt_cb_data {
+       bt_state_t state;
+       bt_status_t status;
+
+       bt_bdaddr_t bdaddr;
+       bt_bdname_t bdname;
+       uint32_t cod;
+
+       bt_ssp_variant_t ssp_variant;
+       uint32_t passkey;
+
+       int num;
+       bt_property_t *props;
+};
+
+struct hh_cb_data {
+       bt_bdaddr_t bdaddr;
+
+       bthh_status_t status;
+       bthh_hid_info_t hid_info;
+       bthh_protocol_mode_t mode;
+       bthh_connection_state_t state;
+
+       uint8_t *report;
+       int size;
+};
+
+static char exec_dir[PATH_MAX + 1];
+
+static gint scheduled_cbacks_num = 0;
+
+static gboolean check_callbacks_called(gpointer user_data)
+{
+       /*
+        * Wait for all callbacks scheduled in current test context to execute
+        * in main loop. This will avoid late callback calls after test case has
+        * already failed or timed out.
+        */
+
+       if (g_atomic_int_get(&scheduled_cbacks_num) == 0) {
+               tester_teardown_complete();
+               return FALSE;
+       }
+
+       return TRUE;
+}
+static void check_daemon_term(void)
+{
+       int status;
+       pid_t pid;
+       struct test_data *data = tester_get_data();
+
+       if (!data)
+               return;
+
+       pid = waitpid(data->bluetoothd_pid, &status, WNOHANG);
+       if (pid != data->bluetoothd_pid)
+               return;
+
+       data->bluetoothd_pid = 0;
+
+       if (WIFEXITED(status) && (WEXITSTATUS(status) == EXIT_SUCCESS)) {
+               g_idle_add(check_callbacks_called, NULL);
+               return;
+       }
+
+       tester_warn("Unexpected Daemon shutdown with status %d", status);
+}
+
+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 SIGCHLD:
+               check_daemon_term();
+               break;
+       }
+
+       return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+       GIOChannel *channel;
+       guint source;
+       sigset_t mask;
+       int fd;
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGCHLD);
+
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0)
+               return 0;
+
+       fd = signalfd(-1, &mask, 0);
+       if (fd < 0)
+               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 void mgmt_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       tester_print("%s%s", prefix, str);
+}
+
+static void test_update_state(void)
+{
+       struct test_data *data = tester_get_data();
+
+       if (data->conditions_left == 0 && !data->test_result_set) {
+               data->test_result_set = true;
+               tester_test_passed();
+       }
+}
+
+static void test_mgmt_settings_set(struct test_data *data)
+{
+       data->conditions_left--;
+
+       test_update_state();
+}
+
+static void command_generic_new_settings(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test_data = data->test_data;
+       uint32_t settings;
+
+       if (length != 4) {
+               tester_warn("Invalid parameter size for new settings event");
+               tester_test_failed();
+               return;
+       }
+
+       settings = get_le32(param);
+
+       if ((settings & test_data->expect_settings_set) !=
+                                       test_data->expect_settings_set)
+               return;
+
+       test_mgmt_settings_set(data);
+       mgmt_unregister(data->mgmt, data->mgmt_settings_id);
+}
+
+static void check_cb_count(void)
+{
+       struct test_data *data = tester_get_data();
+
+       if (!data->test_checks_valid)
+               return;
+
+       if (data->cb_count == 0) {
+               data->conditions_left--;
+               test_update_state();
+       }
+}
+
+static void expected_cb_count_init(struct test_data *data)
+{
+       const struct generic_data *test_data = data->test_data;
+
+       data->cb_count = test_data->expected_cb_count;
+
+       check_cb_count();
+}
+
+static void mgmt_cb_init(struct test_data *data)
+{
+       const struct generic_data *test_data = data->test_data;
+
+       if (!test_data->expect_settings_set)
+               test_mgmt_settings_set(data);
+       else
+               data->mgmt_settings_id = mgmt_register(data->mgmt,
+                               MGMT_EV_NEW_SETTINGS, data->mgmt_index,
+                               command_generic_new_settings, NULL, NULL);
+}
+
+static void expected_status_init(struct test_data *data)
+{
+       const struct generic_data *test_data = data->test_data;
+
+       if (test_data->expected_adapter_status == BT_STATUS_NOT_EXPECTED)
+               data->conditions_left--;
+}
+
+static void test_property_init(struct test_data *data)
+{
+       const struct generic_data *test_data = data->test_data;
+       GSList *l = data->expected_properties_list;
+       int i;
+
+       if (!test_data->expected_properties_num) {
+               data->conditions_left--;
+               return;
+       }
+
+       for (i = 0; i < test_data->expected_properties_num; i++)
+               l = g_slist_prepend(l, &(test_data->expected_properties[i]));
+
+       data->expected_properties_list = l;
+}
+
+static void init_test_conditions(struct test_data *data)
+{
+       data->test_checks_valid = true;
+
+       data->conditions_left = 4;
+
+       expected_cb_count_init(data);
+       mgmt_cb_init(data);
+       expected_status_init(data);
+       test_property_init(data);
+}
+
+static void check_expected_status(uint8_t status)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test_data = data->test_data;
+
+       if (test_data->expected_adapter_status == status) {
+               data->conditions_left--;
+               test_update_state();
+       } else
+               tester_test_failed();
+}
+
+static int locate_property(gconstpointer expected_data,
+                                               gconstpointer received_prop)
+{
+       bt_property_t rec_prop = *((bt_property_t *)received_prop);
+       bt_property_t exp_prop =
+                       ((struct priority_property *)expected_data)->prop;
+
+       if (exp_prop.type && (exp_prop.type != rec_prop.type))
+               return 1;
+       if (exp_prop.len && (exp_prop.len != rec_prop.len))
+               return 1;
+       if (exp_prop.val && memcmp(exp_prop.val, rec_prop.val, exp_prop.len))
+               return 1;
+
+       return 0;
+}
+
+static int compare_priorities(gconstpointer prop_list, gconstpointer priority)
+{
+       int prio = GPOINTER_TO_INT(priority);
+       int comp_prio = ((struct priority_property *)prop_list)->prio;
+
+       if (prio > comp_prio)
+               return 0;
+
+       return 1;
+}
+
+static bool check_prop_priority(int rec_prop_prio)
+{
+       struct test_data *data = tester_get_data();
+       GSList *l = data->expected_properties_list;
+
+       if (!rec_prop_prio || !g_slist_length(l))
+               return true;
+
+       if (g_slist_find_custom(l, GINT_TO_POINTER(rec_prop_prio),
+                                                       &compare_priorities))
+               return false;
+
+       return true;
+}
+
+static void check_expected_property(bt_property_t received_prop)
+{
+       struct test_data *data = tester_get_data();
+       int rec_prio;
+       GSList *l = data->expected_properties_list;
+       GSList *found_exp_prop;
+
+       if (!g_slist_length(l))
+               return;
+
+       found_exp_prop = g_slist_find_custom(l, &received_prop,
+                                                       &locate_property);
+
+       if (found_exp_prop) {
+               rec_prio = ((struct priority_property *)
+                                               (found_exp_prop->data))->prio;
+               if (check_prop_priority(rec_prio))
+                       l = g_slist_remove(l, found_exp_prop->data);
+       }
+
+       data->expected_properties_list = l;
+
+       if (g_slist_length(l))
+               return;
+
+       data->conditions_left--;
+       test_update_state();
+}
+
+static bool check_test_property(bt_property_t received_prop,
+                                               bt_property_t expected_prop)
+{
+       if (expected_prop.type && (expected_prop.type != received_prop.type))
+               return false;
+       if (expected_prop.len && (expected_prop.len != received_prop.len))
+               return false;
+       if (expected_prop.val && memcmp(expected_prop.val, received_prop.val,
+                                                       expected_prop.len))
+               return false;
+
+       return true;
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct mgmt_rp_read_info *rp = param;
+       char addr[18];
+       uint16_t manufacturer;
+       uint32_t supported_settings, current_settings;
+
+       tester_print("Read Info callback");
+       tester_print("  Status: 0x%02x", status);
+
+       if (status || !param) {
+               tester_pre_setup_failed();
+               return;
+       }
+
+       ba2str(&rp->bdaddr, addr);
+       manufacturer = btohs(rp->manufacturer);
+       supported_settings = btohl(rp->supported_settings);
+       current_settings = btohl(rp->current_settings);
+
+       tester_print("  Address: %s", addr);
+       tester_print("  Version: 0x%02x", rp->version);
+       tester_print("  Manufacturer: 0x%04x", manufacturer);
+       tester_print("  Supported settings: 0x%08x", supported_settings);
+       tester_print("  Current settings: 0x%08x", current_settings);
+       tester_print("  Class: 0x%02x%02x%02x",
+                       rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+       tester_print("  Name: %s", rp->name);
+       tester_print("  Short name: %s", rp->short_name);
+
+       if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+               tester_pre_setup_failed();
+               return;
+       }
+
+       tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+
+       tester_print("Index Added callback");
+       tester_print("  Index: 0x%04x", index);
+
+       data->mgmt_index = index;
+
+       mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+                                       read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+
+       tester_print("Index Removed callback");
+       tester_print("  Index: 0x%04x", index);
+
+       if (index != data->mgmt_index)
+               return;
+
+       mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+       mgmt_unref(data->mgmt);
+       data->mgmt = NULL;
+
+       tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+
+       tester_print("Read Index List callback");
+       tester_print("  Status: 0x%02x", status);
+
+       if (status || !param) {
+               tester_pre_setup_failed();
+               return;
+       }
+
+       mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+                                       index_added_callback, NULL, NULL);
+
+       mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+                                       index_removed_callback, NULL, NULL);
+
+       data->hciemu = hciemu_new(data->hciemu_type);
+       if (!data->hciemu) {
+               tester_warn("Failed to setup HCI emulation");
+               tester_pre_setup_failed();
+               return;
+       }
+
+       tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       data->signalfd = setup_signalfd();
+       if (!data->signalfd) {
+               tester_warn("Failed to setup signalfd");
+               tester_pre_setup_failed();
+               return;
+       }
+
+       data->mgmt = mgmt_new_default();
+       if (!data->mgmt) {
+               tester_warn("Failed to setup management interface");
+               tester_pre_setup_failed();
+               return;
+       }
+
+       if (!tester_use_debug())
+               fclose(stderr);
+       else
+               mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL);
+
+       mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0,
+                               NULL, read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       hciemu_unref(data->hciemu);
+       data->hciemu = NULL;
+
+       g_source_remove(data->signalfd);
+       data->signalfd = 0;
+}
+
+static void bluetoothd_start(int hci_index)
+{
+       char prg_name[PATH_MAX + 1];
+       char index[8];
+       char *prg_argv[5];
+
+       snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
+       snprintf(index, sizeof(index), "%d", hci_index);
+
+       prg_argv[0] = prg_name;
+       prg_argv[1] = "-i";
+       prg_argv[2] = index;
+       prg_argv[3] = "-d";
+       prg_argv[4] = NULL;
+
+       if (!tester_use_debug())
+               fclose(stderr);
+
+       execve(prg_argv[0], prg_argv, NULL);
+}
+
+static void emulator(int pipe, int hci_index)
+{
+       static const char SYSTEM_SOCKET_PATH[] = "\0android_system";
+       char buf[1024];
+       struct sockaddr_un addr;
+       struct timeval tv;
+       int fd;
+       ssize_t len;
+
+       fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (fd < 0)
+               goto failed;
+
+       tv.tv_sec = WAIT_FOR_SIGNAL_TIME;
+       tv.tv_usec = 0;
+       setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH));
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Failed to bind system socket");
+               goto failed;
+       }
+
+       len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL));
+       if (len != sizeof(EMULATOR_SIGNAL))
+               goto failed;
+
+       memset(buf, 0, sizeof(buf));
+
+       len = read(fd, buf, sizeof(buf));
+       if (len <= 0 || strcmp(buf, "bluetooth.start=daemon"))
+               goto failed;
+
+       close(pipe);
+       close(fd);
+       return bluetoothd_start(hci_index);
+
+failed:
+       close(pipe);
+
+       if (fd >= 0)
+               close(fd);
+}
+
+static void emu_connectable_complete(uint16_t opcode, uint8_t status,
+                                       const void *param, uint8_t len,
+                                       void *user_data)
+{
+       switch (opcode) {
+       case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+       case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+               break;
+       default:
+               return;
+       }
+
+       tester_print("Emulated remote set connectable status 0x%02x", status);
+
+       if (status)
+               tester_setup_failed();
+       else
+               tester_setup_complete();
+}
+
+static void setup_powered_emulated_remote(void)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost;
+
+       tester_print("Controller powered on");
+
+       bthost = hciemu_client_get_host(data->hciemu);
+       bthost_set_cmd_complete_cb(bthost, emu_connectable_complete, data);
+
+       if (data->hciemu_type == HCIEMU_TYPE_LE)
+               bthost_set_adv_enable(bthost, 0x01);
+       else
+               bthost_write_scan_enable(bthost, 0x03);
+}
+
+static void enable_success_cb(bt_state_t state)
+{
+       struct test_data *data = tester_get_data();
+
+       if (state == BT_STATE_ON) {
+               setup_powered_emulated_remote();
+               data->cb_count--;
+               check_cb_count();
+       }
+}
+
+static void disable_success_cb(bt_state_t state)
+{
+       struct test_data *data = tester_get_data();
+
+       if (state == BT_STATE_OFF) {
+               data->cb_count--;
+               check_cb_count();
+       }
+}
+
+static gboolean adapter_state_changed(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       struct bt_cb_data *cb_data = user_data;
+
+       if (data->test_checks_valid &&
+                       test->expected_hal_cb.adapter_state_changed_cb) {
+               test->expected_hal_cb.adapter_state_changed_cb(cb_data->state);
+               goto cleanup;
+       }
+
+       if (!data->test_checks_valid && cb_data->state == BT_STATE_ON)
+               setup_powered_emulated_remote();
+
+cleanup:
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void adapter_state_changed_cb(bt_state_t state)
+{
+       struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+       cb_data->state = state;
+
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(adapter_state_changed, cb_data);
+}
+
+static void discovery_start_success_cb(bt_discovery_state_t state)
+{
+       struct test_data *data = tester_get_data();
+
+       if (state == BT_DISCOVERY_STARTED) {
+               data->cb_count--;
+               check_cb_count();
+       }
+}
+
+static void discovery_start_done_cb(bt_discovery_state_t state)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t status;
+
+       status = data->if_bluetooth->start_discovery();
+       data->cb_count--;
+
+       check_cb_count();
+       check_expected_status(status);
+}
+
+static void discovery_stop_success_cb(bt_discovery_state_t state)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t status;
+
+       if (state == BT_DISCOVERY_STARTED && data->cb_count == 2) {
+               status = data->if_bluetooth->cancel_discovery();
+               check_expected_status(status);
+               data->cb_count--;
+               return;
+       }
+       if (state == BT_DISCOVERY_STOPPED && data->cb_count == 1) {
+               data->cb_count--;
+               check_cb_count();
+       }
+}
+
+static void discovery_device_found_state_changed_cb(bt_discovery_state_t state)
+{
+       struct test_data *data = tester_get_data();
+
+       if (state == BT_DISCOVERY_STARTED && data->cb_count == 3) {
+               data->cb_count--;
+               return;
+       }
+       if (state == BT_DISCOVERY_STOPPED && data->cb_count == 1) {
+               data->cb_count--;
+               check_cb_count();
+       }
+}
+
+static void remote_discovery_state_changed_cb(bt_discovery_state_t state)
+{
+       struct test_data *data = tester_get_data();
+
+       if (state == BT_DISCOVERY_STARTED && data->cb_count == 3) {
+               data->cb_count--;
+               return;
+       }
+       if (state == BT_DISCOVERY_STOPPED && data->cb_count == 1) {
+               data->cb_count--;
+               check_cb_count();
+       }
+}
+
+static void remote_setprop_disc_state_changed_cb(bt_discovery_state_t state)
+{
+       struct test_data *data = tester_get_data();
+
+       if (state == BT_DISCOVERY_STARTED && data->cb_count == 3) {
+               data->cb_count--;
+               return;
+       }
+       if (state == BT_DISCOVERY_STOPPED) {
+               data->cb_count--;
+               check_cb_count();
+       }
+}
+
+static gboolean discovery_state_changed(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       struct bt_cb_data *cb_data = user_data;
+
+       if (test && test->expected_hal_cb.discovery_state_changed_cb)
+               test->expected_hal_cb.discovery_state_changed_cb(
+                                                               cb_data->state);
+
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void discovery_state_changed_cb(bt_discovery_state_t state)
+{
+       struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+       cb_data->state = state;
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(discovery_state_changed, cb_data);
+}
+
+static bt_property_t *copy_properties(int num_properties,
+                                               bt_property_t *properties)
+{
+       int i;
+       bt_property_t *props = g_new0(bt_property_t, num_properties);
+
+       for (i = 0; i < num_properties; i++) {
+               props[i].type = properties[i].type;
+               props[i].len = properties[i].len;
+               props[i].val = g_memdup(properties[i].val, properties[i].len);
+       }
+
+       return props;
+}
+
+static void free_properties(int num_properties, bt_property_t *properties)
+{
+       int i;
+
+       for (i = 0; i < num_properties; i++)
+               g_free(properties[i].val);
+
+       g_free(properties);
+}
+
+static void discovery_device_found_cb(int num_properties,
+                                               bt_property_t *properties)
+{
+       struct test_data *data = tester_get_data();
+       uint8_t *remote_bdaddr =
+                       (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+       uint32_t emu_remote_type = BT_DEVICE_DEVTYPE_BREDR;
+       int32_t emu_remote_rssi = -60;
+       bt_bdaddr_t emu_remote_bdaddr;
+       int i;
+       bt_property_t expected_prop;
+       bt_property_t received_prop;
+
+       data->cb_count--;
+       check_cb_count();
+
+       if (num_properties < 1) {
+               tester_test_failed();
+               return;
+       }
+
+       bdaddr2android((const bdaddr_t *) remote_bdaddr, &emu_remote_bdaddr);
+
+       for (i = 0; i < num_properties; i++) {
+               received_prop = properties[i];
+
+               switch (properties[i].type) {
+               case BT_PROPERTY_BDADDR:
+                       expected_prop.type = BT_PROPERTY_BDADDR;
+                       expected_prop.len = sizeof(emu_remote_bdaddr);
+                       expected_prop.val = &emu_remote_bdaddr;
+                       break;
+
+               case BT_PROPERTY_TYPE_OF_DEVICE:
+                       expected_prop.type = BT_PROPERTY_TYPE_OF_DEVICE;
+                       expected_prop.len = sizeof(emu_remote_type);
+                       expected_prop.val = &emu_remote_type;
+                       break;
+
+               case BT_PROPERTY_REMOTE_RSSI:
+                       expected_prop.type = BT_PROPERTY_REMOTE_RSSI;
+                       expected_prop.len = sizeof(emu_remote_rssi);
+                       expected_prop.val = &emu_remote_rssi;
+                       break;
+
+               default:
+                       expected_prop.type = 0;
+                       expected_prop.len = 0;
+                       expected_prop.val = NULL;
+                       break;
+               }
+
+               if (!check_test_property(received_prop, expected_prop)) {
+                       data->if_bluetooth->cancel_discovery();
+                       tester_test_failed();
+                       return;
+               }
+       }
+
+       data->if_bluetooth->cancel_discovery();
+}
+
+static void remote_getprops_device_found_cb(int num_properties,
+                                               bt_property_t *properties)
+{
+       struct test_data *data = tester_get_data();
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t remote_addr;
+
+       bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+       if (data->cb_count == 2)
+               data->cb_count--;
+
+       data->if_bluetooth->cancel_discovery();
+       data->if_bluetooth->get_remote_device_properties(&remote_addr);
+}
+
+static void remote_get_property_device_found_cb(int num_properties,
+                                               bt_property_t *properties)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       bt_status_t status;
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t remote_addr;
+
+       const bt_property_t prop = test->expected_properties[0].prop;
+
+       bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+       if (data->cb_count == 2)
+               data->cb_count--;
+
+       data->if_bluetooth->cancel_discovery();
+       status = data->if_bluetooth->get_remote_device_property(&remote_addr,
+                                                               prop.type);
+       check_expected_status(status);
+}
+
+static void remote_setprop_device_found_cb(int num_properties,
+                                               bt_property_t *properties)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       bt_status_t status;
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t remote_addr;
+
+       const bt_property_t prop = test->expected_properties[0].prop;
+
+       bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+       if (data->cb_count == 2)
+               data->cb_count--;
+
+       data->if_bluetooth->cancel_discovery();
+       status = data->if_bluetooth->set_remote_device_property(&remote_addr,
+                                                                       &prop);
+       check_expected_status(status);
+
+       status = data->if_bluetooth->get_remote_device_property(&remote_addr, prop.type);
+}
+
+static void remote_setprop_fail_device_found_cb(int num_properties,
+                                               bt_property_t *properties)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       bt_status_t status;
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t remote_addr;
+
+       const bt_property_t prop = test->expected_properties[0].prop;
+
+       bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+       if (data->cb_count == 2)
+               data->cb_count--;
+
+       data->if_bluetooth->cancel_discovery();
+       status = data->if_bluetooth->set_remote_device_property(&remote_addr,
+                                                                       &prop);
+       check_expected_status(status);
+}
+
+static void bond_device_found_cb(int num_properties, bt_property_t *properties)
+{
+       struct test_data *data = tester_get_data();
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t remote_addr;
+       bt_status_t status;
+
+       bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+       if (data->cb_count == 4) {
+               data->cb_count--;
+               status = data->if_bluetooth->create_bond(&remote_addr);
+               check_expected_status(status);
+       }
+}
+
+static void bond_nostatus_device_found_cb(int num_properties,
+                                               bt_property_t *properties)
+{
+       struct test_data *data = tester_get_data();
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t remote_addr;
+
+       bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+       if (data->cb_count == 4) {
+               data->cb_count--;
+               data->if_bluetooth->create_bond(&remote_addr);
+       }
+}
+
+static gboolean device_found(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       struct bt_cb_data *cb_data = user_data;
+
+       if (data->test_checks_valid && test->expected_hal_cb.device_found_cb)
+               test->expected_hal_cb.device_found_cb(cb_data->num,
+                                                               cb_data->props);
+
+       free_properties(cb_data->num, cb_data->props);
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void device_found_cb(int num_properties, bt_property_t *properties)
+{
+       struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+       cb_data->num = num_properties;
+       cb_data->props = copy_properties(num_properties, properties);
+
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(device_found, cb_data);
+}
+
+static void check_count_properties_cb(bt_status_t status, int num_properties,
+                                               bt_property_t *properties)
+{
+       int i;
+
+       for (i = 0; i < num_properties; i++)
+               check_expected_property(properties[i]);
+}
+
+static gboolean adapter_properties(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       struct bt_cb_data *cb_data = user_data;
+
+       if (data->test_checks_valid &&
+                               test->expected_hal_cb.adapter_properties_cb)
+               test->expected_hal_cb.adapter_properties_cb(cb_data->status,
+                                               cb_data->num, cb_data->props);
+
+       free_properties(cb_data->num, cb_data->props);
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void adapter_properties_cb(bt_status_t status, int num_properties,
+                                               bt_property_t *properties)
+{
+       struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+       cb_data->status = status;
+       cb_data->num = num_properties;
+       cb_data->props = copy_properties(num_properties, properties);
+
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(adapter_properties, cb_data);
+}
+
+static void remote_test_device_properties_cb(bt_status_t status,
+                               bt_bdaddr_t *bd_addr, int num_properties,
+                               bt_property_t *properties)
+{
+       int i;
+
+       for (i = 0; i < num_properties; i++)
+               check_expected_property(properties[i]);
+}
+
+static gboolean remote_device_properties(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       struct bt_cb_data *cb_data = user_data;
+
+       if (data->test_checks_valid &&
+                       test->expected_hal_cb.remote_device_properties_cb)
+               test->expected_hal_cb.remote_device_properties_cb(
+                                       cb_data->status, &cb_data->bdaddr,
+                                       cb_data->num, cb_data->props);
+
+       free_properties(cb_data->num, cb_data->props);
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void remote_device_properties_cb(bt_status_t status,
+                               bt_bdaddr_t *bd_addr, int num_properties,
+                               bt_property_t *properties)
+{
+       struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+       cb_data->status = status;
+       cb_data->bdaddr = *bd_addr;
+       cb_data->num = num_properties;
+       cb_data->props = copy_properties(num_properties, properties);
+
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(remote_device_properties, cb_data);
+}
+
+static void bond_test_bonded_state_changed_cb(bt_status_t status,
+                       bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state)
+{
+       struct test_data *data = tester_get_data();
+
+       switch (state) {
+       case BT_BOND_STATE_BONDING:
+               data->cb_count--;
+               break;
+       case BT_BOND_STATE_BONDED:
+               data->cb_count--;
+               check_cb_count();
+               break;
+       default:
+               tester_test_failed();
+               break;
+       }
+}
+
+static void bond_test_none_state_changed_cb(bt_status_t status,
+                       bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state)
+{
+       struct test_data *data = tester_get_data();
+
+       switch (state) {
+       case BT_BOND_STATE_BONDING:
+               data->cb_count--;
+               break;
+       case BT_BOND_STATE_NONE:
+               data->cb_count--;
+               check_cb_count();
+               break;
+       default:
+               tester_test_failed();
+               break;
+       }
+}
+
+static void bond_remove_success_state_changed_cb(bt_status_t status,
+                       bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t remove_status;
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t remote_addr;
+
+       bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+       if (state == BT_BOND_STATE_BONDED) {
+               data->cb_count--;
+               remove_status = data->if_bluetooth->remove_bond(&remote_addr);
+               check_expected_status(remove_status);
+               return;
+       }
+
+       if (state == BT_BOND_STATE_NONE) {
+               data->cb_count--;
+               check_cb_count();
+       }
+}
+
+static gboolean bond_state_changed(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       struct bt_cb_data *cb_data = user_data;
+
+       if (data->test_checks_valid &&
+                               test->expected_hal_cb.bond_state_changed_cb)
+               test->expected_hal_cb.bond_state_changed_cb(cb_data->status,
+                                       &cb_data->bdaddr, cb_data->state);
+
+       g_free(cb_data);
+       return FALSE;
+}
+
+static void bond_state_changed_cb(bt_status_t status,
+                       bt_bdaddr_t *remote_bd_addr, bt_bond_state_t state)
+{
+       struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+       cb_data->status = status;
+       cb_data->bdaddr = *remote_bd_addr;
+       cb_data->state = state;
+
+       g_idle_add(bond_state_changed, cb_data);
+}
+
+static void bond_create_pin_success_request_cb(bt_bdaddr_t *remote_bd_addr,
+                                       bt_bdname_t *bd_name, uint32_t cod)
+{
+       struct test_data *data = tester_get_data();
+       const bt_bdaddr_t *bdaddr = remote_bd_addr;
+       bt_pin_code_t pin_code = {
+       .pin = { 0x30, 0x30, 0x30, 0x30 },
+       };
+       uint8_t pin_len = 4;
+
+       data->cb_count--;
+
+       data->if_bluetooth->pin_reply(bdaddr, TRUE, pin_len, &pin_code);
+}
+
+static void bond_create_pin_fail_request_cb(bt_bdaddr_t *remote_bd_addr,
+                                       bt_bdname_t *bd_name, uint32_t cod)
+{
+       struct test_data *data = tester_get_data();
+       const bt_bdaddr_t *bdaddr = remote_bd_addr;
+       bt_pin_code_t pin_code = {
+       .pin = { 0x31, 0x31, 0x31, 0x31 },
+       };
+       uint8_t pin_len = 4;
+
+       data->cb_count--;
+
+       data->if_bluetooth->pin_reply(bdaddr, TRUE, pin_len, &pin_code);
+}
+
+static gboolean pin_request(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       struct bt_cb_data *cb_data = user_data;
+
+       if (data->test_checks_valid && test->expected_hal_cb.pin_request_cb)
+               test->expected_hal_cb.pin_request_cb(&cb_data->bdaddr,
+                                               &cb_data->bdname, cb_data->cod);
+
+       g_free(cb_data);
+       return FALSE;
+}
+
+static void pin_request_cb(bt_bdaddr_t *remote_bd_addr,
+                                       bt_bdname_t *bd_name, uint32_t cod)
+{
+       struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+       cb_data->bdaddr = *remote_bd_addr;
+       cb_data->bdname = *bd_name;
+       cb_data->cod = cod;
+
+       g_idle_add(pin_request, cb_data);
+}
+
+static void bond_create_ssp_request_cb(const bt_bdaddr_t *remote_bd_addr,
+                                       bt_ssp_variant_t pairing_variant,
+                                       bool accept, uint32_t pass_key)
+{
+       struct test_data *data = tester_get_data();
+
+       data->if_bluetooth->ssp_reply(remote_bd_addr,
+                                       BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
+                                       accept, pass_key);
+
+       data->cb_count--;
+}
+
+static void bond_create_ssp_success_request_cb(bt_bdaddr_t *remote_bd_addr,
+                                       bt_bdname_t *bd_name, uint32_t cod,
+                                       bt_ssp_variant_t pairing_variant,
+                                       uint32_t pass_key)
+{
+       bool accept = true;
+
+       bond_create_ssp_request_cb(remote_bd_addr, pairing_variant, accept,
+                                                               pass_key);
+}
+
+static void bond_create_ssp_fail_request_cb(bt_bdaddr_t *remote_bd_addr,
+                                       bt_bdname_t *bd_name, uint32_t cod,
+                                       bt_ssp_variant_t pairing_variant,
+                                       uint32_t pass_key)
+{
+       bool accept = false;
+
+       bond_create_ssp_request_cb(remote_bd_addr, pairing_variant, accept,
+                                                               pass_key);
+}
+
+static void bond_cancel_success_ssp_request_cb(bt_bdaddr_t *remote_bd_addr,
+                                       bt_bdname_t *bd_name, uint32_t cod,
+                                       bt_ssp_variant_t pairing_variant,
+                                       uint32_t pass_key)
+{
+       struct test_data *data = tester_get_data();
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t remote_addr;
+       bt_status_t status;
+
+       bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+       data->cb_count--;
+
+       status = data->if_bluetooth->cancel_bond(&remote_addr);
+       check_expected_status(status);
+}
+
+static gboolean ssp_request(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       struct bt_cb_data *cb_data = user_data;
+
+       if (data->test_checks_valid && test->expected_hal_cb.ssp_request_cb)
+               test->expected_hal_cb.ssp_request_cb(&cb_data->bdaddr,
+                                       &cb_data->bdname, cb_data->cod,
+                                       cb_data->ssp_variant, cb_data->passkey);
+
+       g_free(cb_data);
+       return FALSE;
+}
+
+static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name,
+                               uint32_t cod, bt_ssp_variant_t pairing_variant,
+                               uint32_t pass_key)
+{
+       struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+       cb_data->bdaddr = *remote_bd_addr;
+       cb_data->bdname = *bd_name;
+       cb_data->cod = cod;
+       cb_data->ssp_variant = pairing_variant;
+       cb_data->passkey = pass_key;
+
+       g_idle_add(ssp_request, cb_data);
+}
+
+static bt_bdaddr_t enable_done_bdaddr_val = { {0x00} };
+static const char enable_done_bdname_val[] = "BlueZ for Android";
+static bt_uuid_t enable_done_uuids_val = {
+       .uu = { 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+                                       0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb},
+};
+static bt_device_type_t enable_done_tod_val = BT_DEVICE_DEVTYPE_DUAL;
+static bt_scan_mode_t enable_done_scanmode_val = BT_SCAN_MODE_NONE;
+static uint32_t enable_done_disctimeout_val = 120;
+
+static struct priority_property enable_done_props[] = {
+       {
+       .prop.type = BT_PROPERTY_BDADDR,
+       .prop.len = sizeof(enable_done_bdaddr_val),
+       .prop.val = &enable_done_bdaddr_val,
+       .prio = 1,
+       },
+       {
+       .prop.type = BT_PROPERTY_BDNAME,
+       .prop.len = sizeof(enable_done_bdname_val) - 1,
+       .prop.val = &enable_done_bdname_val,
+       .prio = 2,
+       },
+       {
+       .prop.type = BT_PROPERTY_UUIDS,
+       .prop.len = sizeof(enable_done_uuids_val),
+       .prop.val = &enable_done_uuids_val,
+       .prio = 2,
+       },
+       {
+       .prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+       .prop.len = sizeof(uint32_t),
+       .prop.val = NULL,
+       .prio = 2,
+       },
+       {
+       .prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+       .prop.len = sizeof(enable_done_tod_val),
+       .prop.val = &enable_done_tod_val,
+       .prio = 2,
+       },
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+       .prop.len = sizeof(enable_done_scanmode_val),
+       .prop.val = &enable_done_scanmode_val,
+       .prio = 2,
+       },
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+       .prop.len = 0,
+       .prop.val = NULL,
+       .prio = 2,
+       },
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+       .prop.len = sizeof(enable_done_disctimeout_val),
+       .prop.val = &enable_done_disctimeout_val,
+       .prio = 2,
+       },
+};
+
+static const struct generic_data bluetooth_enable_success_test = {
+       .expected_hal_cb.adapter_state_changed_cb = enable_success_cb,
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_cb_count = 1,
+       .expected_properties_num = 8,
+       .expected_properties = enable_done_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_enable_success2_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+       .expected_properties_num = 8,
+       .expected_properties = enable_done_props,
+};
+
+static const struct generic_data bluetooth_disable_success_test = {
+       .expected_hal_cb.adapter_state_changed_cb = disable_success_cb,
+       .expected_cb_count = 1,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static char test_set_bdname[] = "test_bdname_set";
+
+static struct priority_property setprop_bdname_props[] = {
+       {
+       .prop.type = BT_PROPERTY_BDNAME,
+       .prop.val = test_set_bdname,
+       .prop.len = sizeof(test_set_bdname) - 1,
+       .prio = 0,
+       },
+};
+
+static const struct generic_data bluetooth_setprop_bdname_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = setprop_bdname_props,
+};
+
+static bt_scan_mode_t test_setprop_scanmode_val =
+                                       BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE;
+
+static struct priority_property setprop_scanmode_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+       .prop.val = &test_setprop_scanmode_val,
+       .prop.len = sizeof(bt_scan_mode_t),
+       },
+};
+
+static const struct generic_data bluetooth_setprop_scanmode_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = setprop_scanmode_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static uint32_t test_setprop_disctimeout_val = 120;
+
+static struct priority_property setprop_disctimeout_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+       .prop.val = &test_setprop_disctimeout_val,
+       .prop.len = sizeof(test_setprop_disctimeout_val),
+       },
+};
+
+static const struct generic_data bluetooth_setprop_disctimeout_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = setprop_disctimeout_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_bdaddr_t test_getprop_bdaddr_val = { {0x00} };
+
+static struct priority_property getprop_bdaddr_props[] = {
+       {
+       .prop.type = BT_PROPERTY_BDADDR,
+       .prop.val = &test_getprop_bdaddr_val,
+       .prop.len = sizeof(test_getprop_bdaddr_val),
+       },
+};
+
+static const struct generic_data bluetooth_getprop_bdaddr_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = getprop_bdaddr_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const char test_bdname[] = "test_bdname_setget";
+
+static struct priority_property getprop_bdname_props[] = {
+       {
+       .prop.type = BT_PROPERTY_BDNAME,
+       .prop.val = &test_bdname,
+       .prop.len = sizeof(test_bdname) - 1,
+       },
+};
+
+static const struct generic_data bluetooth_getprop_bdname_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = getprop_bdname_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static unsigned char setprop_uuids[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00,
+                       0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00 };
+
+static struct priority_property setprop_uuid_prop[] = {
+       {
+       .prop.type = BT_PROPERTY_UUIDS,
+       .prop.val = &setprop_uuids,
+       .prop.len = sizeof(setprop_uuids),
+       },
+};
+
+static const struct generic_data bluetooth_setprop_uuid_invalid_test = {
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static uint32_t setprop_class_of_device = 0;
+
+static struct priority_property setprop_cod_props[] = {
+       {
+       .prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+       .prop.val = &setprop_class_of_device,
+       .prop.len = sizeof(setprop_class_of_device),
+       },
+};
+
+static const struct generic_data bluetooth_setprop_cod_invalid_test = {
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_device_type_t setprop_type_of_device = BT_DEVICE_DEVTYPE_DUAL;
+
+static struct priority_property setprop_tod_props[] = {
+       {
+       .prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+       .prop.val = &setprop_type_of_device,
+       .prop.len = sizeof(setprop_type_of_device),
+       },
+};
+
+static const struct generic_data bluetooth_setprop_tod_invalid_test = {
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static int32_t setprop_remote_rssi = 0;
+
+static struct priority_property setprop_remote_rssi_props[] = {
+       {
+       .prop.type = BT_PROPERTY_REMOTE_RSSI,
+       .prop.val = &setprop_remote_rssi,
+       .prop.len = sizeof(setprop_remote_rssi),
+       },
+};
+
+static const struct generic_data bluetooth_setprop_remote_rssi_invalid_test = {
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_service_record_t setprop_remote_service = {
+       .uuid = { {0x00} },
+       .channel = 12,
+       .name = "bt_name",
+};
+
+static struct priority_property setprop_service_record_props[] = {
+       {
+       .prop.type = BT_PROPERTY_SERVICE_RECORD,
+       .prop.val = &setprop_remote_service,
+       .prop.len = sizeof(setprop_remote_service),
+       },
+};
+
+static const struct generic_data
+                       bluetooth_setprop_service_record_invalid_test = {
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_bdaddr_t setprop_bdaddr = {
+       .address = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+};
+
+static struct priority_property setprop_bdaddr_props[] = {
+       {
+       .prop.type = BT_PROPERTY_BDADDR,
+       .prop.val = &setprop_bdaddr,
+       .prop.len = sizeof(setprop_bdaddr),
+       },
+};
+
+static const struct generic_data bluetooth_setprop_bdaddr_invalid_test = {
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_scan_mode_t setprop_scanmode_connectable = BT_SCAN_MODE_CONNECTABLE;
+
+static struct priority_property setprop_scanmode_connectable_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+       .prop.val = &setprop_scanmode_connectable,
+       .prop.len = sizeof(setprop_scanmode_connectable),
+       },
+};
+
+static const struct generic_data
+                       bluetooth_setprop_scanmode_connectable_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = setprop_scanmode_connectable_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_bdaddr_t setprop_bonded_devices = {
+       .address = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 },
+};
+
+static struct priority_property setprop_bonded_devices_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+       .prop.val = &setprop_bonded_devices,
+       .prop.len = sizeof(setprop_bonded_devices),
+       },
+};
+
+static const struct generic_data
+                       bluetooth_setprop_bonded_devices_invalid_test = {
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static uint32_t getprop_cod = 0x00020c;
+
+static struct priority_property getprop_cod_props[] = {
+       {
+       .prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+       .prop.val = &getprop_cod,
+       .prop.len = sizeof(getprop_cod),
+       },
+};
+
+static const struct generic_data bluetooth_getprop_cod_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = getprop_cod_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_device_type_t getprop_tod = BT_DEVICE_DEVTYPE_DUAL;
+
+static struct priority_property getprop_tod_props[] = {
+       {
+       .prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+       .prop.val = &getprop_tod,
+       .prop.len = sizeof(getprop_tod),
+       },
+};
+
+static const struct generic_data bluetooth_getprop_tod_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = getprop_tod_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_scan_mode_t getprop_scanmode = BT_SCAN_MODE_NONE;
+
+static struct priority_property getprop_scanmode_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+       .prop.val = &getprop_scanmode,
+       .prop.len = sizeof(getprop_scanmode),
+       },
+};
+
+static const struct generic_data bluetooth_getprop_scanmode_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = getprop_scanmode_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static uint32_t getprop_disctimeout_val = 120;
+
+static struct priority_property getprop_disctimeout_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+       .prop.val = &getprop_disctimeout_val,
+       .prop.len = sizeof(getprop_disctimeout_val),
+       },
+};
+
+static const struct generic_data bluetooth_getprop_disctimeout_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = getprop_disctimeout_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_uuid_t getprop_uuids = {
+       .uu = { 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+                                       0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB },
+};
+
+static struct priority_property getprop_uuids_props[] = {
+       {
+       .prop.type = BT_PROPERTY_UUIDS,
+       .prop.val = &getprop_uuids,
+       .prop.len = sizeof(getprop_uuids),
+       },
+};
+
+static const struct generic_data bluetooth_getprop_uuids_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = getprop_uuids_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static struct priority_property getprop_bondeddev_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+       .prop.val = NULL,
+       .prop.len = 0,
+       },
+};
+
+static const struct generic_data bluetooth_getprop_bondeddev_success_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = getprop_bondeddev_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_scan_mode_t setprop_scanmode_none = BT_SCAN_MODE_NONE;
+
+static struct priority_property setprop_scanmode_none_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+       .prop.val = &setprop_scanmode_none,
+       .prop.len = sizeof(setprop_scanmode_none),
+       },
+};
+
+static const struct generic_data
+                       bluetooth_setprop_scanmode_none_success2_test = {
+       .expected_hal_cb.adapter_properties_cb = check_count_properties_cb,
+       .expected_properties_num = 1,
+       .expected_properties = setprop_scanmode_none_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_discovery_start_success_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                               discovery_start_success_cb,
+       .expected_cb_count = 1,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_discovery_start_success2_test = {
+       .expected_hal_cb.discovery_state_changed_cb = discovery_start_done_cb,
+       .expected_cb_count = 1,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_discovery_stop_success2_test = {
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_discovery_stop_success_test = {
+       .expected_hal_cb.discovery_state_changed_cb = discovery_stop_success_cb,
+       .expected_cb_count = 2,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bluetooth_discovery_device_found_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       discovery_device_found_state_changed_cb,
+       .expected_hal_cb.device_found_cb = discovery_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_adapter_status = BT_STATUS_NOT_EXPECTED,
+};
+
+static const char remote_get_properties_bdname_val[] = "00:AA:01:01:00:00";
+static uint32_t remote_get_properties_cod_val = 0;
+static bt_device_type_t remote_get_properties_tod_val = BT_DEVICE_DEVTYPE_BREDR;
+static int32_t remote_get_properties_rssi_val = -60;
+
+static struct priority_property remote_getprops_props[] = {
+       {
+       .prop.type = BT_PROPERTY_BDNAME,
+       .prop.val = &remote_get_properties_bdname_val,
+       .prop.len = sizeof(remote_get_properties_bdname_val) - 1,
+       },
+       {
+       .prop.type = BT_PROPERTY_UUIDS,
+       .prop.val = NULL,
+       .prop.len = 0,
+       },
+       {
+       .prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+       .prop.val = &remote_get_properties_cod_val,
+       .prop.len = sizeof(remote_get_properties_cod_val),
+       },
+       {
+       .prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+       .prop.val = &remote_get_properties_tod_val,
+       .prop.len = sizeof(remote_get_properties_tod_val),
+       },
+       {
+       .prop.type = BT_PROPERTY_REMOTE_RSSI,
+       .prop.val = &remote_get_properties_rssi_val,
+       .prop.len = sizeof(remote_get_properties_rssi_val),
+       },
+       {
+       .prop.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP,
+       .prop.val = NULL,
+       .prop.len = 4,
+       },
+};
+
+static const struct generic_data bt_dev_getprops_success_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_getprops_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties_num = 6,
+       .expected_properties = remote_getprops_props,
+       .expected_adapter_status = BT_STATUS_NOT_EXPECTED,
+};
+
+static const char remote_getprop_bdname_val[] = "00:AA:01:01:00:00";
+
+static struct priority_property remote_getprop_bdname_props[] = {
+       {
+       .prop.type = BT_PROPERTY_BDNAME,
+       .prop.val = &remote_getprop_bdname_val,
+       .prop.len = sizeof(remote_getprop_bdname_val) - 1,
+       },
+};
+
+static const struct generic_data bt_dev_getprop_bdname_success_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties_num = 1,
+       .expected_properties = remote_getprop_bdname_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static struct priority_property remote_getprop_uuids_props[] = {
+       {
+       .prop.type = BT_PROPERTY_UUIDS,
+       .prop.val = NULL,
+       .prop.len = 0,
+       },
+};
+
+static const struct generic_data bt_dev_getprop_uuids_success_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties_num = 1,
+       .expected_properties = remote_getprop_uuids_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static uint32_t remote_getprop_cod_val = 0;
+
+static struct priority_property remote_getprop_cod_props[] = {
+       {
+       .prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+       .prop.val = &remote_getprop_cod_val,
+       .prop.len = sizeof(remote_getprop_cod_val),
+       },
+};
+
+static const struct generic_data bt_dev_getprop_cod_success_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties_num = 1,
+       .expected_properties = remote_getprop_cod_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_device_type_t remote_getprop_tod_val = BT_DEVICE_DEVTYPE_BREDR;
+
+static struct priority_property remote_getprop_tod_props[] = {
+       {
+       .prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+       .prop.val = &remote_getprop_tod_val,
+       .prop.len = sizeof(remote_getprop_tod_val),
+       },
+};
+
+static const struct generic_data bt_dev_getprop_tod_success_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties_num = 1,
+       .expected_properties = remote_getprop_tod_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static int32_t remote_getprop_rssi_val = -60;
+
+static struct priority_property remote_getprop_rssi_props[] = {
+       {
+       .prop.type = BT_PROPERTY_REMOTE_RSSI,
+       .prop.val = &remote_getprop_rssi_val,
+       .prop.len = sizeof(remote_getprop_rssi_val),
+       },
+};
+
+static const struct generic_data bt_dev_getprop_rssi_success_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties_num = 1,
+       .expected_properties = remote_getprop_rssi_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static struct priority_property remote_getprop_timestamp_props[] = {
+       {
+       .prop.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP,
+       .prop.val = NULL,
+       .prop.len = 4,
+       },
+};
+
+static const struct generic_data bt_dev_getprop_timpestamp_success_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties_num = 1,
+       .expected_properties = remote_getprop_timestamp_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_bdaddr_t remote_getprop_bdaddr_val = {
+       .address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 }
+};
+
+static struct priority_property remote_getprop_bdaddr_props[] = {
+       {
+       .prop.type = BT_PROPERTY_BDADDR,
+       .prop.val = &remote_getprop_bdaddr_val,
+       .prop.len = sizeof(remote_getprop_bdaddr_val),
+       },
+};
+
+static const struct generic_data bt_dev_getprop_bdaddr_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_getprop_bdaddr_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_service_record_t remote_getprop_servrec_val = {
+       .uuid = { {0x00} },
+       .channel = 12,
+       .name = "bt_name",
+};
+
+static struct priority_property remote_getprop_servrec_props[] = {
+       {
+       .prop.type = BT_PROPERTY_SERVICE_RECORD,
+       .prop.val = &remote_getprop_servrec_val,
+       .prop.len = sizeof(remote_getprop_servrec_val),
+       },
+};
+
+static const struct generic_data bt_dev_getprop_servrec_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_getprop_servrec_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_scan_mode_t remote_getprop_scanmode_val = BT_SCAN_MODE_CONNECTABLE;
+
+static struct priority_property remote_getprop_scanmode_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+       .prop.val = &remote_getprop_scanmode_val,
+       .prop.len = sizeof(remote_getprop_scanmode_val),
+       },
+};
+
+static const struct generic_data bt_dev_getprop_scanmode_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_getprop_scanmode_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static struct priority_property remote_getprop_bondeddev_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+       .prop.val = NULL,
+       .prop.len = 0,
+       },
+};
+
+static const struct generic_data bt_dev_getprop_bondeddev_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_getprop_bondeddev_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static uint32_t remote_getprop_disctimeout_val = 120;
+
+static struct priority_property remote_getprop_disctimeout_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+       .prop.val = &remote_getprop_disctimeout_val,
+       .prop.len = sizeof(remote_getprop_disctimeout_val),
+       },
+};
+
+static const struct generic_data bt_dev_getprop_disctimeout_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_getprop_disctimeout_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static struct priority_property remote_getprop_verinfo_props[] = {
+       {
+       .prop.type = BT_PROPERTY_REMOTE_VERSION_INFO,
+       .prop.val = NULL,
+       .prop.len = 0,
+       },
+};
+
+static const struct generic_data bt_dev_getprop_verinfo_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_getprop_verinfo_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static struct priority_property remote_getprop_fname_props[] = {
+       {
+       .prop.type = BT_PROPERTY_REMOTE_VERSION_INFO,
+       .prop.val = NULL,
+       .prop.len = 0,
+       },
+};
+
+static const struct generic_data bt_dev_getprop_fname_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_get_property_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_getprop_fname_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static const char remote_setprop_fname_val[] = "set_fname_test";
+
+static struct priority_property remote_setprop_fname_props[] = {
+       {
+       .prop.type = BT_PROPERTY_REMOTE_FRIENDLY_NAME,
+       .prop.val = &remote_setprop_fname_val,
+       .prop.len = sizeof(remote_setprop_fname_val) - 1,
+       },
+};
+
+static const struct generic_data bt_dev_setprop_fname_success_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_setprop_disc_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_device_found_cb,
+       .expected_hal_cb.remote_device_properties_cb =
+                                       remote_test_device_properties_cb,
+       .expected_cb_count = 3,
+       .expected_properties_num = 1,
+       .expected_properties = remote_setprop_fname_props,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const char remote_setprop_bdname_val[] = "setprop_bdname_fail";
+
+static struct priority_property remote_setprop_bdname_props[] = {
+       {
+       .prop.type = BT_PROPERTY_BDNAME,
+       .prop.val = &remote_setprop_bdname_val,
+       .prop.len = sizeof(remote_setprop_bdname_val) - 1,
+       },
+};
+
+static const struct generic_data bt_dev_setprop_bdname_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_bdname_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static struct priority_property remote_setprop_uuids_props[] = {
+       {
+       .prop.type = BT_PROPERTY_UUIDS,
+       .prop.val = NULL,
+       .prop.len = 0,
+       },
+};
+
+static const struct generic_data bt_dev_setprop_uuids_fail_test  = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_uuids_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static uint32_t remote_setprop_cod_val = 0;
+
+static struct priority_property remote_setprop_cod_props[] = {
+       {
+       .prop.type = BT_PROPERTY_CLASS_OF_DEVICE,
+       .prop.val = &remote_setprop_cod_val,
+       .prop.len = sizeof(remote_setprop_cod_val),
+       },
+};
+
+static const struct generic_data bt_dev_setprop_cod_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_cod_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_device_type_t remote_setprop_tod_val = BT_DEVICE_DEVTYPE_BREDR;
+
+static struct priority_property remote_setprop_tod_props[] = {
+       {
+       .prop.type = BT_PROPERTY_TYPE_OF_DEVICE,
+       .prop.val = &remote_setprop_tod_val,
+       .prop.len = sizeof(remote_setprop_tod_val),
+       },
+};
+
+static const struct generic_data bt_dev_setprop_tod_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_tod_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static int32_t remote_setprop_rssi_val = -60;
+
+static struct priority_property remote_setprop_rssi_props[] = {
+       {
+       .prop.type = BT_PROPERTY_REMOTE_RSSI,
+       .prop.val = &remote_setprop_rssi_val,
+       .prop.len = sizeof(remote_setprop_rssi_val),
+       },
+};
+
+static const struct generic_data bt_dev_setprop_rssi_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_rssi_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static int32_t remote_setprop_timestamp_val = 0xAB;
+
+static struct priority_property remote_setprop_timestamp_props[] = {
+       {
+       .prop.type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP,
+       .prop.val = (&remote_setprop_timestamp_val),
+       .prop.len = sizeof(remote_setprop_timestamp_val),
+       },
+};
+
+static const struct generic_data bt_dev_setprop_timpestamp_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_timestamp_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_bdaddr_t remote_setprop_bdaddr_val = {
+       .address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 }
+};
+
+static struct priority_property remote_setprop_bdaddr_props[] = {
+       {
+       .prop.type = BT_PROPERTY_BDADDR,
+       .prop.val = &remote_setprop_bdaddr_val,
+       .prop.len = sizeof(remote_setprop_bdaddr_val),
+       },
+};
+
+static const struct generic_data bt_dev_setprop_bdaddr_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_bdaddr_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_service_record_t remote_setprop_servrec_val = {
+       .uuid = { {0x00} },
+       .channel = 12,
+       .name = "bt_name",
+};
+
+static struct priority_property remote_setprop_servrec_props[] = {
+       {
+       .prop.type = BT_PROPERTY_SERVICE_RECORD,
+       .prop.val = &remote_setprop_servrec_val,
+       .prop.len = sizeof(remote_setprop_servrec_val),
+       },
+};
+
+static const struct generic_data bt_dev_setprop_servrec_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_servrec_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_scan_mode_t remote_setprop_scanmode_val = BT_SCAN_MODE_CONNECTABLE;
+
+static struct priority_property remote_setprop_scanmode_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE,
+       .prop.val = &remote_setprop_scanmode_val,
+       .prop.len = sizeof(remote_setprop_scanmode_val),
+       },
+};
+
+static const struct generic_data bt_dev_setprop_scanmode_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_scanmode_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static bt_bdaddr_t remote_setprop_bondeddev_val = {
+       .address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 }
+};
+
+static struct priority_property remote_setprop_bondeddev_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_BONDED_DEVICES,
+       .prop.val = &remote_setprop_bondeddev_val,
+       .prop.len = sizeof(remote_setprop_bondeddev_val),
+       },
+};
+
+static const struct generic_data bt_dev_setprop_bondeddev_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_bondeddev_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static uint32_t remote_setprop_disctimeout_val = 120;
+
+static struct priority_property remote_setprop_disctimeout_props[] = {
+       {
+       .prop.type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
+       .prop.val = &remote_setprop_disctimeout_val,
+       .prop.len = sizeof(remote_setprop_disctimeout_val),
+       },
+};
+
+static const struct generic_data bt_dev_setprop_disctimeout_fail_test = {
+       .expected_hal_cb.discovery_state_changed_cb =
+                                       remote_discovery_state_changed_cb,
+       .expected_hal_cb.device_found_cb = remote_setprop_fail_device_found_cb,
+       .expected_cb_count = 3,
+       .expected_properties = remote_setprop_disctimeout_props,
+       .expected_adapter_status = BT_STATUS_FAIL,
+};
+
+static const struct generic_data bt_bond_create_pin_success_test = {
+       .expected_hal_cb.device_found_cb = bond_device_found_cb,
+       .expected_hal_cb.bond_state_changed_cb =
+                                       bond_test_bonded_state_changed_cb,
+       .expected_hal_cb.pin_request_cb = bond_create_pin_success_request_cb,
+       .expected_cb_count = 4,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bt_bond_create_pin_fail_test = {
+       .expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb,
+       .expected_hal_cb.bond_state_changed_cb =
+                                               bond_test_none_state_changed_cb,
+       .expected_hal_cb.pin_request_cb = bond_create_pin_fail_request_cb,
+       .expected_cb_count = 4,
+       .expected_adapter_status = MGMT_STATUS_AUTH_FAILED,
+};
+
+static const struct generic_data bt_bond_create_ssp_success_test = {
+       .expected_hal_cb.device_found_cb = bond_device_found_cb,
+       .expected_hal_cb.bond_state_changed_cb =
+                                       bond_test_bonded_state_changed_cb,
+       .expected_hal_cb.ssp_request_cb = bond_create_ssp_success_request_cb,
+       .expected_cb_count = 4,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bt_bond_create_ssp_fail_test = {
+       .expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb,
+       .expected_hal_cb.bond_state_changed_cb =
+                                               bond_test_none_state_changed_cb,
+       .expected_hal_cb.ssp_request_cb = bond_create_ssp_fail_request_cb,
+       .expected_cb_count = 4,
+       .expected_adapter_status = MGMT_STATUS_AUTH_FAILED,
+};
+
+static const struct generic_data bt_bond_create_no_disc_success_test = {
+       .expected_hal_cb.bond_state_changed_cb =
+                                       bond_test_bonded_state_changed_cb,
+       .expected_hal_cb.ssp_request_cb = bond_create_ssp_success_request_cb,
+       .expected_cb_count = 3,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bt_bond_create_bad_addr_success_test = {
+       .expected_adapter_status = MGMT_STATUS_CONNECT_FAILED,
+};
+
+static const struct generic_data bt_bond_cancel_success_test = {
+       .expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb,
+       .expected_hal_cb.bond_state_changed_cb =
+                                               bond_test_none_state_changed_cb,
+       .expected_hal_cb.ssp_request_cb = bond_cancel_success_ssp_request_cb,
+       .expected_cb_count = 4,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static const struct generic_data bt_bond_remove_success_test = {
+       .expected_hal_cb.device_found_cb = bond_nostatus_device_found_cb,
+       .expected_hal_cb.bond_state_changed_cb =
+                                       bond_remove_success_state_changed_cb,
+       .expected_hal_cb.ssp_request_cb = bond_create_ssp_success_request_cb,
+       .expected_cb_count = 4,
+       .expected_adapter_status = BT_STATUS_SUCCESS,
+};
+
+static bt_callbacks_t bt_callbacks = {
+       .size = sizeof(bt_callbacks),
+       .adapter_state_changed_cb = adapter_state_changed_cb,
+       .adapter_properties_cb = adapter_properties_cb,
+       .remote_device_properties_cb = remote_device_properties_cb,
+       .device_found_cb = device_found_cb,
+       .discovery_state_changed_cb = discovery_state_changed_cb,
+       .pin_request_cb = pin_request_cb,
+       .ssp_request_cb = ssp_request_cb,
+       .bond_state_changed_cb = bond_state_changed_cb,
+       .acl_state_changed_cb = NULL,
+       .thread_evt_cb = NULL,
+       .dut_mode_recv_cb = NULL,
+       .le_test_mode_cb = NULL
+};
+
+static bool setup(struct test_data *data)
+{
+       const hw_module_t *module;
+       hw_device_t *device;
+       int signal_fd[2];
+       char buf[1024];
+       pid_t pid;
+       int len;
+       int err;
+
+       if (pipe(signal_fd))
+               return false;
+
+       pid = fork();
+
+       if (pid < 0) {
+               close(signal_fd[0]);
+               close(signal_fd[1]);
+               return false;
+       }
+
+       if (pid == 0) {
+               if (!tester_use_debug())
+                       fclose(stderr);
+
+               close(signal_fd[0]);
+               emulator(signal_fd[1], data->mgmt_index);
+               exit(0);
+       }
+
+       close(signal_fd[1]);
+       data->bluetoothd_pid = pid;
+
+       len = read(signal_fd[0], buf, sizeof(buf));
+       if (len <= 0 || strcmp(buf, EMULATOR_SIGNAL)) {
+               close(signal_fd[0]);
+               return false;
+       }
+
+       close(signal_fd[0]);
+
+       err = hw_get_module(BT_HARDWARE_MODULE_ID, &module);
+       if (err)
+               return false;
+
+       err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device);
+       if (err)
+               return false;
+
+       data->device = device;
+
+       data->if_bluetooth = ((bluetooth_device_t *)
+                                       device)->get_bluetooth_interface();
+       if (!data->if_bluetooth)
+               return false;
+
+       return true;
+}
+
+static void setup_base(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t status;
+
+       if (!setup(data)) {
+               tester_setup_failed();
+               return;
+       }
+
+       status = data->if_bluetooth->init(&bt_callbacks);
+       if (status != BT_STATUS_SUCCESS) {
+               data->if_bluetooth = NULL;
+               tester_setup_failed();
+               return;
+       }
+
+       tester_setup_complete();
+}
+
+static void setup_enabled_adapter(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t status;
+
+       if (!setup(data)) {
+               tester_setup_failed();
+               return;
+       }
+
+       status = data->if_bluetooth->init(&bt_callbacks);
+       if (status != BT_STATUS_SUCCESS) {
+               data->if_bluetooth = NULL;
+               tester_setup_failed();
+               return;
+       }
+
+       status = data->if_bluetooth->enable();
+       if (status != BT_STATUS_SUCCESS)
+               tester_setup_failed();
+}
+
+static void teardown(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       if (data->if_hid) {
+               data->if_hid->cleanup();
+               data->if_hid = NULL;
+       }
+
+       if (data->if_bluetooth) {
+               data->if_bluetooth->cleanup();
+               data->if_bluetooth = NULL;
+       }
+
+       /* Test result already known, no need to check further */
+       data->test_checks_valid = false;
+
+       if (data->expected_properties_list)
+               g_slist_free(data->expected_properties_list);
+
+       data->device->close(data->device);
+
+       if (!data->bluetoothd_pid)
+               tester_teardown_complete();
+}
+
+static void test_dummy(const void *test_data)
+{
+       tester_test_passed();
+}
+
+static void test_enable(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t adapter_status;
+
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_master_bdaddr(data->hciemu);
+
+       init_test_conditions(data);
+
+       bdaddr2android((const bdaddr_t *)bdaddr,
+                                       &enable_done_bdaddr_val.address);
+
+       adapter_status = data->if_bluetooth->enable();
+       check_expected_status(adapter_status);
+}
+
+static void test_enable_done(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t adapter_status;
+
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_master_bdaddr(data->hciemu);
+
+       init_test_conditions(data);
+
+       bdaddr2android((const bdaddr_t *)bdaddr,
+                                       &enable_done_bdaddr_val.address);
+
+       adapter_status = data->if_bluetooth->enable();
+       check_expected_status(adapter_status);
+}
+
+static void test_disable(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->disable();
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_bdname_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(setprop_bdname_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_scanmode_succes(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(setprop_scanmode_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_disctimeout_succes(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(setprop_disctimeout_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_getprop_bdaddr_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t prop = setprop_bdaddr_props[0].prop;
+       bt_status_t adapter_status;
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_master_bdaddr(data->hciemu);
+
+       init_test_conditions(data);
+
+       bdaddr2android((const bdaddr_t *)bdaddr,
+                                       &test_getprop_bdaddr_val.address);
+
+       adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+       check_expected_status(adapter_status);
+}
+
+static void test_getprop_bdname_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(getprop_bdname_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       if (adapter_status != BT_STATUS_SUCCESS) {
+               tester_test_failed();
+               return;
+       }
+
+       adapter_status = data->if_bluetooth->get_adapter_property((*prop).type);
+       check_expected_status(adapter_status);
+}
+static void test_setprop_uuid_invalid(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(setprop_uuid_prop[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_cod_invalid(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(setprop_cod_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_tod_invalid(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       const bt_property_t *prop = &test->set_property;
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_rssi_invalid(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(setprop_remote_rssi_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_service_record_invalid(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(setprop_service_record_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_bdaddr_invalid(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(setprop_bdaddr_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_scanmode_connectable_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop =
+                               &(setprop_scanmode_connectable_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_bonded_devices_invalid(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(setprop_bonded_devices_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_getprop_cod_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t prop = setprop_cod_props[0].prop;
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+       check_expected_status(adapter_status);
+}
+
+static void test_getprop_tod_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t prop = setprop_tod_props[0].prop;
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+       check_expected_status(adapter_status);
+}
+
+static void test_getprop_scanmode_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t prop = setprop_scanmode_props[0].prop;
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+       check_expected_status(adapter_status);
+}
+
+static void test_getprop_disctimeout_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t prop = setprop_disctimeout_props[0].prop;
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+       check_expected_status(adapter_status);
+}
+
+static void test_getprop_uuids_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t prop = getprop_uuids_props[0].prop;
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+       check_expected_status(adapter_status);
+}
+
+static void test_getprop_bondeddev_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t prop = getprop_bondeddev_props[0].prop;
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->get_adapter_property(prop.type);
+       check_expected_status(adapter_status);
+}
+
+static void test_setprop_scanmode_none_done(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const bt_property_t *prop = &(setprop_scanmode_none_props[0].prop);
+       bt_status_t adapter_status;
+
+       init_test_conditions(data);
+
+       adapter_status = data->if_bluetooth->set_adapter_property(prop);
+       check_expected_status(adapter_status);
+}
+
+static void test_discovery_start_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t status;
+
+       init_test_conditions(data);
+
+       status = data->if_bluetooth->start_discovery();
+       check_expected_status(status);
+}
+
+static void test_discovery_stop_done(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t status;
+
+       init_test_conditions(data);
+
+       status = data->if_bluetooth->cancel_discovery();
+       check_expected_status(status);
+}
+
+static void test_discovery_stop_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_discovery_start_done(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_discovery_device_found(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprops_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_bdname_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_uuids_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_cod_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_tod_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_rssi_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_timestamp_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_bdaddr_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_servrec_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_scanmode_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_bondeddev_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_disctimeout_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_verinfo_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_getprop_fname_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_fname_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_bdname_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_uuids_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_cod_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_tod_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_rssi_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_timestamp_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_bdaddr_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_servrec_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_scanmode_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_bondeddev_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_dev_setprop_disctimeout_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       init_test_conditions(data);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void bond_device_auth_fail_callback(uint16_t index, uint16_t length,
+                                                       const void *param,
+                                                       void *user_data)
+{
+       const struct mgmt_ev_auth_failed *ev = param;
+
+       check_expected_status(ev->status);
+}
+
+static void test_bond_create_pin_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 };
+       const void *pin = pair_device_pin;
+       uint8_t pin_len = 4;
+
+       init_test_conditions(data);
+
+       bthost_set_pin_code(bthost, pin, pin_len);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_bond_create_pin_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 };
+       const void *pin = pair_device_pin;
+       uint8_t pin_len = 4;
+
+       init_test_conditions(data);
+
+       mgmt_register(data->mgmt, MGMT_EV_AUTH_FAILED, data->mgmt_index,
+                                       bond_device_auth_fail_callback, data,
+                                       NULL);
+
+       bthost_set_pin_code(bthost, pin, pin_len);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_bond_create_ssp_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       init_test_conditions(data);
+
+       bthost_write_ssp_mode(bthost, 0x01);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_bond_create_ssp_fail(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       init_test_conditions(data);
+
+       mgmt_register(data->mgmt, MGMT_EV_AUTH_FAILED, data->mgmt_index,
+                                       bond_device_auth_fail_callback, data,
+                                       NULL);
+
+       bthost_write_ssp_mode(bthost, 0x01);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_bond_create_no_disc_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       uint8_t *bdaddr = (uint8_t *)hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t remote_addr;
+       bt_status_t status;
+
+       init_test_conditions(data);
+
+       bdaddr2android((const bdaddr_t *)bdaddr, &remote_addr.address);
+
+       bthost_write_ssp_mode(bthost, 0x01);
+
+       status = data->if_bluetooth->create_bond(&remote_addr);
+       check_expected_status(status);
+}
+
+static void test_bond_create_bad_addr_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_bdaddr_t bad_addr = {
+               .address = { 0x12, 0x34, 0x56, 0x78, 0x90, 0x12 }
+       };
+
+       init_test_conditions(data);
+
+       mgmt_register(data->mgmt, MGMT_EV_CONNECT_FAILED, data->mgmt_index,
+                                       bond_device_auth_fail_callback, data,
+                                       NULL);
+
+       data->if_bluetooth->create_bond(&bad_addr);
+}
+
+static void test_bond_cancel_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       init_test_conditions(data);
+
+       bthost_write_ssp_mode(bthost, 0x01);
+
+       data->if_bluetooth->start_discovery();
+}
+
+static void test_bond_remove_success(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       init_test_conditions(data);
+
+       bthost_write_ssp_mode(bthost, 0x01);
+
+       data->if_bluetooth->start_discovery();
+}
+
+/* Test Socket HAL */
+
+static gboolean adapter_socket_state_changed(gpointer user_data)
+{
+       struct bt_cb_data *cb_data = user_data;
+
+       switch (cb_data->state) {
+       case BT_STATE_ON:
+               setup_powered_emulated_remote();
+               break;
+       case BT_STATE_OFF:
+               tester_setup_failed();
+               break;
+       default:
+               break;
+       }
+
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void adapter_socket_state_changed_cb(bt_state_t state)
+{
+       struct bt_cb_data *cb_data = g_new0(struct bt_cb_data, 1);
+
+       cb_data->state = state;
+
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(adapter_socket_state_changed, cb_data);
+}
+
+const bt_bdaddr_t bdaddr_dummy = {
+       .address = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
+};
+
+static const struct socket_data btsock_inv_param_socktype = {
+       .bdaddr = &bdaddr_dummy,
+       .sock_type = 0,
+       .channel = 1,
+       .service_uuid = NULL,
+       .service_name = "Test service",
+       .flags = 0,
+       .expected_status = BT_STATUS_PARM_INVALID,
+};
+
+static const struct socket_data btsock_inv_param_socktype_l2cap = {
+       .bdaddr = &bdaddr_dummy,
+       .sock_type = BTSOCK_L2CAP,
+       .channel = 1,
+       .service_uuid = NULL,
+       .service_name = "Test service",
+       .flags = 0,
+       .expected_status = BT_STATUS_UNSUPPORTED,
+};
+
+/* Test invalid: channel & uuid are both zeroes */
+static const struct socket_data btsock_inv_params_chan_uuid = {
+       .bdaddr = &bdaddr_dummy,
+       .sock_type = BTSOCK_RFCOMM,
+       .channel = 0,
+       .service_uuid = NULL,
+       .service_name = "Test service",
+       .flags = 0,
+       .expected_status = BT_STATUS_PARM_INVALID,
+};
+
+static const struct socket_data btsock_success = {
+       .bdaddr = &bdaddr_dummy,
+       .sock_type = BTSOCK_RFCOMM,
+       .channel = 1,
+       .service_uuid = NULL,
+       .service_name = "Test service",
+       .flags = 0,
+       .expected_status = BT_STATUS_SUCCESS,
+       .test_channel = false
+};
+
+static const struct socket_data btsock_success_check_chan = {
+       .sock_type = BTSOCK_RFCOMM,
+       .channel = 1,
+       .service_uuid = NULL,
+       .service_name = "Test service",
+       .flags = 0,
+       .expected_status = BT_STATUS_SUCCESS,
+       .test_channel = true,
+};
+
+static const struct socket_data btsock_inv_param_bdaddr = {
+       .bdaddr = NULL,
+       .sock_type = BTSOCK_RFCOMM,
+       .channel = 1,
+       .service_uuid = NULL,
+       .service_name = "Test service",
+       .flags = 0,
+       .expected_status = BT_STATUS_PARM_INVALID,
+};
+
+static const struct socket_data btsock_inv_listen_listen = {
+       .sock_type = BTSOCK_RFCOMM,
+       .channel = 1,
+       .service_uuid = NULL,
+       .service_name = "Test service",
+       .flags = 0,
+       .expected_status = BT_STATUS_BUSY,
+       .test_channel = true,
+};
+
+static bt_callbacks_t bt_socket_callbacks = {
+       .size = sizeof(bt_callbacks),
+       .adapter_state_changed_cb = adapter_socket_state_changed_cb,
+       .adapter_properties_cb = NULL,
+       .remote_device_properties_cb = NULL,
+       .device_found_cb = NULL,
+       .discovery_state_changed_cb = NULL,
+       .pin_request_cb = NULL,
+       .ssp_request_cb = NULL,
+       .bond_state_changed_cb = NULL,
+       .acl_state_changed_cb = NULL,
+       .thread_evt_cb = NULL,
+       .dut_mode_recv_cb = NULL,
+       .le_test_mode_cb = NULL
+};
+
+static void setup_socket_interface(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t status;
+       const void *sock;
+
+       if (!setup(data)) {
+               tester_setup_failed();
+               return;
+       }
+
+       status = data->if_bluetooth->init(&bt_socket_callbacks);
+       if (status != BT_STATUS_SUCCESS) {
+               data->if_bluetooth = NULL;
+               tester_setup_failed();
+               return;
+       }
+
+       sock = data->if_bluetooth->get_profile_interface(BT_PROFILE_SOCKETS_ID);
+       if (!sock) {
+               tester_setup_failed();
+               return;
+       }
+
+       data->if_sock = sock;
+
+       tester_setup_complete();
+}
+
+static void setup_socket_interface_enabled(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t status;
+       const void *sock;
+
+       if (!setup(data)) {
+               tester_setup_failed();
+               return;
+       }
+
+       status = data->if_bluetooth->init(&bt_socket_callbacks);
+       if (status != BT_STATUS_SUCCESS) {
+               data->if_bluetooth = NULL;
+               tester_setup_failed();
+               return;
+       }
+
+       sock = data->if_bluetooth->get_profile_interface(BT_PROFILE_SOCKETS_ID);
+       if (!sock) {
+               tester_setup_failed();
+               return;
+       }
+
+       data->if_sock = sock;
+
+       status = data->if_bluetooth->enable();
+       if (status != BT_STATUS_SUCCESS)
+               tester_setup_failed();
+}
+
+static void test_generic_listen(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct socket_data *test = data->test_data;
+       bt_status_t status;
+       int sock_fd = -1;
+
+       status = data->if_sock->listen(test->sock_type,
+                                       test->service_name, test->service_uuid,
+                                       test->channel, &sock_fd, test->flags);
+       if (status != test->expected_status) {
+               tester_test_failed();
+               goto clean;
+       }
+
+       /* Check that file descriptor is valid */
+       if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) {
+               tester_test_failed();
+               return;
+       }
+
+       if (status == BT_STATUS_SUCCESS && test->test_channel) {
+               int channel, len;
+
+               len = read(sock_fd, &channel, sizeof(channel));
+               if (len != sizeof(channel) || channel != test->channel) {
+                       tester_test_failed();
+                       goto clean;
+               }
+
+               tester_print("read correct channel: %d", channel);
+       }
+
+       tester_test_passed();
+
+clean:
+       if (sock_fd >= 0)
+               close(sock_fd);
+}
+
+static void test_listen_close(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct socket_data *test = data->test_data;
+       bt_status_t status;
+       int sock_fd = -1;
+
+       status = data->if_sock->listen(test->sock_type,
+                                       test->service_name, test->service_uuid,
+                                       test->channel, &sock_fd, test->flags);
+       if (status != test->expected_status) {
+               tester_warn("sock->listen() failed");
+               tester_test_failed();
+               goto clean;
+       }
+
+       /* Check that file descriptor is valid */
+       if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) {
+               tester_warn("sock_fd %d is not valid", sock_fd);
+               tester_test_failed();
+               return;
+       }
+
+       tester_print("Got valid sock_fd: %d", sock_fd);
+
+       /* Now close sock_fd */
+       close(sock_fd);
+       sock_fd = -1;
+
+       /* Try to listen again */
+       status = data->if_sock->listen(test->sock_type,
+                                       test->service_name, test->service_uuid,
+                                       test->channel, &sock_fd, test->flags);
+       if (status != test->expected_status) {
+               tester_warn("sock->listen() failed");
+               tester_test_failed();
+               goto clean;
+       }
+
+       /* Check that file descriptor is valid */
+       if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) {
+               tester_warn("sock_fd %d is not valid", sock_fd);
+               tester_test_failed();
+               return;
+       }
+
+       tester_print("Got valid sock_fd: %d", sock_fd);
+
+       tester_test_passed();
+
+clean:
+       if (sock_fd >= 0)
+               close(sock_fd);
+}
+
+static void test_listen_listen(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct socket_data *test = data->test_data;
+       bt_status_t status;
+       int sock_fd1 = -1, sock_fd2 = -1;
+
+       status = data->if_sock->listen(test->sock_type,
+                                       test->service_name, test->service_uuid,
+                                       test->channel, &sock_fd1, test->flags);
+       if (status != BT_STATUS_SUCCESS) {
+               tester_warn("sock->listen() failed");
+               tester_test_failed();
+               goto clean;
+       }
+
+       status = data->if_sock->listen(test->sock_type,
+                                       test->service_name, test->service_uuid,
+                                       test->channel, &sock_fd2, test->flags);
+       if (status != test->expected_status) {
+               tester_warn("sock->listen() failed, status %d", status);
+               tester_test_failed();
+               goto clean;
+       }
+
+       tester_print("status after second listen(): %d", status);
+
+       tester_test_passed();
+
+clean:
+       if (sock_fd1 >= 0)
+               close(sock_fd1);
+
+       if (sock_fd2 >= 0)
+               close(sock_fd2);
+}
+
+static void test_generic_connect(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct socket_data *test = data->test_data;
+       bt_status_t status;
+       int sock_fd = -1;
+
+       status = data->if_sock->connect(test->bdaddr, test->sock_type,
+                                       test->service_uuid, test->channel,
+                                       &sock_fd, test->flags);
+       if (status != test->expected_status) {
+               tester_test_failed();
+               goto clean;
+       }
+
+       /* Check that file descriptor is valid */
+       if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) {
+               tester_test_failed();
+               return;
+       }
+
+       tester_test_passed();
+
+clean:
+       if (sock_fd >= 0)
+               close(sock_fd);
+}
+
+static gboolean socket_chan_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       int sock_fd = g_io_channel_unix_get_fd(io);
+       struct test_data *data = tester_get_data();
+       const struct socket_data *test = data->test_data;
+       int channel, len;
+
+       tester_print("%s", __func__);
+
+       if (cond & G_IO_HUP) {
+               tester_warn("Socket %d hang up", sock_fd);
+               goto failed;
+       }
+
+       if (cond & (G_IO_ERR | G_IO_NVAL)) {
+               tester_warn("Socket error: sock %d cond %d", sock_fd, cond);
+               goto failed;
+       }
+
+       if (test->test_channel) {
+               len = read(sock_fd, &channel, sizeof(channel));
+               if (len != sizeof(channel) || channel != test->channel)
+                       goto failed;
+
+               tester_print("read correct channel: %d", channel);
+               tester_test_passed();
+               return FALSE;
+       }
+
+failed:
+       tester_test_failed();
+       return FALSE;
+}
+
+static void test_socket_real_connect(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct socket_data *test = data->test_data;
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+       const uint8_t *client_bdaddr;
+       bt_bdaddr_t emu_bdaddr;
+       bt_status_t status;
+       int sock_fd = -1;
+
+       client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
+       if (!client_bdaddr) {
+               tester_warn("No client bdaddr");
+               tester_test_failed();
+               return;
+       }
+
+       bdaddr2android((bdaddr_t *) client_bdaddr, &emu_bdaddr);
+
+       bthost_add_l2cap_server(bthost, 0x0003, NULL, NULL);
+
+       status = data->if_sock->connect(&emu_bdaddr, test->sock_type,
+                                       test->service_uuid, test->channel,
+                                       &sock_fd, test->flags);
+       if (status != test->expected_status) {
+               tester_test_failed();
+               goto clean;
+       }
+
+       /* Check that file descriptor is valid */
+       if (status == BT_STATUS_SUCCESS && fcntl(sock_fd, F_GETFD) < 0) {
+               tester_test_failed();
+               return;
+       }
+
+       tester_print("status %d sock_fd %d", status, sock_fd);
+
+       if (status == BT_STATUS_SUCCESS) {
+               GIOChannel *io;
+
+               io = g_io_channel_unix_new(sock_fd);
+               g_io_channel_set_close_on_unref(io, TRUE);
+
+               g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               socket_chan_cb, NULL);
+
+               g_io_channel_unref(io);
+       }
+
+       return;
+
+clean:
+       if (sock_fd >= 0)
+               close(sock_fd);
+}
+
+static gboolean hidhost_connection_state(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct hidhost_generic_data *test = data->test_data;
+       struct hh_cb_data *cb_data = user_data;
+
+       data->cb_count++;
+
+       if (cb_data->state == BTHH_CONN_STATE_CONNECTED)
+               tester_setup_complete();
+
+       if (test && test->expected_hal_cb.connection_state_cb)
+               test->expected_hal_cb.connection_state_cb(&cb_data->bdaddr,
+                                                               cb_data->state);
+
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void hidhost_connection_state_cb(bt_bdaddr_t *bd_addr,
+                                               bthh_connection_state_t state)
+{
+       struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1);
+
+       cb_data->state = state;
+       cb_data->bdaddr = *bd_addr;
+
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(hidhost_connection_state, cb_data);
+}
+
+static gboolean hidhost_virual_unplug(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct hidhost_generic_data *test = data->test_data;
+       struct hh_cb_data *cb_data = user_data;
+
+       data->cb_count++;
+
+       if (test && test->expected_hal_cb.virtual_unplug_cb)
+               test->expected_hal_cb.virtual_unplug_cb(&cb_data->bdaddr,
+                                                       cb_data->status);
+
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void hidhost_virual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t status)
+{
+       struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1);
+
+       cb_data->bdaddr = *bd_addr;
+       cb_data->status = status;
+
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(hidhost_virual_unplug, cb_data);
+}
+
+static gboolean hidhost_hid_info(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct hidhost_generic_data *test = data->test_data;
+       struct hh_cb_data *cb_data = user_data;
+
+       data->cb_count++;
+
+       if (test && test->expected_hal_cb.hid_info_cb)
+               test->expected_hal_cb.hid_info_cb(&cb_data->bdaddr,
+                                                       cb_data->hid_info);
+
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void hidhost_hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid)
+{
+       struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1);
+
+       cb_data->bdaddr = *bd_addr;
+       cb_data->hid_info = hid;
+
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(hidhost_hid_info, cb_data);
+}
+
+static gboolean hidhost_protocol_mode(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct hidhost_generic_data *test = data->test_data;
+       struct hh_cb_data *cb_data = user_data;
+
+       data->cb_count++;
+
+       if (test && test->expected_hal_cb.protocol_mode_cb)
+               test->expected_hal_cb.protocol_mode_cb(&cb_data->bdaddr,
+                                               cb_data->status, cb_data->mode);
+
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void hidhost_protocol_mode_cb(bt_bdaddr_t *bd_addr,
+                                               bthh_status_t status,
+                                               bthh_protocol_mode_t mode)
+{
+       struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1);
+
+       cb_data->bdaddr = *bd_addr;
+       cb_data->status = status;
+       cb_data->mode = mode;
+
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(hidhost_protocol_mode, cb_data);
+}
+
+static gboolean hidhost_get_report(gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct hidhost_generic_data *test = data->test_data;
+       struct hh_cb_data *cb_data = user_data;
+
+       data->cb_count++;
+
+       if (test && test->expected_hal_cb.get_report_cb)
+               test->expected_hal_cb.get_report_cb(&cb_data->bdaddr,
+                       cb_data->status, cb_data->report, cb_data->size);
+
+       g_free(cb_data->report);
+       g_free(cb_data);
+
+       g_atomic_int_dec_and_test(&scheduled_cbacks_num);
+       return FALSE;
+}
+
+static void hidhost_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status,
+                                               uint8_t *report, int size)
+{
+       struct hh_cb_data *cb_data = g_new0(struct hh_cb_data, 1);
+
+       cb_data->bdaddr = *bd_addr;
+       cb_data->status = status;
+       cb_data->report = g_memdup(report, size);
+       cb_data->size = size;
+
+       g_atomic_int_inc(&scheduled_cbacks_num);
+       g_idle_add(hidhost_get_report, cb_data);
+}
+
+static bthh_callbacks_t bthh_callbacks = {
+       .size = sizeof(bthh_callbacks),
+       .connection_state_cb = hidhost_connection_state_cb,
+       .hid_info_cb = hidhost_hid_info_cb,
+       .protocol_mode_cb = hidhost_protocol_mode_cb,
+       .idle_time_cb = NULL,
+       .get_report_cb = hidhost_get_report_cb,
+       .virtual_unplug_cb = hidhost_virual_unplug_cb
+};
+
+static bool setup_hidhost(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       bt_status_t status;
+       const void *hid;
+
+       if (!setup(data))
+               return false;
+
+       status = data->if_bluetooth->init(&bt_callbacks);
+       if (status != BT_STATUS_SUCCESS) {
+               data->if_bluetooth = NULL;
+               return false;
+       }
+
+       hid = data->if_bluetooth->get_profile_interface(BT_PROFILE_HIDHOST_ID);
+       if (!hid)
+               return false;
+
+       data->if_hid = hid;
+
+       status = data->if_hid->init(&bthh_callbacks);
+       if (status != BT_STATUS_SUCCESS) {
+               data->if_hid = NULL;
+               return false;
+       }
+
+       return true;
+}
+
+static void setup_hidhost_interface(const void *test_data)
+{
+       if (setup_hidhost(test_data))
+               tester_setup_complete();
+       else
+               tester_setup_failed();
+}
+
+#define HID_GET_REPORT_PROTOCOL                0x60
+#define HID_GET_BOOT_PROTOCOL          0x61
+#define HID_SET_REPORT_PROTOCOL                0x70
+#define HID_SET_BOOT_PROTOCOL          0x71
+
+#define HID_SET_INPUT_REPORT           0x51
+#define HID_SET_OUTPUT_REPORT          0x52
+#define HID_SET_FEATURE_REPORT         0x53
+
+#define HID_SEND_DATA                  0xa2
+
+#define HID_GET_INPUT_REPORT           0x49
+#define HID_GET_OUTPUT_REPORT          0x4a
+#define HID_GET_FEATURE_REPORT         0x4b
+
+static void hid_prepare_reply_protocol_mode(const void *data, uint16_t len)
+{
+       struct test_data *t_data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+       uint8_t pdu[2] = { 0, 0 };
+       uint16_t pdu_len = 0;
+
+       pdu_len = 2;
+       pdu[0] = 0xa0;
+       pdu[1] = 0x00;
+
+       bthost_send_cid(bthost, t_data->ctrl_handle, t_data->ctrl_cid,
+                                               (void *)pdu, pdu_len);
+}
+
+static void hid_prepare_reply_report(const void *data, uint16_t len)
+{
+       struct test_data *t_data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+       uint8_t pdu[3] = { 0, 0, 0 };
+       uint16_t pdu_len = 0;
+
+       pdu_len = 3;
+       pdu[0] = 0xa2;
+       pdu[1] = 0x01;
+       pdu[2] = 0x00;
+
+       bthost_send_cid(bthost, t_data->ctrl_handle, t_data->ctrl_cid,
+                                               (void *)pdu, pdu_len);
+}
+
+static void hid_intr_cid_hook_cb(const void *data, uint16_t len,
+                                                       void *user_data)
+{
+       uint8_t header = ((uint8_t *) data)[0];
+
+       switch (header) {
+       case HID_SEND_DATA:
+               tester_test_passed();
+               break;
+       }
+}
+
+static void hid_intr_connect_cb(uint16_t handle, uint16_t cid, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       data->intr_handle = handle;
+       data->intr_cid = cid;
+
+       bthost_add_cid_hook(bthost, handle, cid, hid_intr_cid_hook_cb, NULL);
+}
+
+static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len,
+                                                       void *user_data)
+{
+       uint8_t header = ((uint8_t *) data)[0];
+
+       switch (header) {
+       case HID_GET_REPORT_PROTOCOL:
+       case HID_GET_BOOT_PROTOCOL:
+       case HID_SET_REPORT_PROTOCOL:
+       case HID_SET_BOOT_PROTOCOL:
+               hid_prepare_reply_protocol_mode(data, len);
+               break;
+       case HID_GET_INPUT_REPORT:
+       case HID_GET_OUTPUT_REPORT:
+       case HID_GET_FEATURE_REPORT:
+               hid_prepare_reply_report(data, len);
+               break;
+       /*
+        * HID device doesnot reply for this commads, so reaching pdu's
+        * to hid device means assuming test passed
+        */
+       case HID_SET_INPUT_REPORT:
+       case HID_SET_OUTPUT_REPORT:
+       case HID_SET_FEATURE_REPORT:
+       case HID_SEND_DATA:
+               tester_test_passed();
+               break;
+       }
+}
+
+static void hid_ctrl_connect_cb(uint16_t handle, uint16_t cid, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       data->ctrl_handle = handle;
+       data->ctrl_cid = cid;
+
+       bthost_add_cid_hook(bthost, handle, cid, hid_ctrl_cid_hook_cb, NULL);
+}
+
+static const uint8_t did_req_pdu[] = { 0x06, /* PDU id */
+                       0x00, 0x00, /* Transaction id */
+                       0x00, 0x0f, /* Req length */
+                       0x35, 0x03, /* Attributes length */
+                       0x19, 0x12, 0x00, 0xff, 0xff, 0x35, 0x05, 0x0a, 0x00,
+                       0x00, 0xff, 0xff, 0x00 }; /* no continuation */
+
+static const uint8_t did_rsp_pdu[] = { 0x07, /* PDU id */
+                       0x00, 0x00, /* Transaction id */
+                       0x00, 0x4f, /* Response length */
+                       0x00, 0x4c, /* Attributes length */
+                       0x35, 0x4a, 0x35, 0x48, 0x09, 0x00, 0x00, 0x0a, 0x00,
+                       0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19,
+                       0x12, 0x00, 0x09, 0x00, 0x05, 0x35, 0x03, 0x19, 0x10,
+                       0x02, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19,
+                       0x12, 0x00, 0x09, 0x01, 0x03, 0x09, 0x02, 0x00, 0x09,
+                       0x01, 0x03, 0x09, 0x02, 0x01, 0x09, 0x1d, 0x6b, 0x09,
+                       0x02, 0x02, 0x09, 0x02, 0x46, 0x09, 0x02, 0x03, 0x09,
+                       0x05, 0x0e, 0x09, 0x02, 0x04, 0x28, 0x01, 0x09, 0x02,
+                       0x05, 0x09, 0x00, 0x02,
+                       0x00 }; /* no continuation */
+
+static const uint8_t hid_rsp_pdu[] = { 0x07, /* PDU id */
+                       0x00, 0x01, /* Transaction id */
+                       0x01, 0x71, /* Response length */
+                       0x01, 0x6E, /* Attributes length */
+                       0x36, 0x01, 0x6b, 0x36, 0x01, 0x68, 0x09, 0x00, 0x00,
+                       0x0a, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35,
+                       0x03, 0x19, 0x11, 0x24, 0x09, 0x00, 0x04, 0x35, 0x0d,
+                       0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x11, 0x35,
+                       0x03, 0x19, 0x00, 0x11, 0x09, 0x00, 0x05, 0x35, 0x03,
+                       0x19, 0x10, 0x02, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09,
+                       0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09,
+                       0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x24,
+                       0x09, 0x01, 0x00, 0x09, 0x00, 0x0d, 0x35, 0x0f, 0x35,
+                       0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x13,
+                       0x35, 0x03, 0x19, 0x00, 0x11, 0x09, 0x01, 0x00, 0x25,
+                       0x1e, 0x4c, 0x6f, 0x67, 0x69, 0x74, 0x65, 0x63, 0x68,
+                       0x20, 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74,
+                       0x68, 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x20, 0x4d,
+                       0x35, 0x35, 0x35, 0x62, 0x09, 0x01, 0x01, 0x25, 0x0f,
+                       0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68,
+                       0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x09, 0x01, 0x02,
+                       0x25, 0x08, 0x4c, 0x6f, 0x67, 0x69, 0x74, 0x65, 0x63,
+                       0x68, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, 0x09, 0x02,
+                       0x01, 0x09, 0x01, 0x11, 0x09, 0x02, 0x02, 0x08, 0x80,
+                       0x09, 0x02, 0x03, 0x08, 0x21, 0x09, 0x02, 0x04, 0x28,
+                       0x01, 0x09, 0x02, 0x05, 0x28, 0x01, 0x09, 0x02, 0x06,
+                       0x35, 0x74, 0x35, 0x72, 0x08, 0x22, 0x25, 0x6e, 0x05,
+                       0x01, 0x09, 0x02, 0xa1, 0x01, 0x85, 0x02, 0x09, 0x01,
+                       0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x08, 0x15,
+                       0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02,
+                       0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x16, 0x01, 0xf8,
+                       0x26, 0xff, 0x07, 0x75, 0x0c, 0x95, 0x02, 0x81, 0x06,
+                       0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95,
+                       0x01, 0x81, 0x06, 0x05, 0x0c, 0x0a, 0x38, 0x02, 0x81,
+                       0x06, 0x05, 0x09, 0x19, 0x09, 0x29, 0x10, 0x15, 0x00,
+                       0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0xc0,
+                       0xc0, 0x06, 0x00, 0xff, 0x09, 0x01, 0xa1, 0x01, 0x85,
+                       0x10, 0x75, 0x08, 0x95, 0x06, 0x15, 0x00, 0x26, 0xff,
+                       0x00, 0x09, 0x01, 0x81, 0x00, 0x09, 0x01, 0x91, 0x00,
+                       0xc0, 0x09, 0x02, 0x07, 0x35, 0x08, 0x35, 0x06, 0x09,
+                       0x04, 0x09, 0x09, 0x01, 0x00, 0x09, 0x02, 0x08, 0x28,
+                       0x00, 0x09, 0x02, 0x09, 0x28, 0x01, 0x09, 0x02, 0x0a,
+                       0x28, 0x01, 0x09, 0x02, 0x0b, 0x09, 0x01, 0x00, 0x09,
+                       0x02, 0x0c, 0x09, 0x0c, 0x80, 0x09, 0x02, 0x0d, 0x28,
+                       0x00, 0x09, 0x02, 0x0e, 0x28, 0x01,
+                       0x00 }; /* no continuation */
+
+static void hid_sdp_cid_hook_cb(const void *data, uint16_t len, void *user_data)
+{
+       struct test_data *t_data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(t_data->hciemu);
+
+       if (!memcmp(did_req_pdu, data, len)) {
+               bthost_send_cid(bthost, t_data->sdp_handle, t_data->sdp_cid,
+                                       did_rsp_pdu, sizeof(did_rsp_pdu));
+               return;
+       }
+
+       bthost_send_cid(bthost, t_data->sdp_handle, t_data->sdp_cid,
+                                       hid_rsp_pdu, sizeof(hid_rsp_pdu));
+}
+
+static void hid_sdp_search_cb(uint16_t handle, uint16_t cid, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       data->sdp_handle = handle;
+       data->sdp_cid = cid;
+
+       bthost_add_cid_hook(bthost, handle, cid, hid_sdp_cid_hook_cb, NULL);
+}
+
+static void emu_powered_complete(uint16_t opcode, uint8_t status,
+                                       const void *param, uint8_t len,
+                                       void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t bdaddr;
+       bt_status_t bt_status;
+
+       switch (opcode) {
+       case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+       case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+               break;
+       default:
+               return;
+       }
+
+       if (status) {
+               tester_setup_failed();
+               return;
+       }
+
+       data->cb_count = 0;
+       bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+       bt_status = data->if_hid->connect(&bdaddr);
+       if (bt_status != BT_STATUS_SUCCESS)
+               tester_setup_failed();
+}
+
+static void setup_hidhost_connect(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost;
+
+       if (!setup_hidhost(test_data)) {
+               tester_setup_failed();
+               return;
+       }
+
+       bthost = hciemu_client_get_host(data->hciemu);
+
+       /* Emulate SDP (PSM = 1) */
+       bthost_add_l2cap_server(bthost, 1, hid_sdp_search_cb, NULL);
+       /* Emulate Control Channel (PSM = 17) */
+       bthost_add_l2cap_server(bthost, 17, hid_ctrl_connect_cb, NULL);
+       /* Emulate Interrupt Channel (PSM = 19) */
+       bthost_add_l2cap_server(bthost, 19, hid_intr_connect_cb, NULL);
+
+       bthost_set_cmd_complete_cb(bthost, emu_powered_complete, data);
+       bthost_write_scan_enable(bthost, 0x03);
+}
+
+static void hid_discon_cb(bt_bdaddr_t *bd_addr, bthh_connection_state_t state)
+{
+       if (state == BTHH_CONN_STATE_DISCONNECTED)
+               tester_test_passed();
+}
+
+static const struct hidhost_generic_data hidhost_test_disconnect = {
+       .expected_hal_cb.connection_state_cb = hid_discon_cb,
+};
+
+static void test_hidhost_disconnect(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t bdaddr;
+       bt_status_t bt_status;
+
+       data->cb_count = 0;
+       bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+       bt_status = data->if_hid->disconnect(&bdaddr);
+       if (bt_status != BT_STATUS_SUCCESS)
+               tester_test_failed();
+}
+
+static void test_hidhost_virtual_unplug(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t bdaddr;
+       bt_status_t bt_status;
+
+       data->cb_count = 0;
+       bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+       bt_status = data->if_hid->virtual_unplug(&bdaddr);
+       if (bt_status != BT_STATUS_SUCCESS)
+               tester_test_failed();
+}
+
+static void hid_protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t status,
+                                               bthh_protocol_mode_t mode)
+{
+       struct test_data *data = tester_get_data();
+       const struct hidhost_generic_data *test = data->test_data;
+
+       if (data->cb_count == test->expected_cb_count &&
+                                       status == test->expected_status &&
+                                       mode == test->expected_protocol_mode)
+               tester_test_passed();
+       else
+               tester_test_failed();
+}
+
+static const struct hidhost_generic_data hidhost_test_get_protocol = {
+       .expected_hal_cb.protocol_mode_cb = hid_protocol_mode_cb,
+       .expected_cb_count = 1,
+       .expected_protocol_mode = BTHH_BOOT_MODE,
+       .expected_status = BTHH_OK,
+};
+
+static void test_hidhost_get_protocol(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t bdaddr;
+       bt_status_t bt_status;
+
+       data->cb_count = 0;
+       bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+       bt_status = data->if_hid->get_protocol(&bdaddr, BTHH_REPORT_MODE);
+       if (bt_status != BT_STATUS_SUCCESS)
+               tester_test_failed();
+}
+
+static void test_hidhost_set_protocol(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t bdaddr;
+       bt_status_t bt_status;
+
+       data->cb_count = 0;
+       bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+       bt_status = data->if_hid->set_protocol(&bdaddr, BTHH_REPORT_MODE);
+       if (bt_status != BT_STATUS_SUCCESS)
+               tester_test_failed();
+}
+
+static void test_hidhost_set_report(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t bdaddr;
+       bt_status_t bt_status;
+       char *buf = "010101";
+
+       data->cb_count = 0;
+       bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+       bt_status = data->if_hid->set_report(&bdaddr, BTHH_INPUT_REPORT, buf);
+       if (bt_status != BT_STATUS_SUCCESS)
+               tester_test_failed();
+}
+
+static void test_hidhost_send_data(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t bdaddr;
+       bt_status_t bt_status;
+       char *buf = "fe0201";
+
+       data->cb_count = 0;
+       bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+       bt_status = data->if_hid->send_data(&bdaddr, buf);
+       if (bt_status != BT_STATUS_SUCCESS)
+               tester_test_failed();
+}
+
+static void hid_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status,
+                                               uint8_t *report, int size)
+{
+       struct test_data *data = tester_get_data();
+       const struct hidhost_generic_data *test = data->test_data;
+
+       if (data->cb_count == test->expected_cb_count &&
+                                       status == test->expected_status &&
+                                       size == test->expected_report_size)
+               tester_test_passed();
+       else
+               tester_test_failed();
+}
+
+static const struct hidhost_generic_data hidhost_test_get_report = {
+       .expected_hal_cb.get_report_cb = hid_get_report_cb,
+       .expected_cb_count = 1,
+       .expected_status = BTHH_OK,
+       .expected_report_size = 2,
+};
+
+static void test_hidhost_get_report(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu);
+       bt_bdaddr_t bdaddr;
+       bt_status_t bt_status;
+
+       data->cb_count = 0;
+       bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr);
+       bt_status = data->if_hid->get_report(&bdaddr, BTHH_INPUT_REPORT, 1, 20);
+       if (bt_status != BT_STATUS_SUCCESS)
+               tester_test_failed();
+}
+
+#define test_bredr(name, data, test_setup, test, test_teardown) \
+       do { \
+               struct test_data *user; \
+               user = g_malloc0(sizeof(struct test_data)); \
+               if (!user) \
+                       break; \
+               user->hciemu_type = HCIEMU_TYPE_BREDR; \
+               user->test_data = data; \
+               tester_add_full(name, data, test_pre_setup, test_setup, \
+                               test, test_teardown, test_post_teardown, \
+                                                       1, user, g_free); \
+       } while (0)
+
+#define test_bredrle(name, data, test_setup, test, test_teardown) \
+       do { \
+               struct test_data *user; \
+               user = g_malloc0(sizeof(struct test_data)); \
+               if (!user) \
+                       break; \
+               user->hciemu_type = HCIEMU_TYPE_BREDRLE; \
+               user->test_data = data; \
+               tester_add_full(name, data, test_pre_setup, test_setup, \
+                               test, test_teardown, test_post_teardown, \
+                                                       3, user, g_free); \
+       } while (0)
+
+int main(int argc, char *argv[])
+{
+       snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0]));
+
+       tester_init(&argc, &argv);
+
+       test_bredrle("Bluetooth Init", NULL, setup_base, test_dummy, teardown);
+
+       test_bredrle("Bluetooth Enable - Success",
+                                               &bluetooth_enable_success_test,
+                                               setup_base, test_enable,
+                                               teardown);
+
+       test_bredrle("Bluetooth Enable - Success 2",
+                                               &bluetooth_enable_success2_test,
+                                               setup_enabled_adapter,
+                                               test_enable_done, teardown);
+
+       test_bredrle("Bluetooth Disable - Success",
+                                               &bluetooth_disable_success_test,
+                                               setup_enabled_adapter,
+                                               test_disable, teardown);
+
+       test_bredrle("Bluetooth Set BDNAME - Success",
+                                       &bluetooth_setprop_bdname_success_test,
+                                       setup_enabled_adapter,
+                                       test_setprop_bdname_success, teardown);
+
+       test_bredrle("Bluetooth Set SCAN_MODE - Success",
+                               &bluetooth_setprop_scanmode_success_test,
+                               setup_enabled_adapter,
+                               test_setprop_scanmode_succes, teardown);
+
+       test_bredrle("Bluetooth Set DISCOVERY_TIMEOUT - Success",
+                               &bluetooth_setprop_disctimeout_success_test,
+                               setup_enabled_adapter,
+                               test_setprop_disctimeout_succes, teardown);
+
+       test_bredrle("Bluetooth Get BDADDR - Success",
+                                       &bluetooth_getprop_bdaddr_success_test,
+                                       setup_enabled_adapter,
+                                       test_getprop_bdaddr_success, teardown);
+
+       test_bredrle("Bluetooth Get BDNAME - Success",
+                                       &bluetooth_getprop_bdname_success_test,
+                                       setup_enabled_adapter,
+                                       test_getprop_bdname_success, teardown);
+
+       test_bredrle("Bluetooth Set UUID - Invalid",
+                                       &bluetooth_setprop_uuid_invalid_test,
+                                       setup_enabled_adapter,
+                                       test_setprop_uuid_invalid, teardown);
+
+       test_bredrle("Bluetooth Set CLASS_OF_DEVICE - Invalid",
+                                       &bluetooth_setprop_cod_invalid_test,
+                                       setup_enabled_adapter,
+                                       test_setprop_cod_invalid, teardown);
+
+       test_bredrle("Bluetooth Set TYPE_OF_DEVICE - Invalid",
+                                       &bluetooth_setprop_tod_invalid_test,
+                                       setup_enabled_adapter,
+                                       test_setprop_tod_invalid, teardown);
+
+       test_bredrle("Bluetooth Set REMOTE_RSSI - Invalid",
+                               &bluetooth_setprop_remote_rssi_invalid_test,
+                               setup_enabled_adapter,
+                               test_setprop_rssi_invalid, teardown);
+
+       test_bredrle("Bluetooth Set SERVICE_RECORD - Invalid",
+                               &bluetooth_setprop_service_record_invalid_test,
+                               setup_enabled_adapter,
+                               test_setprop_service_record_invalid, teardown);
+
+       test_bredrle("Bluetooth Set BDADDR - Invalid",
+                                       &bluetooth_setprop_bdaddr_invalid_test,
+                                       setup_enabled_adapter,
+                                       test_setprop_bdaddr_invalid, teardown);
+
+       test_bredrle("Bluetooth Set SCAN_MODE CONNECTABLE - Success",
+                       &bluetooth_setprop_scanmode_connectable_success_test,
+                       setup_enabled_adapter,
+                       test_setprop_scanmode_connectable_success, teardown);
+
+       test_bredrle("Bluetooth Set BONDED_DEVICES - Invalid",
+                               &bluetooth_setprop_bonded_devices_invalid_test,
+                               setup_enabled_adapter,
+                               test_setprop_bonded_devices_invalid, teardown);
+
+       test_bredrle("Bluetooth Get CLASS_OF_DEVICE - Success",
+                                       &bluetooth_getprop_cod_success_test,
+                                       setup_enabled_adapter,
+                                       test_getprop_cod_success, teardown);
+
+       test_bredrle("Bluetooth Get TYPE_OF_DEVICE - Success",
+                                       &bluetooth_getprop_tod_success_test,
+                                       setup_enabled_adapter,
+                                       test_getprop_tod_success, teardown);
+
+       test_bredrle("Bluetooth Get SCAN_MODE - Success",
+                               &bluetooth_getprop_scanmode_success_test,
+                               setup_enabled_adapter,
+                               test_getprop_scanmode_success, teardown);
+
+       test_bredrle("Bluetooth Get DISCOVERY_TIMEOUT - Success",
+                               &bluetooth_getprop_disctimeout_success_test,
+                               setup_enabled_adapter,
+                               test_getprop_disctimeout_success, teardown);
+
+       test_bredrle("Bluetooth Get UUIDS - Success",
+                                       &bluetooth_getprop_uuids_success_test,
+                                       setup_enabled_adapter,
+                                       test_getprop_uuids_success, teardown);
+
+       test_bredrle("Bluetooth Get BONDED_DEVICES - Success",
+                               &bluetooth_getprop_bondeddev_success_test,
+                               setup_enabled_adapter,
+                               test_getprop_bondeddev_success, teardown);
+
+       test_bredrle("Bluetooth Set SCAN_MODE NONE - Success 2",
+                               &bluetooth_setprop_scanmode_none_success2_test,
+                               setup_enabled_adapter,
+                               test_setprop_scanmode_none_done, teardown);
+
+       test_bredrle("Bluetooth BR/EDR Discovery Start - Success",
+                               &bluetooth_discovery_start_success_test,
+                               setup_enabled_adapter,
+                               test_discovery_start_success, teardown);
+
+       test_bredrle("Bluetooth BR/EDR Discovery Start - Success 2",
+                               &bluetooth_discovery_start_success2_test,
+                               setup_enabled_adapter,
+                               test_discovery_start_done, teardown);
+
+       test_bredrle("Bluetooth BR/EDR Discovery Stop - Success",
+                               &bluetooth_discovery_stop_success_test,
+                               setup_enabled_adapter,
+                               test_discovery_stop_success, teardown);
+
+       test_bredrle("Bluetooth BR/EDR Discovery Stop - Success 2",
+                               &bluetooth_discovery_stop_success2_test,
+                               setup_enabled_adapter,
+                               test_discovery_stop_done, teardown);
+
+       test_bredr("Bluetooth BR/EDR Discovery Device Found",
+                               &bluetooth_discovery_device_found_test,
+                               setup_enabled_adapter,
+                               test_discovery_device_found, teardown);
+
+       test_bredr("Bluetooth Device Get Props - Success",
+                                       &bt_dev_getprops_success_test,
+                                       setup_enabled_adapter,
+                                       test_dev_getprops_success, teardown);
+
+       test_bredr("Bluetooth Device Get BDNAME - Success",
+                               &bt_dev_getprop_bdname_success_test,
+                               setup_enabled_adapter,
+                               test_dev_getprop_bdname_success, teardown);
+
+       test_bredr("Bluetooth Device Get UUIDS - Success",
+                               &bt_dev_getprop_uuids_success_test,
+                               setup_enabled_adapter,
+                               test_dev_getprop_uuids_success, teardown);
+
+       test_bredr("Bluetooth Device Get COD - Success",
+                                       &bt_dev_getprop_cod_success_test,
+                                       setup_enabled_adapter,
+                                       test_dev_getprop_cod_success, teardown);
+
+       test_bredr("Bluetooth Device Get TOD - Success",
+                                       &bt_dev_getprop_tod_success_test,
+                                       setup_enabled_adapter,
+                                       test_dev_getprop_tod_success, teardown);
+
+       test_bredr("Bluetooth Device Get RSSI - Success",
+                               &bt_dev_getprop_rssi_success_test,
+                               setup_enabled_adapter,
+                               test_dev_getprop_rssi_success, teardown);
+
+       test_bredr("Bluetooth Device Get TIMESTAMP - Success",
+                               &bt_dev_getprop_timpestamp_success_test,
+                               setup_enabled_adapter,
+                               test_dev_getprop_timestamp_success, teardown);
+
+       test_bredr("Bluetooth Device Get BDADDR - Fail",
+                               &bt_dev_getprop_bdaddr_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_getprop_bdaddr_fail, teardown);
+
+       test_bredr("Bluetooth Device Get SERVICE_RECORD - Fail",
+                               &bt_dev_getprop_servrec_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_getprop_servrec_fail, teardown);
+
+       test_bredr("Bluetooth Device Get SCAN_MODE - Fail",
+                               &bt_dev_getprop_scanmode_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_getprop_scanmode_fail, teardown);
+
+       test_bredr("Bluetooth Device Get BONDED_DEVICES - Fail",
+                               &bt_dev_getprop_bondeddev_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_getprop_bondeddev_fail, teardown);
+
+       test_bredr("Bluetooth Device Get DISCOVERY_TIMEOUT - Fail",
+                               &bt_dev_getprop_disctimeout_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_getprop_disctimeout_fail, teardown);
+
+       test_bredr("Bluetooth Device Get VERSION_INFO - Fail",
+                               &bt_dev_getprop_verinfo_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_getprop_verinfo_fail, teardown);
+
+       test_bredr("Bluetooth Device Get FRIENDLY_NAME - Fail",
+                                       &bt_dev_getprop_fname_fail_test,
+                                       setup_enabled_adapter,
+                                       test_dev_getprop_fname_fail, teardown);
+
+       test_bredr("Bluetooth Device Set FRIENDLY_NAME - Success",
+                               &bt_dev_setprop_fname_success_test,
+                               setup_enabled_adapter,
+                               test_dev_setprop_fname_success, teardown);
+
+       test_bredr("Bluetooth Device Set BDNAME - Fail",
+                                       &bt_dev_setprop_bdname_fail_test,
+                                       setup_enabled_adapter,
+                                       test_dev_setprop_bdname_fail, teardown);
+
+       test_bredr("Bluetooth Device Set UUIDS - Fail",
+                                       &bt_dev_setprop_uuids_fail_test,
+                                       setup_enabled_adapter,
+                                       test_dev_setprop_uuids_fail, teardown);
+
+       test_bredr("Bluetooth Device Set COD - Fail",
+                                       &bt_dev_setprop_cod_fail_test,
+                                       setup_enabled_adapter,
+                                       test_dev_setprop_cod_fail, teardown);
+
+       test_bredr("Bluetooth Device Set TOD - Fail",
+                                       &bt_dev_setprop_tod_fail_test,
+                                       setup_enabled_adapter,
+                                       test_dev_setprop_tod_fail, teardown);
+
+       test_bredr("Bluetooth Device Set RSSI - Fail",
+                               &bt_dev_setprop_rssi_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_setprop_rssi_fail, teardown);
+
+       test_bredr("Bluetooth Device Set TIMESTAMP - Fail",
+                               &bt_dev_setprop_timpestamp_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_setprop_timestamp_fail, teardown);
+
+       test_bredr("Bluetooth Device Set BDADDR - Fail",
+                               &bt_dev_setprop_bdaddr_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_setprop_bdaddr_fail, teardown);
+
+       test_bredr("Bluetooth Device Set SERVICE_RECORD - Fail",
+                               &bt_dev_setprop_servrec_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_setprop_servrec_fail, teardown);
+
+       test_bredr("Bluetooth Device Set SCAN_MODE - Fail",
+                               &bt_dev_setprop_scanmode_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_setprop_scanmode_fail, teardown);
+
+       test_bredr("Bluetooth Device Set BONDED_DEVICES - Fail",
+                               &bt_dev_setprop_bondeddev_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_setprop_bondeddev_fail, teardown);
+
+       test_bredr("Bluetooth Device Set DISCOVERY_TIMEOUT - Fail",
+                               &bt_dev_setprop_disctimeout_fail_test,
+                               setup_enabled_adapter,
+                               test_dev_setprop_disctimeout_fail, teardown);
+
+       test_bredr("Bluetooth Create Bond PIN - Success",
+                                       &bt_bond_create_pin_success_test,
+                                       setup_enabled_adapter,
+                                       test_bond_create_pin_success, teardown);
+
+       test_bredr("Bluetooth Create Bond PIN - Bad PIN",
+                                       &bt_bond_create_pin_fail_test,
+                                       setup_enabled_adapter,
+                                       test_bond_create_pin_fail, teardown);
+
+       test_bredr("Bluetooth Create Bond SSP - Success",
+                                       &bt_bond_create_ssp_success_test,
+                                       setup_enabled_adapter,
+                                       test_bond_create_ssp_success, teardown);
+
+       test_bredr("Bluetooth Create Bond SSP - Negative reply",
+                                       &bt_bond_create_ssp_fail_test,
+                                       setup_enabled_adapter,
+                                       test_bond_create_ssp_fail, teardown);
+
+       test_bredrle("Bluetooth Create Bond - No Discovery",
+                               &bt_bond_create_no_disc_success_test,
+                               setup_enabled_adapter,
+                               test_bond_create_no_disc_success, teardown);
+
+       test_bredrle("Bluetooth Create Bond - Bad Address",
+                               &bt_bond_create_bad_addr_success_test,
+                               setup_enabled_adapter,
+                               test_bond_create_bad_addr_success, teardown);
+
+       test_bredr("Bluetooth Cancel Bonding - Success",
+                                       &bt_bond_cancel_success_test,
+                                       setup_enabled_adapter,
+                                       test_bond_cancel_success, teardown);
+
+       test_bredr("Bluetooth Remove Bond - Success",
+                                       &bt_bond_remove_success_test,
+                                       setup_enabled_adapter,
+                                       test_bond_remove_success, teardown);
+
+       test_bredrle("Socket Init", NULL, setup_socket_interface,
+                                               test_dummy, teardown);
+
+       test_bredrle("Socket Listen - Invalid: sock_type 0",
+                       &btsock_inv_param_socktype, setup_socket_interface,
+                       test_generic_listen, teardown);
+
+       test_bredrle("Socket Listen - Invalid: sock_type L2CAP",
+                       &btsock_inv_param_socktype_l2cap,
+                       setup_socket_interface, test_generic_listen, teardown);
+
+       test_bredrle("Socket Listen - Invalid: chan, uuid",
+                       &btsock_inv_params_chan_uuid,
+                       setup_socket_interface, test_generic_listen, teardown);
+
+       test_bredrle("Socket Listen - Check returned fd valid",
+                       &btsock_success,
+                       setup_socket_interface, test_generic_listen, teardown);
+
+       test_bredrle("Socket Listen - Check returned channel",
+                       &btsock_success_check_chan,
+                       setup_socket_interface, test_generic_listen, teardown);
+
+       test_bredrle("Socket Listen - Close and Listen again",
+                       &btsock_success_check_chan,
+                       setup_socket_interface, test_listen_close, teardown);
+
+       test_bredrle("Socket Listen - Invalid: double Listen",
+                       &btsock_inv_listen_listen,
+                       setup_socket_interface, test_listen_listen, teardown);
+
+       test_bredrle("Socket Connect - Check returned fd valid",
+                       &btsock_success, setup_socket_interface,
+                       test_generic_connect, teardown);
+
+       test_bredrle("Socket Connect - Invalid: sock_type 0",
+                       &btsock_inv_param_socktype, setup_socket_interface,
+                       test_generic_connect, teardown);
+
+       test_bredrle("Socket Connect - Invalid: sock_type L2CAP",
+                       &btsock_inv_param_socktype_l2cap,
+                       setup_socket_interface, test_generic_connect, teardown);
+
+       test_bredrle("Socket Connect - Invalid: chan, uuid",
+                       &btsock_inv_params_chan_uuid,
+                       setup_socket_interface, test_generic_connect, teardown);
+
+       test_bredrle("Socket Connect - Invalid: bdaddr",
+                       &btsock_inv_param_bdaddr,
+                       setup_socket_interface, test_generic_connect, teardown);
+
+       test_bredrle("Socket Connect - Check returned chan",
+                       &btsock_success_check_chan,
+                       setup_socket_interface_enabled,
+                       test_socket_real_connect, teardown);
+
+       test_bredrle("HIDHost Init", NULL, setup_hidhost_interface,
+                                               test_dummy, teardown);
+
+       test_bredrle("HIDHost Connect Success",
+                               NULL, setup_hidhost_connect,
+                               test_dummy, teardown);
+
+       test_bredrle("HIDHost Disconnect Success",
+                               &hidhost_test_disconnect, setup_hidhost_connect,
+                               test_hidhost_disconnect, teardown);
+
+       test_bredrle("HIDHost VirtualUnplug Success",
+                               &hidhost_test_disconnect, setup_hidhost_connect,
+                               test_hidhost_virtual_unplug, teardown);
+
+       test_bredrle("HIDHost GetProtocol Success",
+                       &hidhost_test_get_protocol, setup_hidhost_connect,
+                               test_hidhost_get_protocol, teardown);
+
+       test_bredrle("HIDHost SetProtocol Success",
+                       &hidhost_test_get_protocol, setup_hidhost_connect,
+                               test_hidhost_set_protocol, teardown);
+
+       test_bredrle("HIDHost GetReport Success",
+                       &hidhost_test_get_report, setup_hidhost_connect,
+                               test_hidhost_get_report, teardown);
+
+       test_bredrle("HIDHost SetReport Success",
+                               NULL, setup_hidhost_connect,
+                               test_hidhost_set_report, teardown);
+
+       test_bredrle("HIDHost SendData Success",
+                               NULL, setup_hidhost_connect,
+                               test_hidhost_send_data, teardown);
+       return tester_run();
+}
diff --git a/android/audio-ipc-api.txt b/android/audio-ipc-api.txt
new file mode 100644 (file)
index 0000000..f4a497d
--- /dev/null
@@ -0,0 +1,87 @@
+Bluetooth Audio Plugin
+======================
+
+The audio plugin happen to be in a different socket but all the rules for
+HAL socket apply here as well, the abstract socket name is
+"\0bluez_audio_socket" (tentative):
+
+       .---Audio---.                             .--Android--.
+       |  Plugin   |                             |   Daemon  |
+       |           |          Command            |           |
+       |           | --------------------------> |           |
+       |           |                             |           |
+       |           | <-------------------------- |           |
+       |           |          Response           |           |
+       |           |                             |           |
+       |           |                             |           |
+       |           |                             |           |
+       '-----------'                             '-----------'
+
+
+       Audio HAL                               Daemon
+       ----------------------------------------------------
+
+       call dev->open()                    --> command 0x01
+       return dev->open()                  <-- response 0x01
+
+       call dev->open_output_stream()      --> command 0x03
+       return dev->open_output_stream()    <-- response 0x03
+
+       call stream->write()                --> command 0x05
+       return stream->write()              <-- response 0x05
+
+       call stream->common.standby()       --> command 0x06
+       return stream->common.standby()     <-- response 0x06
+
+       call dev->close_output_stream()     --> command 0x04
+       return dev->close_output_stream()   <-- response 0x04
+
+       call dev->close()                   --> command 0x02
+       return dev->close()                 <-- response 0x02
+
+Audio Service (ID 0)
+====================
+
+       Opcode 0x00 - Error response
+
+               Response parameters: Status (1 octet)
+
+       Opcode 0x01 - Open Audio Endpoint commmand
+
+               Command parameters: Service UUID (16 octets)
+                                   Codec ID (1 octet)
+                                   Number of codec presets (1 octet)
+                                   Codec capabilities length (1 octet)
+                                   Codec capabilities (variable)
+                                   Codec preset # length (1 octet)
+                                   Codec preset # configuration (variable)
+                                   ...
+               Response parameters: Endpoint ID (1 octet)
+
+       Opcode 0x02 - Close Audio Endpoint command
+
+               Command parameters: Endpoint ID (1 octet)
+               Response parameters: <none>
+
+       Opcode 0x03 - Open Stream command
+
+               Command parameters: Endpoint ID (1 octet)
+               Response parameters: Outgoing MTU (2 octets)
+                                    Codec configuration length (1 octet)
+                                    Codec configuration (1 octet)
+                                    File descriptor (inline)
+
+       Opcode 0x04 - Close Stream command
+
+               Command parameters: Endpoint ID (1 octet)
+               Response parameters: <none>
+
+       Opcode 0x05 - Resume Stream command
+
+               Command parameters: Endpoint ID (1 octet)
+               Response parameters: <none>
+
+       Opcode 0x06 - Suspend Stream command
+
+               Command parameters: Endpoint ID (1 octet)
+               Response parameters: <none>
diff --git a/android/audio-msg.h b/android/audio-msg.h
new file mode 100644 (file)
index 0000000..5981355
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define BLUEZ_AUDIO_MTU 1024
+
+static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket";
+
+#define AUDIO_SERVICE_ID               0
+#define AUDIO_SERVICE_ID_MAX           AUDIO_SERVICE_ID
+
+#define AUDIO_STATUS_SUCCESS           IPC_STATUS_SUCCESS
+#define AUDIO_STATUS_FAILED            0x01
+
+#define AUDIO_OP_STATUS                        IPC_OP_STATUS
+
+#define AUDIO_OP_OPEN                  0x01
+struct audio_preset {
+       uint8_t len;
+       uint8_t data[0];
+} __attribute__((packed));
+
+struct audio_cmd_open {
+       uint8_t uuid[16];
+       uint8_t codec;
+       uint8_t presets;
+       struct audio_preset preset[0];
+} __attribute__((packed));
+
+struct audio_rsp_open {
+       uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_CLOSE                 0x02
+struct audio_cmd_close {
+       uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_OPEN_STREAM           0x03
+struct audio_cmd_open_stream {
+       uint8_t id;
+} __attribute__((packed));
+
+struct audio_rsp_open_stream {
+       uint16_t mtu;
+       struct audio_preset preset[0];
+} __attribute__((packed));
+
+#define AUDIO_OP_CLOSE_STREAM          0x04
+struct audio_cmd_close_stream {
+       uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_RESUME_STREAM         0x05
+struct audio_cmd_resume_stream {
+       uint8_t id;
+} __attribute__((packed));
+
+#define AUDIO_OP_SUSPEND_STREAM                0x06
+struct audio_cmd_suspend_stream {
+       uint8_t id;
+} __attribute__((packed));
diff --git a/android/audio_utils/resampler.c b/android/audio_utils/resampler.c
new file mode 100644 (file)
index 0000000..ce30375
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+** Copyright 2011, The Android Open-Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <system/audio.h>
+#include <audio_utils/resampler.h>
+#include <speex/speex_resampler.h>
+
+#include "hal-log.h"
+
+struct resampler {
+    struct resampler_itfe itfe;
+    SpeexResamplerState *speex_resampler;       // handle on speex resampler
+    struct resampler_buffer_provider *provider; // buffer provider installed by client
+    uint32_t in_sample_rate;                    // input sampling rate in Hz
+    uint32_t out_sample_rate;                   // output sampling rate in Hz
+    uint32_t channel_count;                     // number of channels (interleaved)
+    int16_t *in_buf;                            // input buffer
+    size_t in_buf_size;                         // input buffer size
+    size_t frames_in;                           // number of frames in input buffer
+    size_t frames_rq;                           // cached number of output frames
+    size_t frames_needed;                       // minimum number of input frames to produce
+                                                // frames_rq output frames
+    int32_t speex_delay_ns;                     // delay introduced by speex resampler in ns
+};
+
+
+//------------------------------------------------------------------------------
+// speex based resampler
+//------------------------------------------------------------------------------
+
+static void resampler_reset(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    rsmp->frames_in = 0;
+    rsmp->frames_rq = 0;
+
+    if (rsmp != NULL && rsmp->speex_resampler != NULL) {
+        speex_resampler_reset_mem(rsmp->speex_resampler);
+    }
+}
+
+static int32_t resampler_delay_ns(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    int32_t delay = (int32_t)((1000000000 * (int64_t)rsmp->frames_in) / rsmp->in_sample_rate);
+    delay += rsmp->speex_delay_ns;
+
+    return delay;
+}
+
+// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount
+// with the actual number of frames produced.
+static int resampler_resample_from_provider(struct resampler_itfe *resampler,
+                       int16_t *out,
+                       size_t *outFrameCount)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+    size_t framesRq;
+    size_t framesWr;
+    size_t inFrames;
+
+    if (rsmp == NULL || out == NULL || outFrameCount == NULL) {
+        return -EINVAL;
+    }
+    if (rsmp->provider == NULL) {
+        *outFrameCount = 0;
+        return -ENOSYS;
+    }
+
+    framesRq = *outFrameCount;
+    // update and cache the number of frames needed at the input sampling rate to produce
+    // the number of frames requested at the output sampling rate
+    if (framesRq != rsmp->frames_rq) {
+        rsmp->frames_needed = (framesRq * rsmp->in_sample_rate) / rsmp->out_sample_rate + 1;
+        rsmp->frames_rq = framesRq;
+    }
+
+    framesWr = 0;
+    inFrames = 0;
+    while (framesWr < framesRq) {
+        size_t outFrames;
+        if (rsmp->frames_in < rsmp->frames_needed) {
+            struct resampler_buffer buf;
+            // make sure that the number of frames present in rsmp->in_buf (rsmp->frames_in) is at
+            // least the number of frames needed to produce the number of frames requested at
+            // the output sampling rate
+            if (rsmp->in_buf_size < rsmp->frames_needed) {
+                rsmp->in_buf_size = rsmp->frames_needed;
+                rsmp->in_buf = (int16_t *)realloc(rsmp->in_buf,
+                                        rsmp->in_buf_size * rsmp->channel_count * sizeof(int16_t));
+            }
+            buf.frame_count = rsmp->frames_needed - rsmp->frames_in;
+            rsmp->provider->get_next_buffer(rsmp->provider, &buf);
+            if (buf.raw == NULL) {
+                break;
+            }
+            memcpy(rsmp->in_buf + rsmp->frames_in * rsmp->channel_count,
+                    buf.raw,
+                    buf.frame_count * rsmp->channel_count * sizeof(int16_t));
+            rsmp->frames_in += buf.frame_count;
+            rsmp->provider->release_buffer(rsmp->provider, &buf);
+        }
+
+        outFrames = framesRq - framesWr;
+        inFrames = rsmp->frames_in;
+        if (rsmp->channel_count == 1) {
+            speex_resampler_process_int(rsmp->speex_resampler,
+                                        0,
+                                        rsmp->in_buf,
+                                        (void *) &inFrames,
+                                        out + framesWr,
+                                        (void *) &outFrames);
+        } else {
+            speex_resampler_process_interleaved_int(rsmp->speex_resampler,
+                                        rsmp->in_buf,
+                                        (void *) &inFrames,
+                                        out + framesWr * rsmp->channel_count,
+                                        (void *) &outFrames);
+        }
+        framesWr += outFrames;
+        rsmp->frames_in -= inFrames;
+
+        if ((framesWr != framesRq) && (rsmp->frames_in != 0))
+            warn("ReSampler::resample() remaining %zd frames in and %zd out",
+                rsmp->frames_in, (framesRq - framesWr));
+    }
+    if (rsmp->frames_in) {
+        memmove(rsmp->in_buf,
+                rsmp->in_buf + inFrames * rsmp->channel_count,
+                rsmp->frames_in * rsmp->channel_count * sizeof(int16_t));
+    }
+    *outFrameCount = framesWr;
+
+    return 0;
+}
+
+static int resampler_resample_from_input(struct resampler_itfe *resampler,
+                                  int16_t *in,
+                                  size_t *inFrameCount,
+                                  int16_t *out,
+                                  size_t *outFrameCount)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    if (rsmp == NULL || in == NULL || inFrameCount == NULL ||
+            out == NULL || outFrameCount == NULL) {
+        return -EINVAL;
+    }
+    if (rsmp->provider != NULL) {
+        *outFrameCount = 0;
+        return -ENOSYS;
+    }
+
+    if (rsmp->channel_count == 1) {
+        speex_resampler_process_int(rsmp->speex_resampler,
+                                    0,
+                                    in,
+                                    (void *) inFrameCount,
+                                    out,
+                                    (void *) outFrameCount);
+    } else {
+        speex_resampler_process_interleaved_int(rsmp->speex_resampler,
+                                                in,
+                                                (void *) inFrameCount,
+                                                out,
+                                                (void *) outFrameCount);
+    }
+
+    DBG("resampler_resample_from_input() DONE in %zd out %zd", *inFrameCount, *outFrameCount);
+
+    return 0;
+}
+
+int create_resampler(uint32_t inSampleRate,
+                    uint32_t outSampleRate,
+                    uint32_t channelCount,
+                    uint32_t quality,
+                    struct resampler_buffer_provider* provider,
+                    struct resampler_itfe **resampler)
+{
+    int error;
+    struct resampler *rsmp;
+    int frames;
+
+    DBG("create_resampler() In SR %d Out SR %d channels %d",
+         inSampleRate, outSampleRate, channelCount);
+
+    if (resampler == NULL) {
+        return -EINVAL;
+    }
+
+    *resampler = NULL;
+
+    if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) {
+        return -EINVAL;
+    }
+
+    rsmp = (struct resampler *)calloc(1, sizeof(struct resampler));
+
+    rsmp->speex_resampler = speex_resampler_init(channelCount,
+                                      inSampleRate,
+                                      outSampleRate,
+                                      quality,
+                                      &error);
+    if (rsmp->speex_resampler == NULL) {
+        error("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error));
+        free(rsmp);
+        return -ENODEV;
+    }
+
+    rsmp->itfe.reset = resampler_reset;
+    rsmp->itfe.resample_from_provider = resampler_resample_from_provider;
+    rsmp->itfe.resample_from_input = resampler_resample_from_input;
+    rsmp->itfe.delay_ns = resampler_delay_ns;
+
+    rsmp->provider = provider;
+    rsmp->in_sample_rate = inSampleRate;
+    rsmp->out_sample_rate = outSampleRate;
+    rsmp->channel_count = channelCount;
+    rsmp->in_buf = NULL;
+    rsmp->in_buf_size = 0;
+
+    resampler_reset(&rsmp->itfe);
+
+    frames = speex_resampler_get_input_latency(rsmp->speex_resampler);
+    rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate);
+    frames = speex_resampler_get_output_latency(rsmp->speex_resampler);
+    rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate);
+
+    *resampler = &rsmp->itfe;
+    DBG("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p",
+         rsmp, &rsmp->itfe, rsmp->speex_resampler);
+    return 0;
+}
+
+void release_resampler(struct resampler_itfe *resampler)
+{
+    struct resampler *rsmp = (struct resampler *)resampler;
+
+    if (rsmp == NULL) {
+        return;
+    }
+
+    free(rsmp->in_buf);
+
+    if (rsmp->speex_resampler != NULL) {
+        speex_resampler_destroy(rsmp->speex_resampler);
+    }
+    free(rsmp);
+}
diff --git a/android/audio_utils/resampler.h b/android/audio_utils/resampler.h
new file mode 100644 (file)
index 0000000..0c7046f
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+** Copyright 2008, The Android Open-Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_RESAMPLER_H
+#define ANDROID_RESAMPLER_H
+
+#include <stdint.h>
+#include <sys/time.h>
+
+__BEGIN_DECLS
+
+
+#define RESAMPLER_QUALITY_MAX 10
+#define RESAMPLER_QUALITY_MIN 0
+#define RESAMPLER_QUALITY_DEFAULT 4
+#define RESAMPLER_QUALITY_VOIP 3
+#define RESAMPLER_QUALITY_DESKTOP 5
+
+struct resampler_buffer {
+    union {
+        void*       raw;
+        short*      i16;
+        int8_t*     i8;
+    };
+    size_t frame_count;
+};
+
+/* call back interface used by the resampler to get new data */
+struct resampler_buffer_provider
+{
+    /**
+     *  get a new buffer of data:
+     *   as input: buffer->frame_count is the number of frames requested
+     *   as output: buffer->frame_count is the number of frames returned
+     *              buffer->raw points to data returned
+     */
+    int (*get_next_buffer)(struct resampler_buffer_provider *provider,
+            struct resampler_buffer *buffer);
+    /**
+     *  release a consumed buffer of data:
+     *   as input: buffer->frame_count is the number of frames released
+     *             buffer->raw points to data released
+     */
+    void (*release_buffer)(struct resampler_buffer_provider *provider,
+            struct resampler_buffer *buffer);
+};
+
+/* resampler interface */
+struct resampler_itfe {
+    /**
+     * reset resampler state
+     */
+    void (*reset)(struct resampler_itfe *resampler);
+    /**
+     * resample input from buffer provider and output at most *outFrameCount to out buffer.
+     * *outFrameCount is updated with the actual number of frames produced.
+     */
+    int (*resample_from_provider)(struct resampler_itfe *resampler,
+                    int16_t *out,
+                    size_t *outFrameCount);
+    /**
+     * resample at most *inFrameCount frames from in buffer and output at most
+     * *outFrameCount to out buffer. *inFrameCount and *outFrameCount are updated respectively
+     * with the number of frames remaining in input and written to output.
+     */
+    int (*resample_from_input)(struct resampler_itfe *resampler,
+                    int16_t *in,
+                    size_t *inFrameCount,
+                    int16_t *out,
+                    size_t *outFrameCount);
+    /**
+     * return the latency introduced by the resampler in ns.
+     */
+    int32_t (*delay_ns)(struct resampler_itfe *resampler);
+};
+
+/**
+ * create a resampler according to input parameters passed.
+ * If resampler_buffer_provider is not NULL only resample_from_provider() can be called.
+ * If resampler_buffer_provider is NULL only resample_from_input() can be called.
+ */
+int create_resampler(uint32_t inSampleRate,
+          uint32_t outSampleRate,
+          uint32_t channelCount,
+          uint32_t quality,
+          struct resampler_buffer_provider *provider,
+          struct resampler_itfe **);
+
+/**
+ * release resampler resources.
+ */
+void release_resampler(struct resampler_itfe *);
+
+__END_DECLS
+
+#endif // ANDROID_RESAMPLER_H
diff --git a/android/avctp.c b/android/avctp.c
new file mode 100644 (file)
index 0000000..be3fed1
--- /dev/null
@@ -0,0 +1,1629 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011  Texas Instruments, Inc.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+
+#include <bluetooth/sdp.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+#include "src/uinput.h"
+
+#include "avctp.h"
+
+/*
+ * AV/C Panel 1.23, page 76:
+ * command with the pressed value is valid for two seconds
+ */
+#define AVC_PRESS_TIMEOUT      2
+
+#define QUIRK_NO_RELEASE 1 << 0
+
+/* Message types */
+#define AVCTP_COMMAND          0
+#define AVCTP_RESPONSE         1
+
+/* Packet types */
+#define AVCTP_PACKET_SINGLE    0
+#define AVCTP_PACKET_START     1
+#define AVCTP_PACKET_CONTINUE  2
+#define AVCTP_PACKET_END       3
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avctp_header {
+       uint8_t ipid:1;
+       uint8_t cr:1;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+       uint16_t pid;
+} __attribute__ ((packed));
+#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));
+#define AVC_HEADER_LENGTH 3
+
+#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));
+#define AVC_HEADER_LENGTH 3
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avctp_control_req {
+       struct avctp_pending_req *p;
+       uint8_t code;
+       uint8_t subunit;
+       uint8_t op;
+       struct iovec *iov;
+       int iov_cnt;
+       avctp_rsp_cb func;
+       void *user_data;
+};
+
+struct avctp_browsing_req {
+       struct avctp_pending_req *p;
+       struct iovec *iov;
+       int iov_cnt;
+       avctp_browsing_rsp_cb func;
+       void *user_data;
+};
+
+typedef int (*avctp_process_cb) (void *data);
+
+struct avctp_pending_req {
+       struct avctp_channel *chan;
+       uint8_t transaction;
+       guint timeout;
+       int err;
+       avctp_process_cb process;
+       void *data;
+       avctp_destroy_cb_t destroy;
+};
+
+struct avctp_channel {
+       struct avctp *session;
+       GIOChannel *io;
+       uint8_t transaction;
+       guint watch;
+       uint16_t imtu;
+       uint16_t omtu;
+       uint8_t *buffer;
+       GSList *handlers;
+       struct avctp_pending_req *p;
+       GQueue *queue;
+       GSList *processed;
+       guint process_id;
+       avctp_destroy_cb_t destroy;
+};
+
+struct key_pressed {
+       uint8_t op;
+       uint8_t *params;
+       size_t params_len;
+       guint timer;
+};
+
+struct avctp {
+       int uinput;
+
+       unsigned int passthrough_id;
+       unsigned int unit_id;
+       unsigned int subunit_id;
+
+       struct avctp_channel *control;
+       struct avctp_channel *browsing;
+
+       struct avctp_passthrough_handler *handler;
+
+       uint8_t key_quirks[256];
+       struct key_pressed key;
+       uint16_t version;
+
+       avctp_destroy_cb_t destroy;
+       void *data;
+};
+
+struct avctp_passthrough_handler {
+       avctp_passthrough_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+struct avctp_pdu_handler {
+       uint8_t opcode;
+       avctp_control_pdu_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+struct avctp_browsing_pdu_handler {
+       avctp_browsing_pdu_cb cb;
+       void *user_data;
+       unsigned int id;
+       avctp_destroy_cb_t destroy;
+};
+
+static struct {
+       const char *name;
+       uint8_t avc;
+       uint16_t uinput;
+} key_map[] = {
+       { "SELECT",             AVC_SELECT,             KEY_SELECT },
+       { "UP",                 AVC_UP,                 KEY_UP },
+       { "DOWN",               AVC_DOWN,               KEY_DOWN },
+       { "LEFT",               AVC_LEFT,               KEY_LEFT },
+       { "RIGHT",              AVC_RIGHT,              KEY_RIGHT },
+       { "ROOT MENU",          AVC_ROOT_MENU,          KEY_MENU },
+       { "CONTENTS MENU",      AVC_CONTENTS_MENU,      KEY_PROGRAM },
+       { "FAVORITE MENU",      AVC_FAVORITE_MENU,      KEY_FAVORITES },
+       { "EXIT",               AVC_EXIT,               KEY_EXIT },
+       { "ON DEMAND MENU",     AVC_ON_DEMAND_MENU,     KEY_MENU },
+       { "APPS MENU",          AVC_APPS_MENU,          KEY_MENU },
+       { "0",                  AVC_0,                  KEY_0 },
+       { "1",                  AVC_1,                  KEY_1 },
+       { "2",                  AVC_2,                  KEY_2 },
+       { "3",                  AVC_3,                  KEY_3 },
+       { "4",                  AVC_4,                  KEY_4 },
+       { "5",                  AVC_5,                  KEY_5 },
+       { "6",                  AVC_6,                  KEY_6 },
+       { "7",                  AVC_7,                  KEY_7 },
+       { "8",                  AVC_8,                  KEY_8 },
+       { "9",                  AVC_9,                  KEY_9 },
+       { "DOT",                AVC_DOT,                KEY_DOT },
+       { "ENTER",              AVC_ENTER,              KEY_ENTER },
+       { "CHANNEL UP",         AVC_CHANNEL_UP,         KEY_CHANNELUP },
+       { "CHANNEL DOWN",       AVC_CHANNEL_DOWN,       KEY_CHANNELDOWN },
+       { "CHANNEL PREVIOUS",   AVC_CHANNEL_PREVIOUS,   KEY_LAST },
+       { "INPUT SELECT",       AVC_INPUT_SELECT,       KEY_CONFIG },
+       { "INFO",               AVC_INFO,               KEY_INFO },
+       { "HELP",               AVC_HELP,               KEY_HELP },
+       { "POWER",              AVC_POWER,              KEY_POWER2 },
+       { "VOLUME UP",          AVC_VOLUME_UP,          KEY_VOLUMEUP },
+       { "VOLUME DOWN",        AVC_VOLUME_DOWN,        KEY_VOLUMEDOWN },
+       { "MUTE",               AVC_MUTE,               KEY_MUTE },
+       { "PLAY",               AVC_PLAY,               KEY_PLAYCD },
+       { "STOP",               AVC_STOP,               KEY_STOPCD },
+       { "PAUSE",              AVC_PAUSE,              KEY_PAUSECD },
+       { "FORWARD",            AVC_FORWARD,            KEY_NEXTSONG },
+       { "BACKWARD",           AVC_BACKWARD,           KEY_PREVIOUSSONG },
+       { "RECORD",             AVC_RECORD,             KEY_RECORD },
+       { "REWIND",             AVC_REWIND,             KEY_REWIND },
+       { "FAST FORWARD",       AVC_FAST_FORWARD,       KEY_FASTFORWARD },
+       { "LIST",               AVC_LIST,               KEY_LIST },
+       { "F1",                 AVC_F1,                 KEY_F1 },
+       { "F2",                 AVC_F2,                 KEY_F2 },
+       { "F3",                 AVC_F3,                 KEY_F3 },
+       { "F4",                 AVC_F4,                 KEY_F4 },
+       { "F5",                 AVC_F5,                 KEY_F5 },
+       { "F6",                 AVC_F6,                 KEY_F6 },
+       { "F7",                 AVC_F7,                 KEY_F7 },
+       { "F8",                 AVC_F8,                 KEY_F8 },
+       { "F9",                 AVC_F9,                 KEY_F9 },
+       { "RED",                AVC_RED,                KEY_RED },
+       { "GREEN",              AVC_GREEN,              KEY_GREEN },
+       { "BLUE",               AVC_BLUE,               KEY_BLUE },
+       { "YELLOW",             AVC_YELLOW,             KEY_YELLOW },
+       { NULL }
+};
+
+static gboolean process_queue(gpointer user_data);
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+                                       uint8_t subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data);
+
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+{
+       struct uinput_event event;
+       int err;
+
+       memset(&event, 0, sizeof(event));
+       event.type      = type;
+       event.code      = code;
+       event.value     = value;
+
+       do {
+               err = write(fd, &event, sizeof(event));
+       } while (err < 0 && errno == EINTR);
+
+       if (err < 0) {
+               err = -errno;
+               error("send_event: %s (%d)", strerror(-err), -err);
+       }
+
+       return err;
+}
+
+static void send_key(int fd, uint16_t key, int pressed)
+{
+       send_event(fd, EV_KEY, key, pressed);
+       send_event(fd, EV_SYN, SYN_REPORT, 0);
+}
+
+static gboolean auto_release(gpointer user_data)
+{
+       struct avctp *session = user_data;
+
+       session->key.timer = 0;
+
+       DBG("AV/C: key press timeout");
+
+       send_key(session->uinput, session->key.op, 0);
+
+       return FALSE;
+}
+
+static ssize_t handle_panel_passthrough(struct avctp *session,
+                                       uint8_t transaction, uint8_t *code,
+                                       uint8_t *subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       struct avctp_passthrough_handler *handler = session->handler;
+       const char *status;
+       int pressed, i;
+
+       if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) {
+               *code = AVC_CTYPE_REJECTED;
+               return operand_count;
+       }
+
+       if (operand_count == 0)
+               goto done;
+
+       if (operands[0] & 0x80) {
+               status = "released";
+               pressed = 0;
+       } else {
+               status = "pressed";
+               pressed = 1;
+       }
+
+       if (session->key.timer == 0 && handler != NULL) {
+               if (handler->cb(session, operands[0] & 0x7F,
+                                               pressed, handler->user_data))
+                       goto done;
+       }
+
+       if (session->uinput < 0) {
+               DBG("AV/C: uinput not initialized");
+               *code = AVC_CTYPE_NOT_IMPLEMENTED;
+               return 0;
+       }
+
+       for (i = 0; key_map[i].name != NULL; i++) {
+               uint8_t key_quirks;
+
+               if ((operands[0] & 0x7F) != key_map[i].avc)
+                       continue;
+
+               DBG("AV/C: %s %s", key_map[i].name, status);
+
+               key_quirks = session->key_quirks[key_map[i].avc];
+
+               if (key_quirks & QUIRK_NO_RELEASE) {
+                       if (!pressed) {
+                               DBG("AV/C: Ignoring release");
+                               break;
+                       }
+
+                       DBG("AV/C: treating key press as press + release");
+                       send_key(session->uinput, key_map[i].uinput, 1);
+                       send_key(session->uinput, key_map[i].uinput, 0);
+                       break;
+               }
+
+               if (pressed) {
+                       if (session->key.timer > 0) {
+                               g_source_remove(session->key.timer);
+                               send_key(session->uinput, session->key.op, 0);
+                       }
+
+                       session->key.op = key_map[i].uinput;
+                       session->key.timer = g_timeout_add_seconds(
+                                                       AVC_PRESS_TIMEOUT,
+                                                       auto_release,
+                                                       session);
+               } else if (session->key.timer > 0) {
+                       g_source_remove(session->key.timer);
+                       session->key.timer = 0;
+               }
+
+               send_key(session->uinput, key_map[i].uinput, pressed);
+               break;
+       }
+
+       if (key_map[i].name == NULL) {
+               DBG("AV/C: unknown button 0x%02X %s",
+                                               operands[0] & 0x7F, status);
+               *code = AVC_CTYPE_NOT_IMPLEMENTED;
+               return operand_count;
+       }
+
+done:
+       *code = AVC_CTYPE_ACCEPTED;
+       return operand_count;
+}
+
+static ssize_t handle_unit_info(struct avctp *session,
+                                       uint8_t transaction, uint8_t *code,
+                                       uint8_t *subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       if (*code != AVC_CTYPE_STATUS) {
+               *code = AVC_CTYPE_REJECTED;
+               return 0;
+       }
+
+       *code = AVC_CTYPE_STABLE;
+
+       /*
+        * The first operand should be 0x07 for the UNITINFO response.
+        * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+        * Interface Command Set (section 9.2.1, page 45) specs
+        * explain this value but both use it
+        */
+       if (operand_count >= 1)
+               operands[0] = 0x07;
+       if (operand_count >= 2)
+               operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+       DBG("reply to AVC_OP_UNITINFO");
+
+       return operand_count;
+}
+
+static ssize_t handle_subunit_info(struct avctp *session,
+                                       uint8_t transaction, uint8_t *code,
+                                       uint8_t *subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       if (*code != AVC_CTYPE_STATUS) {
+               *code = AVC_CTYPE_REJECTED;
+               return 0;
+       }
+
+       *code = AVC_CTYPE_STABLE;
+
+       /*
+        * The first operand should be 0x07 for the UNITINFO response.
+        * Neither AVRCP (section 22.1, page 117) nor AVC Digital
+        * Interface Command Set (section 9.2.1, page 45) specs
+        * explain this value but both use it
+        */
+       if (operand_count >= 2)
+               operands[1] = AVC_SUBUNIT_PANEL << 3;
+
+       DBG("reply to AVC_OP_SUBUNITINFO");
+
+       return operand_count;
+}
+
+static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode)
+{
+       for (; list; list = list->next) {
+               struct avctp_pdu_handler *handler = list->data;
+
+               if (handler->opcode == opcode)
+                       return handler;
+       }
+
+       return NULL;
+}
+
+static void pending_destroy(gpointer data, gpointer user_data)
+{
+       struct avctp_pending_req *req = data;
+
+       if (req->destroy)
+               req->destroy(req->data);
+
+       if (req->timeout > 0)
+               g_source_remove(req->timeout);
+
+       g_free(req);
+}
+
+static void avctp_channel_destroy(struct avctp_channel *chan)
+{
+       g_io_channel_shutdown(chan->io, TRUE, NULL);
+       g_io_channel_unref(chan->io);
+
+       if (chan->watch)
+               g_source_remove(chan->watch);
+
+       if (chan->p)
+               pending_destroy(chan->p, NULL);
+
+       if (chan->process_id > 0)
+               g_source_remove(chan->process_id);
+
+       if (chan->destroy)
+               chan->destroy(chan);
+
+       g_free(chan->buffer);
+       g_queue_foreach(chan->queue, pending_destroy, NULL);
+       g_queue_free(chan->queue);
+       g_slist_foreach(chan->processed, pending_destroy, NULL);
+       g_slist_free(chan->processed);
+       g_slist_free_full(chan->handlers, g_free);
+       g_free(chan);
+}
+
+static int avctp_send(struct avctp_channel *control, uint8_t transaction,
+                               uint8_t cr, uint8_t code,
+                               uint8_t subunit, uint8_t opcode,
+                               const struct iovec *iov, int iov_cnt)
+{
+       struct avctp_header avctp;
+       struct avc_header avc;
+       struct msghdr msg;
+       int sk, err = 0;
+       struct iovec pdu[iov_cnt + 2];
+       int i;
+       size_t len = sizeof(avctp) + sizeof(avc);
+
+       DBG("");
+
+       pdu[0].iov_base = &avctp;
+       pdu[0].iov_len  = sizeof(avctp);
+       pdu[1].iov_base = &avc;
+       pdu[1].iov_len  = sizeof(avc);
+
+       for (i = 0; i < iov_cnt; i++) {
+               pdu[i + 2].iov_base = iov[i].iov_base;
+               pdu[i + 2].iov_len  = iov[i].iov_len;
+               len += iov[i].iov_len;
+       }
+
+       if (control->omtu < len)
+               return -EOVERFLOW;
+
+       sk = g_io_channel_unix_get_fd(control->io);
+
+       memset(&avctp, 0, sizeof(avctp));
+
+       avctp.transaction = transaction;
+       avctp.packet_type = AVCTP_PACKET_SINGLE;
+       avctp.cr = cr;
+       avctp.pid = htons(AV_REMOTE_SVCLASS_ID);
+
+       memset(&avc, 0, sizeof(avc));
+
+       avc.code = code;
+       avc.subunit_type = subunit;
+       avc.opcode = opcode;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = pdu;
+       msg.msg_iovlen = iov_cnt + 2;
+
+       if (sendmsg(sk, &msg, 0) < 0)
+               err = -errno;
+
+       return err;
+}
+
+static int avctp_browsing_send(struct avctp_channel *browsing,
+                               uint8_t transaction, uint8_t cr,
+                               const struct iovec *iov, int iov_cnt)
+{
+       struct avctp_header avctp;
+       struct msghdr msg;
+       struct iovec pdu[iov_cnt + 1];
+       int sk, err = 0;
+       int i;
+       size_t len = sizeof(avctp);
+
+       for (i = 0; i < iov_cnt; i++) {
+               pdu[i + 1].iov_base = iov[i].iov_base;
+               pdu[i + 1].iov_len  = iov[i].iov_len;
+               len += iov[i].iov_len;
+       }
+
+       pdu[0].iov_base = &avctp;
+       pdu[0].iov_len  = sizeof(avctp);
+
+       if (browsing->omtu < len)
+               return -EOVERFLOW;
+
+       sk = g_io_channel_unix_get_fd(browsing->io);
+
+       memset(&avctp, 0, sizeof(avctp));
+
+       avctp.transaction = transaction;
+       avctp.packet_type = AVCTP_PACKET_SINGLE;
+       avctp.cr = cr;
+       avctp.pid = htons(AV_REMOTE_SVCLASS_ID);
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = pdu;
+       msg.msg_iovlen = iov_cnt + 1;
+
+       if (sendmsg(sk, &msg, 0) < 0)
+               err = -errno;
+
+       return err;
+}
+
+static void control_req_destroy(void *data)
+{
+       struct avctp_control_req *req = data;
+       struct avctp_pending_req *p = req->p;
+       struct avctp *session = p->chan->session;
+       int i;
+
+       if (p->err == 0 || req->func == NULL)
+               goto done;
+
+       req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0,
+                                                       req->user_data);
+
+done:
+       for (i = 0; i < req->iov_cnt; i++)
+               g_free(req->iov[i].iov_base);
+
+       g_free(req->iov);
+       g_free(req);
+}
+
+static void browsing_req_destroy(void *data)
+{
+       struct avctp_browsing_req *req = data;
+       struct avctp_pending_req *p = req->p;
+       struct avctp *session = p->chan->session;
+       int i;
+
+       if (p->err == 0 || req->func == NULL)
+               goto done;
+
+       req->func(session, NULL, 0, req->user_data);
+
+done:
+       for (i = 0; i < req->iov_cnt; i++)
+               g_free(req->iov[i].iov_base);
+
+       g_free(req->iov);
+       g_free(req);
+}
+
+static gboolean req_timeout(gpointer user_data)
+{
+       struct avctp_channel *chan = user_data;
+       struct avctp_pending_req *p = chan->p;
+
+       DBG("transaction %u", p->transaction);
+
+       p->timeout = 0;
+       p->err = -ETIMEDOUT;
+
+       pending_destroy(p, NULL);
+       chan->p = NULL;
+
+       if (chan->process_id == 0)
+               chan->process_id = g_idle_add(process_queue, chan);
+
+       return FALSE;
+}
+
+static int process_control(void *data)
+{
+       struct avctp_control_req *req = data;
+       struct avctp_pending_req *p = req->p;
+
+       return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code,
+                               req->subunit, req->op, req->iov, req->iov_cnt);
+}
+
+static int process_browsing(void *data)
+{
+       struct avctp_browsing_req *req = data;
+       struct avctp_pending_req *p = req->p;
+
+       return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND,
+                                               req->iov, req->iov_cnt);
+}
+
+static gboolean process_queue(void *user_data)
+{
+       struct avctp_channel *chan = user_data;
+       struct avctp_pending_req *p = chan->p;
+
+       chan->process_id = 0;
+
+       if (p != NULL)
+               return FALSE;
+
+       while ((p = g_queue_pop_head(chan->queue))) {
+
+               if (p->process(p->data) == 0)
+                       break;
+
+               pending_destroy(p, NULL);
+       }
+
+       if (p == NULL)
+               return FALSE;
+
+       chan->p = p;
+       p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
+
+       return FALSE;
+
+}
+
+static void control_response(struct avctp_channel *control,
+                                       struct avctp_header *avctp,
+                                       struct avc_header *avc,
+                                       uint8_t *operands,
+                                       size_t operand_count)
+{
+       struct avctp_pending_req *p = control->p;
+       struct avctp_control_req *req;
+       GSList *l;
+
+       if (p && p->transaction == avctp->transaction) {
+               control->processed = g_slist_prepend(control->processed, p);
+
+               if (p->timeout > 0) {
+                       g_source_remove(p->timeout);
+                       p->timeout = 0;
+               }
+
+               control->p = NULL;
+
+               if (control->process_id == 0)
+                       control->process_id = g_idle_add(process_queue,
+                                                               control);
+       }
+
+       for (l = control->processed; l; l = l->next) {
+               p = l->data;
+               req = p->data;
+
+               if (p->transaction != avctp->transaction)
+                       continue;
+
+               if (req->func && req->func(control->session, avc->code,
+                                               avc->subunit_type,
+                                               operands, operand_count,
+                                               req->user_data))
+                       return;
+
+               control->processed = g_slist_remove(control->processed, p);
+               pending_destroy(p, NULL);
+
+               return;
+       }
+}
+
+static void browsing_response(struct avctp_channel *browsing,
+                                       struct avctp_header *avctp,
+                                       uint8_t *operands,
+                                       size_t operand_count)
+{
+       struct avctp_pending_req *p = browsing->p;
+       struct avctp_browsing_req *req;
+       GSList *l;
+
+       if (p && p->transaction == avctp->transaction) {
+               browsing->processed = g_slist_prepend(browsing->processed, p);
+
+               if (p->timeout > 0) {
+                       g_source_remove(p->timeout);
+                       p->timeout = 0;
+               }
+
+               browsing->p = NULL;
+
+               if (browsing->process_id == 0)
+                       browsing->process_id = g_idle_add(process_queue,
+                                                               browsing);
+       }
+
+       for (l = browsing->processed; l; l = l->next) {
+               p = l->data;
+               req = p->data;
+
+               if (p->transaction != avctp->transaction)
+                       continue;
+
+               if (req->func && req->func(browsing->session, operands,
+                                               operand_count, req->user_data))
+                       return;
+
+               browsing->processed = g_slist_remove(browsing->processed, p);
+               pending_destroy(p, NULL);
+
+               return;
+       }
+}
+
+static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
+                               gpointer data)
+{
+       struct avctp *session = data;
+       struct avctp_channel *browsing = session->browsing;
+       uint8_t *buf = browsing->buffer;
+       uint8_t *operands;
+       struct avctp_header *avctp;
+       int sock, ret, packet_size, operand_count;
+       struct avctp_browsing_pdu_handler *handler;
+
+       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+               goto failed;
+
+       sock = g_io_channel_unix_get_fd(chan);
+
+       ret = read(sock, buf, browsing->imtu);
+       if (ret <= 0)
+               goto failed;
+
+       if (ret < AVCTP_HEADER_LENGTH) {
+               error("Too small AVCTP packet");
+               goto failed;
+       }
+
+       avctp = (struct avctp_header *) buf;
+
+       if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+               error("Invalid packet type");
+               goto failed;
+       }
+
+       operands = buf + AVCTP_HEADER_LENGTH;
+       ret -= AVCTP_HEADER_LENGTH;
+       operand_count = ret;
+
+       if (avctp->cr == AVCTP_RESPONSE) {
+               browsing_response(browsing, avctp, operands, operand_count);
+               return TRUE;
+       }
+
+       packet_size = AVCTP_HEADER_LENGTH;
+       avctp->cr = AVCTP_RESPONSE;
+
+       handler = g_slist_nth_data(browsing->handlers, 0);
+       if (handler == NULL) {
+               DBG("handler not found");
+               /* FIXME: Add general reject */
+               /* packet_size += avrcp_browsing_general_reject(operands); */
+               goto send;
+       }
+
+       ret = handler->cb(session, avctp->transaction, operands, operand_count,
+                                                       handler->user_data);
+       if (ret < 0) {
+               if (ret == -EAGAIN)
+                       return TRUE;
+               goto failed;
+       }
+
+       packet_size += ret;
+
+send:
+       if (packet_size != 0) {
+               ret = write(sock, buf, packet_size);
+               if (ret != packet_size)
+                       goto failed;
+       }
+
+       return TRUE;
+
+failed:
+       DBG("AVCTP Browsing: disconnected");
+
+       if (session->browsing) {
+               avctp_channel_destroy(session->browsing);
+               session->browsing = NULL;
+       }
+
+       return FALSE;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+       struct avctp *session = data;
+       struct avctp_channel *control = session->control;
+       uint8_t *buf = control->buffer;
+       uint8_t *operands, code, subunit;
+       struct avctp_header *avctp;
+       struct avc_header *avc;
+       int packet_size, operand_count, sock;
+       struct avctp_pdu_handler *handler;
+       ssize_t ret;
+
+       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+               goto failed;
+
+       sock = g_io_channel_unix_get_fd(chan);
+
+       ret = read(sock, buf, control->imtu);
+       if (ret <= 0)
+               goto failed;
+
+       if (ret < AVCTP_HEADER_LENGTH) {
+               error("Too small AVCTP packet");
+               goto failed;
+       }
+
+       avctp = (struct avctp_header *) buf;
+
+       ret -= AVCTP_HEADER_LENGTH;
+       if (ret < AVC_HEADER_LENGTH) {
+               error("Too small AVC packet");
+               goto failed;
+       }
+
+       avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH);
+
+       ret -= AVC_HEADER_LENGTH;
+
+       operands = (uint8_t *) avc + AVC_HEADER_LENGTH;
+       operand_count = ret;
+
+       if (avctp->cr == AVCTP_RESPONSE) {
+               control_response(control, avctp, avc, operands, operand_count);
+               return TRUE;
+       }
+
+       packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH;
+       avctp->cr = AVCTP_RESPONSE;
+
+       if (avctp->packet_type != AVCTP_PACKET_SINGLE) {
+               avc->code = AVC_CTYPE_NOT_IMPLEMENTED;
+               goto done;
+       }
+
+       if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) {
+               avctp->ipid = 1;
+               packet_size = AVCTP_HEADER_LENGTH;
+               goto done;
+       }
+
+       handler = find_handler(control->handlers, avc->opcode);
+       if (!handler) {
+               DBG("handler not found for 0x%02x", avc->opcode);
+               avc->code = AVC_CTYPE_REJECTED;
+               goto done;
+       }
+
+       code = avc->code;
+       subunit = avc->subunit_type;
+
+       ret = handler->cb(session, avctp->transaction, &code,
+                                       &subunit, operands, operand_count,
+                                       handler->user_data);
+       if (ret < 0) {
+               if (ret == -EAGAIN)
+                       return TRUE;
+               goto failed;
+       }
+
+       packet_size += ret;
+       avc->code = code;
+       avc->subunit_type = subunit;
+
+done:
+       ret = write(sock, buf, packet_size);
+       if (ret != packet_size)
+               goto failed;
+
+       return TRUE;
+
+failed:
+       DBG("AVCTP session %p got disconnected", session);
+       avctp_shutdown(session);
+       return FALSE;
+}
+
+static int uinput_create(const char *name)
+{
+       struct uinput_dev dev;
+       int fd, err, i;
+
+       fd = open("/dev/uinput", O_RDWR);
+       if (fd < 0) {
+               fd = open("/dev/input/uinput", O_RDWR);
+               if (fd < 0) {
+                       fd = open("/dev/misc/uinput", O_RDWR);
+                       if (fd < 0) {
+                               err = -errno;
+                               error("Can't open input device: %s (%d)",
+                                                       strerror(-err), -err);
+                               return err;
+                       }
+               }
+       }
+
+       memset(&dev, 0, sizeof(dev));
+       if (name)
+               strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
+
+       dev.id.bustype = BUS_BLUETOOTH;
+       dev.id.vendor  = 0x0000;
+       dev.id.product = 0x0000;
+       dev.id.version = 0x0000;
+
+       if (write(fd, &dev, sizeof(dev)) < 0) {
+               err = -errno;
+               error("Can't write device information: %s (%d)",
+                                               strerror(-err), -err);
+               close(fd);
+               return err;
+       }
+
+       ioctl(fd, UI_SET_EVBIT, EV_KEY);
+       ioctl(fd, UI_SET_EVBIT, EV_REL);
+       ioctl(fd, UI_SET_EVBIT, EV_REP);
+       ioctl(fd, UI_SET_EVBIT, EV_SYN);
+
+       for (i = 0; key_map[i].name != NULL; i++)
+               ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
+
+       if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
+               err = -errno;
+               error("Can't create uinput device: %s (%d)",
+                                               strerror(-err), -err);
+               close(fd);
+               return err;
+       }
+
+       return fd;
+}
+
+int avctp_init_uinput(struct avctp *session, const char *name,
+                                                       const char *address)
+{
+       if (g_str_equal(name, "Nokia CK-20W")) {
+               session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE;
+               session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE;
+               session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE;
+               session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE;
+       }
+
+       session->uinput = uinput_create(address);
+       if (session->uinput < 0) {
+               error("AVCTP: failed to init uinput for %s", address);
+               return session->uinput;
+       }
+
+       return 0;
+}
+
+static struct avctp_channel *avctp_channel_create(struct avctp *session, int fd,
+                                               size_t imtu, size_t omtu,
+                                               avctp_destroy_cb_t destroy)
+{
+       struct avctp_channel *chan;
+
+       chan = g_new0(struct avctp_channel, 1);
+       chan->session = session;
+       chan->io = g_io_channel_unix_new(fd);
+       chan->queue = g_queue_new();
+       chan->imtu = imtu;
+       chan->omtu = omtu;
+       chan->buffer = g_malloc0(MAX(imtu, omtu));
+       chan->destroy = destroy;
+
+       return chan;
+}
+
+static void handler_free(void *data)
+{
+       struct avctp_browsing_pdu_handler *handler = data;
+
+       if (handler->destroy)
+               handler->destroy(handler->user_data);
+
+       g_free(data);
+}
+
+static void avctp_destroy_browsing(void *data)
+{
+       struct avctp_channel *chan = data;
+
+       g_slist_free_full(chan->handlers, handler_free);
+
+       chan->handlers = NULL;
+}
+
+static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
+                                               avctp_process_cb process,
+                                               void *data,
+                                               avctp_destroy_cb_t destroy)
+{
+       struct avctp_pending_req *p;
+       GSList *l, *tmp;
+
+       if (!chan->processed)
+               goto done;
+
+       tmp = g_slist_copy(chan->processed);
+
+       /* Find first unused transaction id */
+       for (l = tmp; l; l = g_slist_next(l)) {
+               struct avctp_pending_req *req = l->data;
+
+               if (req->transaction == chan->transaction) {
+                       chan->transaction++;
+                       chan->transaction %= 16;
+                       tmp = g_slist_delete_link(tmp, l);
+                       l = tmp;
+               }
+       }
+
+       g_slist_free(tmp);
+
+done:
+       p = g_new0(struct avctp_pending_req, 1);
+       p->chan = chan;
+       p->transaction = chan->transaction;
+       p->process = process;
+       p->data = data;
+       p->destroy = destroy;
+
+       chan->transaction++;
+       chan->transaction %= 16;
+
+       return p;
+}
+
+static int avctp_send_req(struct avctp *session, uint8_t code, uint8_t subunit,
+                       uint8_t opcode, const struct iovec *iov, int iov_cnt,
+                       avctp_rsp_cb func, void *user_data)
+{
+       struct avctp_channel *control = session->control;
+       struct avctp_pending_req *p;
+       struct avctp_control_req *req;
+       struct iovec *pdu;
+       int i;
+
+       if (control == NULL)
+               return -ENOTCONN;
+
+       pdu = g_new0(struct iovec, iov_cnt);
+
+       for (i = 0; i < iov_cnt; i++) {
+               pdu[i].iov_len = iov[i].iov_len;
+               pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len);
+       }
+
+       req = g_new0(struct avctp_control_req, 1);
+       req->code = code;
+       req->subunit = subunit;
+       req->op = opcode;
+       req->func = func;
+       req->iov = pdu;
+       req->iov_cnt = iov_cnt;
+       req->user_data = user_data;
+
+       p = pending_create(control, process_control, req, control_req_destroy);
+
+       req->p = p;
+
+       g_queue_push_tail(control->queue, p);
+
+       if (control->process_id == 0)
+               control->process_id = g_idle_add(process_queue, control);
+
+       return 0;
+}
+
+int avctp_send_browsing_req(struct avctp *session,
+                               const struct iovec *iov, int iov_cnt,
+                               avctp_browsing_rsp_cb func, void *user_data)
+{
+       struct avctp_channel *browsing = session->browsing;
+       struct avctp_pending_req *p;
+       struct avctp_browsing_req *req;
+       struct iovec *pdu;
+       int i;
+
+       if (browsing == NULL)
+               return -ENOTCONN;
+
+       pdu = g_new0(struct iovec, iov_cnt);
+
+       for (i = 0; i < iov_cnt; i++) {
+               pdu[i].iov_len = iov[i].iov_len;
+               pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len);
+       }
+
+       req = g_new0(struct avctp_browsing_req, 1);
+       req->func = func;
+       req->iov = pdu;
+       req->iov_cnt = iov_cnt;
+       req->user_data = user_data;
+
+       p = pending_create(browsing, process_browsing, req,
+                       browsing_req_destroy);
+
+       req->p = p;
+
+       g_queue_push_tail(browsing->queue, p);
+
+       /* Connection did not complete, delay process of the request */
+       if (browsing->watch == 0)
+               return 0;
+
+       if (browsing->process_id == 0)
+               browsing->process_id = g_idle_add(process_queue, browsing);
+
+       return 0;
+}
+
+int avctp_send_browsing(struct avctp *session, uint8_t transaction,
+                                       const struct iovec *iov, int iov_cnt)
+{
+       struct avctp_channel *browsing = session->browsing;
+
+       if (browsing == NULL)
+               return -ENOTCONN;
+
+       return avctp_browsing_send(browsing, transaction, AVCTP_RESPONSE,
+                                                               iov, iov_cnt);
+}
+
+static const char *op2str(uint8_t op)
+{
+       int i;
+
+       for (i = 0; key_map[i].name != NULL; i++) {
+               if ((op & 0x7F) == key_map[i].avc)
+                       return key_map[i].name;
+       }
+
+       return "UNKNOWN";
+}
+
+static int avctp_passthrough_press(struct avctp *session, uint8_t op,
+                                       uint8_t *params, size_t params_len)
+{
+       struct iovec iov[2];
+       int iov_cnt;
+       uint8_t operands[2];
+
+       DBG("%s", op2str(op));
+
+       iov[0].iov_base = operands;
+       iov[0].iov_len = sizeof(operands);
+
+       /* Button pressed */
+       operands[0] = op & 0x7f;
+
+       if (params_len > 0) {
+               iov[1].iov_base = params;
+               iov[1].iov_len = params_len;
+               iov_cnt = 2;
+               operands[1] = params_len;
+       } else {
+               iov_cnt = 1;
+               operands[1] = 0;
+       }
+
+       return avctp_send_req(session, AVC_CTYPE_CONTROL,
+                               AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+                               iov, iov_cnt, avctp_passthrough_rsp, NULL);
+}
+
+static int avctp_passthrough_release(struct avctp *session, uint8_t op,
+                                       uint8_t *params, size_t params_len)
+{
+       struct iovec iov[2];
+       int iov_cnt;
+       uint8_t operands[2];
+
+       DBG("%s", op2str(op));
+
+       iov[0].iov_base = operands;
+       iov[0].iov_len = sizeof(operands);
+
+       /* Button released */
+       operands[0] = op | 0x80;
+
+       if (params_len > 0) {
+               iov[1].iov_base = params;
+               iov[1].iov_len = params_len;
+               iov_cnt = 2;
+               operands[1] = params_len;
+       } else {
+               iov_cnt = 1;
+               operands[1] = 0;
+       }
+
+       return avctp_send_req(session, AVC_CTYPE_CONTROL,
+                               AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH,
+                               iov, iov_cnt, NULL, NULL);
+}
+
+static gboolean repeat_timeout(gpointer user_data)
+{
+       struct avctp *session = user_data;
+
+       avctp_passthrough_release(session, session->key.op, session->key.params,
+                                               session->key.params_len);
+       avctp_passthrough_press(session, session->key.op, session->key.params,
+                                               session->key.params_len);
+
+       return TRUE;
+}
+
+static void release_pressed(struct avctp *session)
+{
+       avctp_passthrough_release(session, session->key.op, session->key.params,
+                                               session->key.params_len);
+
+       if (session->key.timer > 0)
+               g_source_remove(session->key.timer);
+
+       session->key.timer = 0;
+}
+
+static bool set_pressed(struct avctp *session, uint8_t op, uint8_t *params,
+                                                       size_t params_len)
+{
+       if (session->key.timer > 0) {
+               if (session->key.op == op)
+                       return TRUE;
+               release_pressed(session);
+       }
+
+       if (op != AVC_FAST_FORWARD && op != AVC_REWIND)
+               return FALSE;
+
+       session->key.op = op;
+       session->key.params = params;
+       session->key.params_len = params_len;
+       session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT,
+                                                       repeat_timeout,
+                                                       session);
+
+       return TRUE;
+}
+
+static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
+                                       uint8_t subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       uint8_t *params;
+       size_t params_len;
+
+       DBG("code 0x%02x operand_count %zd", code, operand_count);
+
+       if (code != AVC_CTYPE_ACCEPTED)
+               return FALSE;
+
+       if (operands[0] == AVC_VENDOR_UNIQUE) {
+               params = &operands[2];
+               params_len = operand_count - 2;
+       } else {
+               params = NULL;
+               params_len = 0;
+       }
+
+       if (set_pressed(session, operands[0], params, params_len))
+               return FALSE;
+
+       avctp_passthrough_release(session, operands[0], params, params_len);
+
+       return FALSE;
+}
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params,
+                                                       size_t params_len)
+{
+       /* Auto release if key pressed */
+       if (session->key.timer > 0)
+               release_pressed(session);
+
+       return avctp_passthrough_press(session, op, params, params_len);
+}
+
+int avctp_send_vendor(struct avctp *session, uint8_t transaction,
+                               uint8_t code, uint8_t subunit,
+                               const struct iovec *iov, int iov_cnt)
+{
+       struct avctp_channel *control = session->control;
+
+       if (control == NULL)
+               return -ENOTCONN;
+
+       return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit,
+                                               AVC_OP_VENDORDEP, iov, iov_cnt);
+}
+
+int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit,
+                                       const struct iovec *iov, int iov_cnt,
+                                       avctp_rsp_cb func, void *user_data)
+{
+       return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP, iov,
+                                               iov_cnt, func, user_data);
+}
+
+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+                                               avctp_passthrough_cb cb,
+                                               void *user_data)
+{
+       struct avctp_channel *control = session->control;
+       struct avctp_passthrough_handler *handler;
+       static unsigned int id = 0;
+
+       if (control == NULL || session->handler != NULL)
+               return 0;
+
+       handler = g_new(struct avctp_passthrough_handler, 1);
+       handler->cb = cb;
+       handler->user_data = user_data;
+       handler->id = ++id;
+
+       session->handler = handler;
+
+       return handler->id;
+}
+
+bool avctp_unregister_passthrough_handler(struct avctp *session,
+                                                       unsigned int id)
+{
+       if (session->handler == NULL)
+               return false;
+
+       if (session->handler->id != id)
+               return false;
+
+       g_free(session->handler);
+       session->handler = NULL;
+       return true;
+}
+
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+                                               avctp_control_pdu_cb cb,
+                                               void *user_data)
+{
+       struct avctp_channel *control = session->control;
+       struct avctp_pdu_handler *handler;
+       static unsigned int id = 0;
+
+       if (control == NULL)
+               return 0;
+
+       handler = find_handler(control->handlers, opcode);
+       if (handler)
+               return 0;
+
+       handler = g_new(struct avctp_pdu_handler, 1);
+       handler->opcode = opcode;
+       handler->cb = cb;
+       handler->user_data = user_data;
+       handler->id = ++id;
+
+       control->handlers = g_slist_append(control->handlers, handler);
+
+       return handler->id;
+}
+
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+                                               avctp_browsing_pdu_cb cb,
+                                               void *user_data,
+                                               avctp_destroy_cb_t destroy)
+{
+       struct avctp_channel *browsing = session->browsing;
+       struct avctp_browsing_pdu_handler *handler;
+       static unsigned int id = 0;
+
+       if (browsing == NULL)
+               return 0;
+
+       if (browsing->handlers != NULL)
+               return 0;
+
+       handler = g_new(struct avctp_browsing_pdu_handler, 1);
+       handler->cb = cb;
+       handler->user_data = user_data;
+       handler->id = ++id;
+       handler->destroy = destroy;
+
+       browsing->handlers = g_slist_append(browsing->handlers, handler);
+
+       return handler->id;
+}
+
+bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id)
+{
+       struct avctp_channel *control = session->control;
+       GSList *l;
+
+       if (!control)
+               return false;
+
+       for (l = control->handlers; l; l = g_slist_next(l)) {
+               struct avctp_pdu_handler *handler = l->data;
+
+               if (handler->id != id)
+                       continue;
+
+               control->handlers = g_slist_remove(control->handlers, handler);
+               g_free(handler);
+               return true;
+       }
+
+       return false;
+}
+
+bool avctp_unregister_browsing_pdu_handler(struct avctp *session,
+                                                       unsigned int id)
+{
+       struct avctp_channel *browsing = session->browsing;
+       GSList *l;
+
+       if (browsing == NULL)
+               return false;
+
+       for (l = browsing->handlers; l; l = g_slist_next(l)) {
+               struct avctp_browsing_pdu_handler *handler = l->data;
+
+               if (handler->id != id)
+                       continue;
+
+               browsing->handlers = g_slist_remove(browsing->handlers,
+                                                               handler);
+               g_free(handler);
+               return true;
+       }
+
+       return false;
+}
+
+struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
+{
+       struct avctp *session;
+       struct avctp_channel *control;
+       GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+       session = g_new0(struct avctp, 1);
+       session->version = version;
+
+       control = avctp_channel_create(session, fd, imtu, omtu, NULL);
+       if (!control) {
+               g_free(session);
+               return NULL;
+       }
+
+       session->uinput = -1;
+       session->control = control;
+       session->passthrough_id = avctp_register_pdu_handler(session,
+                                               AVC_OP_PASSTHROUGH,
+                                               handle_panel_passthrough,
+                                               NULL);
+       session->unit_id = avctp_register_pdu_handler(session,
+                                               AVC_OP_UNITINFO,
+                                               handle_unit_info,
+                                               NULL);
+       session->subunit_id = avctp_register_pdu_handler(session,
+                                               AVC_OP_SUBUNITINFO,
+                                               handle_subunit_info,
+                                               NULL);
+
+       control->watch = g_io_add_watch(session->control->io, cond,
+                                               (GIOFunc) session_cb, session);
+
+       return session;
+}
+
+int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu,
+                                                               size_t omtu)
+{
+       struct avctp_channel *browsing;
+       GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+       if (session->browsing)
+               return -EISCONN;
+
+       browsing = avctp_channel_create(session, fd, imtu, omtu,
+                                               avctp_destroy_browsing);
+       if (!browsing)
+               return -EINVAL;
+
+       session->browsing = browsing;
+       browsing->watch = g_io_add_watch(session->browsing->io, cond,
+                                       (GIOFunc) session_browsing_cb, session);
+
+       return 0;
+}
+
+void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb,
+                                                       void *user_data)
+{
+       session->destroy = cb;
+       session->data = user_data;
+}
+
+void avctp_shutdown(struct avctp *session)
+{
+       if (!session)
+               return;
+
+       if (session->browsing)
+               avctp_channel_destroy(session->browsing);
+
+       if (session->control)
+               avctp_channel_destroy(session->control);
+
+       if (session->destroy)
+               session->destroy(session->data);
+
+       g_free(session->handler);
+
+       if (session->key.timer > 0)
+               g_source_remove(session->key.timer);
+
+       if (session->uinput >= 0) {
+               DBG("AVCTP: closing uinput");
+
+               ioctl(session->uinput, UI_DEV_DESTROY);
+               close(session->uinput);
+               session->uinput = -1;
+       }
+
+       g_free(session);
+}
diff --git a/android/avctp.h b/android/avctp.h
new file mode 100644 (file)
index 0000000..2b33858
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define AVCTP_CONTROL_PSM              23
+#define AVCTP_BROWSING_PSM             27
+
+#define AVC_MTU 512
+
+/* ctype entries */
+#define AVC_CTYPE_CONTROL              0x0
+#define AVC_CTYPE_STATUS               0x1
+#define AVC_CTYPE_NOTIFY               0x3
+#define AVC_CTYPE_NOT_IMPLEMENTED      0x8
+#define AVC_CTYPE_ACCEPTED             0x9
+#define AVC_CTYPE_REJECTED             0xA
+#define AVC_CTYPE_STABLE               0xC
+#define AVC_CTYPE_CHANGED              0xD
+#define AVC_CTYPE_INTERIM              0xF
+
+/* opcodes */
+#define AVC_OP_VENDORDEP               0x00
+#define AVC_OP_UNITINFO                        0x30
+#define AVC_OP_SUBUNITINFO             0x31
+#define AVC_OP_PASSTHROUGH             0x7c
+
+/* subunits of interest */
+#define AVC_SUBUNIT_PANEL              0x09
+
+/* operands in passthrough commands */
+#define AVC_SELECT                     0x00
+#define AVC_UP                         0x01
+#define AVC_DOWN                       0x02
+#define AVC_LEFT                       0x03
+#define AVC_RIGHT                      0x04
+#define AVC_ROOT_MENU                  0x09
+#define AVC_CONTENTS_MENU              0x0b
+#define AVC_FAVORITE_MENU              0x0c
+#define AVC_EXIT                       0x0d
+#define AVC_ON_DEMAND_MENU             0x0e
+#define AVC_APPS_MENU                  0x0f
+#define AVC_0                          0x20
+#define AVC_1                          0x21
+#define AVC_2                          0x22
+#define AVC_3                          0x23
+#define AVC_4                          0x24
+#define AVC_5                          0x25
+#define AVC_6                          0x26
+#define AVC_7                          0x27
+#define AVC_8                          0x28
+#define AVC_9                          0x29
+#define AVC_DOT                                0x2a
+#define AVC_ENTER                      0x2b
+#define AVC_CHANNEL_UP                 0x30
+#define AVC_CHANNEL_DOWN               0x31
+#define AVC_CHANNEL_PREVIOUS           0x32
+#define AVC_INPUT_SELECT               0x34
+#define AVC_INFO                       0x35
+#define AVC_HELP                       0x36
+#define AVC_PAGE_UP                    0x37
+#define AVC_PAGE_DOWN                  0x38
+#define AVC_LOCK                       0x3a
+#define AVC_POWER                      0x40
+#define AVC_VOLUME_UP                  0x41
+#define AVC_VOLUME_DOWN                        0x42
+#define AVC_MUTE                       0x43
+#define AVC_PLAY                       0x44
+#define AVC_STOP                       0x45
+#define AVC_PAUSE                      0x46
+#define AVC_RECORD                     0x47
+#define AVC_REWIND                     0x48
+#define AVC_FAST_FORWARD               0x49
+#define AVC_EJECT                      0x4a
+#define AVC_FORWARD                    0x4b
+#define AVC_BACKWARD                   0x4c
+#define AVC_LIST                       0x4d
+#define AVC_F1                         0x71
+#define AVC_F2                         0x72
+#define AVC_F3                         0x73
+#define AVC_F4                         0x74
+#define AVC_F5                         0x75
+#define AVC_F6                         0x76
+#define AVC_F7                         0x77
+#define AVC_F8                         0x78
+#define AVC_F9                         0x79
+#define AVC_RED                                0x7a
+#define AVC_GREEN                      0x7b
+#define AVC_BLUE                       0x7c
+#define AVC_YELLOW                     0x7c
+
+#define AVC_VENDOR_UNIQUE              0x7e
+
+#define AVC_VENDOR_NEXT_GROUP          0x00
+#define AVC_VENDOR_PREV_GROUP          0x01
+
+struct avctp;
+
+typedef bool (*avctp_passthrough_cb) (struct avctp *session,
+                                       uint8_t op, bool pressed,
+                                       void *user_data);
+typedef ssize_t (*avctp_control_pdu_cb) (struct avctp *session,
+                                       uint8_t transaction, uint8_t *code,
+                                       uint8_t *subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data);
+typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
+                                       uint8_t subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data);
+typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data);
+typedef ssize_t (*avctp_browsing_pdu_cb) (struct avctp *session,
+                                       uint8_t transaction,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data);
+
+typedef void (*avctp_destroy_cb_t) (void *user_data);
+
+struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb,
+                                                       void *user_data);
+
+int avctp_init_uinput(struct avctp *session, const char *name,
+                                                       const char *address);
+int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu,
+                                                               size_t omtu);
+
+void avctp_shutdown(struct avctp *session);
+
+unsigned int avctp_register_passthrough_handler(struct avctp *session,
+                                               avctp_passthrough_cb cb,
+                                               void *user_data);
+bool avctp_unregister_passthrough_handler(struct avctp *session,
+                                                       unsigned int id);
+
+unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode,
+                                               avctp_control_pdu_cb cb,
+                                               void *user_data);
+bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id);
+
+unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
+                                               avctp_browsing_pdu_cb cb,
+                                               void *user_data,
+                                               avctp_destroy_cb_t destroy);
+bool avctp_unregister_browsing_pdu_handler(struct avctp *session,
+                                                       unsigned int id);
+
+int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params,
+                                                       size_t params_len);
+int avctp_send_vendor(struct avctp *session, uint8_t transaction,
+                               uint8_t code, uint8_t subunit,
+                               const struct iovec *iov, int iov_cnt);
+int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit,
+                                       const struct iovec *iov, int iov_cnt,
+                                       avctp_rsp_cb func, void *user_data);
+int avctp_send_browsing(struct avctp *session, uint8_t transaction,
+                                       const struct iovec *iov, int iov_cnt);
+int avctp_send_browsing_req(struct avctp *session,
+                               const struct iovec *iov, int iov_cnt,
+                               avctp_browsing_rsp_cb func, void *user_data);
index 5ae3afc..3d309b9 100644 (file)
@@ -39,7 +39,7 @@
 
 #include <glib.h>
 
-#include "log.h"
+#include "src/log.h"
 #include "avdtp.h"
 
 #define AVDTP_PSM 25
@@ -336,6 +336,12 @@ struct discover_callback {
        void *user_data;
 };
 
+struct disconnect_callback {
+       unsigned int id;
+       avdtp_disconnect_cb_t cb;
+       void *user_data;
+};
+
 struct avdtp_stream {
        GIOChannel *io;
        uint16_t imtu;
@@ -390,6 +396,10 @@ struct avdtp {
 
        struct discover_callback *discover;
        struct pending_req *req;
+
+       GSList *disconnect;
+
+       bool shutdown;
 };
 
 static GSList *lseps = NULL;
@@ -670,6 +680,9 @@ static void stream_free(void *data)
        if (stream->timer)
                g_source_remove(stream->timer);
 
+       if (stream->start_timer > 0)
+               g_source_remove(stream->start_timer);
+
        if (stream->io)
                close_stream(stream);
 
@@ -902,6 +915,11 @@ static void avdtp_sep_set_state(struct avdtp *session,
                session->streams = g_slist_remove(session->streams, stream);
                stream_free(stream);
        }
+
+       if (session->io && session->shutdown && session->streams == NULL) {
+               int sock = g_io_channel_unix_get_fd(session->io);
+               shutdown(sock, SHUT_RDWR);
+       }
 }
 
 static void finalize_discovery(struct avdtp *session, int err)
@@ -912,15 +930,17 @@ static void finalize_discovery(struct avdtp *session, int err)
        if (!discover)
                return;
 
+       session->discover = NULL;
+
        avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err);
 
        if (discover->id > 0)
                g_source_remove(discover->id);
 
-       discover->cb(session, session->seps, err ? &avdtp_err : NULL,
+       if (discover->cb)
+               discover->cb(session, session->seps, err ? &avdtp_err : NULL,
                                                        discover->user_data);
        g_free(discover);
-       session->discover = NULL;
 }
 
 static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
@@ -967,12 +987,22 @@ static void avdtp_free(void *data)
        g_slist_free_full(session->req_queue, pending_req_free);
        g_slist_free_full(session->prio_queue, pending_req_free);
        g_slist_free_full(session->seps, sep_free);
+       g_slist_free_full(session->disconnect, g_free);
 
        g_free(session->buf);
 
        g_free(session);
 }
 
+static void process_disconnect(void *data)
+{
+       struct disconnect_callback *callback = data;
+
+       callback->cb(callback->user_data);
+
+       g_free(callback);
+}
+
 static void connection_lost(struct avdtp *session, int err)
 {
        DBG("Disconnected: %s (%d)", strerror(err), err);
@@ -980,12 +1010,14 @@ static void connection_lost(struct avdtp *session, int err)
        g_slist_foreach(session->streams, (GFunc) release_stream, session);
        session->streams = NULL;
 
+       avdtp_ref(session);
+
        finalize_discovery(session, err);
 
-       if (session->ref > 0)
-               return;
+       g_slist_free_full(session->disconnect, process_disconnect);
+       session->disconnect = NULL;
 
-       avdtp_free(session);
+       avdtp_unref(session);
 }
 
 void avdtp_unref(struct avdtp *session)
@@ -1169,8 +1201,8 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
                goto failed;
        }
 
-       if (!sep->ind->get_capability(session, sep, get_all, &caps,
-                                                       &err, sep->user_data))
+       if (!sep->ind->get_capability(session, sep, &caps, &err,
+                                                       sep->user_data))
                goto failed;
 
        for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
@@ -1186,6 +1218,12 @@ static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
                g_free(cap);
        }
 
+       if (get_all && sep->delay_reporting) {
+               ptr[0] = AVDTP_DELAY_REPORTING;
+               ptr[1] = 0x00;
+               rsp_size += 2;
+       }
+
        g_slist_free(caps);
 
        return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd,
@@ -1259,11 +1297,15 @@ static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
                                        &stream->codec,
                                        &stream->delay_reporting);
 
-       /* Verify that the Media Transport capability's length = 0. Reject otherwise */
+       /*
+        * 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) {
+               if (cap->category == AVDTP_MEDIA_TRANSPORT &&
+                                                       cap->length != 0) {
                        err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
                        goto failed_stream;
                }
@@ -1333,7 +1375,7 @@ static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
                goto failed;
        }
 
-       for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+       for (l = sep->stream->caps, rsp_size = 0; l; l = g_slist_next(l)) {
                struct avdtp_service_capability *cap = l->data;
 
                if (rsp_size + cap->length + 2 > (int) sizeof(buf))
@@ -1703,10 +1745,14 @@ static gboolean avdtp_delayreport_cmd(struct avdtp *session,
 
        stream = sep->stream;
 
-       if (sep->state != AVDTP_STATE_CONFIGURED &&
-                                       sep->state != AVDTP_STATE_STREAMING) {
+       switch (sep->state) {
+       case AVDTP_STATE_IDLE:
+       case AVDTP_STATE_ABORTING:
+       case AVDTP_STATE_CLOSING:
                err = AVDTP_BAD_STATE;
                goto failed;
+       default:
+               break;
        }
 
        stream->delay = ntohs(req->delay);
@@ -1790,7 +1836,8 @@ static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
        switch (header->packet_type) {
        case AVDTP_PKT_TYPE_SINGLE:
                if (size < sizeof(*single)) {
-                       error("Received too small single packet (%zu bytes)", size);
+                       error("Received too small single packet (%zu bytes)",
+                                                                       size);
                        return PARSE_ERROR;
                }
                if (session->in.active) {
@@ -1811,7 +1858,8 @@ static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
                break;
        case AVDTP_PKT_TYPE_START:
                if (size < sizeof(*start)) {
-                       error("Received too small start packet (%zu bytes)", size);
+                       error("Received too small start packet (%zu bytes)",
+                                                                       size);
                        return PARSE_ERROR;
                }
                if (session->in.active) {
@@ -1855,7 +1903,8 @@ static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
                break;
        case AVDTP_PKT_TYPE_END:
                if (size < sizeof(struct avdtp_continue_header)) {
-                       error("Received too small end packet (%zu bytes)", size);
+                       error("Received too small end packet (%zu bytes)",
+                                                                       size);
                        return PARSE_ERROR;
                }
                if (!session->in.active) {
@@ -2018,13 +2067,38 @@ failed:
        return FALSE;
 }
 
+static int set_priority(int fd, int priority)
+{
+       int err;
+
+       err = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority,
+                                                       sizeof(priority));
+       if (err == 0 || errno == ENOTSOCK)
+               return 0;
+
+       err = -errno;
+       error("setsockopt(SO_PRIORITY): %s (%d)", strerror(-err), -err);
+
+       return err;
+}
+
 struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
 {
        struct avdtp *session;
        GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+       int new_fd;
+
+       new_fd = dup(fd);
+       if (new_fd < 0) {
+               error("dup(): %s (%d)", strerror(errno), errno);
+               return NULL;
+       }
+
+       if (set_priority(new_fd, 6) < 0)
+               return NULL;
 
        session = g_new0(struct avdtp, 1);
-       session->io = g_io_channel_unix_new(fd);
+       session->io = g_io_channel_unix_new(new_fd);
        session->version = version;
        session->imtu = imtu;
        session->omtu = omtu;
@@ -2044,6 +2118,66 @@ struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
        return avdtp_ref(session);
 }
 
+unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
+                                               avdtp_disconnect_cb_t cb,
+                                               void *user_data)
+{
+       struct disconnect_callback *callback;
+       static unsigned int id = 0;
+
+       callback = g_new0(struct disconnect_callback, 1);
+       callback->id = ++id;
+       callback->cb = cb;
+       callback->user_data = user_data;
+       session->disconnect = g_slist_append(session->disconnect, callback);
+
+       return id;
+}
+
+gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id)
+{
+       GSList *l;
+
+       for (l = session->disconnect; l; l = g_slist_next(l)) {
+               struct disconnect_callback *callback = l->data;
+
+               if (callback->id != id)
+                       continue;
+
+               session->disconnect = g_slist_remove(session->disconnect,
+                                                               callback);
+               g_free(callback);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+void avdtp_shutdown(struct avdtp *session)
+{
+       GSList *l;
+       bool aborting = false;
+
+       if (!session->io)
+               return;
+
+       for (l = session->streams; l; l = g_slist_next(l)) {
+               struct avdtp_stream *stream = l->data;
+
+               if (stream->abort_int || avdtp_abort(session, stream) == 0)
+                       aborting = true;
+       }
+
+       if (aborting) {
+               /* defer shutdown until all streams are aborted properly */
+               session->shutdown = true;
+       } else {
+               int sock = g_io_channel_unix_get_fd(session->io);
+
+               shutdown(sock, SHUT_RDWR);
+       }
+}
+
 static void queue_request(struct avdtp *session, struct pending_req *req,
                        gboolean priority)
 {
@@ -2128,7 +2262,7 @@ static int cancel_request(struct avdtp *session, int err)
                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);
+                                               &averr, lsep->user_data);
                goto failed;
        case AVDTP_DISCOVER:
                error("Discover: %s (%d)", strerror(err), err);
@@ -2214,6 +2348,11 @@ static int send_request(struct avdtp *session, gboolean priority,
 {
        struct pending_req *req;
 
+       if (size > 0 && !buffer) {
+               DBG("Invalid buffer %p", buffer);
+               return -EINVAL;
+       }
+
        if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
                DBG("Unable to send requests while aborting");
                return -EINVAL;
@@ -2221,11 +2360,14 @@ static int send_request(struct avdtp *session, gboolean priority,
 
        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;
 
+       if (size > 0) {
+               req->data = g_malloc(size);
+               memcpy(req->data, buffer, size);
+       }
+
        return send_req(session, priority, req);
 }
 
@@ -2324,9 +2466,9 @@ static gboolean avdtp_get_capabilities_resp(struct avdtp *session,
 }
 
 static gboolean avdtp_set_configuration_resp(struct avdtp *session,
-                                               struct avdtp_stream *stream,
-                                               struct avdtp_single_header *resp,
-                                               int size)
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_single_header *resp,
+                                       int size)
 {
        struct avdtp_local_sep *sep = stream->lsep;
 
@@ -2341,12 +2483,14 @@ static gboolean avdtp_set_configuration_resp(struct avdtp *session,
 
 static gboolean avdtp_reconfigure_resp(struct avdtp *session,
                                        struct avdtp_stream *stream,
-                                       struct avdtp_single_header *resp, int size)
+                                       struct avdtp_single_header *resp,
+                                       int size)
 {
        return TRUE;
 }
 
-static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream,
+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;
@@ -2426,7 +2570,8 @@ static gboolean avdtp_delay_report_resp(struct avdtp *session,
        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);
+               sep->cfm->delay_report(session, sep, stream, NULL,
+                                                       sep->user_data);
 
        return TRUE;
 }
@@ -2733,6 +2878,9 @@ gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
        if (stream != stream->session->pending_open)
                return FALSE;
 
+       if (set_priority(fd, 5) < 0)
+               return FALSE;
+
        io = g_io_channel_unix_new(fd);
 
        handle_transport_connect(stream->session, io, imtu, omtu);
@@ -2799,13 +2947,19 @@ struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
 {
        struct avdtp_service_capability *cap;
 
-       if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING)
+       if (category < AVDTP_MEDIA_TRANSPORT ||
+                                       category > AVDTP_DELAY_REPORTING)
+               return NULL;
+
+       if (length > 0 && !data)
                return NULL;
 
        cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
        cap->category = category;
        cap->length = length;
-       memcpy(cap->data, data, length);
+
+       if (length > 0)
+               memcpy(cap->data, data, length);
 
        return cap;
 }
@@ -3186,7 +3340,7 @@ struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type,
        sep->ind = ind;
        sep->cfm = cfm;
        sep->user_data = user_data;
-       sep->delay_reporting = TRUE;
+       sep->delay_reporting = delay_reporting;
 
        DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
                        sep->info.type, sep->codec, sep->info.seid);
@@ -3218,7 +3372,7 @@ const char *avdtp_strerror(struct avdtp_error *err)
        if (err->category == AVDTP_ERRNO)
                return strerror(err->err.posix_errno);
 
-       switch(err->err.error_code) {
+       switch (err->err.error_code) {
        case AVDTP_BAD_HEADER_FORMAT:
                return "Bad Header Format";
        case AVDTP_BAD_LENGTH:
index 9760875..7fdf597 100644 (file)
@@ -156,12 +156,13 @@ struct avdtp_sep_cfm {
                                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 */
+/*
+ * 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,
@@ -200,9 +201,17 @@ struct avdtp_sep_ind {
 
 typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
                                        struct avdtp_error *err, void *user_data);
+typedef void (*avdtp_disconnect_cb_t) (void *user_data);
 
 struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
 
+unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
+                                               avdtp_disconnect_cb_t cb,
+                                               void *user_data);
+gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id);
+
+void avdtp_shutdown(struct avdtp *session);
+
 void avdtp_unref(struct avdtp *session);
 struct avdtp *avdtp_ref(struct avdtp *session);
 
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
new file mode 100644 (file)
index 0000000..036867e
--- /dev/null
@@ -0,0 +1,2910 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <glib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "lib/bluetooth.h"
+
+#include "src/shared/util.h"
+#include "src/log.h"
+
+#include "avctp.h"
+#include "avrcp-lib.h"
+
+
+/* Packet types */
+#define AVRCP_PACKET_TYPE_SINGLE               0x00
+#define AVRCP_PACKET_TYPE_START                        0x01
+#define AVRCP_PACKET_TYPE_CONTINUING           0x02
+#define AVRCP_PACKET_TYPE_END                  0x03
+
+#define AVRCP_CHARSET_UTF8     0x006a
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avrcp_header {
+       uint8_t company_id[3];
+       uint8_t pdu_id;
+       uint8_t packet_type:2;
+       uint8_t rsvd:6;
+       uint16_t params_len;
+       uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avrcp_header {
+       uint8_t company_id[3];
+       uint8_t pdu_id;
+       uint8_t rsvd:6;
+       uint8_t packet_type:2;
+       uint16_t params_len;
+       uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_HEADER_LENGTH 7
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct avrcp_browsing_header {
+       uint8_t pdu_id;
+       uint16_t params_len;
+       uint8_t params[0];
+} __attribute__ ((packed));
+#define AVRCP_BROWSING_HEADER_LENGTH 3
+
+struct avrcp_control_handler {
+       uint8_t id;
+       uint8_t code;
+       uint8_t rsp;
+       ssize_t (*func) (struct avrcp *session, uint8_t transaction,
+                       uint16_t params_len, uint8_t *params, void *user_data);
+};
+
+struct avrcp_browsing_handler {
+       uint8_t id;
+       ssize_t (*func) (struct avrcp *session, uint8_t transaction,
+                       uint16_t params_len, uint8_t *params, void *user_data);
+};
+
+struct avrcp {
+       struct avctp *conn;
+       struct avrcp_player *player;
+
+       const struct avrcp_control_handler *control_handlers;
+       void *control_data;
+       unsigned int control_id;
+
+       const struct avrcp_passthrough_handler *passthrough_handlers;
+       void *passthrough_data;
+       unsigned int passthrough_id;
+
+       const struct avrcp_browsing_handler *browsing_handlers;
+       void *browsing_data;
+       unsigned int browsing_id;
+
+       avrcp_destroy_cb_t destroy;
+       void *destroy_data;
+};
+
+struct avrcp_player {
+       const struct avrcp_control_ind *ind;
+       const struct avrcp_control_cfm *cfm;
+
+       void *user_data;
+};
+
+static inline uint32_t ntoh24(const uint8_t src[3])
+{
+       return src[0] << 16 | src[1] << 8 | src[2];
+}
+
+static inline void hton24(uint8_t dst[3], uint32_t src)
+{
+       dst[0] = (src & 0xff0000) >> 16;
+       dst[1] = (src & 0x00ff00) >> 8;
+       dst[2] = (src & 0x0000ff);
+}
+
+void avrcp_shutdown(struct avrcp *session)
+{
+       if (session->conn) {
+               if (session->control_id > 0)
+                       avctp_unregister_pdu_handler(session->conn,
+                                                       session->control_id);
+               if (session->passthrough_id > 0)
+                       avctp_unregister_passthrough_handler(session->conn,
+                                               session->passthrough_id);
+
+               /* clear destroy callback that would call shutdown again */
+               avctp_set_destroy_cb(session->conn, NULL, NULL);
+               avctp_shutdown(session->conn);
+       }
+
+       if (session->destroy)
+               session->destroy(session->destroy_data);
+
+       g_free(session->player);
+       g_free(session);
+}
+
+static struct avrcp_header *parse_pdu(uint8_t *operands, size_t operand_count)
+{
+       struct avrcp_header *pdu;
+
+       if (!operands || operand_count < sizeof(*pdu)) {
+               error("AVRCP: packet too small (%zu bytes)", operand_count);
+               return NULL;
+       }
+
+       pdu = (void *) operands;
+       pdu->params_len = ntohs(pdu->params_len);
+
+       if (operand_count != pdu->params_len + sizeof(*pdu)) {
+               error("AVRCP: invalid parameter length (%u bytes)",
+                                                       pdu->params_len);
+               return NULL;
+       }
+
+       return pdu;
+}
+
+static struct avrcp_browsing_header *parse_browsing_pdu(uint8_t *operands,
+                                                       size_t operand_count)
+{
+       struct avrcp_browsing_header *pdu;
+
+       if (!operands || operand_count < sizeof(*pdu)) {
+               error("AVRCP: packet too small (%zu bytes)", operand_count);
+               return NULL;
+       }
+
+       pdu = (void *) operands;
+       pdu->params_len = ntohs(pdu->params_len);
+
+       if (operand_count != pdu->params_len + sizeof(*pdu)) {
+               error("AVRCP: invalid parameter length (%u bytes)",
+                                                       pdu->params_len);
+               return NULL;
+       }
+
+       return pdu;
+}
+
+static uint8_t errno2status(int err)
+{
+       switch (err) {
+       case -ENOSYS:
+               return AVRCP_STATUS_INVALID_COMMAND;
+       case -EINVAL:
+               return AVRCP_STATUS_INVALID_PARAM;
+       case 0:
+               return AVRCP_STATUS_SUCCESS;
+       case -ENOTDIR:
+               return AVRCP_STATUS_NOT_DIRECTORY;
+       case -EBADRQC:
+               return AVRCP_STATUS_INVALID_SCOPE;
+       case -ERANGE:
+               return AVRCP_STATUS_OUT_OF_BOUNDS;
+       case -ENOENT:
+               return AVRCP_STATUS_DOES_NOT_EXIST;
+       default:
+               return AVRCP_STATUS_INTERNAL_ERROR;
+       }
+}
+
+static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
+                                       uint8_t *code, uint8_t *subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       const struct avrcp_control_handler *handler;
+       struct avrcp_header *pdu;
+       uint32_t company_id;
+       ssize_t ret;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               pdu = (void *) operands;
+               pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+               goto reject;
+       }
+
+       company_id = ntoh24(pdu->company_id);
+       if (company_id != IEEEID_BTSIG) {
+               *code = AVC_CTYPE_NOT_IMPLEMENTED;
+               return 0;
+       }
+
+       DBG("AVRCP PDU 0x%02X, len 0x%04X", pdu->pdu_id, pdu->params_len);
+
+       pdu->packet_type = 0;
+       pdu->rsvd = 0;
+
+       if (!session->control_handlers)
+               goto reject;
+
+       for (handler = session->control_handlers; handler->id; handler++) {
+               if (handler->id == pdu->pdu_id)
+                       break;
+       }
+
+       if (handler->id != pdu->pdu_id || handler->code != *code) {
+               pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+               goto reject;
+       }
+
+       if (!handler->func) {
+               pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+               goto reject;
+       }
+
+       ret = handler->func(session, transaction, pdu->params_len, pdu->params,
+                                                       session->control_data);
+       if (ret < 0) {
+               if (ret == -EAGAIN)
+                       return ret;
+               pdu->params[0] = errno2status(ret);
+               goto reject;
+       }
+
+       *code = handler->rsp;
+       pdu->params_len = htons(ret);
+
+       return AVRCP_HEADER_LENGTH + ret;
+
+reject:
+       pdu->params_len = htons(1);
+       *code = AVC_CTYPE_REJECTED;
+
+       return AVRCP_HEADER_LENGTH + 1;
+}
+
+static bool handle_passthrough_pdu(struct avctp *conn, uint8_t op,
+                                               bool pressed, void *user_data)
+{
+       struct avrcp *session = user_data;
+       const struct avrcp_passthrough_handler *handler;
+
+       if (!session->passthrough_handlers)
+               return false;
+
+       for (handler = session->passthrough_handlers; handler->func;
+                                                               handler++) {
+               if (handler->op == op)
+                       break;
+       }
+
+       if (handler->func == NULL)
+               return false;
+
+       return handler->func(session, pressed, session->passthrough_data);
+}
+
+static void disconnect_cb(void *data)
+{
+       struct avrcp *session = data;
+
+       session->conn = NULL;
+
+       avrcp_shutdown(session);
+}
+
+struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
+{
+       struct avrcp *session;
+
+       session = g_new0(struct avrcp, 1);
+
+       session->conn = avctp_new(fd, imtu, omtu, version);
+       if (!session->conn) {
+               g_free(session);
+               return NULL;
+       }
+
+       session->passthrough_id = avctp_register_passthrough_handler(
+                                                       session->conn,
+                                                       handle_passthrough_pdu,
+                                                       session);
+       session->control_id = avctp_register_pdu_handler(session->conn,
+                                                       AVC_OP_VENDORDEP,
+                                                       handle_vendordep_pdu,
+                                                       session);
+
+       avctp_set_destroy_cb(session->conn, disconnect_cb, session);
+
+       return session;
+}
+
+static ssize_t handle_browsing_pdu(struct avctp *conn,
+                                       uint8_t transaction, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       struct avrcp *session = user_data;
+       const struct avrcp_browsing_handler *handler;
+       struct avrcp_browsing_header *pdu;
+       int ret;
+
+       pdu = parse_browsing_pdu(operands, operand_count);
+       if (!pdu) {
+               pdu = (void *) operands;
+               pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+               goto reject;
+       }
+
+       DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id,
+                                                       pdu->params_len);
+
+       if (!session->browsing_handlers) {
+               pdu->pdu_id = AVRCP_GENERAL_REJECT;
+               pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
+               goto reject;
+       }
+
+       for (handler = session->browsing_handlers; handler->id; handler++) {
+               if (handler->id == pdu->pdu_id)
+                       break;
+       }
+
+       if (handler->id != pdu->pdu_id) {
+               pdu->pdu_id = AVRCP_GENERAL_REJECT;
+               pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
+               goto reject;
+       }
+
+       if (!handler->func) {
+               pdu->params[0] = AVRCP_STATUS_INVALID_PARAM;
+               goto reject;
+       }
+
+       ret = handler->func(session, transaction, pdu->params_len, pdu->params,
+                                                       session->control_data);
+       if (ret < 0) {
+               if (ret == -EAGAIN)
+                       return ret;
+               pdu->params[0] = errno2status(ret);
+               goto reject;
+       }
+
+       pdu->params_len = htons(ret);
+
+       return AVRCP_BROWSING_HEADER_LENGTH + ret;
+
+reject:
+       pdu->params_len = htons(1);
+
+       return AVRCP_BROWSING_HEADER_LENGTH + 1;
+}
+
+static void browsing_disconnect_cb(void *data)
+{
+       struct avrcp *session = data;
+
+       session->browsing_id = 0;
+}
+
+int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu,
+                                                               size_t omtu)
+{
+       int err;
+
+       err = avctp_connect_browsing(session->conn, fd, imtu, omtu);
+       if (err < 0)
+               return err;
+
+       session->browsing_id = avctp_register_browsing_pdu_handler(
+                                                       session->conn,
+                                                       handle_browsing_pdu,
+                                                       session,
+                                                       browsing_disconnect_cb);
+
+       return 0;
+}
+
+void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb,
+                                                       void *user_data)
+{
+       session->destroy = cb;
+       session->destroy_data = user_data;
+}
+
+static ssize_t get_capabilities(struct avrcp *session, uint8_t transaction,
+                               uint16_t params_len, uint8_t *params,
+                               void *user_data)
+{
+       struct avrcp_player *player = user_data;
+
+       if (!params || params_len != 1)
+               return -EINVAL;
+
+       switch (params[0]) {
+       case CAP_COMPANY_ID:
+               params[1] = 1;
+               hton24(&params[2], IEEEID_BTSIG);
+               return 5;
+       case CAP_EVENTS_SUPPORTED:
+               if (!player->ind || !player->ind->get_capabilities)
+                       return -ENOSYS;
+               return player->ind->get_capabilities(session, transaction,
+                                                       player->user_data);
+       }
+
+       return -EINVAL;
+}
+
+static ssize_t list_attributes(struct avrcp *session, uint8_t transaction,
+                               uint16_t params_len, uint8_t *params,
+                               void *user_data)
+{
+       struct avrcp_player *player = user_data;
+
+       DBG("");
+
+       if (!player->ind || !player->ind->list_attributes)
+               return -ENOSYS;
+
+       return player->ind->list_attributes(session, transaction,
+                                                       player->user_data);
+}
+
+static bool check_attributes(uint8_t number, const uint8_t *attrs)
+{
+       int i;
+
+       for (i = 0; i < number; i++) {
+               if (attrs[i] > AVRCP_ATTRIBUTE_LAST ||
+                                       attrs[i] == AVRCP_ATTRIBUTE_ILEGAL)
+                       return false;
+       }
+
+       return true;
+}
+
+static ssize_t get_attribute_text(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+
+       DBG("");
+
+       if (!params || params_len != 1 + params[0])
+               return -EINVAL;
+
+       if (!check_attributes(params[0], &params[1]))
+               return -EINVAL;
+
+       if (!player->ind || !player->ind->get_attribute_text)
+               return -ENOSYS;
+
+       return player->ind->get_attribute_text(session, transaction, params[0],
+                                               &params[1], player->user_data);
+}
+
+static ssize_t list_values(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+
+       DBG("");
+
+       if (!params || params_len != 1)
+               return -EINVAL;
+
+       if (params[0] > AVRCP_ATTRIBUTE_LAST ||
+                                       params[0] == AVRCP_ATTRIBUTE_ILEGAL)
+               return -EINVAL;
+
+       if (!player->ind || !player->ind->list_values)
+               return -ENOSYS;
+
+       return player->ind->list_values(session, transaction, params[0],
+                                                       player->user_data);
+}
+
+static bool check_value(uint8_t attr, uint8_t number, const uint8_t *values)
+{
+       int i;
+
+       for (i = 0; i < number; i++) {
+               /* Check for invalid value */
+               switch (attr) {
+               case AVRCP_ATTRIBUTE_EQUALIZER:
+                       if (values[i] < AVRCP_EQUALIZER_OFF ||
+                                               values[i] > AVRCP_EQUALIZER_ON)
+                               return false;
+               case AVRCP_ATTRIBUTE_REPEAT_MODE:
+                       if (values[i] < AVRCP_REPEAT_MODE_OFF ||
+                                       values[i] > AVRCP_REPEAT_MODE_GROUP)
+                               return false;
+               case AVRCP_ATTRIBUTE_SHUFFLE:
+                       if (values[i] < AVRCP_SHUFFLE_OFF ||
+                                       values[i] > AVRCP_SHUFFLE_GROUP)
+                               return false;
+               case AVRCP_ATTRIBUTE_SCAN:
+                       if (values[i] < AVRCP_SCAN_OFF ||
+                                       values[i] > AVRCP_SCAN_GROUP)
+                               return false;
+               }
+       }
+
+       return true;
+}
+
+static ssize_t get_value_text(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+
+       DBG("");
+
+       if (params_len != 2 + params[1])
+               return -EINVAL;
+
+       if (params[0] > AVRCP_ATTRIBUTE_LAST ||
+                                       params[0] == AVRCP_ATTRIBUTE_ILEGAL)
+               return -EINVAL;
+
+       if (!check_value(params[0], params[1], &params[2]))
+               return -EINVAL;
+
+       if (!player->ind || !player->ind->get_value_text)
+               return -ENOSYS;
+
+       return player->ind->get_value_text(session, transaction, params[0],
+                                               params[1], &params[2],
+                                               player->user_data);
+}
+
+static ssize_t get_value(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+
+       DBG("");
+
+       if (!params || params_len < 1 + params[0])
+               return -EINVAL;
+
+       if (!check_attributes(params[0], &params[1]))
+               return -EINVAL;
+
+       if (!player->ind || !player->ind->get_value)
+               return -ENOSYS;
+
+       return player->ind->get_value(session, transaction, params[0],
+                                       &params[1], player->user_data);
+}
+
+static ssize_t set_value(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       int i;
+
+       DBG("");
+
+       if (!params || params_len != params[0] * 2 + 1)
+               return -EINVAL;
+
+       for (i = 0; i < params[0]; i++) {
+               uint8_t attr = params[i * 2 + 1];
+               uint8_t val = params[i * 2 + 2];
+
+               if (!check_value(attr, 1, &val))
+                       return -EINVAL;
+       }
+
+       if (!player->ind || !player->ind->set_value)
+               return -ENOSYS;
+
+       return player->ind->set_value(session, transaction, params[0],
+                                       &params[1], player->user_data);
+}
+
+static ssize_t get_play_status(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+
+       DBG("");
+
+       if (!player->ind || !player->ind->get_play_status)
+               return -ENOSYS;
+
+       return player->ind->get_play_status(session, transaction,
+                                                       player->user_data);
+}
+
+static ssize_t get_element_attributes(struct avrcp *session,
+                                               uint8_t transaction,
+                                               uint16_t params_len,
+                                               uint8_t *params,
+                                               void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       uint64_t uid;
+       uint8_t number;
+       uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+       int i;
+
+       DBG("");
+
+       if (!params || params_len != 9 + params[8] * 4)
+               return -EINVAL;
+
+       uid = get_be64(params);
+       number = params[8];
+
+       for (i = 0; i < number; i++) {
+               attrs[i] = get_be32(&params[9 + i * 4]);
+
+               if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+                               attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+                       return -EINVAL;
+       }
+
+       if (!player->ind || !player->ind->get_element_attributes)
+               return -ENOSYS;
+
+       return player->ind->get_element_attributes(session, transaction, uid,
+                                                       number, attrs,
+                                                       player->user_data);
+}
+
+static ssize_t register_notification(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       uint32_t interval;
+
+       DBG("");
+
+       if (!params || params_len != 5)
+               return -EINVAL;
+
+       if (!player->ind || !player->ind->register_notification)
+               return -ENOSYS;
+
+       interval = get_be32(&params[1]);
+
+       return player->ind->register_notification(session, transaction,
+                                                       params[0], interval,
+                                                       player->user_data);
+}
+
+static ssize_t set_volume(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       uint8_t volume;
+
+       DBG("");
+
+       if (!player->ind || !player->ind->set_volume)
+               return -ENOSYS;
+
+       if (!params || params_len != sizeof(volume))
+               return -EINVAL;
+
+       volume = params[0] & 0x7f;
+
+       return player->ind->set_volume(session, transaction, volume,
+                                                       player->user_data);
+}
+
+static ssize_t set_addressed(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       uint16_t id;
+
+       DBG("");
+
+       if (!params || params_len != 2)
+               return -EINVAL;
+
+       if (!player->ind || !player->ind->set_addressed)
+               return -ENOSYS;
+
+       id = get_be16(params);
+
+       return player->ind->set_addressed(session, transaction, id,
+                                                       player->user_data);
+}
+
+static const struct avrcp_control_handler player_handlers[] = {
+               { AVRCP_GET_CAPABILITIES,
+                                       AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+                                       get_capabilities },
+               { AVRCP_LIST_PLAYER_ATTRIBUTES,
+                                       AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+                                       list_attributes },
+               { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+                                       AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+                                       get_attribute_text },
+               { AVRCP_LIST_PLAYER_VALUES,
+                                       AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+                                       list_values },
+               { AVRCP_GET_PLAYER_VALUE_TEXT,
+                                       AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+                                       get_value_text },
+               { AVRCP_GET_CURRENT_PLAYER_VALUE,
+                                       AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+                                       get_value },
+               { AVRCP_SET_PLAYER_VALUE,
+                                       AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+                                       set_value },
+               { AVRCP_GET_PLAY_STATUS,
+                                       AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+                                       get_play_status },
+               { AVRCP_GET_ELEMENT_ATTRIBUTES,
+                                       AVC_CTYPE_STATUS, AVC_CTYPE_STABLE,
+                                       get_element_attributes },
+               { AVRCP_REGISTER_NOTIFICATION,
+                                       AVC_CTYPE_NOTIFY, AVC_CTYPE_INTERIM,
+                                       register_notification },
+               { AVRCP_SET_ABSOLUTE_VOLUME,
+                                       AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+                                       set_volume },
+               { AVRCP_SET_ADDRESSED_PLAYER,
+                                       AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE,
+                                       set_addressed },
+               { },
+};
+
+static void avrcp_set_control_handlers(struct avrcp *session,
+                               const struct avrcp_control_handler *handlers,
+                               void *user_data)
+{
+       session->control_handlers = handlers;
+       session->control_data = user_data;
+}
+
+static ssize_t get_folder_items(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       uint8_t scope;
+       uint32_t start, end;
+       uint16_t number;
+       uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+       int i;
+
+       DBG("");
+
+       if (!player->ind || !player->ind->get_folder_items)
+               return -ENOSYS;
+
+       if (!params || params_len < 10)
+               return -EINVAL;
+
+       scope = params[0];
+       if (scope > AVRCP_MEDIA_NOW_PLAYING)
+               return -EBADRQC;
+
+       start = get_be32(&params[1]);
+       end = get_be32(&params[5]);
+
+       if (start > end)
+               return -ERANGE;
+
+       number = get_be16(&params[9]);
+
+       for (i = 0; i < number; i++) {
+               attrs[i] = get_be32(&params[11 + i * 4]);
+
+               if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+                               attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+                       return -EINVAL;
+       }
+
+       return player->ind->get_folder_items(session, transaction, scope, start,
+                                                       end, number, attrs,
+                                                       player->user_data);
+}
+
+static ssize_t change_path(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       uint8_t direction;
+       uint16_t counter;
+       uint64_t uid;
+
+       DBG("");
+
+       if (!player->ind || !player->ind->change_path)
+               return -ENOSYS;
+
+       if (!params || params_len < 11)
+               return -EINVAL;
+
+       counter = get_be16(&params[0]);
+       direction = params[2];
+       uid = get_be64(&params[3]);
+
+       return player->ind->change_path(session, transaction, counter,
+                                       direction, uid, player->user_data);
+}
+
+static ssize_t get_item_attributes(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       uint8_t scope;
+       uint64_t uid;
+       uint16_t counter;
+       uint8_t number;
+       uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+       int i;
+
+       DBG("");
+
+       if (!player->ind || !player->ind->get_item_attributes)
+               return -ENOSYS;
+
+       if (!params || params_len < 12)
+               return -EINVAL;
+
+       scope = params[0];
+       if (scope > AVRCP_MEDIA_NOW_PLAYING)
+               return -EBADRQC;
+
+       uid = get_be64(&params[1]);
+       counter = get_be16(&params[9]);
+       number = params[11];
+
+       for (i = 0; i < number; i++) {
+               attrs[i] = get_be32(&params[12 + i * 4]);
+
+               if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
+                               attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST)
+                       return -EINVAL;
+       }
+
+       return player->ind->get_item_attributes(session, transaction, scope,
+                                               uid, counter, number, attrs,
+                                               player->user_data);
+}
+
+static ssize_t play_item(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       uint8_t scope;
+       uint64_t uid;
+       uint16_t counter;
+
+       DBG("");
+
+       if (!player->ind || !player->ind->play_item)
+               return -ENOSYS;
+
+       if (!params || params_len < 11)
+               return -EINVAL;
+
+       scope = params[0];
+       if (scope > AVRCP_MEDIA_NOW_PLAYING)
+               return -EBADRQC;
+
+       uid = get_be64(&params[1]);
+       counter = get_be16(&params[9]);
+
+       return player->ind->play_item(session, transaction, scope, uid,
+                                               counter, player->user_data);
+}
+
+static ssize_t search(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       char *string;
+       uint16_t len;
+       int ret;
+
+       DBG("");
+
+       if (!player->ind || !player->ind->search)
+               return -ENOSYS;
+
+       if (!params || params_len < 4)
+               return -EINVAL;
+
+       len = get_be16(&params[2]);
+       if (!len)
+               return -EINVAL;
+
+       string = strndup((void *) &params[4], len);
+
+       ret = player->ind->search(session, transaction, string,
+                                                       player->user_data);
+
+       free(string);
+
+       return ret;
+}
+
+static ssize_t add_to_now_playing(struct avrcp *session, uint8_t transaction,
+                                       uint16_t params_len, uint8_t *params,
+                                       void *user_data)
+{
+       struct avrcp_player *player = user_data;
+       uint8_t scope;
+       uint64_t uid;
+       uint16_t counter;
+
+       DBG("");
+
+       if (!player->ind || !player->ind->add_to_now_playing)
+               return -ENOSYS;
+
+       if (!params || params_len < 11)
+               return -EINVAL;
+
+       scope = params[0];
+       if (scope > AVRCP_MEDIA_NOW_PLAYING)
+               return -EBADRQC;
+
+       uid = get_be64(&params[1]);
+       counter = get_be16(&params[9]);
+
+       return player->ind->add_to_now_playing(session, transaction, scope, uid,
+                                               counter, player->user_data);
+}
+
+static const struct avrcp_browsing_handler browsing_handlers[] = {
+               { AVRCP_GET_FOLDER_ITEMS, get_folder_items },
+               { AVRCP_CHANGE_PATH, change_path },
+               { AVRCP_GET_ITEM_ATTRIBUTES, get_item_attributes },
+               { AVRCP_PLAY_ITEM, play_item },
+               { AVRCP_SEARCH, search },
+               { AVRCP_ADD_TO_NOW_PLAYING, add_to_now_playing },
+               { },
+};
+
+static void avrcp_set_browsing_handlers(struct avrcp *session,
+                               const struct avrcp_browsing_handler *handlers,
+                               void *user_data)
+{
+       session->browsing_handlers = handlers;
+       session->browsing_data = user_data;
+}
+
+void avrcp_register_player(struct avrcp *session,
+                               const struct avrcp_control_ind *ind,
+                               const struct avrcp_control_cfm *cfm,
+                               void *user_data)
+{
+       struct avrcp_player *player;
+
+       player = g_new0(struct avrcp_player, 1);
+       player->ind = ind;
+       player->cfm = cfm;
+       player->user_data = user_data;
+
+       avrcp_set_control_handlers(session, player_handlers, player);
+       avrcp_set_browsing_handlers(session, browsing_handlers, player);
+       session->player = player;
+}
+
+void avrcp_set_passthrough_handlers(struct avrcp *session,
+                       const struct avrcp_passthrough_handler *handlers,
+                       void *user_data)
+{
+       session->passthrough_handlers = handlers;
+       session->passthrough_data = user_data;
+}
+
+int avrcp_init_uinput(struct avrcp *session, const char *name,
+                                                       const char *address)
+{
+       return avctp_init_uinput(session->conn, name, address);
+}
+
+int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
+                                       uint8_t subunit, uint8_t pdu_id,
+                                       const struct iovec *iov, int iov_cnt)
+{
+       struct iovec pdu[iov_cnt + 1];
+       struct avrcp_header hdr;
+       int i;
+
+       memset(&hdr, 0, sizeof(hdr));
+
+       pdu[0].iov_base = &hdr;
+       pdu[0].iov_len = sizeof(hdr);
+
+       for (i = 0; i < iov_cnt; i++) {
+               pdu[i + 1].iov_base = iov[i].iov_base;
+               pdu[i + 1].iov_len = iov[i].iov_len;
+               hdr.params_len += iov[i].iov_len;
+       }
+
+       hton24(hdr.company_id, IEEEID_BTSIG);
+       hdr.pdu_id = pdu_id;
+       hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE;
+       hdr.params_len = htons(hdr.params_len);
+
+       return avctp_send_vendor(session->conn, transaction, code, subunit,
+                                                       pdu, iov_cnt + 1);
+}
+
+static int status2errno(uint8_t status)
+{
+       switch (status) {
+       case AVRCP_STATUS_INVALID_COMMAND:
+               return -ENOSYS;
+       case AVRCP_STATUS_INVALID_PARAM:
+               return -EINVAL;
+       case AVRCP_STATUS_SUCCESS:
+               return 0;
+       case AVRCP_STATUS_NOT_DIRECTORY:
+               return -ENOTDIR;
+       case AVRCP_STATUS_INVALID_SCOPE:
+               return -EBADRQC;
+       case AVRCP_STATUS_OUT_OF_BOUNDS:
+               return -ERANGE;
+       case AVRCP_STATUS_INTERNAL_ERROR:
+       case AVRCP_STATUS_INVALID_PLAYER_ID:
+       case AVRCP_STATUS_PLAYER_NOT_BROWSABLE:
+       case AVRCP_STATUS_NO_AVAILABLE_PLAYERS:
+       case AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED:
+               return -EPERM;
+       default:
+               return -EPROTO;
+       }
+}
+
+static int parse_status(struct avrcp_header *pdu)
+{
+       if (pdu->params_len < 1)
+               return -EPROTO;
+
+       return status2errno(pdu->params[0]);
+}
+
+static int parse_browsing_status(struct avrcp_browsing_header *pdu)
+{
+       if (pdu->params_len < 1)
+               return -EPROTO;
+
+       return status2errno(pdu->params[0]);
+}
+
+static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit,
+                                       uint8_t pdu_id, const struct iovec *iov,
+                                       int iov_cnt, avctp_rsp_cb func,
+                                       void *user_data)
+{
+       struct iovec pdu[iov_cnt + 1];
+       struct avrcp_header hdr;
+       int i;
+
+       memset(&hdr, 0, sizeof(hdr));
+
+       pdu[0].iov_base = &hdr;
+       pdu[0].iov_len = sizeof(hdr);
+
+       for (i = 0; i < iov_cnt; i++) {
+               pdu[i + 1].iov_base = iov[i].iov_base;
+               pdu[i + 1].iov_len = iov[i].iov_len;
+               hdr.params_len += iov[i].iov_len;
+       }
+
+       hton24(hdr.company_id, IEEEID_BTSIG);
+       hdr.pdu_id = pdu_id;
+       hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE;
+       hdr.params_len = htons(hdr.params_len);
+
+       return avctp_send_vendor_req(session->conn, code, subunit, pdu,
+                                               iov_cnt + 1, func, user_data);
+}
+
+static int avrcp_send_browsing_req(struct avrcp *session, uint8_t pdu_id,
+                                       const struct iovec *iov, int iov_cnt,
+                                       avctp_browsing_rsp_cb func,
+                                       void *user_data)
+{
+       struct iovec pdu[iov_cnt + 1];
+       struct avrcp_browsing_header hdr;
+       int i;
+
+       memset(&hdr, 0, sizeof(hdr));
+
+       for (i = 0; i < iov_cnt; i++) {
+               pdu[i + 1].iov_base = iov[i].iov_base;
+               pdu[i + 1].iov_len = iov[i].iov_len;
+               hdr.params_len += iov[i].iov_len;
+       }
+
+       hdr.pdu_id = pdu_id;
+       hdr.params_len = htons(hdr.params_len);
+
+       pdu[0].iov_base = &hdr;
+       pdu[0].iov_len = sizeof(hdr);
+
+       return avctp_send_browsing_req(session->conn, pdu, iov_cnt + 1,
+                                                       func, user_data);
+}
+
+static int avrcp_send_browsing(struct avrcp *session, uint8_t transaction,
+                               uint8_t pdu_id, const struct iovec *iov,
+                               int iov_cnt)
+{
+       struct iovec pdu[iov_cnt + 1];
+       struct avrcp_browsing_header hdr;
+       int i;
+
+       memset(&hdr, 0, sizeof(hdr));
+
+       for (i = 0; i < iov_cnt; i++) {
+               pdu[i + 1].iov_base = iov[i].iov_base;
+               pdu[i + 1].iov_len = iov[i].iov_len;
+               hdr.params_len += iov[i].iov_len;
+       }
+
+       hdr.pdu_id = pdu_id;
+       hdr.params_len = htons(hdr.params_len);
+
+       pdu[0].iov_base = &hdr;
+       pdu[0].iov_len = sizeof(hdr);
+
+       return avctp_send_browsing(session->conn, transaction, pdu,
+                                                               iov_cnt + 1);
+}
+
+static gboolean get_capabilities_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu;
+       uint8_t number = 0;
+       uint8_t *params = NULL;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->get_capabilities)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       if (pdu->params_len < 2) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       switch (pdu->params[0]) {
+       case CAP_COMPANY_ID:
+       case CAP_EVENTS_SUPPORTED:
+               break;
+       default:
+               err = -EPROTO;
+               goto done;
+       }
+
+       number = pdu->params[1];
+
+       if (number > 0)
+               params = &pdu->params[2];
+
+       err = 0;
+
+done:
+       player->cfm->get_capabilities(session, err, number, params,
+                                                       player->user_data);
+
+       return FALSE;
+}
+
+
+int avrcp_get_capabilities(struct avrcp *session, uint8_t param)
+{
+       struct iovec iov;
+
+       iov.iov_base = &param;
+       iov.iov_len = sizeof(param);
+
+       return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+                                       AVRCP_GET_CAPABILITIES, &iov, 1,
+                                       get_capabilities_rsp, session);
+}
+
+static gboolean register_notification_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu;
+       uint8_t event = 0;
+       uint16_t value16;
+       uint32_t value32;
+       uint64_t value64;
+       uint8_t *params = NULL;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->register_notification)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       if (pdu->params_len < 1) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       event = pdu->params[0];
+
+       switch (event) {
+       case AVRCP_EVENT_STATUS_CHANGED:
+       case AVRCP_EVENT_VOLUME_CHANGED:
+               if (pdu->params_len != 2) {
+                       err = -EPROTO;
+                       goto done;
+               }
+               params = &pdu->params[1];
+               break;
+       case AVRCP_EVENT_TRACK_CHANGED:
+               if (pdu->params_len != 9) {
+                       err = -EPROTO;
+                       goto done;
+               }
+               value64 = get_be64(&pdu->params[1]);
+               params = (uint8_t *) &value64;
+               break;
+       case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+               if (pdu->params_len != 5) {
+                       err = -EPROTO;
+                       goto done;
+               }
+               value32 = get_be32(&pdu->params[1]);
+               params = (uint8_t *) &value32;
+               break;
+       case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+       case AVRCP_EVENT_SETTINGS_CHANGED:
+               if (pdu->params_len < 2) {
+                       err = -EPROTO;
+                       goto done;
+               }
+               params = &pdu->params[1];
+               break;
+       case AVRCP_EVENT_UIDS_CHANGED:
+               if (pdu->params_len != 3) {
+                       err = -EPROTO;
+                       goto done;
+               }
+               value16 = get_be16(&pdu->params[1]);
+               params = (uint8_t *) &value16;
+               break;
+       }
+
+       err = 0;
+
+done:
+       return player->cfm->register_notification(session, err, code, event,
+                                               params, player->user_data);
+}
+
+int avrcp_register_notification(struct avrcp *session, uint8_t event,
+                                                       uint32_t interval)
+{
+       struct iovec iov;
+       uint8_t pdu[5];
+
+       pdu[0] = event;
+       put_be32(interval, &pdu[1]);
+
+       iov.iov_base = pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_req(session, AVC_CTYPE_NOTIFY, AVC_SUBUNIT_PANEL,
+                               AVRCP_REGISTER_NOTIFICATION, &iov, 1,
+                               register_notification_rsp, session);
+}
+
+static gboolean list_attributes_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu = (void *) operands;
+       uint8_t number = 0;
+       uint8_t *attrs = NULL;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->list_attributes)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       number = pdu->params[0];
+       if (number > 0)
+               attrs = &pdu->params[1];
+
+       err = 0;
+
+done:
+       player->cfm->list_attributes(session, err, number, attrs,
+                                                       player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_list_player_attributes(struct avrcp *session)
+{
+       return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+                               AVRCP_LIST_PLAYER_ATTRIBUTES, NULL, 0,
+                               list_attributes_rsp, session);
+}
+
+static int parse_text_rsp(struct avrcp_header *pdu, uint8_t *number,
+                                       uint8_t *attrs, char **text)
+{
+       uint8_t *ptr;
+       uint16_t params_len;
+       int i;
+
+       if (pdu->params_len < 1)
+               return -EPROTO;
+
+       *number = pdu->params[0];
+       if (*number > AVRCP_ATTRIBUTE_LAST) {
+               *number = 0;
+               return -EPROTO;
+       }
+
+       params_len = pdu->params_len - 1;
+       for (i = 0, ptr = &pdu->params[1]; i < *number && params_len > 0; i++) {
+               uint8_t len;
+
+               if (params_len < 4)
+                       goto fail;
+
+               attrs[i] = ptr[0];
+               len = ptr[3];
+
+               params_len -= 4;
+               ptr += 4;
+
+               if (len > params_len)
+                       goto fail;
+
+               if (len > 0) {
+                       text[i] = g_strndup((const char *) &ptr[4], len);
+                       params_len -= len;
+                       ptr += len;
+               }
+       }
+
+       if (i != *number)
+               goto fail;
+
+       return 0;
+
+fail:
+       for (i -= 1; i >= 0; i--)
+               g_free(text[i]);
+
+       *number = 0;
+
+       return -EPROTO;
+}
+
+static gboolean get_attribute_text_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu;
+       uint8_t number = 0;
+       uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+       char *text[AVRCP_ATTRIBUTE_LAST];
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->get_attribute_text)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       err = parse_text_rsp(pdu, &number, attrs, text);
+
+done:
+       player->cfm->get_attribute_text(session, err, number, attrs, text,
+                                                       player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number,
+                                                               uint8_t *attrs)
+{
+       struct iovec iov[2];
+
+       if (!number || number > AVRCP_ATTRIBUTE_LAST)
+               return -EINVAL;
+
+       iov[0].iov_base = &number;
+       iov[0].iov_len = sizeof(number);
+
+       iov[1].iov_base = attrs;
+       iov[1].iov_len = number;
+
+       return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+                               AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, iov, 2,
+                               get_attribute_text_rsp, session);
+}
+
+static gboolean list_values_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu;
+       uint8_t number = 0;
+       uint8_t *values = NULL;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->list_values)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       number = pdu->params[0];
+       if (number > 0)
+               values = &pdu->params[1];
+
+       err = 0;
+
+done:
+       player->cfm->list_values(session, err, number, values,
+                                                       player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_list_player_values(struct avrcp *session, uint8_t attr)
+{
+       struct iovec iov;
+
+       iov.iov_base = &attr;
+       iov.iov_len = sizeof(attr);
+
+       return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+                                       AVRCP_LIST_PLAYER_VALUES, &iov, 1,
+                                       list_values_rsp, session);
+}
+
+static gboolean get_value_text_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu;
+       uint8_t number = 0;
+       uint8_t values[AVRCP_ATTRIBUTE_LAST];
+       char *text[AVRCP_ATTRIBUTE_LAST];
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->get_value_text)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       err = parse_text_rsp(pdu, &number, values, text);
+
+done:
+       player->cfm->get_value_text(session, err, number, values, text,
+                                                       player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr,
+                                       uint8_t number, uint8_t *values)
+{
+       struct iovec iov[2];
+       uint8_t pdu[2];
+
+       if (!number)
+               return -EINVAL;
+
+       pdu[0] = attr;
+       pdu[1] = number;
+
+       iov[0].iov_base = pdu;
+       iov[0].iov_len = sizeof(pdu);
+
+       iov[1].iov_base = values;
+       iov[1].iov_len = number;
+
+       return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+                                       AVRCP_GET_PLAYER_VALUE_TEXT, iov, 2,
+                                       get_value_text_rsp, session);
+}
+
+static int parse_value(struct avrcp_header *pdu, uint8_t *number,
+                                       uint8_t *attrs, uint8_t *values)
+{
+       int i;
+
+       if (pdu->params_len < 1)
+               return -EPROTO;
+
+       *number = pdu->params[0];
+
+       /*
+        * Check if PDU is big enough to hold the number of (attribute, value)
+        * tuples.
+        */
+       if (*number > AVRCP_ATTRIBUTE_LAST ||
+                                       1 + *number * 2 != pdu->params_len) {
+               number = 0;
+               return -EPROTO;
+       }
+
+       for (i = 0; i < *number; i++) {
+               attrs[i] = pdu->params[i * 2 + 1];
+               values[i] = pdu->params[i * 2 + 2];
+       }
+
+       return 0;
+}
+
+static gboolean get_value_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu = (void *) operands;
+       uint8_t number = 0;
+       uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
+       uint8_t values[AVRCP_ATTRIBUTE_LAST];
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->get_value)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       err = parse_value(pdu, &number, attrs, values);
+
+done:
+       player->cfm->get_value(session, err, number, attrs, values,
+                                                       player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_get_current_player_value(struct avrcp *session, uint8_t number,
+                                                       uint8_t *attrs)
+
+{
+       struct iovec iov[2];
+
+       if (number > AVRCP_ATTRIBUTE_LAST)
+               return -EINVAL;
+
+       iov[0].iov_base = &number;
+       iov[0].iov_len = sizeof(number);
+
+       iov[1].iov_base = attrs;
+       iov[1].iov_len = number;
+
+       return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+                               AVRCP_GET_CURRENT_PLAYER_VALUE, iov, 2,
+                               get_value_rsp, session);
+}
+
+static gboolean set_value_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->set_value)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       err = 0;
+
+done:
+       player->cfm->set_value(session, err, player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_set_player_value(struct avrcp *session, uint8_t number,
+                                       uint8_t *attrs, uint8_t *values)
+{
+       struct iovec iov;
+       uint8_t pdu[2 * AVRCP_ATTRIBUTE_LAST + 1];
+       int i;
+
+       if (number > AVRCP_ATTRIBUTE_LAST)
+               return -EINVAL;
+
+       pdu[0] = number;
+
+       for (i = 0; i < number; i++) {
+               pdu[i * 2 + 1] = attrs[i];
+               pdu[i * 2 + 2] = values[i];
+       }
+
+       iov.iov_base = pdu;
+       iov.iov_len = 1 + number * 2;
+
+       return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+                                       AVRCP_SET_PLAYER_VALUE, &iov, 1,
+                                       set_value_rsp, session);
+}
+
+static gboolean get_play_status_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu;
+       uint8_t status = 0;
+       uint32_t position = 0;
+       uint32_t duration = 0;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->get_play_status)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       if (pdu->params_len < 5) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       duration = get_be32(&pdu->params[0]);
+       position = get_be32(&pdu->params[4]);
+       status = pdu->params[8];
+       err = 0;
+
+done:
+       player->cfm->get_play_status(session, err, status, position, duration,
+                                                       player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_get_play_status(struct avrcp *session)
+{
+       return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+                               AVRCP_GET_PLAY_STATUS, NULL, 0,
+                               get_play_status_rsp, session);
+}
+
+static gboolean set_volume_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu;
+       uint8_t value = 0;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->set_volume)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       if (pdu->params_len < 1) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       value = pdu->params[0] & 0x7f;
+       err = 0;
+
+done:
+       player->cfm->set_volume(session, err, value, player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_set_volume(struct avrcp *session, uint8_t volume)
+{
+       struct iovec iov;
+
+       iov.iov_base = &volume;
+       iov.iov_len = sizeof(volume);
+
+       return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+                               AVRCP_SET_ABSOLUTE_VOLUME, &iov, 1,
+                               set_volume_rsp, session);
+}
+
+static int parse_attribute_list(uint8_t *params, uint16_t params_len,
+                               uint8_t number, uint32_t *attrs, char **text)
+{
+       int i;
+
+       if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+               return -EPROTO;
+
+       for (i = 0; number > 0 && params_len > i; number--) {
+               uint16_t charset, len;
+
+               if (params_len < 8)
+                       goto fail;
+
+               attrs[i] = get_be32(&params[i]);
+               i += sizeof(uint32_t);
+
+               charset = get_be16(&params[i]);
+               i += sizeof(uint16_t);
+
+               len = get_be16(&params[i]);
+               i += sizeof(uint16_t);
+
+               if (len > params_len)
+                       goto fail;
+
+               if (charset == AVRCP_CHARSET_UTF8)
+                       text[i] = g_strndup((const char *) &params[i], len);
+
+               i += len;
+       }
+
+       return 0;
+
+fail:
+       for (i -= 1; i >= 0; i--)
+               g_free(text[i]);
+
+       return -EPROTO;
+}
+
+static int parse_elements(struct avrcp_header *pdu, uint8_t *number,
+                                               uint32_t *attrs, char **text)
+{
+       if (pdu->params_len < 1)
+               return -EPROTO;
+
+       *number = pdu->params[0];
+       if (*number > AVRCP_MEDIA_ATTRIBUTE_LAST) {
+               *number = 0;
+               return -EPROTO;
+       }
+
+       return parse_attribute_list(&pdu->params[1], pdu->params_len - 1,
+                                                       *number, attrs, text);
+}
+
+static int parse_items(struct avrcp_browsing_header *pdu, uint8_t *number,
+                                               uint32_t *attrs, char **text)
+{
+       if (pdu->params_len < 2)
+               return -EPROTO;
+
+       *number = pdu->params[1];
+       if (*number > AVRCP_MEDIA_ATTRIBUTE_LAST) {
+               *number = 0;
+               return -EPROTO;
+       }
+
+       return parse_attribute_list(&pdu->params[2], pdu->params_len - 2,
+                                                       *number, attrs, text);
+}
+
+static gboolean get_element_attributes_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu;
+       uint8_t number = 0;
+       uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+       char *text[AVRCP_MEDIA_ATTRIBUTE_LAST];
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->get_element_attributes)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       if (code == AVC_CTYPE_REJECTED) {
+               err = parse_status(pdu);
+               goto done;
+       }
+
+       err = parse_elements(pdu, &number, attrs, text);
+
+done:
+       player->cfm->get_element_attributes(session, err, number, attrs, text,
+                                                       player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_get_element_attributes(struct avrcp *session)
+{
+       struct iovec iov;
+       uint8_t pdu[9];
+
+       /* This returns all attributes */
+       memset(pdu, 0, sizeof(pdu));
+
+       iov.iov_base = pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL,
+                               AVRCP_GET_ELEMENT_ATTRIBUTES, &iov, 1,
+                               get_element_attributes_rsp, session);
+}
+
+static gboolean set_addressed_rsp(struct avctp *conn,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_header *pdu;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->set_addressed)
+               return FALSE;
+
+       pdu = parse_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       err = parse_status(pdu);
+
+done:
+       player->cfm->set_addressed(session, err, player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id)
+{
+       struct iovec iov;
+       uint8_t pdu[2];
+
+       put_be16(player_id, pdu);
+
+       iov.iov_base = pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
+                                       AVRCP_SET_ADDRESSED_PLAYER, &iov, 1,
+                                       set_addressed_rsp, session);
+}
+
+static gboolean set_browsed_rsp(struct avctp *conn, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_browsing_header *pdu;
+       uint16_t counter = 0;
+       uint32_t items = 0;
+       uint8_t depth = 0, count;
+       char **folders, *path = NULL;
+       int err;
+       size_t i;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->set_browsed)
+               return FALSE;
+
+       pdu = parse_browsing_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       err = parse_browsing_status(pdu);
+       if (err < 0)
+               goto done;
+
+       if (pdu->params_len < 10) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       counter = get_be16(&pdu->params[1]);
+       items = get_be32(&pdu->params[3]);
+       depth = pdu->params[9];
+
+       folders = g_new0(char *, depth + 2);
+       folders[0] = g_strdup("/Filesystem");
+
+       for (i = 10, count = 1; count - 1 < depth && i < pdu->params_len;
+                                                               count++) {
+               uint8_t len;
+
+               len = pdu->params[i++];
+
+               if (i + len > pdu->params_len || len == 0) {
+                       g_strfreev(folders);
+                       err = -EPROTO;
+                       goto done;
+               }
+
+               folders[count] = g_memdup(&pdu->params[i], len);
+               i += len;
+       }
+
+       path = g_build_pathv("/", folders);
+       g_strfreev(folders);
+
+done:
+       player->cfm->set_browsed(session, err, counter, items, path,
+                                                       player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id)
+{
+       struct iovec iov;
+       uint8_t pdu[2];
+
+       put_be16(player_id, pdu);
+
+       iov.iov_base = pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_browsing_req(session, AVRCP_SET_BROWSED_PLAYER,
+                                       &iov, 1, set_browsed_rsp, session);
+}
+
+static gboolean get_folder_items_rsp(struct avctp *conn,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_browsing_header *pdu;
+       uint16_t counter = 0, number = 0;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->get_folder_items)
+               return FALSE;
+
+       pdu = parse_browsing_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       err = parse_browsing_status(pdu);
+       if (err < 0)
+               goto done;
+
+       if (pdu->params_len < 5) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       counter = get_be16(&pdu->params[1]);
+       number = get_be16(&pdu->params[3]);
+
+       /* FIXME: Add proper parsing for each item type */
+
+done:
+       player->cfm->get_folder_items(session, err, counter, number,
+                                       &pdu->params[5], player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_get_folder_items(struct avrcp *session, uint8_t scope,
+                               uint32_t start, uint32_t end, uint8_t number,
+                               uint32_t *attrs)
+{
+
+       struct iovec iov[2];
+       uint8_t pdu[10];
+       int i;
+
+       pdu[0] = scope;
+       put_be32(start, &pdu[1]);
+       put_be32(end, &pdu[5]);
+       pdu[9] = number;
+
+       iov[0].iov_base = pdu;
+       iov[0].iov_len = sizeof(pdu);
+
+       if (!number)
+               return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS,
+                                               iov, 1, get_folder_items_rsp,
+                                               session);
+
+       for (i = 0; i < number; i++)
+               put_be32(attrs[i], &attrs[i]);
+
+       iov[1].iov_base = attrs;
+       iov[1].iov_len = number * sizeof(*attrs);
+
+       return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS,
+                                       iov, 2, get_folder_items_rsp, session);
+}
+
+static gboolean change_path_rsp(struct avctp *conn, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_browsing_header *pdu;
+       uint32_t items = 0;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->change_path)
+               return FALSE;
+
+       pdu = parse_browsing_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       err = parse_browsing_status(pdu);
+       if (err < 0)
+               goto done;
+
+       if (pdu->params_len < 5) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       items = get_be32(&pdu->params[1]);
+
+done:
+       player->cfm->change_path(session, err, items, player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid,
+                                                       uint16_t counter)
+{
+       struct iovec iov;
+       uint8_t pdu[11];
+
+       put_be16(counter, &pdu[0]);
+       pdu[2] = direction;
+       put_be64(uid, &pdu[3]);
+
+       iov.iov_base = pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_browsing_req(session, AVRCP_CHANGE_PATH,
+                                       &iov, 1, change_path_rsp, session);
+}
+
+static gboolean get_item_attributes_rsp(struct avctp *conn, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_browsing_header *pdu;
+       uint8_t number = 0;
+       uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST];
+       char *text[AVRCP_MEDIA_ATTRIBUTE_LAST];
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->get_item_attributes)
+               return FALSE;
+
+       pdu = parse_browsing_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       err = parse_browsing_status(pdu);
+       if (err < 0)
+               goto done;
+
+       err = parse_items(pdu, &number, attrs, text);
+
+done:
+       player->cfm->get_item_attributes(session, err, number, attrs, text,
+                                                       player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope,
+                               uint64_t uid, uint16_t counter, uint8_t number,
+                               uint32_t *attrs)
+{
+       struct iovec iov[2];
+       uint8_t pdu[12];
+       int i;
+
+       pdu[0] = scope;
+       put_be64(uid, &pdu[1]);
+       put_be16(counter, &pdu[9]);
+       pdu[11] = number;
+
+       iov[0].iov_base = pdu;
+       iov[0].iov_len = sizeof(pdu);
+
+       if (!number)
+               return avrcp_send_browsing_req(session,
+                                               AVRCP_GET_ITEM_ATTRIBUTES,
+                                               iov, 1, get_item_attributes_rsp,
+                                               session);
+
+       if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+               return -EINVAL;
+
+       for (i = 0; i < number; i++) {
+               if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST ||
+                               attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL)
+                       return -EINVAL;
+               put_be32(attrs[i], &attrs[i]);
+       }
+
+       iov[1].iov_base = attrs;
+       iov[1].iov_len = number * sizeof(*attrs);
+
+       return avrcp_send_browsing_req(session, AVRCP_GET_ITEM_ATTRIBUTES,
+                                       iov, 2, get_item_attributes_rsp,
+                                       session);
+}
+
+static gboolean play_item_rsp(struct avctp *conn, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_browsing_header *pdu;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->play_item)
+               return FALSE;
+
+       pdu = parse_browsing_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       err = parse_browsing_status(pdu);
+
+done:
+       player->cfm->play_item(session, err, player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid,
+                                                       uint16_t counter)
+{
+       struct iovec iov;
+       uint8_t pdu[11];
+
+       if (scope > AVRCP_MEDIA_NOW_PLAYING)
+               return -EINVAL;
+
+       pdu[0] = scope;
+       put_be64(uid, &pdu[1]);
+       put_be16(counter, &pdu[9]);
+
+       iov.iov_base = pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_browsing_req(session, AVRCP_PLAY_ITEM, &iov, 1,
+                                               play_item_rsp, session);
+}
+
+static gboolean search_rsp(struct avctp *conn, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_browsing_header *pdu;
+       uint16_t counter = 0;
+       uint32_t items = 0;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->search)
+               return FALSE;
+
+       pdu = parse_browsing_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       err = parse_browsing_status(pdu);
+       if (err < 0)
+               goto done;
+
+       if (pdu->params_len < 7) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       counter = get_be16(&pdu->params[1]);
+       items = get_be32(&pdu->params[3]);
+
+       err = 0;
+
+done:
+       player->cfm->search(session, err, counter, items, player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_search(struct avrcp *session, const char *string)
+{
+       struct iovec iov[2];
+       uint8_t pdu[4];
+       size_t len;
+
+       if (!string)
+               return -EINVAL;
+
+       len = strnlen(string, UINT8_MAX);
+
+       put_be16(AVRCP_CHARSET_UTF8, &pdu[0]);
+       put_be16(len, &pdu[2]);
+
+       iov[0].iov_base = pdu;
+       iov[0].iov_len = sizeof(pdu);
+
+       iov[1].iov_base = (void *) string;
+       iov[1].iov_len = len;
+
+       return avrcp_send_browsing_req(session, AVRCP_SEARCH, iov, 2,
+                                                       search_rsp, session);
+}
+
+static gboolean add_to_now_playing_rsp(struct avctp *conn, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       struct avrcp *session = user_data;
+       struct avrcp_player *player = session->player;
+       struct avrcp_browsing_header *pdu;
+       int err;
+
+       DBG("");
+
+       if (!player || !player->cfm || !player->cfm->add_to_now_playing)
+               return FALSE;
+
+       pdu = parse_browsing_pdu(operands, operand_count);
+       if (!pdu) {
+               err = -EPROTO;
+               goto done;
+       }
+
+       err = parse_browsing_status(pdu);
+
+done:
+       player->cfm->add_to_now_playing(session, err, player->user_data);
+
+       return FALSE;
+}
+
+int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid,
+                                                       uint16_t counter)
+{
+       struct iovec iov;
+       uint8_t pdu[11];
+
+       if (scope > AVRCP_MEDIA_NOW_PLAYING)
+               return -EINVAL;
+
+       pdu[0] = scope;
+       put_be64(uid, &pdu[1]);
+       put_be16(counter, &pdu[9]);
+
+       iov.iov_base = pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_browsing_req(session, AVRCP_ADD_TO_NOW_PLAYING,
+                                       &iov, 1, add_to_now_playing_rsp,
+                                       session);
+}
+
+int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
+                                               uint8_t number, uint8_t *events)
+{
+       uint8_t pdu[2];
+       struct iovec iov[2];
+
+       if (number > AVRCP_EVENT_LAST)
+               return -EINVAL;
+
+       pdu[0] = CAP_EVENTS_SUPPORTED;
+       pdu[1] = number;
+
+       iov[0].iov_base = pdu;
+       iov[0].iov_len = sizeof(pdu);
+
+       iov[1].iov_base = events;
+       iov[1].iov_len = number;
+
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                               AVC_SUBUNIT_PANEL, AVRCP_GET_CAPABILITIES,
+                               iov, 2);
+}
+
+int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint8_t *attrs)
+{
+       struct iovec iov[2];
+
+       if (number > AVRCP_ATTRIBUTE_LAST)
+               return -EINVAL;
+
+       iov[0].iov_base = &number;
+       iov[0].iov_len = sizeof(number);
+
+       if (!number)
+               return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                               AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES,
+                               iov, 1);
+
+       iov[1].iov_base = attrs;
+       iov[1].iov_len = number;
+
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                               AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES,
+                               iov, 2);
+}
+
+int avrcp_get_player_attribute_text_rsp(struct avrcp *session,
+                                       uint8_t transaction, uint8_t number,
+                                       uint8_t *attrs, const char **text)
+{
+       struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2];
+       uint8_t val[AVRCP_ATTRIBUTE_LAST][4];
+       int i;
+
+       if (number > AVRCP_ATTRIBUTE_LAST)
+               return -EINVAL;
+
+       iov[0].iov_base = &number;
+       iov[0].iov_len = sizeof(number);
+
+       for (i = 0; i < number; i++) {
+               uint8_t len = 0;
+
+               if (attrs[i] > AVRCP_ATTRIBUTE_LAST ||
+                                       attrs[i] == AVRCP_ATTRIBUTE_ILEGAL)
+                       return -EINVAL;
+
+               if (text[i])
+                       len = strlen(text[i]);
+
+               val[i][0] = attrs[i];
+               put_be16(AVRCP_CHARSET_UTF8, &val[i][1]);
+               val[i][3] = len;
+
+               iov[i + 1].iov_base = val[i];
+               iov[i + 1].iov_len = sizeof(val[i]);
+
+               iov[i + 2].iov_base = (void *) text[i];
+               iov[i + 2].iov_len = len;
+       }
+
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                       AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+                       iov, 1 + i * 2);
+}
+
+int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint8_t *values)
+{
+       struct iovec iov[2];
+
+       if (number > AVRCP_ATTRIBUTE_LAST)
+               return -EINVAL;
+
+       iov[0].iov_base = &number;
+       iov[0].iov_len = sizeof(number);
+
+       iov[1].iov_base = values;
+       iov[1].iov_len = number;
+
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                               AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_VALUES,
+                               iov, 2);
+}
+
+int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction,
+                               uint32_t position, uint32_t duration,
+                               uint8_t status)
+{
+       struct iovec iov;
+       uint8_t pdu[9];
+
+       put_be32(duration, &pdu[0]);
+       put_be32(position, &pdu[4]);
+       pdu[8] = status;
+
+       iov.iov_base = &pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                               AVC_SUBUNIT_PANEL, AVRCP_GET_PLAY_STATUS,
+                               &iov, 1);
+}
+
+int avrcp_get_player_values_text_rsp(struct avrcp *session,
+                                       uint8_t transaction, uint8_t number,
+                                       uint8_t *values, const char **text)
+{
+       struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2];
+       uint8_t val[AVRCP_ATTRIBUTE_LAST][4];
+       int i;
+
+       if (number > AVRCP_ATTRIBUTE_LAST)
+               return -EINVAL;
+
+       iov[0].iov_base = &number;
+       iov[0].iov_len = sizeof(number);
+
+       for (i = 0; i < number; i++) {
+               uint8_t len = 0;
+
+               if (text[i])
+                       len = strlen(text[i]);
+
+               val[i][0] = values[i];
+               put_be16(AVRCP_CHARSET_UTF8, &val[i][1]);
+               val[i][3] = len;
+
+               iov[i + 1].iov_base = val[i];
+               iov[i + 1].iov_len = sizeof(val[i]);
+
+               iov[i + 2].iov_base = (void *) text[i];
+               iov[i + 2].iov_len = len;
+       }
+
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                               AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_VALUE_TEXT,
+                               iov, 1 + i * 2);
+}
+
+int avrcp_get_current_player_value_rsp(struct avrcp *session,
+                                       uint8_t transaction, uint8_t number,
+                                       uint8_t *attrs, uint8_t *values)
+{
+       struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST];
+       uint8_t val[AVRCP_ATTRIBUTE_LAST][2];
+       int i;
+
+       if (number > AVRCP_ATTRIBUTE_LAST)
+               return -EINVAL;
+
+       iov[0].iov_base = &number;
+       iov[0].iov_len = sizeof(number);
+
+       for (i = 0; i < number; i++) {
+               val[i][0] = attrs[i];
+               val[i][1] = values[i];
+
+               iov[i + 1].iov_base = val[i];
+               iov[i + 1].iov_len = sizeof(val[i]);
+       }
+
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                       AVC_SUBUNIT_PANEL, AVRCP_GET_CURRENT_PLAYER_VALUE,
+                       iov, 1 + i);
+}
+
+int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction)
+{
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                       AVC_SUBUNIT_PANEL, AVRCP_SET_PLAYER_VALUE, NULL, 0);
+}
+
+int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t *params, size_t params_len)
+{
+       struct iovec iov;
+
+       iov.iov_base = params;
+       iov.iov_len = params_len;
+
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                               AVC_SUBUNIT_PANEL, AVRCP_GET_ELEMENT_ATTRIBUTES,
+                               &iov, 1);
+}
+
+int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t code, uint8_t *params,
+                                       size_t params_len)
+{
+       struct iovec iov;
+
+       iov.iov_base = params;
+       iov.iov_len = params_len;
+
+       return avrcp_send(session, transaction, code,
+                               AVC_SUBUNIT_PANEL, AVRCP_REGISTER_NOTIFICATION,
+                               &iov, 1);
+}
+
+int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction,
+                                                       uint8_t volume)
+{
+       struct iovec iov;
+
+       if (volume > 127)
+               return -EINVAL;
+
+       iov.iov_base = &volume;
+       iov.iov_len = sizeof(volume);
+
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                               AVC_SUBUNIT_PANEL, AVRCP_SET_ABSOLUTE_VOLUME,
+                               &iov, 1);
+}
+
+int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction,
+                                                       uint8_t status)
+{
+       struct iovec iov;
+
+       iov.iov_base = &status;
+       iov.iov_len = sizeof(status);
+
+       return avrcp_send(session, transaction, AVC_CTYPE_STABLE,
+                               AVC_SUBUNIT_PANEL, AVRCP_SET_ADDRESSED_PLAYER,
+                               &iov, 1);
+}
+
+int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint16_t counter, uint8_t number,
+                                       uint8_t *type, uint16_t *len,
+                                       uint8_t **params)
+{
+       struct iovec iov[UINT8_MAX * 2 + 1];
+       uint8_t pdu[5];
+       uint8_t item[UINT8_MAX][3];
+       int i;
+
+       pdu[0] = AVRCP_STATUS_SUCCESS;
+       put_be16(counter, &pdu[1]);
+       put_be16(number, &pdu[3]);
+
+       iov[0].iov_base = pdu;
+       iov[0].iov_len = sizeof(pdu);
+
+       for (i = 0; i < number; i++) {
+               item[i][0] = type[i];
+               put_be16(len[i], &item[i][1]);
+
+               iov[i * 2 + 1].iov_base = item[i];
+               iov[i * 2 + 1].iov_len = sizeof(item[i]);
+
+               iov[i * 2 + 2].iov_base = params[i];
+               iov[i * 2 + 2].iov_len = len[i];
+       }
+
+       return avrcp_send_browsing(session, transaction, AVRCP_GET_FOLDER_ITEMS,
+                                                       iov, number * 2 + 1);
+}
+
+int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction,
+                                                               uint32_t items)
+{
+       struct iovec iov;
+       uint8_t pdu[5];
+
+       pdu[0] = AVRCP_STATUS_SUCCESS;
+       put_be32(items, &pdu[1]);
+
+       iov.iov_base = pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_browsing(session, transaction, AVRCP_CHANGE_PATH,
+                                                               &iov, 1);
+}
+
+int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint32_t *attrs,
+                                       const char **text)
+{
+       struct iovec iov[AVRCP_MEDIA_ATTRIBUTE_LAST * 2 + 1];
+       uint8_t val[AVRCP_MEDIA_ATTRIBUTE_LAST][8];
+       uint8_t pdu[2];
+       int i;
+
+       if (number > AVRCP_MEDIA_ATTRIBUTE_LAST)
+               return -EINVAL;
+
+       pdu[0] = AVRCP_STATUS_SUCCESS;
+       pdu[1] = number;
+
+       iov[0].iov_base = pdu;
+       iov[0].iov_len = sizeof(pdu);
+
+       for (i = 0; i < number; i++) {
+               uint16_t len = 0;
+
+               if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST ||
+                               attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL)
+                       return -EINVAL;
+
+               if (text[i])
+                       len = strlen(text[i]);
+
+               put_be32(attrs[i], &val[i][0]);
+               put_be16(AVRCP_CHARSET_UTF8, &val[i][4]);
+               put_be16(len, &val[i][6]);
+
+               iov[i + 1].iov_base = val[i];
+               iov[i + 1].iov_len = sizeof(val[i]);
+
+               iov[i + 2].iov_base = (void *) text[i];
+               iov[i + 2].iov_len = len;
+       }
+
+       return avrcp_send_browsing(session, transaction,
+                                       AVRCP_GET_ITEM_ATTRIBUTES, iov,
+                                       number * 2 + 1);
+}
+
+int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction)
+{
+       struct iovec iov;
+       uint8_t pdu;
+
+       pdu = AVRCP_STATUS_SUCCESS;
+
+       iov.iov_base = &pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_browsing(session, transaction, AVRCP_PLAY_ITEM,
+                                                               &iov, 1);
+}
+
+int avrcp_search_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint16_t counter, uint32_t items)
+{
+       struct iovec iov;
+       uint8_t pdu[7];
+
+       pdu[0] = AVRCP_STATUS_SUCCESS;
+       put_be16(counter, &pdu[1]);
+       put_be32(items, &pdu[3]);
+
+       iov.iov_base = pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_browsing(session, transaction, AVRCP_SEARCH,
+                                                               &iov, 1);
+}
+
+int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction)
+{
+       struct iovec iov;
+       uint8_t pdu;
+
+       pdu = AVRCP_STATUS_SUCCESS;
+
+       iov.iov_base = &pdu;
+       iov.iov_len = sizeof(pdu);
+
+       return avrcp_send_browsing(session, transaction,
+                                       AVRCP_ADD_TO_NOW_PLAYING, &iov, 1);
+}
+
+int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op)
+{
+       uint8_t params[5];
+
+       if (!vendor)
+               return avctp_send_passthrough(session->conn, op, NULL, 0);
+
+       hton24(params, vendor);
+       put_be16(op, &params[3]);
+
+       return avctp_send_passthrough(session->conn, AVC_VENDOR_UNIQUE, params,
+                                                               sizeof(params));
+}
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
new file mode 100644 (file)
index 0000000..ee03958
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/* Control PDU ids */
+#define AVRCP_GET_CAPABILITIES         0x10
+#define AVRCP_LIST_PLAYER_ATTRIBUTES   0X11
+#define AVRCP_LIST_PLAYER_VALUES       0x12
+#define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13
+#define AVRCP_SET_PLAYER_VALUE         0x14
+#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT        0x15
+#define AVRCP_GET_PLAYER_VALUE_TEXT    0x16
+#define AVRCP_DISPLAYABLE_CHARSET      0x17
+#define AVRCP_CT_BATTERY_STATUS                0x18
+#define AVRCP_GET_ELEMENT_ATTRIBUTES   0x20
+#define AVRCP_GET_PLAY_STATUS          0x30
+#define AVRCP_REGISTER_NOTIFICATION    0x31
+#define AVRCP_REQUEST_CONTINUING       0x40
+#define AVRCP_ABORT_CONTINUING         0x41
+#define AVRCP_SET_ABSOLUTE_VOLUME      0x50
+#define AVRCP_SET_ADDRESSED_PLAYER     0x60
+#define AVRCP_SET_BROWSED_PLAYER       0x70
+#define AVRCP_GET_FOLDER_ITEMS         0x71
+#define AVRCP_CHANGE_PATH              0x72
+#define AVRCP_GET_ITEM_ATTRIBUTES      0x73
+#define AVRCP_PLAY_ITEM                        0x74
+#define AVRCP_SEARCH                   0x80
+#define AVRCP_ADD_TO_NOW_PLAYING       0x90
+#define AVRCP_GENERAL_REJECT           0xA0
+
+/* Notification events */
+#define AVRCP_EVENT_STATUS_CHANGED             0x01
+#define AVRCP_EVENT_TRACK_CHANGED              0x02
+#define AVRCP_EVENT_TRACK_REACHED_END          0x03
+#define AVRCP_EVENT_TRACK_REACHED_START                0x04
+#define AVRCP_EVENT_PLAYBACK_POS_CHANGED       0x05
+#define AVRCP_EVENT_SETTINGS_CHANGED           0x08
+#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED  0x0a
+#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED   0x0b
+#define AVRCP_EVENT_UIDS_CHANGED               0x0c
+#define AVRCP_EVENT_VOLUME_CHANGED             0x0d
+#define AVRCP_EVENT_LAST                       AVRCP_EVENT_VOLUME_CHANGED
+
+/* Status codes */
+#define AVRCP_STATUS_INVALID_COMMAND           0x00
+#define AVRCP_STATUS_INVALID_PARAM             0x01
+#define AVRCP_STATUS_PARAM_NOT_FOUND           0x02
+#define AVRCP_STATUS_INTERNAL_ERROR            0x03
+#define AVRCP_STATUS_SUCCESS                   0x04
+#define AVRCP_STATUS_NOT_DIRECTORY             0x08
+#define AVRCP_STATUS_DOES_NOT_EXIST            0x09
+#define AVRCP_STATUS_INVALID_SCOPE             0x0a
+#define AVRCP_STATUS_OUT_OF_BOUNDS             0x0b
+#define AVRCP_STATUS_INVALID_PLAYER_ID         0x11
+#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE      0x12
+#define AVRCP_STATUS_NO_AVAILABLE_PLAYERS      0x15
+#define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED  0x16
+
+/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
+#define CAP_COMPANY_ID                         0x02
+#define CAP_EVENTS_SUPPORTED                   0x03
+
+/* Player Attributes */
+#define AVRCP_ATTRIBUTE_ILEGAL                 0x00
+#define AVRCP_ATTRIBUTE_EQUALIZER              0x01
+#define AVRCP_ATTRIBUTE_REPEAT_MODE            0x02
+#define AVRCP_ATTRIBUTE_SHUFFLE                        0x03
+#define AVRCP_ATTRIBUTE_SCAN                   0x04
+#define AVRCP_ATTRIBUTE_LAST                   AVRCP_ATTRIBUTE_SCAN
+
+/* equalizer values */
+#define AVRCP_EQUALIZER_OFF            0x01
+#define AVRCP_EQUALIZER_ON             0x02
+
+/* repeat mode values */
+#define AVRCP_REPEAT_MODE_OFF          0x01
+#define AVRCP_REPEAT_MODE_SINGLE       0x02
+#define AVRCP_REPEAT_MODE_ALL          0x03
+#define AVRCP_REPEAT_MODE_GROUP                0x04
+
+/* shuffle values */
+#define AVRCP_SHUFFLE_OFF              0x01
+#define AVRCP_SHUFFLE_ALL              0x02
+#define AVRCP_SHUFFLE_GROUP            0x03
+
+/* scan values */
+#define AVRCP_SCAN_OFF                 0x01
+#define AVRCP_SCAN_ALL                 0x02
+#define AVRCP_SCAN_GROUP               0x03
+
+/* media attributes */
+#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL  0x00
+#define AVRCP_MEDIA_ATTRIBUTE_TITLE    0x01
+#define AVRCP_MEDIA_ATTRIBUTE_ARTIST   0x02
+#define AVRCP_MEDIA_ATTRIBUTE_ALBUM    0x03
+#define AVRCP_MEDIA_ATTRIBUTE_TRACK    0x04
+#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05
+#define AVRCP_MEDIA_ATTRIBUTE_GENRE    0x06
+#define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07
+#define AVRCP_MEDIA_ATTRIBUTE_LAST     AVRCP_MEDIA_ATTRIBUTE_DURATION
+
+/* Media Scope */
+#define AVRCP_MEDIA_PLAYER_LIST                        0x00
+#define AVRCP_MEDIA_PLAYER_VFS                 0x01
+#define AVRCP_MEDIA_SEARCH                     0x02
+#define AVRCP_MEDIA_NOW_PLAYING                        0x03
+
+/* Company IDs for vendor dependent commands */
+#define IEEEID_BTSIG           0x001958
+
+struct avrcp;
+
+struct avrcp_control_ind {
+       int (*get_capabilities) (struct avrcp *session, uint8_t transaction,
+                                                       void *user_data);
+       int (*list_attributes) (struct avrcp *session, uint8_t transaction,
+                                                       void *user_data);
+       int (*get_attribute_text) (struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint8_t *attrs,
+                                       void *user_data);
+       int (*list_values) (struct avrcp *session, uint8_t transaction,
+                                       uint8_t attr, void *user_data);
+       int (*get_value_text) (struct avrcp *session, uint8_t transaction,
+                                       uint8_t attr, uint8_t number,
+                                       uint8_t *values, void *user_data);
+       int (*get_value) (struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint8_t *attrs,
+                                       void *user_data);
+       int (*set_value) (struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint8_t *attrs,
+                                       void *user_data);
+       int (*get_play_status) (struct avrcp *session, uint8_t transaction,
+                                       void *user_data);
+       int (*get_element_attributes) (struct avrcp *session,
+                                       uint8_t transaction, uint64_t uid,
+                                       uint8_t number, uint32_t *attrs,
+                                       void *user_data);
+       int (*register_notification) (struct avrcp *session,
+                                       uint8_t transaction, uint8_t event,
+                                       uint32_t interval, void *user_data);
+       int (*set_volume) (struct avrcp *session, uint8_t transaction,
+                                       uint8_t volume, void *user_data);
+       int (*set_addressed) (struct avrcp *session, uint8_t transaction,
+                                       uint16_t id, void *user_data);
+       int (*get_folder_items) (struct avrcp *session, uint8_t transaction,
+                                       uint8_t scope, uint32_t start,
+                                       uint32_t end, uint16_t number,
+                                       uint32_t *attrs, void *user_data);
+       int (*change_path) (struct avrcp *session, uint8_t transaction,
+                                       uint16_t counter, uint8_t direction,
+                                       uint64_t uid, void *user_data);
+       int (*get_item_attributes) (struct avrcp *session, uint8_t transaction,
+                                       uint8_t scope, uint64_t uid,
+                                       uint16_t counter, uint8_t number,
+                                       uint32_t *attrs, void *user_data);
+       int (*play_item) (struct avrcp *session, uint8_t transaction,
+                                       uint8_t scope, uint64_t uid,
+                                       uint16_t counter, void *user_data);
+       int (*search) (struct avrcp *session, uint8_t transaction,
+                                       const char *string, void *user_data);
+       int (*add_to_now_playing) (struct avrcp *session, uint8_t transaction,
+                                       uint8_t scope, uint64_t uid,
+                                       uint16_t counter, void *user_data);
+};
+
+struct avrcp_control_cfm {
+       void (*get_capabilities) (struct avrcp *session, int err,
+                                       uint8_t number, uint8_t *params,
+                                       void *user_data);
+       void (*list_attributes) (struct avrcp *session, int err,
+                                       uint8_t number, uint8_t *attrs,
+                                       void *user_data);
+       void (*get_attribute_text) (struct avrcp *session, int err,
+                                       uint8_t number, uint8_t *attrs,
+                                       char **text, void *user_data);
+       void (*list_values) (struct avrcp *session, int err,
+                                       uint8_t number, uint8_t *values,
+                                       void *user_data);
+       void (*get_value_text) (struct avrcp *session, int err,
+                                       uint8_t number, uint8_t *values,
+                                       char **text, void *user_data);
+       void (*get_value) (struct avrcp *session, int err,
+                                       uint8_t number, uint8_t *attrs,
+                                       uint8_t *values, void *user_data);
+       void (*set_value) (struct avrcp *session, int err, void *user_data);
+       void (*get_play_status) (struct avrcp *session, int err,
+                                       uint8_t status, uint32_t position,
+                                       uint32_t duration, void *user_data);
+       void (*get_element_attributes) (struct avrcp *session, int err,
+                                       uint8_t number, uint32_t *attrs,
+                                       char **text, void *user_data);
+       bool (*register_notification) (struct avrcp *session, int err,
+                                       uint8_t code, uint8_t event,
+                                       uint8_t *params, void *user_data);
+       void (*set_volume) (struct avrcp *session, int err, uint8_t volume,
+                                       void *user_data);
+       void (*set_addressed) (struct avrcp *session, int err,
+                                       void *user_data);
+       void (*set_browsed) (struct avrcp *session, int err,
+                                       uint16_t counter, uint32_t items,
+                                       char *path, void *user_data);
+       void (*get_folder_items) (struct avrcp *session, int err,
+                                       uint16_t counter, uint16_t number,
+                                       uint8_t *params, void *user_data);
+       void (*change_path) (struct avrcp *session, int err,
+                                       uint32_t items, void *user_data);
+       void (*get_item_attributes) (struct avrcp *session, int err,
+                                       uint8_t number, uint32_t *attrs,
+                                       char **text, void *user_data);
+       void (*play_item) (struct avrcp *session, int err, void *user_data);
+       void (*search) (struct avrcp *session, int err, uint16_t counter,
+                                       uint32_t items, void *user_data);
+       void (*add_to_now_playing) (struct avrcp *session, int err,
+                                       void *user_data);
+};
+
+struct avrcp_passthrough_handler {
+       uint8_t op;
+       bool (*func) (struct avrcp *session, bool pressed, void *user_data);
+};
+
+typedef void (*avrcp_destroy_cb_t) (void *user_data);
+
+struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+void avrcp_shutdown(struct avrcp *session);
+void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb,
+                                                       void *user_data);
+int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu,
+                                                               size_t omtu);
+
+void avrcp_register_player(struct avrcp *session,
+                               const struct avrcp_control_ind *ind,
+                               const struct avrcp_control_cfm *cfm,
+                               void *user_data);
+void avrcp_set_passthrough_handlers(struct avrcp *session,
+                       const struct avrcp_passthrough_handler *handlers,
+                       void *user_data);
+int avrcp_init_uinput(struct avrcp *session, const char *name,
+                                                       const char *address);
+int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code,
+                                       uint8_t subunit, uint8_t pdu_id,
+                                       const struct iovec *iov, int iov_cnt);
+int avrcp_get_capabilities(struct avrcp *session, uint8_t param);
+int avrcp_register_notification(struct avrcp *session, uint8_t event,
+                                                       uint32_t interval);
+int avrcp_list_player_attributes(struct avrcp *session);
+int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number,
+                                                       uint8_t *attrs);
+int avrcp_list_player_values(struct avrcp *session, uint8_t attr);
+int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr,
+                                       uint8_t number, uint8_t *values);
+int avrcp_set_player_value(struct avrcp *session, uint8_t number,
+                                       uint8_t *attrs, uint8_t *values);
+int avrcp_get_current_player_value(struct avrcp *session, uint8_t number,
+                                                       uint8_t *attrs);
+int avrcp_get_play_status(struct avrcp *session);
+int avrcp_set_volume(struct avrcp *session, uint8_t volume);
+int avrcp_get_element_attributes(struct avrcp *session);
+int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id);
+int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id);
+int avrcp_get_folder_items(struct avrcp *session, uint8_t scope,
+                               uint32_t start, uint32_t end, uint8_t number,
+                               uint32_t *attrs);
+int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid,
+                                                       uint16_t counter);
+int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope,
+                               uint64_t uid, uint16_t counter, uint8_t number,
+                               uint32_t *attrs);
+int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid,
+                                                       uint16_t counter);
+int avrcp_search(struct avrcp *session, const char *string);
+int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid,
+                                                       uint16_t counter);
+
+int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint8_t *events);
+int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint8_t *attrs);
+int avrcp_get_player_attribute_text_rsp(struct avrcp *session,
+                                       uint8_t transaction, uint8_t number,
+                                       uint8_t *attrs, const char **text);
+int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint8_t *values);
+int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction,
+                               uint32_t position, uint32_t duration,
+                               uint8_t status);
+int avrcp_get_player_values_text_rsp(struct avrcp *session,
+                                       uint8_t transaction, uint8_t number,
+                                       uint8_t *values, const char **text);
+int avrcp_get_current_player_value_rsp(struct avrcp *session,
+                                       uint8_t transaction, uint8_t number,
+                                       uint8_t *attrs, uint8_t *values);
+int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction);
+int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t *params, size_t params_len);
+int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t code, uint8_t *params,
+                                       size_t params_len);
+int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction,
+                                                       uint8_t volume);
+int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction,
+                                                       uint8_t status);
+int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint16_t counter, uint8_t number,
+                                       uint8_t *type, uint16_t *len,
+                                       uint8_t **params);
+int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction,
+                                                               uint32_t items);
+int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint32_t *attrs,
+                                       const char **text);
+int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction);
+int avrcp_search_rsp(struct avrcp *session, uint8_t transaction,
+                                       uint16_t counter, uint32_t items);
+int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction);
+
+int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op);
diff --git a/android/avrcp.c b/android/avrcp.c
new file mode 100644 (file)
index 0000000..caf9335
--- /dev/null
@@ -0,0 +1,1097 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <glib.h>
+
+#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/shared/util.h"
+#include "src/log.h"
+
+#include "avctp.h"
+#include "avrcp-lib.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "bluetooth.h"
+#include "avrcp.h"
+#include "utils.h"
+
+#define L2CAP_PSM_AVCTP 0x17
+
+#define AVRCP_FEATURE_CATEGORY_1       0x0001
+#define AVRCP_FEATURE_CATEGORY_2       0x0002
+#define AVRCP_FEATURE_CATEGORY_3       0x0004
+#define AVRCP_FEATURE_CATEGORY_4       0x0008
+
+static bdaddr_t adapter_addr;
+static uint32_t record_id = 0;
+static GSList *devices = NULL;
+static GIOChannel *server = NULL;
+static struct ipc *hal_ipc = NULL;
+
+struct avrcp_request {
+       struct avrcp_device *dev;
+       uint8_t pdu_id;
+       uint8_t event_id;
+       uint8_t transaction;
+};
+
+struct avrcp_device {
+       bdaddr_t        dst;
+       uint16_t        version;
+       uint16_t        features;
+       struct avrcp    *session;
+       GIOChannel      *io;
+       GQueue          *queue;
+};
+
+static struct avrcp_request *pop_request(uint8_t pdu_id, uint8_t event_id,
+                                                               bool peek)
+{
+       GSList *l;
+
+       for (l = devices; l; l = g_slist_next(l)) {
+               struct avrcp_device *dev = l->data;
+               GList *reqs = g_queue_peek_head_link(dev->queue);
+               int i;
+
+               for (i = 0; reqs; reqs = g_list_next(reqs), i++) {
+                       struct avrcp_request *req = reqs->data;
+
+                       if (req->pdu_id != pdu_id || req->event_id != event_id)
+                               continue;
+
+                       if (!peek)
+                               g_queue_pop_nth(dev->queue, i);
+
+                       return req;
+               }
+       }
+
+       return NULL;
+}
+
+static void handle_get_play_status(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_avrcp_get_play_status *cmd = buf;
+       uint8_t status;
+       struct avrcp_request *req;
+       int ret;
+
+       DBG("");
+
+       req = pop_request(AVRCP_GET_PLAY_STATUS, 0, false);
+       if (!req) {
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       ret = avrcp_get_play_status_rsp(req->dev->session, req->transaction,
+                                       cmd->position, cmd->duration,
+                                       cmd->status);
+       if (ret < 0) {
+               status = HAL_STATUS_FAILED;
+               g_free(req);
+               goto done;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+       g_free(req);
+
+done:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                               HAL_OP_AVRCP_GET_PLAY_STATUS, status);
+}
+
+static void handle_list_player_attrs(const void *buf, uint16_t len)
+{
+       DBG("");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                       HAL_OP_AVRCP_LIST_PLAYER_ATTRS, HAL_STATUS_FAILED);
+}
+
+static void handle_list_player_values(const void *buf, uint16_t len)
+{
+       DBG("");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                       HAL_OP_AVRCP_LIST_PLAYER_VALUES, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_attrs(const void *buf, uint16_t len)
+{
+       DBG("");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                       HAL_OP_AVRCP_GET_PLAYER_ATTRS, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_attrs_text(const void *buf, uint16_t len)
+{
+       DBG("");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                       HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, HAL_STATUS_FAILED);
+}
+
+static void handle_get_player_values_text(const void *buf, uint16_t len)
+{
+       DBG("");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                       HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, HAL_STATUS_FAILED);
+}
+
+static size_t write_element_text(uint8_t id, uint8_t text_len, uint8_t *text,
+                                               uint8_t *pdu)
+{
+       uint16_t charset = 106;
+       size_t len = 0;
+
+       put_be32(id, pdu);
+       pdu += 4;
+       len += 4;
+
+       put_be16(charset, pdu);
+       pdu += 2;
+       len += 2;
+
+       put_be16(text_len, pdu);
+       pdu += 2;
+       len += 2;
+
+       memcpy(pdu, text, text_len);
+       len += text_len;
+
+       return len;
+}
+
+static void write_element_attrs(uint8_t *ptr, uint8_t number, uint8_t *pdu,
+                                                               size_t *len)
+{
+       int i;
+
+       *pdu = number;
+       pdu++;
+       *len += 1;
+
+       for (i = 0; i < number; i++) {
+               struct hal_avrcp_player_setting_text *text = (void *) ptr;
+               size_t ret;
+
+               ret = write_element_text(text->id, text->len, text->text, pdu);
+
+               ptr += sizeof(*text) + text->len;
+               pdu += ret;
+               *len += ret;
+       }
+}
+
+static void handle_get_element_attrs_text(const void *buf, uint16_t len)
+{
+       struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf;
+       uint8_t status;
+       struct avrcp_request *req;
+       uint8_t pdu[IPC_MTU];
+       uint8_t *ptr;
+       size_t pdu_len;
+       int ret;
+
+       DBG("");
+
+       req = pop_request(AVRCP_GET_ELEMENT_ATTRIBUTES, 0, false);
+       if (!req) {
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       ptr = (uint8_t *) &cmd->values[0];
+       pdu_len = 0;
+       write_element_attrs(ptr, cmd->number, pdu, &pdu_len);
+
+       ret = avrcp_get_element_attrs_rsp(req->dev->session, req->transaction,
+                                                               pdu, pdu_len);
+       if (ret < 0) {
+               status = HAL_STATUS_FAILED;
+               g_free(req);
+               goto done;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+       g_free(req);
+
+done:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                       HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, status);
+}
+
+static void handle_set_player_attrs_value(const void *buf, uint16_t len)
+{
+       DBG("");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                       HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, HAL_STATUS_FAILED);
+}
+
+static void handle_register_notification(const void *buf, uint16_t len)
+{
+       struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+       uint8_t status;
+       struct avrcp_request *req;
+       uint8_t pdu[IPC_MTU];
+       size_t pdu_len;
+       uint8_t code;
+       bool peek = false;
+       int ret;
+
+       DBG("");
+
+       switch (cmd->type) {
+       case HAL_AVRCP_EVENT_TYPE_INTERIM:
+               code = AVC_CTYPE_INTERIM;
+               peek = true;
+               break;
+       case HAL_AVRCP_EVENT_TYPE_CHANGED:
+               code = AVC_CTYPE_CHANGED;
+               break;
+       default:
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       req = pop_request(AVRCP_REGISTER_NOTIFICATION, cmd->event, peek);
+       if (!req) {
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       pdu[0] = cmd->event;
+       pdu_len = 1;
+
+       switch (cmd->event) {
+       case AVRCP_EVENT_STATUS_CHANGED:
+       case AVRCP_EVENT_TRACK_CHANGED:
+       case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+               memcpy(&pdu[1], cmd->data, cmd->len);
+               pdu_len += cmd->len;
+               break;
+       default:
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       ret = avrcp_register_notification_rsp(req->dev->session,
+                                               req->transaction, code,
+                                               pdu, pdu_len);
+       if (ret < 0) {
+               status = HAL_STATUS_FAILED;
+               if (!peek)
+                       g_free(req);
+               goto done;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+       if (!peek)
+               g_free(req);
+
+done:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                       HAL_OP_AVRCP_REGISTER_NOTIFICATION, status);
+}
+
+static void handle_set_volume(const void *buf, uint16_t len)
+{
+       struct hal_cmd_avrcp_set_volume *cmd = (void *) buf;
+       struct avrcp_device *dev;
+       uint8_t status;
+       int ret;
+
+       DBG("");
+
+       if (!devices) {
+               error("AVRCP: No device found to set volume");
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       /*
+        * Peek the first device since the HAL cannot really address a specific
+        * device it might mean there could only be one connected.
+        */
+       dev = devices->data;
+
+       ret = avrcp_set_volume(dev->session, cmd->value & 0x7f);
+       if (ret < 0) {
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+done:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME,
+                                                               status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+       /* HAL_OP_AVRCP_GET_PLAY_STATUS */
+       { handle_get_play_status, false,
+                       sizeof(struct hal_cmd_avrcp_get_play_status) },
+       /* HAL_OP_AVRCP_LIST_PLAYER_ATTRS */
+       { handle_list_player_attrs, true,
+                       sizeof(struct hal_cmd_avrcp_list_player_attrs) },
+       /* HAL_OP_AVRCP_LIST_PLAYER_VALUES */
+       { handle_list_player_values, true,
+                       sizeof(struct hal_cmd_avrcp_list_player_values) },
+       /* HAL_OP_AVRCP_GET_PLAYER_ATTRS */
+       { handle_get_player_attrs, true,
+                       sizeof(struct hal_cmd_avrcp_get_player_attrs) },
+       /* HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT */
+       { handle_get_player_attrs_text, true,
+                       sizeof(struct hal_cmd_avrcp_get_player_attrs_text) },
+       /* HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT */
+       { handle_get_player_values_text, true,
+                       sizeof(struct hal_cmd_avrcp_get_player_values_text) },
+       /* HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT */
+       { handle_get_element_attrs_text, true,
+                       sizeof(struct hal_cmd_avrcp_get_element_attrs_text) },
+       /* HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE */
+       { handle_set_player_attrs_value, true,
+                       sizeof(struct hal_cmd_avrcp_set_player_attrs_value) },
+       /* HAL_OP_AVRCP_REGISTER_NOTIFICATION */
+       { handle_register_notification, true,
+                       sizeof(struct hal_cmd_avrcp_register_notification) },
+       /* HAL_OP_AVRCP_SET_VOLUME */
+       { handle_set_volume, false, sizeof(struct hal_cmd_avrcp_set_volume) },
+};
+
+static sdp_record_t *avrcp_record(void)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, l2cap, avctp, avrtg;
+       sdp_profile_desc_t profile[1];
+       sdp_list_t *aproto_control, *proto_control[2];
+       sdp_record_t *record;
+       sdp_data_t *psm, *version, *features;
+       uint16_t lp = L2CAP_PSM_AVCTP;
+       uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104;
+       uint16_t feat = (AVRCP_FEATURE_CATEGORY_1 |
+                                       AVRCP_FEATURE_CATEGORY_2 |
+                                       AVRCP_FEATURE_CATEGORY_3 |
+                                       AVRCP_FEATURE_CATEGORY_4);
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(record, root);
+
+       /* Service Class ID List */
+       sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
+       svclass_id = sdp_list_append(NULL, &avrtg);
+       sdp_set_service_classes(record, svclass_id);
+
+       /* Protocol Descriptor List */
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto_control[0] = sdp_list_append(NULL, &l2cap);
+       psm = sdp_data_alloc(SDP_UINT16, &lp);
+       proto_control[0] = sdp_list_append(proto_control[0], psm);
+       apseq = sdp_list_append(NULL, proto_control[0]);
+
+       sdp_uuid16_create(&avctp, AVCTP_UUID);
+       proto_control[1] = sdp_list_append(NULL, &avctp);
+       version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
+       proto_control[1] = sdp_list_append(proto_control[1], version);
+       apseq = sdp_list_append(apseq, proto_control[1]);
+
+       aproto_control = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto_control);
+
+       /* Bluetooth Profile Descriptor List */
+       sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
+       profile[0].version = avrcp_ver;
+       pfseq = sdp_list_append(NULL, &profile[0]);
+       sdp_set_profile_descs(record, pfseq);
+
+       features = sdp_data_alloc(SDP_UINT16, &feat);
+       sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       sdp_set_info_attr(record, "AVRCP TG", NULL, NULL);
+
+       sdp_data_free(psm);
+       sdp_data_free(version);
+       sdp_list_free(proto_control[0], NULL);
+       sdp_list_free(proto_control[1], NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(aproto_control, NULL);
+       sdp_list_free(pfseq, NULL);
+       sdp_list_free(root, NULL);
+       sdp_list_free(svclass_id, NULL);
+
+       return record;
+}
+
+static void avrcp_device_free(void *data)
+{
+       struct avrcp_device *dev = data;
+
+       if (dev->queue) {
+               g_queue_foreach(dev->queue, (GFunc) g_free, NULL);
+               g_queue_free(dev->queue);
+       }
+
+       if (dev->session)
+               avrcp_shutdown(dev->session);
+
+       if (dev->io) {
+               g_io_channel_shutdown(dev->io, FALSE, NULL);
+               g_io_channel_unref(dev->io);
+       }
+
+       g_free(dev);
+}
+
+static void avrcp_device_remove(struct avrcp_device *dev)
+{
+       devices = g_slist_remove(devices, dev);
+       avrcp_device_free(dev);
+}
+
+static struct avrcp_device *avrcp_device_new(const bdaddr_t *dst)
+{
+       struct avrcp_device *dev;
+
+       dev = g_new0(struct avrcp_device, 1);
+       bacpy(&dev->dst, dst);
+       devices = g_slist_prepend(devices, dev);
+
+       return dev;
+}
+
+static int device_cmp(gconstpointer s, gconstpointer user_data)
+{
+       const struct avrcp_device *dev = s;
+       const bdaddr_t *dst = user_data;
+
+       return bacmp(&dev->dst, dst);
+}
+
+static struct avrcp_device *avrcp_device_find(const bdaddr_t *dst)
+{
+       GSList *l;
+
+       l = g_slist_find_custom(devices, dst, device_cmp);
+       if (!l)
+               return NULL;
+
+       return l->data;
+}
+
+static void disconnect_cb(void *data)
+{
+       struct avrcp_device *dev = data;
+
+       DBG("");
+
+       dev->session = NULL;
+
+       avrcp_device_remove(dev);
+}
+
+static bool handle_fast_forward(struct avrcp *session, bool pressed,
+                                                       void *user_data)
+{
+       struct hal_ev_avrcp_passthrough_cmd ev;
+
+       DBG("pressed %s", pressed ? "true" : "false");
+
+       ev.id = AVC_FAST_FORWARD;
+       ev.state = pressed;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                       HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev);
+
+       return true;
+}
+
+static bool handle_rewind(struct avrcp *session, bool pressed,
+                                                       void *user_data)
+{
+       struct hal_ev_avrcp_passthrough_cmd ev;
+
+       DBG("pressed %s", pressed ? "true" : "false");
+
+       ev.id = AVC_REWIND;
+       ev.state = pressed;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                       HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev);
+
+       return true;
+}
+
+static const struct avrcp_passthrough_handler passthrough_handlers[] = {
+               { AVC_FAST_FORWARD, handle_fast_forward },
+               { AVC_REWIND, handle_rewind },
+               { },
+};
+
+static int handle_get_capabilities_cmd(struct avrcp *session,
+                                       uint8_t transaction, void *user_data)
+{
+       uint8_t events[] = { AVRCP_EVENT_STATUS_CHANGED,
+                                       AVRCP_EVENT_TRACK_CHANGED,
+                                       AVRCP_EVENT_PLAYBACK_POS_CHANGED  };
+
+       DBG("");
+
+       /*
+        * Android do not provide this info via HAL so the list most
+        * be hardcoded according to what RegisterNotification can
+        * actually handle
+        */
+       avrcp_get_capabilities_rsp(session, transaction, sizeof(events),
+                                                               events);
+
+       return -EAGAIN;
+}
+
+static void push_request(struct avrcp_device *dev, uint8_t pdu_id,
+                                       uint8_t event_id, uint8_t transaction)
+{
+       struct avrcp_request *req;
+
+       req = g_new0(struct avrcp_request, 1);
+       req->dev = dev;
+       req->pdu_id = pdu_id;
+       req->event_id = event_id;
+       req->transaction = transaction;
+
+       g_queue_push_tail(dev->queue, req);
+}
+
+static int handle_get_play_status_cmd(struct avrcp *session,
+                                       uint8_t transaction, void *user_data)
+{
+       struct avrcp_device *dev = user_data;
+
+       DBG("");
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                                       HAL_EV_AVRCP_GET_PLAY_STATUS, 0, NULL);
+
+       push_request(dev, AVRCP_GET_PLAY_STATUS, 0, transaction);
+
+       return -EAGAIN;
+}
+
+static int handle_get_element_attrs_cmd(struct avrcp *session,
+                                       uint8_t transaction, uint64_t uid,
+                                       uint8_t number, uint32_t *attrs,
+                                       void *user_data)
+{
+       struct avrcp_device *dev = user_data;
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_avrcp_get_element_attrs *ev = (void *) buf;
+       int i;
+
+       DBG("");
+
+       ev->number = number;
+       /* Set everything in case of empty list */
+       if (ev->number == 0) {
+               for (i = 0; i < HAL_AVRCP_MEDIA_ATTR_DURATION; i++) {
+                       /* Skip 0x00 as the attributes start with 0x01 */
+                       ev->attrs[i] = i + 1;
+               }
+               ev->number = i;
+               goto done;
+       }
+
+       for (i = 0; i < number; i++)
+               ev->attrs[i] = attrs[i];
+
+done:
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                                       HAL_EV_AVRCP_GET_ELEMENT_ATTRS,
+                                       sizeof(*ev) + ev->number, ev);
+
+       push_request(dev, AVRCP_GET_ELEMENT_ATTRIBUTES, 0, transaction);
+
+       return -EAGAIN;
+
+}
+
+static int handle_register_notification_cmd(struct avrcp *session,
+                                               uint8_t transaction,
+                                               uint8_t event,
+                                               uint32_t interval,
+                                               void *user_data)
+{
+       struct avrcp_device *dev = user_data;
+       struct hal_ev_avrcp_register_notification ev;
+
+       DBG("");
+
+       /* TODO: Add any missing events supported by Android */
+       switch (event) {
+       case AVRCP_EVENT_STATUS_CHANGED:
+       case AVRCP_EVENT_TRACK_CHANGED:
+       case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ev.event = event;
+       ev.param = interval;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                                       HAL_EV_AVRCP_REGISTER_NOTIFICATION,
+                                       sizeof(ev), &ev);
+
+       push_request(dev, AVRCP_REGISTER_NOTIFICATION, event, transaction);
+
+       return -EAGAIN;
+}
+
+static const struct avrcp_control_ind control_ind = {
+       .get_capabilities = handle_get_capabilities_cmd,
+       .get_play_status = handle_get_play_status_cmd,
+       .get_element_attributes = handle_get_element_attrs_cmd,
+       .register_notification = handle_register_notification_cmd,
+};
+
+static bool handle_register_notification_rsp(struct avrcp *session, int err,
+                                               uint8_t code, uint8_t event,
+                                               uint8_t *params,
+                                               void *user_data)
+{
+       struct avrcp_device *dev = user_data;
+       struct hal_ev_avrcp_volume_changed ev;
+
+       if (err < 0) {
+               error("AVRCP: %s", strerror(-err));
+               return false;
+       }
+
+       if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
+               return false;
+
+       if (event != AVRCP_EVENT_VOLUME_CHANGED)
+               return false;
+
+       ev.type = code;
+       ev.volume = params[0] & 0x7f;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                                       HAL_EV_AVRCP_VOLUME_CHANGED,
+                                       sizeof(ev), &ev);
+
+       if (code == AVC_CTYPE_INTERIM)
+               return true;
+
+       avrcp_register_notification(dev->session, event, 0);
+       return false;
+}
+
+static void handle_get_capabilities_rsp(struct avrcp *session, int err,
+                                       uint8_t number, uint8_t *events,
+                                       void *user_data)
+{
+       struct avrcp_device *dev = user_data;
+       int i;
+
+       if (err < 0) {
+               error("AVRCP: %s", strerror(-err));
+               return;
+       }
+
+       for (i = 0; i < number; i++) {
+               if (events[i] != AVRCP_EVENT_VOLUME_CHANGED)
+                       continue;
+
+               avrcp_register_notification(dev->session, events[i], 0);
+               break;
+       }
+
+       return;
+}
+
+static void handle_set_volume_rsp(struct avrcp *session, int err,
+                                               uint8_t value, void *user_data)
+{
+       struct hal_ev_avrcp_volume_changed ev;
+
+       if (err < 0) {
+               ev.volume = 0;
+               ev.type = AVC_CTYPE_REJECTED;
+               goto done;
+       }
+
+       ev.volume = value;
+       ev.type = AVC_CTYPE_ACCEPTED;
+
+done:
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                                       HAL_EV_AVRCP_VOLUME_CHANGED,
+                                       sizeof(ev), &ev);
+}
+
+static const struct avrcp_control_cfm control_cfm = {
+       .get_capabilities = handle_get_capabilities_rsp,
+       .register_notification = handle_register_notification_rsp,
+       .set_volume = handle_set_volume_rsp,
+};
+
+static int avrcp_device_add_session(struct avrcp_device *dev, int fd,
+                                               uint16_t imtu, uint16_t omtu)
+{
+       struct hal_ev_avrcp_remote_features ev;
+       char address[18];
+
+       dev->session = avrcp_new(fd, imtu, omtu, dev->version);
+       if (!dev->session)
+               return -EINVAL;
+
+       avrcp_set_destroy_cb(dev->session, disconnect_cb, dev);
+       avrcp_set_passthrough_handlers(dev->session, passthrough_handlers,
+                                                                       dev);
+       avrcp_register_player(dev->session, &control_ind, &control_cfm, dev);
+
+       dev->queue = g_queue_new();
+
+       ba2str(&dev->dst, address);
+
+       /* FIXME: get the real name of the device */
+       avrcp_init_uinput(dev->session, "bluetooth", address);
+
+       bdaddr2android(&dev->dst, ev.bdaddr);
+       ev.features = HAL_AVRCP_FEATURE_NONE;
+
+       DBG("version 0x%02x", dev->version);
+
+       if (dev->version < 0x0103)
+               goto done;
+
+       ev.features |= HAL_AVRCP_FEATURE_METADATA;
+
+       if (dev->version < 0x0104)
+               goto done;
+
+       ev.features |= HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME;
+
+       avrcp_get_capabilities(dev->session, CAP_EVENTS_SUPPORTED);
+
+done:
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP,
+                                       HAL_EV_AVRCP_REMOTE_FEATURES,
+                                       sizeof(ev), &ev);
+
+       return 0;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+       struct avrcp_device *dev = user_data;
+       uint16_t imtu, omtu;
+       char address[18];
+       GError *gerr = NULL;
+       int fd;
+
+       if (err) {
+               error("%s", err->message);
+               return;
+       }
+
+       bt_io_get(chan, &gerr,
+                       BT_IO_OPT_DEST, address,
+                       BT_IO_OPT_IMTU, &imtu,
+                       BT_IO_OPT_OMTU, &omtu,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               g_io_channel_shutdown(chan, TRUE, NULL);
+               return;
+       }
+
+       fd = g_io_channel_unix_get_fd(chan);
+       if (avrcp_device_add_session(dev, fd, imtu, omtu) < 0) {
+               avrcp_device_free(dev);
+               return;
+       }
+
+       g_io_channel_set_close_on_unref(chan, FALSE);
+
+       if (dev->io) {
+               g_io_channel_unref(dev->io);
+               dev->io = NULL;
+       }
+
+       DBG("%s connected", address);
+}
+
+static bool avrcp_device_connect(struct avrcp_device *dev, BtIOConnect cb)
+{
+       GError *err = NULL;
+
+       dev->io = bt_io_connect(cb, dev, NULL, &err,
+                                       BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                                       BT_IO_OPT_DEST_BDADDR, &dev->dst,
+                                       BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               return false;
+       }
+
+       return true;
+}
+
+static void search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+       struct avrcp_device *dev = data;
+       sdp_list_t *list;
+
+       DBG("");
+
+       if (err < 0) {
+               error("Unable to get AV_REMOTE_SVCLASS_ID SDP record: %s",
+                                                       strerror(-err));
+               goto fail;
+       }
+
+       if (!recs || !recs->data) {
+               error("No AVRCP records found");
+               goto fail;
+       }
+
+       for (list = recs; list; list = list->next) {
+               sdp_record_t *rec = list->data;
+               sdp_list_t *l;
+               sdp_profile_desc_t *desc;
+               int features;
+
+               if (sdp_get_profile_descs(rec, &l) < 0)
+                       continue;
+
+               desc = l->data;
+               dev->version = desc->version;
+
+               if (sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES,
+                                                       &features) == 0)
+                       dev->features = features;
+
+               sdp_list_free(l, free);
+               break;
+       }
+
+       if (dev->io) {
+               GError *gerr = NULL;
+               if (!bt_io_accept(dev->io, connect_cb, dev, NULL, &gerr)) {
+                       error("bt_io_accept: %s", gerr->message);
+                       g_error_free(gerr);
+                       goto fail;
+               }
+               return;
+       }
+
+       if (!avrcp_device_connect(dev, connect_cb)) {
+               error("Unable to connect to AVRCP");
+               goto fail;
+       }
+
+       return;
+
+fail:
+       avrcp_device_remove(dev);
+}
+
+static int avrcp_device_search(struct avrcp_device *dev)
+{
+       uuid_t uuid;
+
+       sdp_uuid16_create(&uuid, AV_REMOTE_SVCLASS_ID);
+
+       return bt_search_service(&adapter_addr, &dev->dst, &uuid, search_cb,
+                                                               dev, NULL, 0);
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+       struct avrcp_device *dev;
+       char address[18];
+       bdaddr_t dst;
+       GError *err = NULL;
+
+       bt_io_get(chan, &err,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_DEST, address,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               g_io_channel_shutdown(chan, TRUE, NULL);
+               return;
+       }
+
+       DBG("incoming connect from %s", address);
+
+       dev = avrcp_device_find(&dst);
+       if (dev && dev->session) {
+               error("AVRCP: Refusing unexpected connect");
+               g_io_channel_shutdown(chan, TRUE, NULL);
+               return;
+       }
+
+       dev = avrcp_device_new(&dst);
+       if (avrcp_device_search(dev) < 0) {
+               error("AVRCP: Failed to search SDP details");
+               avrcp_device_free(dev);
+               g_io_channel_shutdown(chan, TRUE, NULL);
+       }
+
+       dev->io = g_io_channel_ref(chan);
+}
+
+bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+       GError *err = NULL;
+       sdp_record_t *rec;
+
+       DBG("");
+
+       bacpy(&adapter_addr, addr);
+
+       server = bt_io_listen(NULL, confirm_cb, NULL, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                               BT_IO_OPT_PSM, L2CAP_PSM_AVCTP,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_INVALID);
+       if (!server) {
+               error("Failed to listen on AVDTP channel: %s", err->message);
+               g_error_free(err);
+               return false;
+       }
+
+       rec = avrcp_record();
+       if (!rec) {
+               error("Failed to allocate AVRCP record");
+               goto fail;
+       }
+
+       if (bt_adapter_add_record(rec, 0) < 0) {
+               error("Failed to register AVRCP record");
+               sdp_record_free(rec);
+               goto fail;
+       }
+       record_id = rec->handle;
+
+       hal_ipc = ipc;
+
+       ipc_register(hal_ipc, HAL_SERVICE_ID_AVRCP, cmd_handlers,
+                                               G_N_ELEMENTS(cmd_handlers));
+
+       return true;
+fail:
+       g_io_channel_shutdown(server, TRUE, NULL);
+       g_io_channel_unref(server);
+       server = NULL;
+
+       return false;
+}
+
+void bt_avrcp_unregister(void)
+{
+       DBG("");
+
+       g_slist_free_full(devices, avrcp_device_free);
+       devices = NULL;
+
+       ipc_unregister(hal_ipc, HAL_SERVICE_ID_AVRCP);
+       hal_ipc = NULL;
+
+       bt_adapter_remove_record(record_id);
+       record_id = 0;
+
+       if (server) {
+               g_io_channel_shutdown(server, TRUE, NULL);
+               g_io_channel_unref(server);
+               server = NULL;
+       }
+}
+
+void bt_avrcp_connect(const bdaddr_t *dst)
+{
+       struct avrcp_device *dev;
+       char addr[18];
+
+       DBG("");
+
+       if (avrcp_device_find(dst))
+               return;
+
+       dev = avrcp_device_new(dst);
+       if (avrcp_device_search(dev) < 0) {
+               error("AVRCP: Failed to search SDP details");
+               avrcp_device_free(dev);
+       }
+
+       ba2str(&dev->dst, addr);
+       DBG("connecting to %s", addr);
+}
+
+void bt_avrcp_disconnect(const bdaddr_t *dst)
+{
+       struct avrcp_device *dev;
+
+       DBG("");
+
+       dev = avrcp_device_find(dst);
+       if (!dev)
+               return;
+
+       if (dev->session) {
+               avrcp_shutdown(dev->session);
+               return;
+       }
+
+       avrcp_device_remove(dev);
+}
diff --git a/android/avrcp.h b/android/avrcp.h
new file mode 100644 (file)
index 0000000..11e79b7
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_avrcp_unregister(void);
+
+void bt_avrcp_connect(const bdaddr_t *dst);
+void bt_avrcp_disconnect(const bdaddr_t *dst);
diff --git a/android/bite-rfcomm.txt b/android/bite-rfcomm.txt
new file mode 100644 (file)
index 0000000..cf0e94a
--- /dev/null
@@ -0,0 +1,35 @@
+
+BITE test results for RFCOMM
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name      Result  Notes
+-------------------------------------------------------------------------------
+TP/RFC/BV-01-C PASS
+TP/RFC/BV-02-C PASS
+TP/RFC/BV-03-C PASS
+TP/RFC/BV-04-C PASS
+TP/RFC/BV-05-C PASS
+TP/RFC/BV-06-C PASS
+TP/RFC/BV-07-C PASS
+TP/RFC/BV-08-C PASS
+TP/RFC/BV-09-C PASS
+TP/RFC/BV-10-C PASS
+TP/RFC/BV-11-C PASS
+TP/RFC/BV-12-C PASS
+TP/RFC/BV-13-C PASS
+TP/RFC/BV-14-C PASS
+TP/RFC/BV-15-C PASS
+TP/RFC/BV-17-C PASS
+TP/RFC/BV-19-C PASS
+TP/RFC/BV-20-C PASS
+TP/RFC/BV-21-C PASS
+TP/RFC/BV-22-C PASS
+TP/RFC/BV-23-C PASS
+TP/RFC/BV-24-C PASS
+-------------------------------------------------------------------------------
diff --git a/android/bite-spp.txt b/android/bite-spp.txt
new file mode 100644 (file)
index 0000000..1994b05
--- /dev/null
@@ -0,0 +1,19 @@
+
+BITE test results for SPP
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name      Result  Notes
+-------------------------------------------------------------------------------
+TP/APP/BV-01-C PASS
+TP/APP/BV-02-C PASS
+TP/APP/BV-03-C PASS
+TP/APP/BV-04-C PASS
+TP/APP/BV-05-C PASS
+TP/APP/BV-06-C PASS
+-------------------------------------------------------------------------------
index a3145cb..379d8ea 100644 (file)
@@ -2,21 +2,21 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 <errno.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 #include <glib.h>
 
 #include "lib/bluetooth.h"
 #include "lib/sdp.h"
 #include "lib/mgmt.h"
+#include "src/shared/util.h"
 #include "src/shared/mgmt.h"
-#include "src/glib-helper.h"
+#include "src/uuid-helper.h"
 #include "src/eir.h"
 #include "lib/sdp.h"
 #include "lib/sdp_lib.h"
 #include "src/sdp-client.h"
 #include "src/sdpd.h"
-#include "log.h"
+#include "src/log.h"
 #include "hal-msg.h"
+#include "ipc-common.h"
 #include "ipc.h"
 #include "utils.h"
 #include "bluetooth.h"
 
+#define DEFAULT_ADAPTER_NAME "BlueZ for Android"
+
+#define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode"
+
+#define SETTINGS_FILE ANDROID_STORAGEDIR"/settings"
+#define DEVICES_FILE ANDROID_STORAGEDIR"/devices"
+#define CACHE_FILE ANDROID_STORAGEDIR"/cache"
+
 #define DEVICE_ID_SOURCE       0x0002  /* USB */
 #define DEVICE_ID_VENDOR       0x1d6b  /* Linux Foundation */
 #define DEVICE_ID_PRODUCT      0x0247  /* BlueZ for Android */
 
+#define ADAPTER_MAJOR_CLASS 0x02 /* Phone */
+#define ADAPTER_MINOR_CLASS 0x03 /* Smartphone */
+
 /* Default to DisplayYesNo */
 #define DEFAULT_IO_CAPABILITY 0x01
+
 /* Default discoverable timeout 120sec as in Android */
 #define DEFAULT_DISCOVERABLE_TIMEOUT 120
 
-#define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \
-                               + (sizeof(struct hal_property)))
+#define DEVICES_CACHE_MAX 300
 
-static uint16_t option_index = MGMT_INDEX_NONE;
+#define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \
+                                       + sizeof(struct hal_property))
 
 #define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \
                                        + sizeof(struct hal_property))
-/* This list contains addresses which are asked for records */
-static GSList *browse_reqs;
 
-static struct mgmt *mgmt_if = NULL;
+#define SCAN_TYPE_NONE 0
+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
+#define BDADDR_LE (BDADDR_LE_RANDOM | BDADDR_LE_PUBLIC)
+
+struct device {
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+
+       bool le;
+       bool bredr;
+
+       bool pairing;
+
+       bool bredr_paired;
+       bool bredr_bonded;
+       bool le_paired;
+       bool le_bonded;
+
+       char *name;
+       char *friendly_name;
+
+       uint32_t class;
+       int32_t rssi;
+
+       time_t bredr_seen;
+       time_t le_seen;
+
+       GSList *uuids;
+
+       bool found; /* if device is found in current discovery session */
+       unsigned int confirm_id; /* mgtm command id if command pending */
+};
+
+struct browse_req {
+       bdaddr_t bdaddr;
+       GSList *uuids;
+       int search_uuid;
+       int reconnect_attempt;
+};
 
 static struct {
        uint16_t index;
@@ -76,8 +134,10 @@ static struct {
        char *name;
 
        uint32_t current_settings;
+       uint32_t supported_settings;
 
-       bool discovering;
+       uint8_t cur_discovery_type;
+       uint8_t exp_discovery_type;
        uint32_t discoverable_timeout;
 
        GSList *uuids;
@@ -86,24 +146,13 @@ static struct {
        .dev_class = 0,
        .name = NULL,
        .current_settings = 0,
-       .discovering = false,
+       .supported_settings = 0,
+       .cur_discovery_type = SCAN_TYPE_NONE,
+       .exp_discovery_type = SCAN_TYPE_NONE,
        .discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT,
        .uuids = NULL,
 };
 
-struct device {
-       bdaddr_t bdaddr;
-       int bond_state;
-       char *name;
-};
-
-struct browse_req {
-       bdaddr_t bdaddr;
-       GSList *uuids;
-       int search_uuid;
-       int reconnect_attempt;
-};
-
 static const uint16_t uuid_list[] = {
        L2CAP_UUID,
        PNP_INFO_SVCLASS_ID,
@@ -111,27 +160,299 @@ static const uint16_t uuid_list[] = {
        0
 };
 
-static GSList *found_devices = NULL;
-static GSList *devices = NULL;
+static uint16_t option_index = MGMT_INDEX_NONE;
+static struct mgmt *mgmt_if = NULL;
+
+static GSList *bonded_devices = NULL;
+static GSList *cached_devices = NULL;
 
-static void adapter_name_changed(const uint8_t *name)
+static bt_le_device_found gatt_device_found_cb = NULL;
+static bt_le_discovery_stopped gatt_discovery_stopped_cb = NULL;
+
+/* This list contains addresses which are asked for records */
+static GSList *browse_reqs;
+
+static struct ipc *hal_ipc = NULL;
+
+static void mgmt_debug(const char *str, void *user_data)
 {
-       struct hal_ev_adapter_props_changed *ev;
-       size_t len = strlen((const char *) name);
-       uint8_t buf[BASELEN_PROP_CHANGED + len];
+       const char *prefix = user_data;
+       info("%s%s", prefix, str);
+}
 
-       memset(buf, 0, sizeof(buf));
-       ev = (void *) buf;
+static void store_adapter_config(void)
+{
+       GKeyFile *key_file;
+       gsize length = 0;
+       char addr[18];
+       char *data;
+
+       key_file = g_key_file_new();
+
+       g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL);
+
+       ba2str(&adapter.bdaddr, addr);
+
+       g_key_file_set_string(key_file, "General", "Address", addr);
+       g_key_file_set_string(key_file, "General", "Name", adapter.name);
+       g_key_file_set_integer(key_file, "General", "DiscoverableTimeout",
+                                               adapter.discoverable_timeout);
+
+       data = g_key_file_to_data(key_file, &length, NULL);
+
+       g_file_set_contents(SETTINGS_FILE, data, length, NULL);
+
+       g_free(data);
+       g_key_file_free(key_file);
+}
+
+static void load_adapter_config(void)
+{
+       GError *gerr = NULL;
+       GKeyFile *key_file;
+       char *str;
+
+       key_file = g_key_file_new();
+       g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL);
+
+       str = g_key_file_get_string(key_file, "General", "Address", NULL);
+       if (!str) {
+               g_key_file_free(key_file);
+               return;
+       }
+
+       str2ba(str, &adapter.bdaddr);
+       g_free(str);
+
+       adapter.name = g_key_file_get_string(key_file, "General", "Name", NULL);
+
+       adapter.discoverable_timeout = g_key_file_get_integer(key_file,
+                               "General", "DiscoverableTimeout", &gerr);
+       if (gerr) {
+               adapter.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT;
+               g_error_free(gerr);
+               gerr = NULL;
+       }
+
+       g_key_file_free(key_file);
+}
+
+static void store_device_info(struct device *dev, const char *path)
+{
+       GKeyFile *key_file;
+       char addr[18];
+       gsize length = 0;
+       char **uuids = NULL;
+       char *str;
+
+       ba2str(&dev->bdaddr, addr);
+
+       key_file = g_key_file_new();
+       g_key_file_load_from_file(key_file, path, 0, NULL);
+
+       g_key_file_set_boolean(key_file, addr, "BREDR", dev->bredr);
+
+       if (dev->le)
+               g_key_file_set_integer(key_file, addr, "AddressType",
+                                                       dev->bdaddr_type);
+
+       g_key_file_set_string(key_file, addr, "Name", dev->name);
+
+       if (dev->friendly_name)
+               g_key_file_set_string(key_file, addr, "FriendlyName",
+                                                       dev->friendly_name);
+       else
+               g_key_file_remove_key(key_file, addr, "FriendlyName", NULL);
+
+       if (dev->class)
+               g_key_file_set_integer(key_file, addr, "Class", dev->class);
+       else
+               g_key_file_remove_key(key_file, addr, "Class", NULL);
+
+       if (dev->bredr_seen > dev->le_seen)
+               g_key_file_set_integer(key_file, addr, "Timestamp",
+                                                       dev->bredr_seen);
+       else
+               g_key_file_set_integer(key_file, addr, "Timestamp",
+                                                               dev->le_seen);
+
+       if (dev->uuids) {
+               GSList *l;
+               int i;
+
+               uuids = g_new0(char *, g_slist_length(dev->uuids) + 1);
+
+               for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) {
+                       int j;
+                       uint8_t *u = l->data;
+                       char *uuid_str = g_malloc0(33);
+
+                       for (j = 0; j < 16; j++)
+                               sprintf(uuid_str + (j * 2), "%2.2X", u[j]);
+
+                       uuids[i] = uuid_str;
+               }
+
+               g_key_file_set_string_list(key_file, addr, "Services",
+                                               (const char **)uuids, i);
+       } else {
+               g_key_file_remove_key(key_file, addr, "Services", NULL);
+       }
+
+       str = g_key_file_to_data(key_file, &length, NULL);
+       g_file_set_contents(path, str, length, NULL);
+       g_free(str);
+
+       g_key_file_free(key_file);
+       g_strfreev(uuids);
+}
+
+static void remove_device_info(struct device *dev, const char *path)
+{
+       GKeyFile *key_file;
+       gsize length = 0;
+       char addr[18];
+       char *str;
+
+       ba2str(&dev->bdaddr, addr);
+
+       key_file = g_key_file_new();
+       g_key_file_load_from_file(key_file, path, 0, NULL);
+
+       g_key_file_remove_group(key_file, addr, NULL);
+
+       str = g_key_file_to_data(key_file, &length, NULL);
+       g_file_set_contents(path, str, length, NULL);
+       g_free(str);
+
+       g_key_file_free(key_file);
+}
+
+static int device_match(gconstpointer a, gconstpointer b)
+{
+       const struct device *dev = a;
+       const bdaddr_t *bdaddr = b;
+
+       return bacmp(&dev->bdaddr, bdaddr);
+}
+
+static struct device *find_device(const bdaddr_t *bdaddr)
+{
+       GSList *l;
+
+       l = g_slist_find_custom(bonded_devices, bdaddr, device_match);
+       if (l)
+               return l->data;
+
+       l = g_slist_find_custom(cached_devices, bdaddr, device_match);
+       if (l)
+               return l->data;
+
+       return NULL;
+}
+
+static void free_device(struct device *dev)
+{
+       if (dev->confirm_id)
+               mgmt_cancel(mgmt_if, dev->confirm_id);
+
+       g_free(dev->name);
+       g_free(dev->friendly_name);
+       g_slist_free_full(dev->uuids, g_free);
+       g_free(dev);
+}
+
+static void cache_device(struct device *new_dev)
+{
+       struct device *dev;
+       GSList *l;
+
+       l = g_slist_find(cached_devices, new_dev);
+       if (l) {
+               cached_devices = g_slist_remove(cached_devices, new_dev);
+               goto cache;
+       }
+
+       if (g_slist_length(cached_devices) < DEVICES_CACHE_MAX)
+               goto cache;
+
+       l = g_slist_last(cached_devices);
+       dev = l->data;
+
+       cached_devices = g_slist_remove(cached_devices, dev);
+       remove_device_info(dev, CACHE_FILE);
+       free_device(dev);
+
+cache:
+       cached_devices = g_slist_prepend(cached_devices, new_dev);
+       store_device_info(new_dev, CACHE_FILE);
+}
+
+static struct device *create_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+       struct device *dev;
+       char addr[18];
+
+       ba2str(bdaddr, addr);
+       DBG("%s", addr);
+
+       dev = g_new0(struct device, 1);
+
+       bacpy(&dev->bdaddr, bdaddr);
+
+       if (bdaddr_type == BDADDR_BREDR) {
+               dev->bredr = true;
+               dev->bredr_seen = time(NULL);
+       } else {
+               dev->le = true;
+               dev->bdaddr_type = bdaddr_type;
+               dev->le_seen = time(NULL);
+       }
+
+       /*
+        * Use address for name, will be change if one is present
+        * eg. in EIR or set by set_property.
+        */
+       dev->name = g_strdup(addr);
+
+       return dev;
+}
+
+static struct device *get_device(const bdaddr_t *bdaddr, uint8_t type)
+{
+       struct device *dev;
+
+       dev = find_device(bdaddr);
+       if (dev)
+               return dev;
+
+       dev = create_device(bdaddr, type);
+
+       cache_device(dev);
+
+       return dev;
+}
+
+static  void send_adapter_property(uint8_t type, uint16_t len, const void *val)
+{
+       uint8_t buf[BASELEN_PROP_CHANGED + len];
+       struct hal_ev_adapter_props_changed *ev = (void *) buf;
 
-       ev->num_props = 1;
        ev->status = HAL_STATUS_SUCCESS;
-       ev->props[0].type = HAL_PROP_ADAPTER_NAME;
-       /* Android expects value without NULL terminator */
+       ev->num_props = 1;
+       ev->props[0].type = type;
        ev->props[0].len = len;
-       memcpy(ev->props->val, name, len);
+       memcpy(ev->props[0].val, val, len);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                               HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf);
+}
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
-                                                       sizeof(buf), ev);
+static void adapter_name_changed(const uint8_t *name)
+{
+       /* Android expects string value without NULL terminator */
+       send_adapter_property(HAL_PROP_ADAPTER_NAME,
+                                       strlen((const char *) name), name);
 }
 
 static void adapter_set_name(const uint8_t *name)
@@ -144,6 +465,8 @@ static void adapter_set_name(const uint8_t *name)
        g_free(adapter.name);
        adapter.name = g_strdup((const char *) name);
 
+       store_adapter_config();
+
        adapter_name_changed(name);
 }
 
@@ -171,8 +494,8 @@ static void powered_changed(void)
 
        DBG("%u", ev.state);
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_STATE_CHANGED,
-                                                       sizeof(ev), &ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                               HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev);
 }
 
 static uint8_t settings2scan_mode(void)
@@ -193,39 +516,19 @@ static uint8_t settings2scan_mode(void)
 
 static void scan_mode_changed(void)
 {
-       uint8_t buf[BASELEN_PROP_CHANGED + 1];
-       struct hal_ev_adapter_props_changed *ev = (void *) buf;
-       uint8_t *mode;
-
-       ev->num_props = 1;
-       ev->status = HAL_STATUS_SUCCESS;
-
-       ev->props[0].type = HAL_PROP_ADAPTER_SCAN_MODE;
-       ev->props[0].len = 1;
+       uint8_t mode;
 
-       mode = ev->props[0].val;
-       *mode = settings2scan_mode();
+       mode = settings2scan_mode();
 
-       DBG("mode %u", *mode);
+       DBG("mode %u", mode);
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
-                                                       sizeof(buf), buf);
+       send_adapter_property(HAL_PROP_ADAPTER_SCAN_MODE, sizeof(mode), &mode);
 }
 
 static void adapter_class_changed(void)
 {
-       uint8_t buf[BASELEN_PROP_CHANGED + sizeof(uint32_t)];
-       struct hal_ev_adapter_props_changed *ev = (void *) buf;
-
-       ev->num_props = 1;
-       ev->status = HAL_STATUS_SUCCESS;
-
-       ev->props[0].type = HAL_PROP_ADAPTER_CLASS;
-       ev->props[0].len = sizeof(uint32_t);
-       memcpy(ev->props->val, &adapter.dev_class, sizeof(uint32_t));
-
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
-                                                       sizeof(buf), buf);
+       send_adapter_property(HAL_PROP_ADAPTER_CLASS, sizeof(adapter.dev_class),
+                                                       &adapter.dev_class);
 }
 
 static void settings_changed(uint32_t settings)
@@ -242,7 +545,6 @@ static void settings_changed(uint32_t settings)
        if (changed_mask & MGMT_SETTING_POWERED)
                powered_changed();
 
-
        scan_mode_mask = MGMT_SETTING_CONNECTABLE |
                                        MGMT_SETTING_DISCOVERABLE;
 
@@ -265,7 +567,7 @@ static void new_settings_callback(uint16_t index, uint16_t length,
                return;
        }
 
-       settings = bt_get_le32(param);
+       settings = get_le32(param);
 
        DBG("settings: 0x%8.8x -> 0x%8.8x", adapter.current_settings,
                                                                settings);
@@ -304,16 +606,36 @@ static void mgmt_dev_class_changed_event(uint16_t index, uint16_t length,
 static void store_link_key(const bdaddr_t *dst, const uint8_t *key,
                                        uint8_t type, uint8_t pin_length)
 {
-       /* TODO store link key */
+       GKeyFile *key_file;
+       char key_str[33];
+       gsize length = 0;
+       char addr[18];
+       char *data;
+       int i;
 
-}
+       key_file = g_key_file_new();
 
-static int bdaddr_cmp(gconstpointer a, gconstpointer b)
-{
-       const bdaddr_t *bda = a;
-       const bdaddr_t *bdb = b;
+       if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+               g_key_file_free(key_file);
+               return;
+       }
+
+       ba2str(dst, addr);
 
-       return bacmp(bdb, bda);
+       DBG("%s type %u pin_len %u", addr, type, pin_length);
+
+       for (i = 0; i < 16; i++)
+               sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+       g_key_file_set_string(key_file, addr, "LinkKey", key_str);
+       g_key_file_set_integer(key_file, addr, "LinkKeyType", type);
+       g_key_file_set_integer(key_file, addr, "LinkKeyPinLength", pin_length);
+
+       data = g_key_file_to_data(key_file, &length, NULL);
+       g_file_set_contents(DEVICES_FILE, data, length, NULL);
+       g_free(data);
+
+       g_key_file_free(key_file);
 }
 
 static void send_bond_state_change(const bdaddr_t *addr, uint8_t status,
@@ -325,91 +647,159 @@ static void send_bond_state_change(const bdaddr_t *addr, uint8_t status,
        ev.state = state;
        bdaddr2android(addr, ev.bdaddr);
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_BOND_STATE_CHANGED,
-                                                       sizeof(ev), &ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                               HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev);
 }
 
-static void cache_device_name(const bdaddr_t *addr, const char *name)
+static void update_bredr_state(struct device *dev, bool pairing, bool paired,
+                                                               bool bonded)
 {
-       struct device *dev = NULL;
-       GSList *l;
-
-       l = g_slist_find_custom(devices, addr, bdaddr_cmp);
-       if (l)
-               dev = l->data;
+       if (pairing == dev->pairing && paired == dev->bredr_paired &&
+                                               bonded == dev->bredr_bonded)
+               return;
 
-       if (!dev) {
-               dev = g_new0(struct device, 1);
-               bacpy(&dev->bdaddr, addr);
-               dev->bond_state = HAL_BOND_STATE_NONE;
-               devices = g_slist_prepend(devices, dev);
+       /* avoid unpairing device on incoming pairing request */
+       if (pairing && dev->bredr_paired)
+               goto done;
+
+       /* avoid unpairing device if pairing failed */
+       if (!pairing && !paired && dev->pairing && dev->bredr_paired)
+               goto done;
+
+       if (paired && !dev->le_paired) {
+               cached_devices = g_slist_remove(cached_devices, dev);
+               bonded_devices = g_slist_prepend(bonded_devices, dev);
+               remove_device_info(dev, CACHE_FILE);
+               store_device_info(dev, DEVICES_FILE);
+       } else if (!paired && !dev->le_paired) {
+               bonded_devices = g_slist_remove(bonded_devices, dev);
+               remove_device_info(dev, DEVICES_FILE);
+               cache_device(dev);
        }
 
-       if (!g_strcmp0(dev->name, name))
+       dev->bredr_paired = paired;
+       dev->bredr_bonded = bonded;
+
+done:
+       dev->pairing = pairing;
+}
+
+static void update_le_state(struct device *dev, bool pairing, bool paired,
+                                                               bool bonded)
+{
+       if (pairing == dev->pairing && paired == dev->le_paired &&
+                                               bonded == dev->le_bonded)
                return;
 
-       g_free(dev->name);
-       dev->name = g_strdup(name);
-       /*TODO: Do some real caching here */
+       /* avoid unpairing device on incoming pairing request */
+       if (pairing && dev->le_paired)
+               goto done;
+
+       /* avoid unpairing device if pairing failed */
+       if (!pairing && !paired && dev->pairing && dev->le_paired)
+               goto done;
+
+       if (paired && !dev->bredr_paired) {
+               cached_devices = g_slist_remove(cached_devices, dev);
+               bonded_devices = g_slist_prepend(bonded_devices, dev);
+               remove_device_info(dev, CACHE_FILE);
+               store_device_info(dev, DEVICES_FILE);
+       } else if (!paired && !dev->bredr_paired) {
+               bonded_devices = g_slist_remove(bonded_devices, dev);
+               remove_device_info(dev, DEVICES_FILE);
+               cache_device(dev);
+       }
+
+       dev->le_paired = paired;
+       dev->le_bonded = bonded;
+
+done:
+       dev->pairing = pairing;
 }
 
-static void set_device_bond_state(const bdaddr_t *addr, uint8_t status,
-                                                               int state) {
+static uint8_t device_bond_state(struct device *dev)
+{
+       if (dev->pairing)
+               return HAL_BOND_STATE_BONDING;
 
-       struct device *dev = NULL;
-       GSList *l;
+       /*
+        * We are checking for paired here instead of bonded as HAL API is
+        * using BOND state also if there was no bonding pairing.
+        */
+       if (dev->bredr_paired || dev->le_paired)
+               return HAL_BOND_STATE_BONDED;
 
-       l = g_slist_find_custom(devices, addr, bdaddr_cmp);
-       if (l)
-               dev = l->data;
+       return HAL_BOND_STATE_NONE;
+}
 
-       if (!dev) {
-               dev = g_new0(struct device, 1);
-               bacpy(&dev->bdaddr, addr);
-               dev->bond_state = HAL_BOND_STATE_NONE;
-               devices = g_slist_prepend(devices, dev);
-       }
+static void update_device_state(struct device *dev, uint8_t addr_type,
+                               uint8_t status, bool pairing, bool paired,
+                               bool bonded)
+{
+       uint8_t old_bond, new_bond;
 
-       if (dev->bond_state != state) {
-               dev->bond_state = state;
-               send_bond_state_change(&dev->bdaddr, status, state);
-       }
+       old_bond = device_bond_state(dev);
+
+       if (addr_type == BDADDR_BREDR)
+               update_bredr_state(dev, pairing, paired, bonded);
+       else
+               update_le_state(dev, pairing, paired, bonded);
+
+       new_bond = device_bond_state(dev);
+
+       if (old_bond != new_bond)
+               send_bond_state_change(&dev->bdaddr, status, new_bond);
 }
 
-static void browse_req_free(struct browse_req *req)
+static  void send_device_property(const bdaddr_t *bdaddr, uint8_t type,
+                                               uint16_t len, const void *val)
 {
-       g_slist_free_full(req->uuids, g_free);
-       g_free(req);
+       uint8_t buf[BASELEN_REMOTE_DEV_PROP + len];
+       struct hal_ev_remote_device_props *ev = (void *) buf;
+
+       ev->status = HAL_STATUS_SUCCESS;
+       bdaddr2android(bdaddr, ev->bdaddr);
+       ev->num_props = 1;
+       ev->props[0].type = type;
+       ev->props[0].len = len;
+       memcpy(ev->props[0].val, val, len);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                               HAL_EV_REMOTE_DEVICE_PROPS, sizeof(buf), buf);
 }
 
-static void fill_uuids(GSList *list, void *buf)
+static void send_device_uuids_notif(struct device *dev)
 {
-       for (; list; list = g_slist_next(list)) {
-               memcpy(buf, list->data, sizeof(uint128_t));
-               buf += sizeof(uint128_t);
+       uint8_t buf[sizeof(uint128_t) * g_slist_length(dev->uuids)];
+       uint8_t *ptr = buf;
+       GSList *l;
+
+       for (l = dev->uuids; l; l = g_slist_next(l)) {
+               memcpy(ptr, l->data, sizeof(uint128_t));
+               ptr += sizeof(uint128_t);
        }
+
+       send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_UUIDS, sizeof(buf),
+                                                                       buf);
 }
 
-static void remote_uuids_callback(struct browse_req *req)
+static void set_device_uuids(struct device *dev, GSList *uuids)
 {
-       struct hal_ev_remote_device_props *ev;
-       int len;
-
-       len = sizeof(*ev) + sizeof(struct hal_property) + (sizeof(uint128_t) *
-                                               g_slist_length(req->uuids));
-       ev = g_malloc(len);
+       g_slist_free_full(dev->uuids, g_free);
+       dev->uuids = uuids;
 
-       ev->status = HAL_STATUS_SUCCESS;
-       bdaddr2android(&req->bdaddr, &ev->bdaddr);
-       ev->num_props = 1;
-       ev->props[0].type = HAL_PROP_DEVICE_UUIDS;
-       ev->props[0].len = sizeof(uint128_t) * g_slist_length(req->uuids);
-       fill_uuids(req->uuids, ev->props[0].val);
+       if (dev->le_paired || dev->bredr_paired)
+               store_device_info(dev, DEVICES_FILE);
+       else
+               store_device_info(dev, CACHE_FILE);
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS,
-                                                               len, ev);
+       send_device_uuids_notif(dev);
+}
 
-       g_free(ev);
+static void browse_req_free(struct browse_req *req)
+{
+       g_slist_free_full(req->uuids, g_free);
+       g_free(req);
 }
 
 static int uuid_128_cmp(gconstpointer a, gconstpointer b)
@@ -469,10 +859,13 @@ static void update_records(struct browse_req *req, sdp_list_t *recs)
 static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
 {
        struct browse_req *req = user_data;
+       struct device *dev;
        uuid_t uuid;
 
-       /* If we have a valid response and req->search_uuid == 2, then L2CAP
-        * UUID & PNP searching was successful -- we are done */
+       /*
+        * If we have a valid response and req->search_uuid == 2, then L2CAP
+        * UUID & PNP searching was successful -- we are done
+        */
        if (err < 0 || req->search_uuid == 2) {
                if (err == -ECONNRESET && req->reconnect_attempt < 1) {
                        req->search_uuid--;
@@ -488,12 +881,16 @@ static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
        if (uuid_list[req->search_uuid]) {
                sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
                bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid,
-                                               browse_cb, user_data, NULL);
+                                               browse_cb, user_data, NULL, 0);
                return;
        }
 
 done:
-       remote_uuids_callback(req);
+       dev = find_device(&req->bdaddr);
+       if (dev) {
+               set_device_uuids(dev, req->uuids);
+               req->uuids = NULL;
+       }
 
        browse_reqs = g_slist_remove(browse_reqs, req);
        browse_req_free(req);
@@ -513,16 +910,16 @@ static uint8_t browse_remote_sdp(const bdaddr_t *addr)
        uuid_t uuid;
 
        if (g_slist_find_custom(browse_reqs, addr, req_cmp))
-               return HAL_STATUS_DONE;
+               return HAL_STATUS_SUCCESS;
 
        req = g_new0(struct browse_req, 1);
        bacpy(&req->bdaddr, addr);
        sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
 
        if (bt_search_service(&adapter.bdaddr,
-                       &req->bdaddr, &uuid, browse_cb, req, NULL) < 0) {
+                       &req->bdaddr, &uuid, browse_cb, req, NULL , 0) < 0) {
                browse_req_free(req);
-               return false;
+               return HAL_STATUS_FAILED;
        }
 
        browse_reqs = g_slist_append(browse_reqs, req);
@@ -535,6 +932,7 @@ static void new_link_key_callback(uint16_t index, uint16_t length,
 {
        const struct mgmt_ev_new_link_key *ev = param;
        const struct mgmt_addr_info *addr = &ev->key.addr;
+       struct device *dev;
        char dst[18];
 
        if (length < sizeof(*ev)) {
@@ -553,6 +951,13 @@ static void new_link_key_callback(uint16_t index, uint16_t length,
                return;
        }
 
+       dev = find_device(&ev->key.addr.bdaddr);
+       if (!dev)
+               return;
+
+       update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+                                                       true, !!ev->store_hint);
+
        if (ev->store_hint) {
                const struct mgmt_link_key_info *key = &ev->key;
 
@@ -560,53 +965,15 @@ static void new_link_key_callback(uint16_t index, uint16_t length,
                                                                key->pin_len);
        }
 
-       set_device_bond_state(&addr->bdaddr, HAL_STATUS_SUCCESS,
-                                                       HAL_BOND_STATE_BONDED);
-
        browse_remote_sdp(&addr->bdaddr);
 }
 
-static const char *get_device_name(const bdaddr_t *addr)
-{
-       GSList *l;
-
-       l = g_slist_find_custom(devices, addr, bdaddr_cmp);
-       if (l) {
-               struct device *dev = l->data;
-               return dev->name;
-       }
-
-       return NULL;
-}
-
-static void send_remote_device_name_prop(const bdaddr_t *bdaddr)
+static uint8_t get_device_name(struct device *dev)
 {
-       struct hal_ev_remote_device_props *ev;
-       const char *name;
-       size_t ev_len;
-       char dst[18];
-
-       /* Use cached name or bdaddr string */
-       name = get_device_name(bdaddr);
-       if (!name) {
-               ba2str(bdaddr, dst);
-               name = dst;
-       }
-
-       ev_len = BASELEN_REMOTE_DEV_PROP + strlen(name);
-       ev = g_malloc0(ev_len);
+       send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_NAME,
+                                               strlen(dev->name), dev->name);
 
-       ev->status = HAL_STATUS_SUCCESS;
-       bdaddr2android(bdaddr, ev->bdaddr);
-       ev->num_props = 1;
-       ev->props[0].type = HAL_PROP_DEVICE_NAME;
-       ev->props[0].len = strlen(name);
-       memcpy(&ev->props[0].val, name, strlen(name));
-
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS,
-                                                               ev_len, ev);
-
-       g_free(ev);
+       return HAL_STATUS_SUCCESS;
 }
 
 static void pin_code_request_callback(uint16_t index, uint16_t length,
@@ -614,6 +981,7 @@ static void pin_code_request_callback(uint16_t index, uint16_t length,
 {
        const struct mgmt_ev_pin_code_request *ev = param;
        struct hal_ev_pin_request hal_ev;
+       struct device *dev;
        char dst[18];
 
        if (length < sizeof(*ev)) {
@@ -623,39 +991,41 @@ static void pin_code_request_callback(uint16_t index, uint16_t length,
 
        ba2str(&ev->addr.bdaddr, dst);
 
-       /* Workaround for Android Bluetooth.apk issue: send remote
-        * device property */
-       send_remote_device_name_prop(&ev->addr.bdaddr);
+       dev = get_device(&ev->addr.bdaddr, BDADDR_BREDR);
+
+       /*
+        * Workaround for Android Bluetooth.apk issue: send remote
+        * device property
+        */
+       get_device_name(dev);
 
-       set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
-                                               HAL_BOND_STATE_BONDING);
+       update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+                                                               false, false);
 
        DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure);
 
-       /* TODO CoD of remote devices should probably be cached
-        * Name we already send in remote device prop */
+       /* Name already sent in remote device prop */
        memset(&hal_ev, 0, sizeof(hal_ev));
        bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);
+       hal_ev.class_of_dev = dev->class;
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST,
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST,
                                                sizeof(hal_ev), &hal_ev);
 }
 
-static void send_ssp_request(const bdaddr_t *addr, uint8_t variant,
+static void send_ssp_request(struct device *dev, uint8_t variant,
                                                        uint32_t passkey)
 {
        struct hal_ev_ssp_request ev;
 
-       /* It is ok to have empty name and CoD of remote devices here since
-       * those information has been already provided on device_connected event
-       * or during device scaning. Android will use that instead.
-       */
-       memset(&ev, 0, sizeof(ev));
-       bdaddr2android(addr, ev.bdaddr);
+       bdaddr2android(&dev->bdaddr, ev.bdaddr);
+       memcpy(ev.name, dev->name, strlen(dev->name));
+       ev.class_of_dev = dev->class;
+
        ev.pairing_variant = variant;
        ev.passkey = passkey;
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST,
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST,
                                                        sizeof(ev), &ev);
 }
 
@@ -663,6 +1033,7 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length,
                                        const void *param, void *user_data)
 {
        const struct mgmt_ev_user_confirm_request *ev = param;
+       struct device *dev;
        char dst[18];
 
        if (length < sizeof(*ev)) {
@@ -673,20 +1044,24 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length,
        ba2str(&ev->addr.bdaddr, dst);
        DBG("%s confirm_hint %u", dst, ev->confirm_hint);
 
-       set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
-                                               HAL_BOND_STATE_BONDING);
+       dev = find_device(&ev->addr.bdaddr);
+       if (!dev)
+               return;
+
+       update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+                                                               false, false);
 
        if (ev->confirm_hint)
-               send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_CONSENT, 0);
+               send_ssp_request(dev, HAL_SSP_VARIANT_CONSENT, 0);
        else
-               send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_CONFIRM,
-                                                               ev->value);
+               send_ssp_request(dev, HAL_SSP_VARIANT_CONFIRM, ev->value);
 }
 
 static void user_passkey_request_callback(uint16_t index, uint16_t length,
                                        const void *param, void *user_data)
 {
        const struct mgmt_ev_user_passkey_request *ev = param;
+       struct device *dev;
        char dst[18];
 
        if (length < sizeof(*ev)) {
@@ -697,16 +1072,22 @@ static void user_passkey_request_callback(uint16_t index, uint16_t length,
        ba2str(&ev->addr.bdaddr, dst);
        DBG("%s", dst);
 
-       set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
-                                               HAL_BOND_STATE_BONDING);
+       dev = find_device(&ev->addr.bdaddr);
+       if (!dev)
+               return;
+
+       update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+                                                               false, false);
 
-       send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_ENTRY, 0);
+       send_ssp_request(dev, HAL_SSP_VARIANT_ENTRY, 0);
 }
 
 static void user_passkey_notify_callback(uint16_t index, uint16_t length,
-                                       const void *param, void *user_data)
+                                                       const void *param,
+                                                       void *user_data)
 {
        const struct mgmt_ev_passkey_notify *ev = param;
+       struct device *dev;
        char dst[18];
 
        if (length < sizeof(*ev)) {
@@ -721,60 +1102,165 @@ static void user_passkey_notify_callback(uint16_t index, uint16_t length,
        if (ev->entered)
                return;
 
-       set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS,
-                                               HAL_BOND_STATE_BONDING);
+       dev = find_device(&ev->addr.bdaddr);
+       if (!dev)
+               return;
+
+       update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
+                                                               false, false);
+
+       send_ssp_request(dev, HAL_SSP_VARIANT_NOTIF, ev->passkey);
+}
+
+static void clear_device_found(gpointer data, gpointer user_data)
+{
+       struct device *dev = data;
+
+       dev->found = false;
+}
+
+static uint8_t get_supported_discovery_type(void)
+{
+       uint8_t type = SCAN_TYPE_NONE;
+
+       if (adapter.current_settings & MGMT_SETTING_BREDR)
+               type |= SCAN_TYPE_BREDR;
+
+       if (adapter.current_settings & MGMT_SETTING_LE)
+               type |= SCAN_TYPE_LE;
+
+       return type;
+}
+
+static bool start_discovery(uint8_t type)
+{
+       struct mgmt_cp_start_discovery cp;
+
+       cp.type = get_supported_discovery_type() & type;
+
+       DBG("type=0x%x", cp.type);
+
+       if (cp.type == SCAN_TYPE_NONE)
+               return false;
+
+       if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index,
+                               sizeof(cp), &cp, NULL, NULL, NULL) > 0)
+               return true;
+
+       error("Failed to start discovery");
+
+       return false;
+}
+
+/*
+ * Send discovery state change event only if it is related to dual type
+ * discovery session (triggered by start/cancel discovery commands)
+ */
+static void check_discovery_state(uint8_t new_type, uint8_t old_type)
+{
+       struct hal_ev_discovery_state_changed ev;
+
+       DBG("%u %u", new_type, old_type);
+
+       if (new_type == get_supported_discovery_type()) {
+               g_slist_foreach(bonded_devices, clear_device_found, NULL);
+               g_slist_foreach(cached_devices, clear_device_found, NULL);
+               ev.state = HAL_DISCOVERY_STATE_STARTED;
+               goto done;
+       }
+
+       if (old_type != get_supported_discovery_type())
+               return;
+
+       ev.state = HAL_DISCOVERY_STATE_STOPPED;
 
-       send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_NOTIF,
-                                                               ev->passkey);
+done:
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(ev), &ev);
 }
 
 static void mgmt_discovering_event(uint16_t index, uint16_t length,
                                        const void *param, void *user_data)
 {
        const struct mgmt_ev_discovering *ev = param;
-       struct hal_ev_discovery_state_changed cp;
+       uint8_t type;
 
        if (length < sizeof(*ev)) {
                error("Too small discovering event");
                return;
        }
 
-       DBG("hci%u type %u discovering %u", index, ev->type,
-                                                       ev->discovering);
+       DBG("type %u discovering %u", ev->type, ev->discovering);
 
-       if (adapter.discovering == !!ev->discovering)
+       if (!!adapter.cur_discovery_type == !!ev->discovering)
                return;
 
-       adapter.discovering = !!ev->discovering;
+       type = ev->discovering ? ev->type : SCAN_TYPE_NONE;
 
-       DBG("new discovering state %u", ev->discovering);
+       check_discovery_state(type, adapter.cur_discovery_type);
 
-       if (adapter.discovering) {
-               cp.state = HAL_DISCOVERY_STATE_STARTED;
-       } else {
-               g_slist_free_full(found_devices, g_free);
-               found_devices = NULL;
+       adapter.cur_discovery_type = type;
+
+       if (ev->discovering)
+               return;
+
+       /* One shot notification about discovery stopped */
+       if (gatt_discovery_stopped_cb) {
+               gatt_discovery_stopped_cb();
+               gatt_discovery_stopped_cb = NULL;
+       }
+
+       type = adapter.exp_discovery_type;
+       adapter.exp_discovery_type = SCAN_TYPE_NONE;
+
+       if (type == SCAN_TYPE_NONE && gatt_device_found_cb)
+               type = SCAN_TYPE_LE;
+
+       if (type != SCAN_TYPE_NONE)
+               start_discovery(type);
+}
+
+static void confirm_device_name_cb(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_rp_confirm_name *rp = param;
+       struct device *dev;
 
-               cp.state = HAL_DISCOVERY_STATE_STOPPED;
+       DBG("Confirm name status: %s (0x%02x)", mgmt_errstr(status), status);
+
+       if (length < sizeof(*rp)) {
+               error("Wrong size of confirm name response");
+               return;
        }
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp), &cp);
+       dev = find_device(&rp->addr.bdaddr);
+       if (!dev)
+               return;
+
+       dev->confirm_id = 0;
 }
 
-static void confirm_device_name(const bdaddr_t *addr, uint8_t addr_type)
+static unsigned int confirm_device_name(const bdaddr_t *addr, uint8_t addr_type,
+                                                       bool resolve_name)
 {
        struct mgmt_cp_confirm_name cp;
+       unsigned int res;
 
        memset(&cp, 0, sizeof(cp));
        bacpy(&cp.addr.bdaddr, addr);
        cp.addr.type = addr_type;
 
-       if (mgmt_reply(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index,
-                               sizeof(cp), &cp, NULL, NULL, NULL) == 0)
+       if (!resolve_name)
+               cp.name_known = 1;
+
+       res = mgmt_send(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index,
+                               sizeof(cp), &cp, confirm_device_name_cb,
+                               NULL, NULL);
+       if (!res)
                error("Failed to send confirm name request");
-}
 
+       return res;
+}
 
 static int fill_hal_prop(void *buf, uint8_t type, uint16_t len,
                                                        const void *val)
@@ -783,99 +1269,266 @@ static int fill_hal_prop(void *buf, uint8_t type, uint16_t len,
 
        prop->type = type;
        prop->len = len;
-       memcpy(prop->val, val, len);
+
+       if (len)
+               memcpy(prop->val, val, len);
 
        return sizeof(*prop) + len;
 }
 
-static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type,
-                                       int8_t rssi, bool confirm,
-                                       const uint8_t *data, uint8_t data_len)
+static uint8_t get_device_android_type(struct device *dev)
 {
-       uint8_t buf[BLUEZ_HAL_MTU];
-       bool new_dev = false;
-       struct eir_data eir;
-       uint8_t *num_prop;
-       uint8_t opcode;
-       int size = 0;
+       if (dev->bredr && dev->le)
+               return HAL_TYPE_DUAL;
+
+       if (dev->le)
+               return HAL_TYPE_LE;
+
+       return HAL_TYPE_BREDR;
+}
+
+uint8_t bt_get_device_android_type(const bdaddr_t *addr)
+{
+       struct device *dev;
+
+       dev = get_device(addr, BDADDR_BREDR);
+
+       return get_device_android_type(dev);
+}
+
+const char *bt_get_adapter_name(void)
+{
+       return adapter.name;
+}
+
+bool bt_device_is_bonded(const bdaddr_t *bdaddr)
+{
+       if (g_slist_find_custom(bonded_devices, bdaddr, device_match))
+               return true;
+
+       return false;
+}
+
+static bool rssi_above_threshold(int old, int new)
+{
+       /* only 8 dBm or more */
+       return abs(old - new) >= 8;
+}
+
+static void update_new_device(struct device *dev, int8_t rssi,
+                                               const struct eir_data *eir)
+{
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_device_found *ev = (void *) buf;
+       bdaddr_t android_bdaddr;
+       uint8_t android_type;
+       int size;
 
        memset(buf, 0, sizeof(buf));
-       memset(&eir, 0, sizeof(eir));
 
-       eir_parse(&eir, data, data_len);
+       if (adapter.cur_discovery_type)
+               dev->found = true;
 
-       if (!g_slist_find_custom(found_devices, bdaddr, bdaddr_cmp)) {
-               bdaddr_t *new_bdaddr;
-               char addr[18];
+       size = sizeof(*ev);
+
+       bdaddr2android(&dev->bdaddr, &android_bdaddr);
+       size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR,
+                                               sizeof(android_bdaddr),
+                                               &android_bdaddr);
+       ev->num_props++;
 
-               new_bdaddr = g_new0(bdaddr_t, 1);
-               bacpy(new_bdaddr, bdaddr);
+       android_type = get_device_android_type(dev);
+       size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+                               sizeof(android_type), &android_type);
+       ev->num_props++;
 
-               found_devices = g_slist_prepend(found_devices, new_bdaddr);
+       if (eir->class)
+               dev->class = eir->class;
 
-               ba2str(new_bdaddr, addr);
-               DBG("New device found: %s", addr);
+       if (dev->class) {
+               size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+                                       sizeof(dev->class), &dev->class);
+               ev->num_props++;
+       }
 
-               new_dev = true;
+       if (rssi && rssi_above_threshold(dev->rssi, rssi))
+               dev->rssi = rssi;
+
+       if (dev->rssi) {
+               size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+                                               sizeof(dev->rssi), &dev->rssi);
+               ev->num_props++;
        }
 
-       if (new_dev) {
-               struct hal_ev_device_found *ev = (void *) buf;
-               bdaddr_t android_bdaddr;
+       if (eir->name && strlen(eir->name)) {
+               g_free(dev->name);
+               dev->name = g_strdup(eir->name);
+       }
 
-               size += sizeof(*ev);
+       if (dev->name) {
+               size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+                                               strlen(dev->name), dev->name);
+               ev->num_props++;
+
+               /* when updating name also send stored friendly name */
+               if (dev->friendly_name) {
+                       size += fill_hal_prop(buf + size,
+                                               HAL_PROP_DEVICE_FRIENDLY_NAME,
+                                               strlen(dev->friendly_name),
+                                               dev->friendly_name);
+                       ev->num_props++;
+               }
+       }
 
-               num_prop = &ev->num_props;
-               opcode = HAL_EV_DEVICE_FOUND;
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_DEVICE_FOUND,
+                                                               size, buf);
+}
 
-               bdaddr2android(bdaddr, &android_bdaddr);
+static void update_device(struct device *dev, int8_t rssi,
+                               const struct eir_data *eir, uint8_t bdaddr_type)
+{
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_remote_device_props *ev = (void *) buf;
+       uint8_t old_type, new_type;
+       int size;
 
-               size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR,
-                               sizeof(android_bdaddr), &android_bdaddr);
-               (*num_prop)++;
-       } else {
-               struct hal_ev_remote_device_props *ev = (void *) buf;
+       memset(buf, 0, sizeof(buf));
 
-               size += sizeof(*ev);
+       size = sizeof(*ev);
 
-               num_prop = &ev->num_props;
-               opcode = HAL_EV_REMOTE_DEVICE_PROPS;
+       ev->status = HAL_STATUS_SUCCESS;
+       bdaddr2android(&dev->bdaddr, ev->bdaddr);
+
+       old_type = get_device_android_type(dev);
+
+       if (bdaddr_type == BDADDR_BREDR) {
+               dev->bredr = true;
+       } else {
+               dev->le = true;
+               dev->bdaddr_type = bdaddr_type;
+       }
+
+       new_type = get_device_android_type(dev);
 
-               ev->status = HAL_STATUS_SUCCESS;
-               bdaddr2android(bdaddr, ev->bdaddr);
+       if (old_type != new_type) {
+               size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+                                               sizeof(new_type), &new_type);
+               ev->num_props++;
        }
 
-       if (eir.class) {
+       if (eir->class && dev->class != eir->class) {
+               dev->class = eir->class;
                size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
-                                               sizeof(eir.class), &eir.class);
-               (*num_prop)++;
+                                       sizeof(dev->class), &dev->class);
+               ev->num_props++;
        }
 
-       if (rssi) {
+       if (rssi && rssi_above_threshold(dev->rssi, rssi)) {
+               dev->rssi = rssi;
                size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
-                                                       sizeof(rssi), &rssi);
-               (*num_prop)++;
+                                               sizeof(dev->rssi), &dev->rssi);
+               ev->num_props++;
        }
 
-       if (eir.name) {
-               cache_device_name(bdaddr, eir.name);
-
+       if (eir->name && strlen(eir->name) && strcmp(dev->name, eir->name)) {
+               g_free(dev->name);
+               dev->name = g_strdup(eir->name);
                size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
-                                               strlen(eir.name), eir.name);
-               (*num_prop)++;
+                                               strlen(dev->name), dev->name);
+               ev->num_props++;
+
+               /* when updating name also send stored friendly name */
+               if (dev->friendly_name) {
+                       size += fill_hal_prop(buf + size,
+                                               HAL_PROP_DEVICE_FRIENDLY_NAME,
+                                               strlen(dev->friendly_name),
+                                               dev->friendly_name);
+                       ev->num_props++;
+               }
+       }
+
+       if (ev->num_props)
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                                       HAL_EV_REMOTE_DEVICE_PROPS, size, buf);
+}
+
+static bool is_new_device(const struct device *dev, unsigned int flags)
+{
+       if (dev->found)
+               return false;
+
+       if (dev->bredr_paired || dev->le_paired)
+               return false;
+
+       if (dev->bdaddr_type != BDADDR_BREDR &&
+                               !(flags & (EIR_LIM_DISC | EIR_GEN_DISC)))
+               return false;
+
+       return true;
+}
+
+static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+                                       int8_t rssi, bool confirm,
+                                       const uint8_t *data, uint8_t data_len)
+{
+       struct eir_data eir;
+       struct device *dev;
+
+       memset(&eir, 0, sizeof(eir));
+
+       eir_parse(&eir, data, data_len);
+
+       dev = get_device(bdaddr, bdaddr_type);
+
+       if (bdaddr_type == BDADDR_BREDR)
+               dev->bredr_seen = time(NULL);
+       else
+               dev->le_seen = time(NULL);
+
+       /*
+        * Device found event needs to be send also for known device if this is
+        * new discovery session. Otherwise framework will ignore it.
+        */
+       if (is_new_device(dev, eir.flags))
+               update_new_device(dev, rssi, &eir);
+       else
+               update_device(dev, rssi, &eir, bdaddr_type);
+
+       eir_data_free(&eir);
+
+       /* Notify Gatt if its registered for LE events */
+       if (bdaddr_type != BDADDR_BREDR && gatt_device_found_cb) {
+               bool discoverable;
+
+               discoverable = eir.flags & (EIR_LIM_DISC | EIR_GEN_DISC);
+
+               gatt_device_found_cb(bdaddr, bdaddr_type, rssi, data_len, data,
+                                                               discoverable);
        }
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, opcode, size, buf);
+       if (!dev->bredr_paired && !dev->le_paired)
+               cache_device(dev);
 
        if (confirm) {
                char addr[18];
+               bool resolve_name = true;
 
                ba2str(bdaddr, addr);
-               info("Device %s needs name confirmation.", addr);
-               confirm_device_name(bdaddr, bdaddr_type);
-       }
 
-       eir_data_free(&eir);
+               /*
+                * Don't need to confirm name if we have it already in cache
+                * Just check if device name is different than bdaddr
+                */
+               if (g_strcmp0(dev->name, addr)) {
+                       get_device_name(dev);
+                       resolve_name = false;
+               }
+
+               info("Device %s needs name confirmation (resolve_name=%d)",
+                                                       addr, resolve_name);
+               dev->confirm_id = confirm_device_name(bdaddr, bdaddr_type,
+                                                               resolve_name);
+       }
 }
 
 static void mgmt_device_found_event(uint16_t index, uint16_t length,
@@ -935,12 +1588,13 @@ static void mgmt_device_connected_event(uint16_t index, uint16_t length,
        hal_ev.state = HAL_ACL_STATE_CONNECTED;
        bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ACL_STATE_CHANGED,
-                                               sizeof(hal_ev), &hal_ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
 }
 
 static void mgmt_device_disconnected_event(uint16_t index, uint16_t length,
-                                       const void *param, void *user_data)
+                                                       const void *param,
+                                                       void *user_data)
 {
        const struct mgmt_ev_device_disconnected *ev = param;
        struct hal_ev_acl_state_changed hal_ev;
@@ -954,8 +1608,8 @@ static void mgmt_device_disconnected_event(uint16_t index, uint16_t length,
        hal_ev.state = HAL_ACL_STATE_DISCONNECTED;
        bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ACL_STATE_CHANGED,
-                                               sizeof(hal_ev), &hal_ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
 }
 
 static uint8_t status_mgmt2hal(uint8_t mgmt)
@@ -984,79 +1638,213 @@ static void mgmt_connect_failed_event(uint16_t index, uint16_t length,
                                        const void *param, void *user_data)
 {
        const struct mgmt_ev_connect_failed *ev = param;
+       struct device *dev;
+
+       if (length < sizeof(*ev)) {
+               error("Too short connect failed event (%u bytes)", length);
+               return;
+       }
 
        DBG("");
 
-       /* In case security mode 3 pairing we will get connect failed event
-       * in case e.g wrong PIN code entered. Let's check if device is
-       * bonding, if so update bond state */
-       set_device_bond_state(&ev->addr.bdaddr, status_mgmt2hal(ev->status),
-                                                       HAL_BOND_STATE_NONE);
+       dev = find_device(&ev->addr.bdaddr);
+       if (!dev)
+               return;
+
+       /*
+        * In case security mode 3 pairing we will get connect failed event
+        * in case e.g wrong PIN code entered. Let's check if device is
+        * bonding, if so update bond state
+        */
+
+       if (!dev->pairing)
+               return;
+
+       update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status),
+                                                       false, false, false);
 }
 
 static void mgmt_auth_failed_event(uint16_t index, uint16_t length,
                                        const void *param, void *user_data)
 {
        const struct mgmt_ev_auth_failed *ev = param;
+       struct device *dev;
+
+       if (length < sizeof(*ev)) {
+               error("Too small auth failed mgmt event (%u bytes)", length);
+               return;
+       }
 
        DBG("");
 
-       set_device_bond_state(&ev->addr.bdaddr, status_mgmt2hal(ev->status),
-                                                       HAL_BOND_STATE_NONE);
+       dev = find_device(&ev->addr.bdaddr);
+       if (!dev)
+               return;
+
+       if (!dev->pairing)
+               return;
+
+       update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status),
+                                                       false, false, false);
 }
 
 static void mgmt_device_unpaired_event(uint16_t index, uint16_t length,
                                        const void *param, void *user_data)
 {
+       const struct mgmt_ev_device_unpaired *ev = param;
+       struct device *dev;
+
+       if (length < sizeof(*ev)) {
+               error("Too small device unpaired event (%u bytes)", length);
+               return;
+       }
+
        DBG("");
+
+       /* TODO should device be disconnected ? */
+
+       dev = find_device(&ev->addr.bdaddr);
+       if (!dev)
+               return;
+
+       update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, false,
+                                                               false, false);
 }
 
-static void register_mgmt_handlers(void)
+static void store_ltk(const bdaddr_t *dst, uint8_t bdaddr_type, bool master,
+                       const uint8_t *key, uint8_t key_type, uint8_t enc_size,
+                       uint16_t ediv, uint64_t rand)
 {
-       mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index,
-                                       new_settings_callback, NULL, NULL);
-
-       mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index,
-                               mgmt_dev_class_changed_event, NULL, NULL);
+       const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s;
+       GKeyFile *key_file;
+       char key_str[33];
+       gsize length = 0;
+       char addr[18];
+       char *data;
+       int i;
 
-       mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index,
-                               mgmt_local_name_changed_event, NULL, NULL);
+       key_file = g_key_file_new();
+       if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
+               g_key_file_free(key_file);
+               return;
+       }
 
-       mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index,
-                                       new_link_key_callback, NULL, NULL);
+       ba2str(dst, addr);
 
-       mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index,
-                                       pin_code_request_callback, NULL, NULL);
+       key_s = master ? "LongTermKey" : "SlaveLongTermKey";
+       keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType";
+       encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize";
+       ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv";
+       rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand";
 
-       mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index,
-                               user_confirm_request_callback, NULL, NULL);
+       for (i = 0; i < 16; i++)
+               sprintf(key_str + (i * 2), "%2.2X", key[i]);
 
-       mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index,
-                               user_passkey_request_callback, NULL, NULL);
+       g_key_file_set_string(key_file, addr, key_s, key_str);
 
-       mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index,
-                               user_passkey_notify_callback, NULL, NULL);
+       g_key_file_set_integer(key_file, addr, keytype_s, key_type);
 
-       mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index,
-                                       mgmt_discovering_event, NULL, NULL);
+       g_key_file_set_integer(key_file, addr, encsize_s, enc_size);
 
-       mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index,
-                                       mgmt_device_found_event, NULL, NULL);
+       g_key_file_set_integer(key_file, addr, ediv_s, ediv);
 
-       mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index,
-                               mgmt_device_connected_event, NULL, NULL);
+       g_key_file_set_uint64(key_file, addr, rand_s, rand);
 
-       mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index,
-                               mgmt_device_disconnected_event, NULL, NULL);
+       data = g_key_file_to_data(key_file, &length, NULL);
+       g_file_set_contents(DEVICES_FILE, data, length, NULL);
+       g_free(data);
 
-       mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index,
-                                       mgmt_connect_failed_event, NULL, NULL);
+       g_key_file_free(key_file);
+}
+
+static void new_long_term_key_event(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_new_long_term_key *ev = param;
+       struct device *dev;
+       char dst[18];
+
+       if (length < sizeof(*ev)) {
+               error("Too small long term key event (%u bytes)", length);
+               return;
+       }
+
+       ba2str(&ev->key.addr.bdaddr, dst);
+
+       DBG("new LTK for %s type %u enc_size %u store_hint %u",
+                       dst, ev->key.type, ev->key.enc_size, ev->store_hint);
+
+       dev = find_device(&ev->key.addr.bdaddr);
+       if (!dev)
+               return;
+
+       update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
+                                                       true, !!ev->store_hint);
+
+       if (ev->store_hint) {
+               const struct mgmt_ltk_info *key = &ev->key;
+               uint16_t ediv;
+               uint64_t rand;
+
+               ediv = le16_to_cpu(key->ediv);
+               rand = le64_to_cpu(key->rand);
+
+               store_ltk(&key->addr.bdaddr, key->addr.type, key->master,
+                               key->val, key->type, key->enc_size, ediv, rand);
+       }
+
+       /* TODO browse services here? */
+}
+
+static void register_mgmt_handlers(void)
+{
+       mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index,
+                                       new_settings_callback, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index,
+                               mgmt_dev_class_changed_event, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index,
+                               mgmt_local_name_changed_event, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index,
+                                       new_link_key_callback, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index,
+                                       pin_code_request_callback, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index,
+                               user_confirm_request_callback, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index,
+                               user_passkey_request_callback, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index,
+                               user_passkey_notify_callback, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index,
+                                       mgmt_discovering_event, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index,
+                                       mgmt_device_found_event, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index,
+                               mgmt_device_connected_event, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index,
+                               mgmt_device_disconnected_event, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index,
+                                       mgmt_connect_failed_event, NULL, NULL);
 
        mgmt_register(mgmt_if, MGMT_EV_AUTH_FAILED, adapter.index,
                                        mgmt_auth_failed_event, NULL, NULL);
 
        mgmt_register(mgmt_if, MGMT_EV_DEVICE_UNPAIRED, adapter.index,
                                mgmt_device_unpaired_event, NULL, NULL);
+
+       mgmt_register(mgmt_if, MGMT_EV_NEW_LONG_TERM_KEY, adapter.index,
+                                       new_long_term_key_event, NULL, NULL);
 }
 
 static void load_link_keys_complete(uint8_t status, uint16_t length,
@@ -1117,18 +1905,39 @@ static void load_link_keys(GSList *keys, bt_bluetooth_ready cb)
        }
 }
 
-/* output uint128 is in host order */
-static void uuid16_to_uint128(uint16_t uuid, uint128_t *u128)
+static void load_ltks(GSList *ltks)
 {
-       uuid_t uuid16, uuid128;
+       struct mgmt_cp_load_long_term_keys *cp;
+       struct mgmt_ltk_info *ltk;
+       size_t ltk_count, cp_size;
+       GSList *l;
+
+       ltk_count = g_slist_length(ltks);
 
-       sdp_uuid16_create(&uuid16, uuid);
-       sdp_uuid16_to_uuid128(&uuid128, &uuid16);
+       DBG("ltks %zu", ltk_count);
 
-       ntoh128(&uuid128.value.uuid128, u128);
+       cp_size = sizeof(*cp) + (ltk_count * sizeof(*ltk));
+
+       cp = g_malloc0(cp_size);
+
+       /*
+        * Even if the list of stored keys is empty, it is important to load
+        * an empty list into the kernel. That way it is ensured that no old
+        * keys from a previous daemon are present.
+        */
+       cp->key_count = htobs(ltk_count);
+
+       for (l = ltks, ltk = cp->keys; l != NULL; l = g_slist_next(l), ltk++)
+               memcpy(ltk, ltks->data, sizeof(*ltk));
+
+       if (mgmt_send(mgmt_if, MGMT_OP_LOAD_LONG_TERM_KEYS, adapter.index,
+                                       cp_size, cp, NULL, NULL, NULL) == 0)
+               error("Failed to load LTKs");
+
+       g_free(cp);
 }
 
-static uint8_t get_uuids(void)
+static uint8_t get_adapter_uuids(void)
 {
        struct hal_ev_adapter_props_changed *ev;
        GSList *list = adapter.uuids;
@@ -1136,7 +1945,6 @@ static uint8_t get_uuids(void)
        int len = uuid_count * sizeof(uint128_t);
        uint8_t buf[BASELEN_PROP_CHANGED + len];
        uint8_t *p;
-       int i;
 
        memset(buf, 0, sizeof(buf));
        ev = (void *) buf;
@@ -1149,20 +1957,15 @@ static uint8_t get_uuids(void)
        p = ev->props->val;
 
        for (; list; list = g_slist_next(list)) {
-               uint16_t uuid = GPOINTER_TO_UINT(list->data);
-               uint128_t uint128;
+               uuid_t *uuid = list->data;
 
-               uuid16_to_uint128(uuid, &uint128);
-
-               /* Android expects swapped bytes in uuid */
-               for (i = 0; i < 16; i++)
-                       p[15 - i] = uint128.data[i];
+               memcpy(p, &uuid->value.uuid128, sizeof(uint128_t));
 
                p += sizeof(uint128_t);
        }
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
-                                                       sizeof(buf), ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                               HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev);
 
        return HAL_STATUS_SUCCESS;
 }
@@ -1178,15 +1981,15 @@ static void remove_uuid_complete(uint8_t status, uint16_t length,
 
        mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
 
-       get_uuids();
+       get_adapter_uuids();
 }
 
-static void remove_uuid(uint16_t uuid)
+static void remove_uuid(uuid_t *uuid)
 {
        uint128_t uint128;
        struct mgmt_cp_remove_uuid cp;
 
-       uuid16_to_uint128(uuid, &uint128);
+       ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128);
        htob128(&uint128, (uint128_t *) cp.uuid);
 
        mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), &cp,
@@ -1204,17 +2007,17 @@ static void add_uuid_complete(uint8_t status, uint16_t length,
 
        mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
 
-       get_uuids();
+       get_adapter_uuids();
 }
 
-static void add_uuid(uint8_t svc_hint, uint16_t uuid)
+static void add_uuid(uint8_t svc_hint, uuid_t *uuid)
 {
        uint128_t uint128;
        struct mgmt_cp_add_uuid cp;
 
-       uuid16_to_uint128(uuid, &uint128);
-
+       ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128);
        htob128(&uint128, (uint128_t *) cp.uuid);
+
        cp.svc_hint = svc_hint;
 
        mgmt_send(mgmt_if, MGMT_OP_ADD_UUID, adapter.index, sizeof(cp), &cp,
@@ -1223,22 +2026,21 @@ static void add_uuid(uint8_t svc_hint, uint16_t uuid)
 
 int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint)
 {
-       uint16_t uuid;
+       uuid_t *uuid;
 
-       /* TODO support all types? */
-       if (rec->svclass.type != SDP_UUID16) {
-               warn("Ignoring unsupported UUID type");
-               return -EINVAL;
-       }
+       uuid = sdp_uuid_to_uuid128(&rec->svclass);
 
-       uuid = rec->svclass.value.uuid16;
+       if (g_slist_find_custom(adapter.uuids, uuid, sdp_uuid_cmp)) {
+               char uuid_str[32];
 
-       if (g_slist_find(adapter.uuids, GUINT_TO_POINTER(uuid))) {
-               DBG("UUID 0x%x already added", uuid);
+               sdp_uuid2strn(uuid, uuid_str, sizeof(uuid_str));
+               DBG("UUID %s already added", uuid_str);
+
+               bt_free(uuid);
                return -EALREADY;
        }
 
-       adapter.uuids = g_slist_prepend(adapter.uuids, GUINT_TO_POINTER(uuid));
+       adapter.uuids = g_slist_prepend(adapter.uuids, uuid);
 
        add_uuid(svc_hint, uuid);
 
@@ -1249,20 +2051,21 @@ void bt_adapter_remove_record(uint32_t handle)
 {
        sdp_record_t *rec;
        GSList *uuid_found;
-       uint16_t uuid;
 
        rec = sdp_record_find(handle);
        if (!rec)
                return;
 
-       uuid = rec->svclass.value.uuid16;
-
-       uuid_found = g_slist_find(adapter.uuids, GUINT_TO_POINTER(uuid));
+       uuid_found = g_slist_find_custom(adapter.uuids, &rec->svclass,
+                                                               sdp_uuid_cmp);
        if (uuid_found) {
+               uuid_t *uuid = uuid_found->data;
+
                remove_uuid(uuid);
 
-               adapter.uuids = g_slist_remove(adapter.uuids,
-                                                       uuid_found->data);
+               adapter.uuids = g_slist_remove(adapter.uuids, uuid);
+
+               free(uuid);
        }
 
        remove_record_from_server(handle);
@@ -1373,7 +2176,7 @@ static uint8_t set_adapter_name(const uint8_t *name, uint16_t len)
        return HAL_STATUS_FAILED;
 }
 
-static uint8_t set_discoverable_timeout(const void *buf, uint16_t len)
+static uint8_t set_adapter_discoverable_timeout(const void *buf, uint16_t len)
 {
        const uint32_t *timeout = buf;
 
@@ -1384,13 +2187,20 @@ static uint8_t set_discoverable_timeout(const void *buf, uint16_t len)
                return HAL_STATUS_FAILED;
        }
 
-       /* Android handles discoverable timeout in Settings app.
+       /*
+        * Android handles discoverable timeout in Settings app.
         * There is no need to use kernel feature for that.
-        * Just need to store this value here */
+        * Just need to store this value here
+        */
 
-       /* TODO: This should be in some storage */
        memcpy(&adapter.discoverable_timeout, timeout, sizeof(uint32_t));
 
+       store_adapter_config();
+
+       send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT,
+                                       sizeof(adapter.discoverable_timeout),
+                                       &adapter.discoverable_timeout);
+
        return HAL_STATUS_SUCCESS;
 }
 
@@ -1404,12 +2214,296 @@ static void clear_uuids(void)
                                        sizeof(cp), &cp, NULL, NULL, NULL);
 }
 
+static struct device *create_device_from_info(GKeyFile *key_file,
+                                                       const char *peer)
+{
+       struct device *dev;
+       uint8_t type;
+       bdaddr_t bdaddr;
+       char **uuids;
+       char *str;
+
+       /* BREDR if not present */
+       type = g_key_file_get_integer(key_file, peer, "AddressType", NULL);
+
+       str2ba(peer, &bdaddr);
+       dev = create_device(&bdaddr, type);
+
+       if (type != BDADDR_BREDR)
+               dev->bredr = g_key_file_get_boolean(key_file, peer, "BREDR",
+                                                                       NULL);
+
+       str = g_key_file_get_string(key_file, peer, "LinkKey", NULL);
+       if (str) {
+               g_free(str);
+               dev->bredr_paired = true;
+               dev->bredr_bonded = true;
+       }
+
+       str = g_key_file_get_string(key_file, peer, "LongTermKey", NULL);
+       if (str) {
+               g_free(str);
+               dev->le_paired = true;
+               dev->le_bonded = true;
+       }
+
+       str = g_key_file_get_string(key_file, peer, "SlaveLongTermKey", NULL);
+       if (str) {
+               g_free(str);
+               dev->le_paired = true;
+               dev->le_bonded = true;
+       }
+
+       str = g_key_file_get_string(key_file, peer, "Name", NULL);
+       if (str) {
+               g_free(dev->name);
+               dev->name = str;
+       }
+
+       str = g_key_file_get_string(key_file, peer, "FriendlyName", NULL);
+       if (str) {
+               g_free(dev->friendly_name);
+               dev->friendly_name = str;
+       }
+
+       dev->class = g_key_file_get_integer(key_file, peer, "Class", NULL);
+
+       if (dev->bredr)
+               dev->bredr_seen = g_key_file_get_integer(key_file, peer,
+                                                               "Timestamp",
+                                                               NULL);
+       else
+               dev->le_seen = g_key_file_get_integer(key_file, peer,
+                                                       "Timestamp", NULL);
+
+       uuids = g_key_file_get_string_list(key_file, peer, "Services", NULL,
+                                                                       NULL);
+       if (uuids) {
+               char **uuid;
+
+               for (uuid = uuids; *uuid; uuid++) {
+                       uint8_t *u = g_malloc0(16);
+                       int i;
+
+                       for (i = 0; i < 16; i++)
+                               sscanf((*uuid) + (i * 2), "%02hhX", &u[i]);
+
+                       dev->uuids = g_slist_append(dev->uuids, u);
+               }
+
+               g_strfreev(uuids);
+       }
+
+       return dev;
+}
+
+static struct mgmt_link_key_info *get_key_info(GKeyFile *key_file,
+                                                       const char *peer)
+{
+       struct mgmt_link_key_info *info = NULL;
+       char *str;
+       unsigned int i;
+
+       str = g_key_file_get_string(key_file, peer, "LinkKey", NULL);
+       if (!str || strlen(str) != 32)
+               goto failed;
+
+       info = g_new0(struct mgmt_link_key_info, 1);
+
+       str2ba(peer, &info->addr.bdaddr);
+
+       info->addr.type = g_key_file_get_integer(key_file, peer, "Type", NULL);
+
+       for (i = 0; i < sizeof(info->val); i++)
+               sscanf(str + (i * 2), "%02hhX", &info->val[i]);
+
+       info->type = g_key_file_get_integer(key_file, peer, "LinkKeyType",
+                                                                       NULL);
+       info->pin_len = g_key_file_get_integer(key_file, peer,
+                                               "LinkKeyPinLength", NULL);
+
+failed:
+       g_free(str);
+
+       return info;
+}
+
+static struct mgmt_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer,
+                                                               bool master)
+{
+       const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s;
+       struct mgmt_ltk_info *info = NULL;
+       char *key;
+       unsigned int i;
+
+       key_s = master ? "LongTermKey" : "SlaveLongTermKey";
+       keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType";
+       encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize";
+       ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv";
+       rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand";
+
+       key = g_key_file_get_string(key_file, peer, key_s, NULL);
+       if (!key || strlen(key) != 32)
+               goto failed;
+
+       info = g_new0(struct mgmt_ltk_info, 1);
+
+       str2ba(peer, &info->addr.bdaddr);
+
+       info->addr.type = g_key_file_get_integer(key_file, peer, "Type", NULL);
+
+       for (i = 0; i < sizeof(info->val); i++)
+               sscanf(key + (i * 2), "%02hhX", &info->val[i]);
+
+       info->type = g_key_file_get_integer(key_file, peer, keytype_s, NULL);
+
+       info->enc_size = g_key_file_get_integer(key_file, peer, encsize_s,
+                                                                       NULL);
+
+       info->rand = g_key_file_get_uint64(key_file, peer, rand_s, NULL);
+       info->rand = cpu_to_le64(info->rand);
+
+       info->ediv = g_key_file_get_integer(key_file, peer, ediv_s, NULL);
+       info->ediv = cpu_to_le16(info->ediv);
+
+       info->master = master;
+
+failed:
+       g_free(key);
+
+       return info;
+}
+
+static time_t device_timestamp(const struct device *dev)
+{
+       if (dev->bredr && dev->le) {
+               if (dev->le_seen > dev->bredr_seen)
+                       return dev->le_seen;
+
+               return dev->bredr_seen;
+       }
+
+       if (dev->bredr)
+               return dev->bredr_seen;
+
+       return dev->le_seen;
+}
+
+static int device_timestamp_cmp(gconstpointer  a, gconstpointer  b)
+{
+       const struct device *deva = a;
+       const struct device *devb = b;
+
+       return device_timestamp(deva) < device_timestamp(devb);
+}
+
+static void load_devices_cache(void)
+{
+       GKeyFile *key_file;
+       gchar **devs;
+       gsize len = 0;
+       unsigned int i;
+
+       key_file = g_key_file_new();
+
+       g_key_file_load_from_file(key_file, CACHE_FILE, 0, NULL);
+
+       devs = g_key_file_get_groups(key_file, &len);
+
+       for (i = 0; i < len; i++) {
+               struct device *dev;
+
+               dev = create_device_from_info(key_file, devs[i]);
+               cached_devices = g_slist_prepend(cached_devices, dev);
+       }
+
+       cached_devices = g_slist_sort(cached_devices, device_timestamp_cmp);
+
+       g_strfreev(devs);
+       g_key_file_free(key_file);
+}
+
+static void load_devices_info(bt_bluetooth_ready cb)
+{
+       GKeyFile *key_file;
+       gchar **devs;
+       gsize len = 0;
+       unsigned int i;
+       GSList *keys = NULL;
+       GSList *ltks = NULL;
+
+       key_file = g_key_file_new();
+
+       g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL);
+
+       devs = g_key_file_get_groups(key_file, &len);
+
+       for (i = 0; i < len; i++) {
+               struct mgmt_link_key_info *key_info;
+               struct mgmt_ltk_info *ltk_info;
+               struct mgmt_ltk_info *slave_ltk_info;
+               struct device *dev;
+
+               key_info = get_key_info(key_file, devs[i]);
+               ltk_info = get_ltk_info(key_file, devs[i], true);
+               slave_ltk_info = get_ltk_info(key_file, devs[i], false);
+
+               if (!key_info && !ltk_info && !slave_ltk_info) {
+                       error("Failed to load keys for %s, skipping", devs[i]);
+
+                       continue;
+               }
+
+               if (key_info)
+                       keys = g_slist_prepend(keys, key_info);
+
+               if (ltk_info)
+                       ltks = g_slist_prepend(ltks, ltk_info);
+
+               if (slave_ltk_info)
+                       ltks = g_slist_prepend(ltks, slave_ltk_info);
+
+               dev = create_device_from_info(key_file, devs[i]);
+
+               bonded_devices = g_slist_prepend(bonded_devices, dev);
+       }
+
+       load_ltks(ltks);
+       g_slist_free_full(ltks, g_free);
+
+       load_link_keys(keys, cb);
+       g_slist_free_full(keys, g_free);
+
+       g_strfreev(devs);
+       g_key_file_free(key_file);
+}
+
+static void set_adapter_class(void)
+{
+       struct mgmt_cp_set_dev_class cp;
+
+       memset(&cp, 0, sizeof(cp));
+
+       /*
+        * kernel assign the major and minor numbers straight to dev_class[0]
+        * and dev_class[1] without considering the proper bit shifting.
+        */
+       cp.major = ADAPTER_MAJOR_CLASS & 0x1f;
+       cp.minor = ADAPTER_MINOR_CLASS << 2;
+
+       if (mgmt_send(mgmt_if, MGMT_OP_SET_DEV_CLASS, adapter.index,
+                                       sizeof(cp), &cp, NULL, NULL, NULL) > 0)
+               return;
+
+       error("Failed to set class of device");
+}
+
 static void read_info_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
        const struct mgmt_rp_read_info *rp = param;
        bt_bluetooth_ready cb = user_data;
-       uint32_t missing_settings, supported_settings;
+       uint32_t missing_settings;
        int err;
 
        DBG("");
@@ -1433,35 +2527,53 @@ static void read_info_complete(uint8_t status, uint16_t length,
                goto failed;
        }
 
+       load_adapter_config();
+
+       if (!bacmp(&adapter.bdaddr, BDADDR_ANY)) {
+               bacpy(&adapter.bdaddr, &rp->bdaddr);
+               adapter.name = g_strdup(DEFAULT_ADAPTER_NAME);
+               store_adapter_config();
+       } else if (bacmp(&adapter.bdaddr, &rp->bdaddr)) {
+               error("Bluetooth address mismatch");
+               err = -ENODEV;
+               goto failed;
+       }
+
+       if (g_strcmp0(adapter.name, (const char *) rp->name))
+               set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name));
+
+       set_adapter_class();
+
        /* Store adapter information */
-       bacpy(&adapter.bdaddr, &rp->bdaddr);
        adapter.dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) |
                                                (rp->dev_class[2] << 16);
-       adapter.name = g_strdup((const char *) rp->name);
 
-       supported_settings = btohs(rp->supported_settings);
+       adapter.supported_settings = btohs(rp->supported_settings);
        adapter.current_settings = btohs(rp->current_settings);
 
-       /* TODO: Read discoverable timeout from storage here */
-
        /* TODO: Register all event notification handlers */
        register_mgmt_handlers();
 
        clear_uuids();
 
-       load_link_keys(NULL, cb);
-
        set_io_capability();
        set_device_id();
 
-       missing_settings = adapter.current_settings ^ supported_settings;
+       missing_settings = adapter.current_settings ^
+                                               adapter.supported_settings;
 
        if (missing_settings & MGMT_SETTING_SSP)
                set_mode(MGMT_OP_SET_SSP, 0x01);
 
+       if (missing_settings & MGMT_SETTING_SECURE_CONN)
+               set_mode(MGMT_OP_SET_SECURE_CONN, 0x01);
+
        if (missing_settings & MGMT_SETTING_PAIRABLE)
                set_mode(MGMT_OP_SET_PAIRABLE, 0x01);
 
+       load_devices_info(cb);
+       load_devices_cache();
+
        return;
 
 failed:
@@ -1605,7 +2717,7 @@ failed:
        cb(-EIO, NULL);
 }
 
-bool bt_bluetooth_start(int index, bt_bluetooth_ready cb)
+bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb)
 {
        DBG("index %d", index);
 
@@ -1615,6 +2727,9 @@ bool bt_bluetooth_start(int index, bt_bluetooth_ready cb)
                return false;
        }
 
+       if (mgmt_dbg)
+               mgmt_set_debug(mgmt_if, mgmt_debug, "mgmt_if: ", NULL);
+
        if (mgmt_send(mgmt_if, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL,
                                read_version_complete, cb, NULL) == 0) {
                error("Error sending READ_VERSION mgmt command");
@@ -1686,25 +2801,18 @@ static bool set_discoverable(uint8_t mode, uint16_t timeout)
        return false;
 }
 
-static uint8_t get_address(void)
+static uint8_t get_adapter_address(void)
 {
-       uint8_t buf[BASELEN_PROP_CHANGED + sizeof(bdaddr_t)];
-       struct hal_ev_adapter_props_changed *ev = (void *) buf;
-
-       ev->num_props = 1;
-       ev->status = HAL_STATUS_SUCCESS;
+       uint8_t buf[6];
 
-       ev->props[0].type = HAL_PROP_ADAPTER_ADDR;
-       ev->props[0].len = sizeof(bdaddr_t);
-       bdaddr2android(&adapter.bdaddr, ev->props[0].val);
+       bdaddr2android(&adapter.bdaddr, buf);
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
-                                                       sizeof(buf), buf);
+       send_adapter_property(HAL_PROP_ADAPTER_ADDR, sizeof(buf), buf);
 
        return HAL_STATUS_SUCCESS;
 }
 
-static uint8_t get_name(void)
+static uint8_t get_adapter_name(void)
 {
        if (!adapter.name)
                return HAL_STATUS_FAILED;
@@ -1714,8 +2822,7 @@ static uint8_t get_name(void)
        return HAL_STATUS_SUCCESS;
 }
 
-
-static uint8_t get_class(void)
+static uint8_t get_adapter_class(void)
 {
        DBG("");
 
@@ -1724,34 +2831,42 @@ static uint8_t get_class(void)
        return HAL_STATUS_SUCCESS;
 }
 
-static uint8_t get_type(void)
+static uint8_t settings2type(void)
 {
-       DBG("Not implemented");
+       bool bredr, le;
 
-       /* TODO: Add implementation */
+       bredr = adapter.current_settings & MGMT_SETTING_BREDR;
+       le = adapter.current_settings & MGMT_SETTING_LE;
 
-       return HAL_STATUS_FAILED;
-}
+       if (bredr && le)
+               return HAL_TYPE_DUAL;
 
-static uint8_t get_service(void)
-{
-       DBG("Not implemented");
+       if (bredr && !le)
+               return HAL_TYPE_BREDR;
 
-       /* TODO: Add implementation */
+       if (!bredr && le)
+               return HAL_TYPE_LE;
 
-       return HAL_STATUS_FAILED;
+       return 0;
 }
 
-static uint8_t get_scan_mode(void)
+static uint8_t get_adapter_type(void)
 {
+       uint8_t type;
+
        DBG("");
 
-       scan_mode_changed();
+       type = settings2type();
+
+       if (!type)
+               return HAL_STATUS_FAILED;
+
+       send_adapter_property(HAL_PROP_ADAPTER_TYPE, sizeof(type), &type);
 
        return HAL_STATUS_SUCCESS;
 }
 
-static uint8_t get_devices(void)
+static uint8_t get_adapter_service_rec(void)
 {
        DBG("Not implemented");
 
@@ -1760,24 +2875,41 @@ static uint8_t get_devices(void)
        return HAL_STATUS_FAILED;
 }
 
-static uint8_t get_discoverable_timeout(void)
+static uint8_t get_adapter_scan_mode(void)
 {
-       struct hal_ev_adapter_props_changed *ev;
-       uint8_t buf[BASELEN_PROP_CHANGED + sizeof(uint32_t)];
+       DBG("");
 
-       memset(buf, 0, sizeof(buf));
-       ev = (void *) buf;
+       scan_mode_changed();
 
-       ev->num_props = 1;
-       ev->status = HAL_STATUS_SUCCESS;
+       return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_adapter_bonded_devices(void)
+{
+       uint8_t buf[sizeof(bdaddr_t) * g_slist_length(bonded_devices)];
+       int i = 0;
+       GSList *l;
 
-       ev->props[0].type = HAL_PROP_ADAPTER_DISC_TIMEOUT;
-       ev->props[0].len = sizeof(uint32_t);
-       memcpy(&ev->props[0].val, &adapter.discoverable_timeout,
-                                                       sizeof(uint32_t));
+       DBG("");
+
+       for (l = bonded_devices; l; l = g_slist_next(l)) {
+               struct device *dev = l->data;
+
+               bdaddr2android(&dev->bdaddr, buf + (i * sizeof(bdaddr_t)));
+               i++;
+       }
+
+       send_adapter_property(HAL_PROP_ADAPTER_BONDED_DEVICES,
+                                               i * sizeof(bdaddr_t), buf);
+
+       return HAL_STATUS_SUCCESS;
+}
 
-       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
-                                                       sizeof(buf), ev);
+static uint8_t get_adapter_discoverable_timeout(void)
+{
+       send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT,
+                                       sizeof(adapter.discoverable_timeout),
+                                       &adapter.discoverable_timeout);
 
        return HAL_STATUS_SUCCESS;
 }
@@ -1789,94 +2921,285 @@ static void handle_get_adapter_prop_cmd(const void *buf, uint16_t len)
 
        switch (cmd->type) {
        case HAL_PROP_ADAPTER_ADDR:
-               status = get_address();
+               status = get_adapter_address();
                break;
        case HAL_PROP_ADAPTER_NAME:
-               status = get_name();
+               status = get_adapter_name();
                break;
        case HAL_PROP_ADAPTER_UUIDS:
-               status = get_uuids();
+               status = get_adapter_uuids();
                break;
        case HAL_PROP_ADAPTER_CLASS:
-               status = get_class();
+               status = get_adapter_class();
                break;
        case HAL_PROP_ADAPTER_TYPE:
-               status = get_type();
+               status = get_adapter_type();
                break;
        case HAL_PROP_ADAPTER_SERVICE_REC:
-               status = get_service();
+               status = get_adapter_service_rec();
                break;
        case HAL_PROP_ADAPTER_SCAN_MODE:
-               status = get_scan_mode();
+               status = get_adapter_scan_mode();
                break;
        case HAL_PROP_ADAPTER_BONDED_DEVICES:
-               status = get_devices();
+               status = get_adapter_bonded_devices();
                break;
        case HAL_PROP_ADAPTER_DISC_TIMEOUT:
-               status = get_discoverable_timeout();
+               status = get_adapter_discoverable_timeout();
                break;
        default:
                status = HAL_STATUS_FAILED;
                break;
        }
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, status);
-}
+       if (status != HAL_STATUS_SUCCESS)
+               error("Failed to get adapter property (type %u status %u)",
+                                                       cmd->type, status);
 
-static void get_properties(void)
-{
-       get_address();
-       get_name();
-       get_uuids();
-       get_class();
-       get_type();
-       get_service();
-       get_scan_mode();
-       get_devices();
-       get_discoverable_timeout();
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP,
+                                                                       status);
 }
 
-static bool start_discovery(void)
+static void get_adapter_properties(void)
 {
-       struct mgmt_cp_start_discovery cp;
-       uint8_t type = 1 << BDADDR_BREDR;
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_adapter_props_changed *ev = (void *) buf;
+       uint8_t bonded[g_slist_length(bonded_devices) * sizeof(bdaddr_t)];
+       uint128_t uuids[g_slist_length(adapter.uuids)];
+       uint8_t android_bdaddr[6];
+       uint8_t type, mode;
+       int size, i;
+       GSList *l;
 
-       if (adapter.current_settings & type)
-               cp.type = type;
-       else
-               cp.type = 0;
+       size = sizeof(*ev);
 
-       DBG("type=0x%x", type);
+       ev->status = HAL_STATUS_SUCCESS;
+       ev->num_props = 0;
 
-       if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index,
-                                       sizeof(cp), &cp, NULL, NULL, NULL) > 0)
-               return true;
+       bdaddr2android(&adapter.bdaddr, &android_bdaddr);
+       size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_ADDR,
+                                       sizeof(android_bdaddr), android_bdaddr);
+       ev->num_props++;
 
-       error("Failed to start discovery");
-       return false;
+       if (adapter.name) {
+               size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_NAME,
+                                       strlen(adapter.name), adapter.name);
+               ev->num_props++;
+       }
+
+       size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_CLASS,
+                               sizeof(adapter.dev_class), &adapter.dev_class);
+       ev->num_props++;
+
+       type = settings2type();
+       if (type) {
+               size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_TYPE,
+                                                       sizeof(type), &type);
+               ev->num_props++;
+       }
+
+       mode = settings2scan_mode();
+       size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_SCAN_MODE,
+                                                       sizeof(mode), &mode);
+       ev->num_props++;
+
+       size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_DISC_TIMEOUT,
+                                       sizeof(adapter.discoverable_timeout),
+                                       &adapter.discoverable_timeout);
+       ev->num_props++;
+
+       for (i = 0, l = bonded_devices; l; l = g_slist_next(l), i++) {
+               struct device *dev = l->data;
+
+               bdaddr2android(&dev->bdaddr, bonded + (i * sizeof(bdaddr_t)));
+       }
+
+       size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_BONDED_DEVICES,
+                                               sizeof(bonded), bonded);
+       ev->num_props++;
+
+       for (i = 0, l = adapter.uuids; l; l = g_slist_next(l), i++) {
+               uuid_t *uuid = l->data;
+
+               memcpy(&uuids[i], &uuid->value.uuid128, sizeof(uint128_t));
+       }
+
+       size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_UUIDS, sizeof(uuids),
+                                                                       uuids);
+       ev->num_props++;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                               HAL_EV_ADAPTER_PROPS_CHANGED, size, buf);
 }
 
-static bool stop_discovery(void)
+static void cancel_pending_confirm_name(gpointer data, gpointer user_data)
+{
+       struct device *dev = data;
+
+       mgmt_cancel(mgmt_if, dev->confirm_id);
+       dev->confirm_id = 0;
+}
+
+static bool stop_discovery(uint8_t type)
 {
        struct mgmt_cp_stop_discovery cp;
-       uint8_t type = 1 << BDADDR_BREDR;
 
-       if (adapter.current_settings & type)
-               cp.type = type;
-       else
-               cp.type = 0;
+       cp.type = get_supported_discovery_type() & type;
 
-       DBG("type=0x%x", type);
+       DBG("type=0x%x", cp.type);
+
+       if (cp.type == SCAN_TYPE_NONE)
+               return false;
+
+       /* Lets drop all confirm name request as we don't need it anymore */
+       g_slist_foreach(cached_devices, cancel_pending_confirm_name, NULL);
 
        if (mgmt_send(mgmt_if, MGMT_OP_STOP_DISCOVERY, adapter.index,
                                        sizeof(cp), &cp, NULL, NULL, NULL) > 0)
                return true;
 
-       error("Failed to start discovery");
+       error("Failed to stop discovery");
+       return false;
+}
+
+struct adv_user_data {
+       bt_le_set_advertising_done cb;
+       void *user_data;
+};
+
+static void set_advertising_cb(uint8_t status, uint16_t length,
+                       const void *param, void *user_data)
+{
+       struct adv_user_data *data = user_data;
+
+       DBG("");
+
+       if (status)
+               error("Failed to set adverising %s (0x%02x))",
+                                               mgmt_errstr(status), status);
+
+       data->cb(status, data->user_data);
+}
+
+bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb,
+                                                        void *user_data)
+{
+       struct adv_user_data *data;
+       uint8_t adv = advertising ? 0x01 : 0x00;
+
+       data = new0(struct adv_user_data, 1);
+       if (!data)
+               return false;
+
+       data->cb = cb;
+       data->user_data = user_data;
+
+       if (mgmt_send(mgmt_if, MGMT_OP_SET_ADVERTISING, adapter.index,
+                       sizeof(adv), &adv, set_advertising_cb, data, free) > 0)
+               return true;
+
+       error("Failed to set advertising");
+       free(data);
+       return false;
+}
+
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb)
+{
+       if (adapter.cur_discovery_type != SCAN_TYPE_LE) {
+               if (cb)
+                       cb();
+
+               gatt_device_found_cb = NULL;
+
+               return true;
+       }
+
+       if (!stop_discovery(SCAN_TYPE_LE))
+               return false;
+
+       gatt_device_found_cb = NULL;
+       gatt_discovery_stopped_cb = cb;
+       adapter.exp_discovery_type = SCAN_TYPE_NONE;
+
+       return true;
+}
+
+bool bt_le_discovery_start(bt_le_device_found cb)
+{
+       if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+               return false;
+
+       /* If core is discovering, don't bother */
+       if (adapter.cur_discovery_type != SCAN_TYPE_NONE) {
+               gatt_device_found_cb = cb;
+               return true;
+       }
+
+       if (start_discovery(SCAN_TYPE_LE)) {
+               gatt_device_found_cb = cb;
+               return true;
+       }
+
        return false;
 }
 
-static uint8_t set_scan_mode(const void *buf, uint16_t len)
+struct read_rssi_user_data {
+       bt_read_device_rssi_done cb;
+       void *user_data;
+};
+
+static void read_device_rssi_cb(uint8_t status, uint16_t length,
+                       const void *param, void *user_data)
+{
+       const struct mgmt_rp_get_conn_info *rp = param;
+       struct read_rssi_user_data *data = user_data;
+
+       DBG("");
+
+       if (status)
+               error("Failed to get conn info: %s (0x%02x))",
+                                               mgmt_errstr(status), status);
+
+       if (length < sizeof(*rp)) {
+               error("Wrong size of get conn info response");
+               return;
+       }
+
+       data->cb(status, &rp->addr.bdaddr, rp->rssi, data->user_data);
+}
+
+bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
+                                                       void *user_data)
+{
+       struct device *dev;
+       struct read_rssi_user_data *data;
+       struct mgmt_cp_get_conn_info cp;
+
+       dev = find_device(addr);
+       if (!dev)
+               return false;
+
+       memcpy(&cp.addr.bdaddr, addr, sizeof(cp.addr.bdaddr));
+       cp.addr.type = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type;
+
+       data = new0(struct read_rssi_user_data, 1);
+       if (!data)
+               return false;
+
+       data->cb = cb;
+       data->user_data = user_data;
+
+       if (!mgmt_send(mgmt_if, MGMT_OP_GET_CONN_INFO, adapter.index,
+                       sizeof(cp), &cp, read_device_rssi_cb, data, free)) {
+               free(data);
+               error("Failed to get conn info");
+               return false;
+       }
+
+       return true;
+}
+
+static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len)
 {
        const uint8_t *mode = buf;
        bool conn, disc, cur_conn, cur_disc;
@@ -1936,7 +3259,7 @@ done:
        /* Android expects property changed callback */
        scan_mode_changed();
 
-       return HAL_STATUS_DONE;
+       return HAL_STATUS_SUCCESS;
 }
 
 static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len)
@@ -1953,13 +3276,13 @@ static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len)
 
        switch (cmd->type) {
        case HAL_PROP_ADAPTER_SCAN_MODE:
-               status = set_scan_mode(cmd->val, cmd->len);
+               status = set_adapter_scan_mode(cmd->val, cmd->len);
                break;
        case HAL_PROP_ADAPTER_NAME:
                status = set_adapter_name(cmd->val, cmd->len);
                break;
        case HAL_PROP_ADAPTER_DISC_TIMEOUT:
-               status = set_discoverable_timeout(cmd->val, cmd->len);
+               status = set_adapter_discoverable_timeout(cmd->val, cmd->len);
                break;
        default:
                DBG("Unhandled property type 0x%x", cmd->type);
@@ -1967,35 +3290,77 @@ static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len)
                break;
        }
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, status);
+       if (status != HAL_STATUS_SUCCESS)
+               error("Failed to set adapter property (type %u status %u)",
+                                                       cmd->type, status);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP,
+                                                                       status);
 }
 
 static void pair_device_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
        const struct mgmt_rp_pair_device *rp = param;
+       struct device *dev;
 
        DBG("status %u", status);
 
-       /* On success bond state change will be send when new link key event
-        * is received */
+       /*
+        * On success bond state change will be send when new link key or LTK
+        * event is received
+        */
        if (status == MGMT_STATUS_SUCCESS)
                return;
 
-       set_device_bond_state(&rp->addr.bdaddr, status_mgmt2hal(status),
-                                                       HAL_BOND_STATE_NONE);
+       dev = find_device(&rp->addr.bdaddr);
+       if (!dev)
+               return;
+
+       update_device_state(dev, rp->addr.type, status_mgmt2hal(status), false,
+                                                               false, false);
+}
+
+static uint8_t select_device_bearer(struct device *dev)
+{
+       if (dev->bredr && dev->le) {
+               if (dev->le_seen > dev->bredr_seen)
+                       return dev->bdaddr_type;
+
+               return BDADDR_BREDR;
+       }
+
+       return dev->bredr ? BDADDR_BREDR : dev->bdaddr_type;
+}
+
+static bool device_is_paired(struct device *dev, uint8_t addr_type)
+{
+       if (addr_type == BDADDR_BREDR)
+               return dev->bredr_paired;
+
+       return dev->le_paired;
 }
 
 static void handle_create_bond_cmd(const void *buf, uint16_t len)
 {
        const struct hal_cmd_create_bond *cmd = buf;
+       struct device *dev;
        uint8_t status;
        struct mgmt_cp_pair_device cp;
 
        cp.io_cap = DEFAULT_IO_CAPABILITY;
-       cp.addr.type = BDADDR_BREDR;
        android2bdaddr(cmd->bdaddr, &cp.addr.bdaddr);
 
+       /* type is used only as fallback when device is not in cache */
+       dev = get_device(&cp.addr.bdaddr, BDADDR_BREDR);
+
+       cp.addr.type = select_device_bearer(dev);
+
+       if (device_is_paired(dev, cp.addr.type)) {
+               status = HAL_STATUS_FAILED;
+               goto fail;
+       }
+
        if (mgmt_send(mgmt_if, MGMT_OP_PAIR_DEVICE, adapter.index, sizeof(cp),
                                &cp, pair_device_complete, NULL, NULL) == 0) {
                status = HAL_STATUS_FAILED;
@@ -2004,63 +3369,107 @@ static void handle_create_bond_cmd(const void *buf, uint16_t len)
 
        status = HAL_STATUS_SUCCESS;
 
-       set_device_bond_state(&cp.addr.bdaddr, HAL_STATUS_SUCCESS,
-                                               HAL_BOND_STATE_BONDING);
+       update_device_state(dev, cp.addr.type, HAL_STATUS_SUCCESS, true, false,
+                                                                       false);
 
 fail:
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND,
+                                                                       status);
 }
 
 static void handle_cancel_bond_cmd(const void *buf, uint16_t len)
 {
        const struct hal_cmd_cancel_bond *cmd = buf;
        struct mgmt_addr_info cp;
+       struct device *dev;
        uint8_t status;
 
-       cp.type = BDADDR_BREDR;
        android2bdaddr(cmd->bdaddr, &cp.bdaddr);
 
-       if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index,
-                                       sizeof(cp), &cp, NULL, NULL, NULL) > 0)
-               status = HAL_STATUS_SUCCESS;
-       else
+       dev = find_device(&cp.bdaddr);
+       if (!dev) {
                status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       cp.type = select_device_bearer(dev);
+
+       if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE,
+                                       adapter.index, sizeof(cp), &cp,
+                                       NULL, NULL, NULL) == 0) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, status);
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND,
+                                                                       status);
 }
 
 static void unpair_device_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
        const struct mgmt_rp_unpair_device *rp = param;
+       struct device *dev;
 
        DBG("status %u", status);
 
-       if (status != MGMT_STATUS_SUCCESS)
+       if (status != MGMT_STATUS_SUCCESS && status != MGMT_STATUS_NOT_PAIRED)
+               return;
+
+       dev = find_device(&rp->addr.bdaddr);
+       if (!dev)
                return;
 
-       set_device_bond_state(&rp->addr.bdaddr, HAL_STATUS_SUCCESS,
-                                                       HAL_BOND_STATE_NONE);
+       update_device_state(dev, rp->addr.type, HAL_STATUS_SUCCESS, false,
+                                                               false, false);
 }
 
 static void handle_remove_bond_cmd(const void *buf, uint16_t len)
 {
        const struct hal_cmd_remove_bond *cmd = buf;
        struct mgmt_cp_unpair_device cp;
+       struct device *dev;
        uint8_t status;
 
        cp.disconnect = 1;
-       cp.addr.type = BDADDR_BREDR;
        android2bdaddr(cmd->bdaddr, &cp.addr.bdaddr);
 
-       if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
-                               sizeof(cp), &cp, unpair_device_complete,
-                               NULL, NULL) > 0)
-               status = HAL_STATUS_SUCCESS;
-       else
+       dev = find_device(&cp.addr.bdaddr);
+       if (!dev) {
                status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       if (dev->le_paired) {
+               cp.addr.type = dev->bdaddr_type;
+
+               if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
+                                       sizeof(cp), &cp, unpair_device_complete,
+                                       NULL, NULL) == 0) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
+       }
+
+       if (dev->bredr_paired) {
+               cp.addr.type = BDADDR_BREDR;
+
+               if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
+                                       sizeof(cp), &cp, unpair_device_complete,
+                                       NULL, NULL) == 0) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
+       }
+
+       status = HAL_STATUS_SUCCESS;
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, status);
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND,
+                                                                       status);
 }
 
 static void handle_pin_reply_cmd(const void *buf, uint16_t len)
@@ -2111,7 +3520,8 @@ static void handle_pin_reply_cmd(const void *buf, uint16_t len)
 
        status = HAL_STATUS_SUCCESS;
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY,
+                                                                       status);
 }
 
 static uint8_t user_confirm_reply(const bdaddr_t *bdaddr, bool accept)
@@ -2199,7 +3609,8 @@ static void handle_ssp_reply_cmd(const void *buf, uint16_t len)
                break;
        }
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY,
+                                                                       status);
 }
 
 static void handle_get_remote_services_cmd(const void *buf, uint16_t len)
@@ -2212,31 +3623,185 @@ static void handle_get_remote_services_cmd(const void *buf, uint16_t len)
 
        status = browse_remote_sdp(&addr);
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES,
-                                                                       status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                                       HAL_OP_GET_REMOTE_SERVICES, status);
+}
+
+static uint8_t get_device_uuids(struct device *dev)
+{
+       send_device_uuids_notif(dev);
+
+       return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_class(struct device *dev)
+{
+       send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_CLASS,
+                                       sizeof(dev->class), &dev->class);
+
+       return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_type(struct device *dev)
+{
+       uint8_t type = get_device_android_type(dev);
+
+       send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_TYPE,
+                                                       sizeof(type), &type);
+
+       return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_service_rec(struct device *dev)
+{
+       DBG("Not implemented");
+
+       /* TODO */
+
+       return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_device_friendly_name(struct device *dev)
+{
+       if (!dev->friendly_name)
+               return HAL_STATUS_FAILED;
+
+       send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_FRIENDLY_NAME,
+                               strlen(dev->friendly_name), dev->friendly_name);
+
+       return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_rssi(struct device *dev)
+{
+       if (!dev->rssi)
+               return HAL_STATUS_FAILED;
+
+       send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_RSSI,
+                                               sizeof(dev->rssi), &dev->rssi);
+
+       return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t get_device_version_info(struct device *dev)
+{
+       DBG("Not implemented");
+
+       /* TODO */
+
+       return HAL_STATUS_FAILED;
+}
+
+static uint8_t get_device_timestamp(struct device *dev)
+{
+       uint32_t timestamp;
+
+       timestamp = device_timestamp(dev);
+
+       send_device_property(&dev->bdaddr, HAL_PROP_DEVICE_TIMESTAMP,
+                                               sizeof(timestamp), &timestamp);
+
+       return HAL_STATUS_SUCCESS;
+}
+
+static void get_remote_device_props(struct device *dev)
+{
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_remote_device_props *ev = (void *) buf;
+       uint128_t uuids[g_slist_length(dev->uuids)];
+       uint8_t android_type;
+       uint32_t timestamp;
+       int size, i;
+       GSList *l;
+
+       memset(buf, 0, sizeof(buf));
+
+       size = sizeof(*ev);
+
+       ev->status = HAL_STATUS_SUCCESS;
+       bdaddr2android(&dev->bdaddr, ev->bdaddr);
+
+       android_type = get_device_android_type(dev);
+       size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
+                                       sizeof(android_type), &android_type);
+       ev->num_props++;
+
+       size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+                                       sizeof(dev->class), &dev->class);
+       ev->num_props++;
+
+       if (dev->rssi) {
+               size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+                                               sizeof(dev->rssi), &dev->rssi);
+               ev->num_props++;
+       }
+
+       size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+                                               strlen(dev->name), dev->name);
+       ev->num_props++;
+
+       if (dev->friendly_name) {
+               size += fill_hal_prop(buf + size,
+                                       HAL_PROP_DEVICE_FRIENDLY_NAME,
+                                       strlen(dev->friendly_name),
+                                       dev->friendly_name);
+               ev->num_props++;
+       }
+
+       for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++)
+               memcpy(&uuids[i], l->data, sizeof(uint128_t));
+
+       size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_UUIDS, sizeof(uuids),
+                                                                       uuids);
+       ev->num_props++;
+
+       timestamp = get_device_timestamp(dev);
+
+       size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TIMESTAMP,
+                                               sizeof(timestamp), &timestamp);
+       ev->num_props++;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                                       HAL_EV_REMOTE_DEVICE_PROPS, size, buf);
+}
+
+static void send_bonded_devices_props(void)
+{
+       GSList *l;
+
+       for (l = bonded_devices; l; l = g_slist_next(l)) {
+               struct device *dev = l->data;
+
+               get_remote_device_props(dev);
+       }
 }
 
 static void handle_enable_cmd(const void *buf, uint16_t len)
 {
        uint8_t status;
 
-       /* Framework expects all properties to be emitted while
-        * enabling adapter */
-       get_properties();
+       /*
+        * Framework expects all properties to be emitted while enabling
+        * adapter
+        */
+       get_adapter_properties();
+
+       /* Sent also properties of bonded devices */
+       send_bonded_devices_props();
 
        if (adapter.current_settings & MGMT_SETTING_POWERED) {
-               status = HAL_STATUS_DONE;
-               goto failed;
+               status = HAL_STATUS_SUCCESS;
+               goto reply;
        }
 
        if (!set_mode(MGMT_OP_SET_POWERED, 0x01)) {
                status = HAL_STATUS_FAILED;
-               goto failed;
+               goto reply;
        }
 
        status = HAL_STATUS_SUCCESS;
-failed:
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status);
+reply:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status);
 }
 
 static void handle_disable_cmd(const void *buf, uint16_t len)
@@ -2244,48 +3809,143 @@ static void handle_disable_cmd(const void *buf, uint16_t len)
        uint8_t status;
 
        if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
-               status = HAL_STATUS_DONE;
-               goto failed;
+               status = HAL_STATUS_SUCCESS;
+               goto reply;
        }
 
+       /* Cancel all pending requests. Need it in case of ongoing paring */
+       mgmt_cancel_index(mgmt_if, adapter.index);
+
        if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) {
                status = HAL_STATUS_FAILED;
-               goto failed;
+               goto reply;
        }
 
        status = HAL_STATUS_SUCCESS;
-failed:
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status);
+reply:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status);
 }
 
 static void handle_get_adapter_props_cmd(const void *buf, uint16_t len)
 {
-       get_properties();
+       get_adapter_properties();
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS,
-                                                       HAL_STATUS_SUCCESS);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                               HAL_OP_GET_ADAPTER_PROPS, HAL_STATUS_SUCCESS);
 }
 
 static void handle_get_remote_device_props_cmd(const void *buf, uint16_t len)
 {
-       /* TODO */
+       const struct hal_cmd_get_remote_device_props *cmd = buf;
+       struct device *dev;
+       uint8_t status;
+       bdaddr_t addr;
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROPS,
-                                                       HAL_STATUS_FAILED);
+       android2bdaddr(cmd->bdaddr, &addr);
+
+       dev = find_device(&addr);
+       if (!dev) {
+               status = HAL_STATUS_INVALID;
+               goto failed;
+       }
+
+       get_remote_device_props(dev);
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                                       HAL_OP_GET_REMOTE_DEVICE_PROPS, status);
 }
 
 static void handle_get_remote_device_prop_cmd(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_get_remote_device_prop *cmd = buf;
+       struct device *dev;
+       uint8_t status;
+       bdaddr_t addr;
+
+       android2bdaddr(cmd->bdaddr, &addr);
+
+       dev = find_device(&addr);
+       if (!dev) {
+               status = HAL_STATUS_INVALID;
+               goto failed;
+       }
+
+       switch (cmd->type) {
+       case HAL_PROP_DEVICE_NAME:
+               status = get_device_name(dev);
+               break;
+       case HAL_PROP_DEVICE_UUIDS:
+               status = get_device_uuids(dev);
+               break;
+       case HAL_PROP_DEVICE_CLASS:
+               status = get_device_class(dev);
+               break;
+       case HAL_PROP_DEVICE_TYPE:
+               status = get_device_type(dev);
+               break;
+       case HAL_PROP_DEVICE_SERVICE_REC:
+               status = get_device_service_rec(dev);
+               break;
+       case HAL_PROP_DEVICE_FRIENDLY_NAME:
+               status = get_device_friendly_name(dev);
+               break;
+       case HAL_PROP_DEVICE_RSSI:
+               status = get_device_rssi(dev);
+               break;
+       case HAL_PROP_DEVICE_VERSION_INFO:
+               status = get_device_version_info(dev);
+               break;
+       case HAL_PROP_DEVICE_TIMESTAMP:
+               status = get_device_timestamp(dev);
+               break;
+       default:
+               status = HAL_STATUS_FAILED;
+               break;
+       }
+
+       if (status != HAL_STATUS_SUCCESS)
+               error("Failed to get device property (type %u status %u)",
+                                                       cmd->type, status);
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                                       HAL_OP_GET_REMOTE_DEVICE_PROP, status);
+}
+
+static uint8_t set_device_friendly_name(struct device *dev, const uint8_t *val,
+                                                               uint16_t len)
+{
+       DBG("");
+
+       g_free(dev->friendly_name);
+       dev->friendly_name = g_strndup((const char *) val, len);
+
+       if (dev->bredr_paired || dev->le_paired)
+               store_device_info(dev, DEVICES_FILE);
+       else
+               store_device_info(dev, CACHE_FILE);
+
+       return HAL_STATUS_SUCCESS;
+}
+
+static uint8_t set_device_version_info(struct device *dev)
+{
+       DBG("Not implemented");
+
        /* TODO */
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROP,
-                                                       HAL_STATUS_FAILED);
+       return HAL_STATUS_FAILED;
 }
 
 static void handle_set_remote_device_prop_cmd(const void *buf, uint16_t len)
 {
        const struct hal_cmd_set_remote_device_prop *cmd = buf;
+       struct device *dev;
        uint8_t status;
+       bdaddr_t addr;
 
        if (len != sizeof(*cmd) + cmd->len) {
                error("Invalid set remote device prop cmd (0x%x), terminating",
@@ -2294,74 +3954,199 @@ static void handle_set_remote_device_prop_cmd(const void *buf, uint16_t len)
                return;
        }
 
-       /* TODO */
+       android2bdaddr(cmd->bdaddr, &addr);
+
+       dev = find_device(&addr);
+       if (!dev) {
+               status = HAL_STATUS_INVALID;
+               goto failed;
+       }
 
        switch (cmd->type) {
+       case HAL_PROP_DEVICE_FRIENDLY_NAME:
+               status = set_device_friendly_name(dev, cmd->val, cmd->len);
+               break;
+       case HAL_PROP_DEVICE_VERSION_INFO:
+               status = set_device_version_info(dev);
+               break;
        default:
-               DBG("Unhandled property type 0x%x", cmd->type);
                status = HAL_STATUS_FAILED;
                break;
        }
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_REMOTE_DEVICE_PROP,
-                                                                       status);
+       if (status != HAL_STATUS_SUCCESS)
+               error("Failed to set device property (type %u status %u)",
+                                                       cmd->type, status);
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                                       HAL_OP_SET_REMOTE_DEVICE_PROP, status);
 }
 
 static void handle_get_remote_service_rec_cmd(const void *buf, uint16_t len)
 {
        /* TODO */
 
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICE_REC,
-                                                       HAL_STATUS_FAILED);
+       error("get_remote_service_record not supported");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_REMOTE_SERVICE_REC, HAL_STATUS_FAILED);
 }
 
 static void handle_start_discovery_cmd(const void *buf, uint16_t len)
 {
        uint8_t status;
 
-       if (adapter.discovering) {
-               status = HAL_STATUS_DONE;
-               goto failed;
-       }
-
        if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
                status = HAL_STATUS_NOT_READY;
                goto failed;
        }
 
-       if (!start_discovery()) {
-               status = HAL_STATUS_FAILED;
-               goto failed;
+       switch (adapter.cur_discovery_type) {
+       case SCAN_TYPE_DUAL:
+       case SCAN_TYPE_BREDR:
+               break;
+       case SCAN_TYPE_NONE:
+               if (!start_discovery(SCAN_TYPE_DUAL)) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
+
+               break;
+       case SCAN_TYPE_LE:
+               if (get_supported_discovery_type() == SCAN_TYPE_LE)
+                       break;
+
+               if (!stop_discovery(SCAN_TYPE_LE)) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
+
+               adapter.exp_discovery_type = SCAN_TYPE_DUAL;
+               break;
        }
 
        status = HAL_STATUS_SUCCESS;
+
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY,
+                                                                       status);
 }
 
 static void handle_cancel_discovery_cmd(const void *buf, uint16_t len)
 {
        uint8_t status;
 
-       if (!adapter.discovering) {
-               status = HAL_STATUS_DONE;
-               goto failed;
-       }
-
        if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
                status = HAL_STATUS_NOT_READY;
                goto failed;
        }
 
-       if (!stop_discovery()) {
+       switch (adapter.cur_discovery_type) {
+       case SCAN_TYPE_NONE:
+               break;
+       case SCAN_TYPE_LE:
+               if (get_supported_discovery_type() != SCAN_TYPE_LE)
+                       break;
+
+               if (gatt_device_found_cb) {
+                       status = HAL_STATUS_BUSY;
+                       goto failed;
+               }
+
+               if (!stop_discovery(SCAN_TYPE_LE)) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
+
+               break;
+       case SCAN_TYPE_DUAL:
+       case SCAN_TYPE_BREDR:
+               if (!stop_discovery(SCAN_TYPE_DUAL)) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
+
+               adapter.exp_discovery_type = gatt_device_found_cb ?
+                                               SCAN_TYPE_LE : SCAN_TYPE_NONE;
+               break;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY,
+                                                                       status);
+}
+
+static void handle_dut_mode_conf_cmd(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_dut_mode_conf *cmd = buf;
+       char path[FILENAME_MAX];
+       uint8_t status;
+       int fd, ret;
+
+       DBG("enable %u", cmd->enable);
+
+       snprintf(path, sizeof(path), DUT_MODE_FILE, adapter.index);
+
+       fd = open(path, O_WRONLY);
+       if (fd < 0) {
                status = HAL_STATUS_FAILED;
                goto failed;
        }
 
-       status = HAL_STATUS_SUCCESS;
+       if (cmd->enable)
+               ret = write(fd, "1", sizeof("1"));
+       else
+               ret = write(fd, "0", sizeof("0"));
+
+       if (ret < 0)
+               status = HAL_STATUS_FAILED;
+       else
+               status = HAL_STATUS_SUCCESS;
+
+       close(fd);
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF,
+                                                                       status);
+}
+
+static void handle_dut_mode_send_cmd(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_dut_mode_send *cmd = buf;
+
+       if (len != sizeof(*cmd) + cmd->len) {
+               error("Invalid dut mode send cmd, terminating");
+               raise(SIGTERM);
+               return;
+       }
+
+       error("dut_mode_send not supported (cmd opcode %u)", cmd->opcode);
+
+       /* TODO */
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND,
+                                                       HAL_STATUS_FAILED);
+}
+
+static void handle_le_test_mode_cmd(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_le_test_mode *cmd = buf;
+
+       if (len != sizeof(*cmd) + cmd->len) {
+               error("Invalid le test mode cmd, terminating");
+               raise(SIGTERM);
+               return;
+       }
+
+       error("le_test_mode not supported (cmd opcode %u)", cmd->opcode);
+
+       /* TODO */
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE,
+                                                       HAL_STATUS_FAILED);
 }
 
 static const struct ipc_handler cmd_handlers[] = {
@@ -2406,19 +4191,91 @@ static const struct ipc_handler cmd_handlers[] = {
        { handle_pin_reply_cmd, false, sizeof(struct hal_cmd_pin_reply) },
        /* HAL_OP_SSP_REPLY */
        { handle_ssp_reply_cmd, false, sizeof(struct hal_cmd_ssp_reply) },
+       /* HAL_OP_DUT_MODE_CONF */
+       { handle_dut_mode_conf_cmd, false,
+                                       sizeof(struct hal_cmd_dut_mode_conf) },
+       /* HAL_OP_DUT_MODE_SEND */
+       { handle_dut_mode_send_cmd, true,
+                                       sizeof(struct hal_cmd_dut_mode_send) },
+       /* HAL_OP_LE_TEST_MODE */
+       { handle_le_test_mode_cmd, true, sizeof(struct hal_cmd_le_test_mode) },
 };
 
-void bt_bluetooth_register(void)
+bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode)
 {
-       DBG("");
+       uint32_t missing_settings;
+
+       DBG("mode 0x%x", mode);
+
+       missing_settings = adapter.current_settings ^
+                                       adapter.supported_settings;
+
+       switch (mode) {
+       case HAL_MODE_DEFAULT:
+               if (missing_settings & MGMT_SETTING_BREDR)
+                       set_mode(MGMT_OP_SET_BREDR, 0x01);
+
+               if (missing_settings & MGMT_SETTING_LE)
+                       set_mode(MGMT_OP_SET_LE, 0x01);
+               break;
+       case HAL_MODE_LE:
+               /* Fail if controller does not support LE */
+               if (!(adapter.supported_settings & MGMT_SETTING_LE)) {
+                       error("LE Mode not supported by controller");
+                       return false;
+               }
+
+               /* If LE it is not yet enabled then enable it */
+               if (!(adapter.current_settings & MGMT_SETTING_LE))
+                       set_mode(MGMT_OP_SET_LE, 0x01);
 
-       ipc_register(HAL_SERVICE_ID_BLUETOOTH, cmd_handlers,
+               /* Disable BR/EDR if it is enabled */
+               if (adapter.current_settings & MGMT_SETTING_BREDR)
+                       set_mode(MGMT_OP_SET_BREDR, 0x00);
+               break;
+       case HAL_MODE_BREDR:
+               /* Fail if controller does not support BR/EDR */
+               if (!(adapter.supported_settings & MGMT_SETTING_BREDR)) {
+                       error("BR/EDR Mode not supported");
+                       return false;
+               }
+
+               /* Enable BR/EDR if it is not enabled */
+               if (missing_settings & MGMT_SETTING_BREDR)
+                       set_mode(MGMT_OP_SET_BREDR, 0x01);
+
+               /*
+                * According to Core Spec 4.0 host should not disable LE in
+                * controller if it was enabled (Vol 2. Part E. 7.3.79).
+                * Core Spec 4.1 removed this limitation and chips seem to be
+                * handling this just fine anyway.
+                */
+               if (adapter.current_settings & MGMT_SETTING_LE)
+                       set_mode(MGMT_OP_SET_LE, 0x00);
+               break;
+       default:
+               error("Unknown mode 0x%x", mode);
+               return false;
+       }
+
+       hal_ipc = ipc;
+
+       ipc_register(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, cmd_handlers,
                                                G_N_ELEMENTS(cmd_handlers));
+
+       return true;
 }
 
 void bt_bluetooth_unregister(void)
 {
        DBG("");
 
-       ipc_unregister(HAL_SERVICE_ID_CORE);
+       g_slist_free_full(bonded_devices, (GDestroyNotify) free_device);
+       bonded_devices = NULL;
+
+       g_slist_free_full(cached_devices, (GDestroyNotify) free_device);
+       cached_devices = NULL;
+
+       ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE);
+       hal_ipc = NULL;
 }
index 86872ee..6a3e766 100644 (file)
@@ -2,37 +2,56 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
 typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr);
-bool bt_bluetooth_start(int index, bt_bluetooth_ready cb);
+bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb);
 
 typedef void (*bt_bluetooth_stopped)(void);
 bool bt_bluetooth_stop(bt_bluetooth_stopped cb);
 
 void bt_bluetooth_cleanup(void);
 
-void bt_bluetooth_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len);
-
-void bt_bluetooth_register(void);
+bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode);
 void bt_bluetooth_unregister(void);
 
 int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint);
 void bt_adapter_remove_record(uint32_t handle);
+
+typedef void (*bt_le_device_found)(const bdaddr_t *addr, uint8_t addr_type,
+                                       int rssi, uint16_t eir_len,
+                                       const void *eir, bool discoverable);
+bool bt_le_discovery_start(bt_le_device_found cb);
+
+typedef void (*bt_le_discovery_stopped)(void);
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb);
+
+typedef void (*bt_le_set_advertising_done)(uint8_t status, void *user_data);
+bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb,
+                                                       void *user_data);
+
+uint8_t bt_get_device_android_type(const bdaddr_t *addr);
+const char *bt_get_adapter_name(void);
+bool bt_device_is_bonded(const bdaddr_t *bdaddr);
+
+typedef void (*bt_read_device_rssi_done)(uint8_t status, const bdaddr_t *addr,
+                                               int8_t rssi, void *user_data);
+bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
+                                                       void *user_data);
diff --git a/android/bluetoothd-snoop.c b/android/bluetoothd-snoop.c
new file mode 100644 (file)
index 0000000..57f97f4
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#if defined(ANDROID)
+#include <sys/capability.h>
+#endif
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/mgmt.h"
+
+#include "monitor/mainloop.h"
+#include "src/shared/btsnoop.h"
+
+#define DEFAULT_SNOOP_FILE "/sdcard/btsnoop_hci.log"
+
+#define MAX_PACKET_SIZE (1486 + 4)
+
+static struct btsnoop *snoop = NULL;
+static uint8_t monitor_buf[MAX_PACKET_SIZE];
+static int monitor_fd = -1;
+
+static void signal_callback(int signum, void *user_data)
+{
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               mainloop_quit();
+               break;
+       }
+}
+
+static uint32_t get_flags_from_opcode(uint16_t opcode)
+{
+       switch (opcode) {
+       case BTSNOOP_OPCODE_NEW_INDEX:
+       case BTSNOOP_OPCODE_DEL_INDEX:
+               break;
+       case BTSNOOP_OPCODE_COMMAND_PKT:
+               return 0x02;
+       case BTSNOOP_OPCODE_EVENT_PKT:
+               return 0x03;
+       case BTSNOOP_OPCODE_ACL_TX_PKT:
+               return 0x00;
+       case BTSNOOP_OPCODE_ACL_RX_PKT:
+               return 0x01;
+       case BTSNOOP_OPCODE_SCO_TX_PKT:
+       case BTSNOOP_OPCODE_SCO_RX_PKT:
+               break;
+       }
+
+       return 0xff;
+}
+
+static void data_callback(int fd, uint32_t events, void *user_data)
+{
+       unsigned char control[32];
+       struct mgmt_hdr hdr;
+       struct msghdr msg;
+       struct iovec iov[2];
+
+       if (events & (EPOLLERR | EPOLLHUP)) {
+               mainloop_remove_fd(monitor_fd);
+               return;
+       }
+
+       iov[0].iov_base = &hdr;
+       iov[0].iov_len = MGMT_HDR_SIZE;
+       iov[1].iov_base = monitor_buf;
+       iov[1].iov_len = sizeof(monitor_buf);
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = iov;
+       msg.msg_iovlen = 2;
+       msg.msg_control = control;
+       msg.msg_controllen = sizeof(control);
+
+       while (true) {
+               struct cmsghdr *cmsg;
+               struct timeval *tv = NULL;
+               struct timeval ctv;
+               uint16_t opcode, index, pktlen;
+               uint32_t flags;
+               ssize_t len;
+
+               len = recvmsg(monitor_fd, &msg, MSG_DONTWAIT);
+               if (len < 0)
+                       break;
+
+               if (len < MGMT_HDR_SIZE)
+                       break;
+
+               for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+                                       cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+                       if (cmsg->cmsg_level != SOL_SOCKET)
+                               continue;
+
+                       if (cmsg->cmsg_type == SCM_TIMESTAMP) {
+                               memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv));
+                               tv = &ctv;
+                       }
+               }
+
+               opcode = btohs(hdr.opcode);
+               index  = btohs(hdr.index);
+               pktlen = btohs(hdr.len);
+
+               if (index)
+                       continue;
+
+               flags = get_flags_from_opcode(opcode);
+               if (flags != 0xff)
+                       btsnoop_write(snoop, tv, flags, monitor_buf, pktlen);
+       }
+}
+
+static int open_monitor(const char *path)
+{
+       struct sockaddr_hci addr;
+       int opt = 1;
+
+       snoop = btsnoop_create(path, BTSNOOP_TYPE_HCI);
+       if (!snoop)
+               return -1;
+
+       monitor_fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+       if (monitor_fd < 0)
+               goto failed;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.hci_family = AF_BLUETOOTH;
+       addr.hci_dev = HCI_DEV_NONE;
+       addr.hci_channel = HCI_CHANNEL_MONITOR;
+
+       if (bind(monitor_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+               goto failed_close;
+
+       if (setsockopt(monitor_fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))
+                                                                       < 0)
+               goto failed_close;
+
+       mainloop_add_fd(monitor_fd, EPOLLIN, data_callback, NULL, NULL);
+
+       return 0;
+
+failed_close:
+       close(monitor_fd);
+       monitor_fd = -1;
+
+failed:
+       btsnoop_unref(snoop);
+       snoop = NULL;
+
+       return -1;
+}
+
+static void close_monitor(void)
+{
+       btsnoop_unref(snoop);
+       snoop = NULL;
+
+       close(monitor_fd);
+       monitor_fd = -1;
+}
+
+static void set_capabilities(void)
+{
+#if defined(ANDROID)
+       struct __user_cap_header_struct header;
+       struct __user_cap_data_struct cap;
+
+       header.version = _LINUX_CAPABILITY_VERSION;
+       header.pid = 0;
+
+       /*
+        * CAP_NET_RAW: for snooping
+        * CAP_DAC_READ_SEARCH: override path search permissions
+        */
+       cap.effective = cap.permitted =
+               CAP_TO_MASK(CAP_NET_RAW) |
+               CAP_TO_MASK(CAP_DAC_READ_SEARCH);
+       cap.inheritable = 0;
+
+       /* TODO: Move to cap_set_proc once bionic support it */
+       if (capset(&header, &cap) < 0)
+               exit(EXIT_FAILURE);
+#endif
+}
+
+int main(int argc, char *argv[])
+{
+       const char *path;
+       sigset_t mask;
+
+       set_capabilities();
+
+       if (argc > 1)
+               path = argv[1];
+       else
+               path = DEFAULT_SNOOP_FILE;
+
+       mainloop_init();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       if (!strcmp(DEFAULT_SNOOP_FILE, path))
+               rename(DEFAULT_SNOOP_FILE, DEFAULT_SNOOP_FILE ".old");
+
+       if (open_monitor(path) < 0) {
+               printf("Failed to start bluetoothd_snoop\n");
+               return EXIT_FAILURE;
+       }
+
+       mainloop_run();
+
+       close_monitor();
+
+       return EXIT_SUCCESS;
+}
index 7154d27..d86fe49 100644 (file)
 #include "history.h"
 
 const struct interface *interfaces[] = {
+       &audio_if,
+       &sco_if,
        &bluetooth_if,
        &av_if,
-#if PLATFORM_SDK_VERSION > 17
+       &rc_if,
        &gatt_if,
        &gatt_client_if,
        &gatt_server_if,
-#endif
        &hf_if,
        &hh_if,
        &pan_if,
+       &hl_if,
        &sock_if,
        NULL
 };
@@ -254,12 +256,12 @@ static int command_line_to_argv(char *line_buffer, char *argv[], int argv_size)
 
 static void process_line(char *line_buffer)
 {
-       char *argv[10];
+       char *argv[50];
        int argc;
        int i = 0;
        struct method *m;
 
-       argc = command_line_to_argv(line_buffer, argv, 10);
+       argc = command_line_to_argv(line_buffer, argv, 50);
        if (argc < 1)
                return;
 
@@ -383,20 +385,24 @@ static void init(void)
        static const char * const inames[] = {
                BT_PROFILE_HANDSFREE_ID,
                BT_PROFILE_ADVANCED_AUDIO_ID,
+               BT_PROFILE_AV_RC_ID,
                BT_PROFILE_HEALTH_ID,
                BT_PROFILE_HIDHOST_ID,
                BT_PROFILE_PAN_ID,
-#if PLATFORM_SDK_VERSION > 17
                BT_PROFILE_GATT_ID,
-#endif
                BT_PROFILE_SOCKETS_ID
        };
        const struct method *m;
        const char *argv[4];
-       char init_line[] = "bluetooth init";
+       char init_audio[] = "audio init";
+       char init_sco[] = "sco init";
+       char init_bt[] = "bluetooth init";
        uint32_t i;
 
-       process_line(init_line);
+       process_line(init_audio);
+       process_line(init_sco);
+       process_line(init_bt);
+
        m = get_interface_method("bluetooth", "get_profile_interface");
 
        for (i = 0; i < NELEM(inames); ++i) {
@@ -405,7 +411,7 @@ static void init(void)
        }
 
        /* Init what is available to init */
-       for (i = 1; i < NELEM(interfaces) - 1; ++i) {
+       for (i = 2; i < NELEM(interfaces) - 1; ++i) {
                m = get_interface_method(interfaces[i]->name, "init");
                if (m != NULL)
                        m->func(2, argv);
index ee285da..3dae27a 100644 (file)
@@ -22,9 +22,7 @@
 
 #include "history.h"
 
-/*
- * Very simple history storage for easy usage of tool
- */
+/* Very simple history storage for easy usage of tool */
 
 #define HISTORY_DEPTH 20
 #define LINE_SIZE 100
diff --git a/android/client/hwmodule.c b/android/client/hwmodule.c
deleted file mode 100644 (file)
index 80e7475..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2013 Intel Corporation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
-*/
-
-#include "hardware/hardware.h"
-
-int hw_get_module(const char *id, const struct hw_module_t **module)
-{
-       extern struct hw_module_t HAL_MODULE_INFO_SYM;
-
-       *module = &HAL_MODULE_INFO_SYM;
-
-       return 0;
-}
diff --git a/android/client/if-audio.c b/android/client/if-audio.c
new file mode 100644 (file)
index 0000000..6a48025
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+#include "pthread.h"
+#include "unistd.h"
+#include <math.h>
+
+audio_hw_device_t *if_audio = NULL;
+static struct audio_stream_out *stream_out = NULL;
+
+static size_t buffer_size = 0;
+static pthread_t play_thread = 0;
+static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+enum state {
+       STATE_STOPPED,
+       STATE_STOPPING,
+       STATE_PLAYING,
+       STATE_SUSPENDED,
+       STATE_MAX
+};
+
+SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)")
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
+       DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+       DELEMENT(AUDIO_CHANNEL_OUT_MONO),
+       DELEMENT(AUDIO_CHANNEL_OUT_STEREO),
+       DELEMENT(AUDIO_CHANNEL_OUT_QUAD),
+       DELEMENT(AUDIO_CHANNEL_OUT_SURROUND),
+       DELEMENT(AUDIO_CHANNEL_OUT_5POINT1),
+       DELEMENT(AUDIO_CHANNEL_OUT_7POINT1),
+       DELEMENT(AUDIO_CHANNEL_OUT_ALL),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ENDMAP
+
+SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)")
+       DELEMENT(AUDIO_FORMAT_DEFAULT),
+       DELEMENT(AUDIO_FORMAT_PCM),
+       DELEMENT(AUDIO_FORMAT_MP3),
+       DELEMENT(AUDIO_FORMAT_AMR_NB),
+       DELEMENT(AUDIO_FORMAT_AMR_WB),
+       DELEMENT(AUDIO_FORMAT_AAC),
+       DELEMENT(AUDIO_FORMAT_HE_AAC_V1),
+       DELEMENT(AUDIO_FORMAT_HE_AAC_V2),
+       DELEMENT(AUDIO_FORMAT_VORBIS),
+       DELEMENT(AUDIO_FORMAT_MAIN_MASK),
+       DELEMENT(AUDIO_FORMAT_SUB_MASK),
+       DELEMENT(AUDIO_FORMAT_PCM_16_BIT),
+       DELEMENT(AUDIO_FORMAT_PCM_8_BIT),
+       DELEMENT(AUDIO_FORMAT_PCM_32_BIT),
+       DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT),
+ENDMAP
+
+static int current_state = STATE_STOPPED;
+
+#define SAMPLERATE 44100
+static short sample[SAMPLERATE];
+static uint16_t sample_pos;
+
+static void init_p(int argc, const char **argv)
+{
+       int err;
+       const hw_module_t *module;
+       audio_hw_device_t *device;
+
+       err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID,
+                                       AUDIO_HARDWARE_MODULE_ID_A2DP, &module);
+       if (err) {
+               haltest_error("hw_get_module_by_class returned %d\n", err);
+               return;
+       }
+
+       err = audio_hw_device_open(module, &device);
+       if (err) {
+               haltest_error("audio_hw_device_open returned %d\n", err);
+               return;
+       }
+
+       if_audio = device;
+}
+
+static int feed_from_file(short *buffer, void *data)
+{
+       FILE *in = data;
+       return fread(buffer, buffer_size, 1, in);
+}
+
+static int feed_from_generator(short *buffer, void *data)
+{
+       size_t i = 0;
+       float volume = 0.5;
+       float *freq = data;
+       float f = 1;
+
+       if (freq)
+               f = *freq;
+
+       /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/
+       for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) {
+               if (sample_pos >= SAMPLERATE)
+                       sample_pos = sample_pos % SAMPLERATE;
+
+               /* Use the same sample for both channels */
+               buffer[i++] = sample[sample_pos] * volume;
+               buffer[i++] = sample[sample_pos] * volume;
+
+               sample_pos += f;
+       }
+
+       return buffer_size;
+}
+
+static void prepare_sample(void)
+{
+       int x;
+       double s;
+
+       haltest_info("Preparing audio sample...\n");
+
+       for (x = 0; x < SAMPLERATE; x++) {
+               /* prepare sinusoidal 1Hz sample */
+               s = (2.0 * 3.14159) * ((double)x / SAMPLERATE);
+               s = sin(s);
+
+               /* remap <-1, 1> to signed 16bit PCM range */
+               sample[x] = s * 32767;
+       }
+
+       sample_pos = 0;
+}
+
+static void *playback_thread(void *data)
+{
+       int (*filbuff_cb) (short*, void*);
+       short buffer[buffer_size / sizeof(short)];
+       size_t len = 0;
+       ssize_t w_len = 0;
+       FILE *in = data;
+       void *cb_data = NULL;
+       float freq = 440.0;
+
+       /* Use file or fall back to generator */
+       if (in) {
+               filbuff_cb = feed_from_file;
+               cb_data = in;
+       } else {
+               prepare_sample();
+               filbuff_cb = feed_from_generator;
+               cb_data = &freq;
+       }
+
+       pthread_mutex_lock(&state_mutex);
+       current_state = STATE_PLAYING;
+       pthread_mutex_unlock(&state_mutex);
+
+       do {
+               pthread_mutex_lock(&state_mutex);
+
+               if (current_state == STATE_STOPPING) {
+                       pthread_mutex_unlock(&state_mutex);
+                       break;
+               } else if (current_state == STATE_SUSPENDED) {
+                       pthread_mutex_unlock(&state_mutex);
+                       usleep(500);
+                       continue;
+               }
+
+               pthread_mutex_unlock(&state_mutex);
+
+               len = filbuff_cb(buffer, cb_data);
+
+               pthread_mutex_lock(&outstream_mutex);
+               if (!stream_out) {
+                       pthread_mutex_unlock(&outstream_mutex);
+                       break;
+               }
+
+               w_len = stream_out->write(stream_out, buffer, buffer_size);
+               pthread_mutex_unlock(&outstream_mutex);
+       } while (len && w_len > 0);
+
+       if (in)
+               fclose(in);
+
+       pthread_mutex_lock(&state_mutex);
+       current_state = STATE_STOPPED;
+       pthread_mutex_unlock(&state_mutex);
+
+       haltest_info("Done playing.\n");
+
+       return NULL;
+}
+
+static void play_p(int argc, const char **argv)
+{
+       const char *fname = NULL;
+       FILE *in = NULL;
+
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       if (argc < 3) {
+               haltest_error("Invalid audio file path.\n");
+               haltest_info("Using sound generator.\n");
+       } else {
+               fname = argv[2];
+               in = fopen(fname, "r");
+
+               if (in == NULL) {
+                       haltest_error("Cannot open file: %s\n", fname);
+                       return;
+               }
+               haltest_info("Playing file: %s\n", fname);
+       }
+
+       if (buffer_size == 0) {
+               haltest_error("Invalid buffer size. Was stream_out opened?\n");
+               goto fail;
+       }
+
+       pthread_mutex_lock(&state_mutex);
+       if (current_state != STATE_STOPPED) {
+               haltest_error("Already playing or stream suspended!\n");
+               pthread_mutex_unlock(&state_mutex);
+               goto fail;
+       }
+       pthread_mutex_unlock(&state_mutex);
+
+       if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) {
+               haltest_error("Cannot create playback thread!\n");
+               goto fail;
+       }
+
+       return;
+fail:
+       if (in)
+               fclose(in);
+}
+
+static void stop_p(int argc, const char **argv)
+{
+       pthread_mutex_lock(&state_mutex);
+       if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) {
+               pthread_mutex_unlock(&state_mutex);
+               return;
+       }
+
+       current_state = STATE_STOPPING;
+       pthread_mutex_unlock(&state_mutex);
+
+       pthread_mutex_lock(&outstream_mutex);
+       stream_out->common.standby(&stream_out->common);
+       pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void open_output_stream_p(int argc, const char **argv)
+{
+       int err;
+
+       RETURN_IF_NULL(if_audio);
+
+       pthread_mutex_lock(&state_mutex);
+       if (current_state == STATE_PLAYING) {
+               haltest_error("Already playing!\n");
+               pthread_mutex_unlock(&state_mutex);
+               return;
+       }
+       pthread_mutex_unlock(&state_mutex);
+
+       err = if_audio->open_output_stream(if_audio,
+                                               0,
+                                               AUDIO_DEVICE_OUT_ALL_A2DP,
+                                               AUDIO_OUTPUT_FLAG_NONE,
+                                               NULL,
+                                               &stream_out);
+       if (err < 0) {
+               haltest_error("open output stream returned %d\n", err);
+               return;
+       }
+
+       buffer_size = stream_out->common.get_buffer_size(&stream_out->common);
+       if (buffer_size == 0)
+               haltest_error("Invalid buffer size received!\n");
+       else
+               haltest_info("Using buffer size: %zu\n", buffer_size);
+}
+
+static void close_output_stream_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       stop_p(argc, argv);
+
+       haltest_info("Waiting for playback thread...\n");
+       pthread_join(play_thread, NULL);
+
+       if_audio->close_output_stream(if_audio, stream_out);
+
+       stream_out = NULL;
+       buffer_size = 0;
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+       int err;
+
+       RETURN_IF_NULL(if_audio);
+
+       pthread_mutex_lock(&state_mutex);
+       if (current_state != STATE_STOPPED) {
+               pthread_mutex_unlock(&state_mutex);
+               close_output_stream_p(0, NULL);
+       } else {
+               pthread_mutex_unlock(&state_mutex);
+       }
+
+       err = audio_hw_device_close(if_audio);
+       if (err < 0) {
+               haltest_error("audio_hw_device_close returned %d\n", err);
+               return;
+       }
+
+       if_audio = NULL;
+}
+
+static void suspend_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       pthread_mutex_lock(&state_mutex);
+       if (current_state != STATE_PLAYING) {
+               pthread_mutex_unlock(&state_mutex);
+               return;
+       }
+       current_state = STATE_SUSPENDED;
+       pthread_mutex_unlock(&state_mutex);
+
+       pthread_mutex_lock(&outstream_mutex);
+       stream_out->common.standby(&stream_out->common);
+       pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void resume_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       pthread_mutex_lock(&state_mutex);
+       if (current_state == STATE_SUSPENDED)
+               current_state = STATE_PLAYING;
+       pthread_mutex_unlock(&state_mutex);
+}
+
+static void get_latency_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       haltest_info("Output audio stream latency: %d\n",
+                                       stream_out->get_latency(stream_out));
+}
+
+static void get_buffer_size_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       haltest_info("Current output buffer size: %zu\n",
+               stream_out->common.get_buffer_size(&stream_out->common));
+}
+
+static void get_channels_p(int argc, const char **argv)
+{
+       audio_channel_mask_t channels;
+
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       channels = stream_out->common.get_channels(&stream_out->common);
+
+       haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels));
+}
+
+static void get_format_p(int argc, const char **argv)
+{
+       audio_format_t format;
+
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       format = stream_out->common.get_format(&stream_out->common);
+
+       haltest_info("Format: %s\n", audio_format_t2str(format));
+}
+
+static void get_sample_rate_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       haltest_info("Current sample rate: %d\n",
+               stream_out->common.get_sample_rate(&stream_out->common));
+}
+
+static void get_parameters_p(int argc, const char **argv)
+{
+       const char *keystr;
+
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       if (argc < 3) {
+               haltest_info("No keys given.\n");
+               keystr = "";
+       } else {
+               keystr = argv[2];
+       }
+
+       haltest_info("Current parameters: %s\n",
+                       stream_out->common.get_parameters(&stream_out->common,
+                                                               keystr));
+}
+
+static void set_parameters_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       if (argc < 3) {
+               haltest_error("No key=value; pairs given.\n");
+               return;
+       }
+
+       stream_out->common.set_parameters(&stream_out->common, argv[2]);
+}
+
+static void set_sample_rate_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio);
+       RETURN_IF_NULL(stream_out);
+
+       if (argc < 3)
+               return;
+
+       stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2]));
+}
+
+static void init_check_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio);
+
+       haltest_info("Init check result: %d\n", if_audio->init_check(if_audio));
+}
+
+static struct method methods[] = {
+       STD_METHOD(init),
+       STD_METHOD(cleanup),
+       STD_METHOD(open_output_stream),
+       STD_METHOD(close_output_stream),
+       STD_METHODH(play, "<path to pcm file>"),
+       STD_METHOD(stop),
+       STD_METHOD(suspend),
+       STD_METHOD(resume),
+       STD_METHOD(get_latency),
+       STD_METHOD(get_buffer_size),
+       STD_METHOD(get_channels),
+       STD_METHOD(get_format),
+       STD_METHOD(get_sample_rate),
+       STD_METHODH(get_parameters, "<A2dpSuspended;closing>"),
+       STD_METHODH(set_parameters, "<A2dpSuspended=value;closing=value>"),
+       STD_METHODH(set_sample_rate, "<sample rate>"),
+       STD_METHOD(init_check),
+       END_METHOD
+};
+
+const struct interface audio_if = {
+       .name = "audio",
+       .methods = methods
+};
index 0470e0d..8d1f69b 100644 (file)
@@ -80,7 +80,7 @@ static void connect_p(int argc, const char **argv)
 {
        bt_bdaddr_t addr;
 
-       RETURN_IF_NULL(if_hh);
+       RETURN_IF_NULL(if_av);
        VERIFY_ADDR_ARG(2, &addr);
 
        EXEC(if_av->connect, &addr);
@@ -101,7 +101,7 @@ static void disconnect_p(int argc, const char **argv)
 {
        bt_bdaddr_t addr;
 
-       RETURN_IF_NULL(if_hh);
+       RETURN_IF_NULL(if_av);
        VERIFY_ADDR_ARG(2, &addr);
 
        EXEC(if_av->disconnect, &addr);
index 0cd43db..4d6ff83 100644 (file)
@@ -80,9 +80,7 @@ static void dump_properties(int num_properties, bt_property_t *properties)
        }
 }
 
-/*
- * Cache for remote devices, stored in sorted array
- */
+/* Cache for remote devices, stored in sorted array */
 static bt_bdaddr_t *remote_devices = NULL;
 static int remote_devices_cnt = 0;
 static int remote_devices_capacity = 0;
@@ -307,13 +305,11 @@ static void dut_mode_recv_cb(uint16_t opcode, uint8_t *buf, uint8_t len)
        haltest_info("%s\n", __func__);
 }
 
-#if PLATFORM_SDK_VERSION > 17
 static void le_test_mode_cb(bt_status_t status, uint16_t num_packets)
 {
-       haltest_info("%s %s %d\n", __func__, bt_state_t2str(status),
+       haltest_info("%s %s %d\n", __func__, bt_status_t2str(status),
                                                                num_packets);
 }
-#endif
 
 static bt_callbacks_t bt_callbacks = {
        .size = sizeof(bt_callbacks),
@@ -328,9 +324,7 @@ static bt_callbacks_t bt_callbacks = {
        .acl_state_changed_cb = acl_state_changed_cb,
        .thread_evt_cb = thread_evt_cb,
        .dut_mode_recv_cb = dut_mode_recv_cb,
-#if PLATFORM_SDK_VERSION > 17
        .le_test_mode_cb = le_test_mode_cb
-#endif
 };
 
 static void init_p(int argc, const char **argv)
@@ -470,9 +464,7 @@ static void set_adapter_property_p(int argc, const char **argv)
        EXEC(if_bluetooth->set_adapter_property, &property);
 }
 
-/*
- * This function is to be used for completion methods that need only address
- */
+/* This function is to be used for completion methods that need only address */
 static void complete_addr_c(int argc, const char **argv, enum_func *enum_func,
                                                                void **user)
 {
@@ -547,9 +539,7 @@ static void set_remote_device_property_p(int argc, const char **argv)
        EXEC(if_bluetooth->set_remote_device_property, &addr, &property);
 }
 
-/*
- * For now uuid is not autocompleted. Use routine for complete_addr_c
- */
+/* For now uuid is not autocompleted. Use routine for complete_addr_c */
 #define get_remote_service_record_c complete_addr_c
 
 static void get_remote_service_record_p(int argc, const char **argv)
@@ -726,10 +716,8 @@ static void get_profile_interface_c(int argc, const char **argv,
                BT_PROFILE_SOCKETS_ID,
                BT_PROFILE_HIDHOST_ID,
                BT_PROFILE_PAN_ID,
-#if PLATFORM_SDK_VERSION > 17
                BT_PROFILE_GATT_ID,
                BT_PROFILE_AV_RC_ID,
-#endif
                NULL
        };
 
@@ -743,7 +731,6 @@ static void get_profile_interface_p(int argc, const char **argv)
 {
        const char *id;
        const void **pif = NULL;
-       const void *dummy = NULL;
 
        RETURN_IF_NULL(if_bluetooth);
        if (argc <= 2) {
@@ -758,19 +745,17 @@ static void get_profile_interface_p(int argc, const char **argv)
        else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_ID, id) == 0)
                pif = (const void **) &if_av;
        else if (strcmp(BT_PROFILE_HEALTH_ID, id) == 0)
-               pif = &dummy; /* TODO: change when if_hl is there */
+               pif = (const void **) &if_hl;
        else if (strcmp(BT_PROFILE_SOCKETS_ID, id) == 0)
                pif = (const void **) &if_sock;
        else if (strcmp(BT_PROFILE_HIDHOST_ID, id) == 0)
                pif = (const void **) &if_hh;
        else if (strcmp(BT_PROFILE_PAN_ID, id) == 0)
                pif = (const void **) &if_pan;
-#if PLATFORM_SDK_VERSION > 17
        else if (strcmp(BT_PROFILE_AV_RC_ID, id) == 0)
-               pif = &dummy; /* TODO: change when if_rc is there */
+               pif = (const void **) &if_rc;
        else if (strcmp(BT_PROFILE_GATT_ID, id) == 0)
                pif = (const void **) &if_gatt;
-#endif
        else
                haltest_error("%s is not correct for get_profile_interface\n",
                                                                        id);
@@ -797,6 +782,32 @@ static void dut_mode_configure_p(int argc, const char **argv)
        EXEC(if_bluetooth->dut_mode_configure, mode);
 }
 
+static void dut_mode_send_p(int argc, const char **argv)
+{
+       haltest_error("not implemented\n");
+}
+
+static void le_test_mode_p(int argc, const char **argv)
+{
+       haltest_error("not implemented\n");
+}
+
+static void config_hci_snoop_log_p(int argc, const char **argv)
+{
+       uint8_t mode;
+
+       RETURN_IF_NULL(if_bluetooth);
+
+       if (argc <= 2) {
+               haltest_error("No mode specified\n");
+               return;
+       }
+
+       mode = strtol(argv[2], NULL, 0);
+
+       EXEC(if_bluetooth->config_hci_snoop_log, mode);
+}
+
 static struct method methods[] = {
        STD_METHOD(init),
        STD_METHOD(cleanup),
@@ -820,6 +831,9 @@ static struct method methods[] = {
        STD_METHODCH(ssp_reply, "<address> <ssp_veriant> 1|0 [<passkey>]"),
        STD_METHODCH(get_profile_interface, "<profile id>"),
        STD_METHODH(dut_mode_configure, "<dut mode>"),
+       STD_METHOD(dut_mode_send),
+       STD_METHOD(le_test_mode),
+       STD_METHODH(config_hci_snoop_log, "<mode>"),
        END_METHOD
 };
 
index bb53952..252e89d 100644 (file)
 
 const btgatt_interface_t *if_gatt = NULL;
 
-/* In version 19 some callback were changed.
+/*
+ * In version 19 some callback were changed.
  * btgatt_char_id_t -> btgatt_gatt_id_t
  * bt_uuid_t        -> btgatt_gatt_id_t
  */
-#if PLATFORM_SDK_VERSION > 18
 #define str2btgatt_descr_id_t str2btgatt_gatt_id_t
 #define btgatt_descr_id_t2str btgatt_gatt_id_t2str
 #define btgatt_descr_id_t btgatt_gatt_id_t
-#else
-#define btgatt_descr_id_t2str gatt_uuid_t2str
-#define str2btgatt_descr_id_t(a, b) gatt_str2bt_uuid_t(a, -1, b)
-#define btgatt_gatt_id_t btgatt_char_id_t
-#define btgatt_descr_id_t bt_uuid_t
-#endif
 
 #define MAX_CHAR_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11)
 #define MAX_SRVC_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11 + 1 + 11)
@@ -70,6 +64,9 @@ const btgatt_interface_t *if_gatt = NULL;
 #define VERIFY_CLIENT_IF(n, v) VERIFY_INT_ARG(n, v, "No client_if specified\n")
 #define VERIFY_SERVER_IF(n, v) VERIFY_INT_ARG(n, v, "No server_if specified\n")
 #define VERIFY_CONN_ID(n, v) VERIFY_INT_ARG(n, v, "No conn_if specified\n")
+#define VERIFY_TRANS_ID(n, v) VERIFY_INT_ARG(n, v, "No trans_id specified\n")
+#define VERIFY_STATUS(n, v) VERIFY_INT_ARG(n, v, "No status specified\n")
+#define VERIFY_OFFSET(n, v) VERIFY_INT_ARG(n, v, "No offset specified\n")
 #define VERIFY_HANDLE(n, v) VERIFY_HEX_ARG(n, v, "No "#v" specified\n")
 #define VERIFY_SERVICE_HANDLE(n, v) VERIFY_HANDLE(n, v)
 
@@ -436,7 +433,7 @@ static void gattc_disconnect_cb(int conn_id, int status, int client_if,
  */
 static void gattc_search_complete_cb(int conn_id, int status)
 {
-       haltest_info("%s: conn_id=%d status=%s\n", __func__, conn_id, status);
+       haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status);
 }
 
 /* Reports GATT services on a remote device */
@@ -583,6 +580,13 @@ static void gattc_read_remote_rssi_cb(int client_if, bt_bdaddr_t *bda, int rssi,
                        client_if, bt_bdaddr_t2str(bda, buf), rssi, status);
 }
 
+/* Callback invoked in response to listen */
+static void gattc_listen_cb(int status, int client_if)
+{
+       haltest_info("%s: client_if=%d status=%d\n", __func__, client_if,
+                                                               status);
+}
+
 static const btgatt_client_callbacks_t btgatt_client_callbacks = {
        .register_client_cb = gattc_register_client_cb,
        .scan_result_cb = gattc_scan_result_cb,
@@ -600,7 +604,8 @@ static const btgatt_client_callbacks_t btgatt_client_callbacks = {
        .read_descriptor_cb = gattc_read_descriptor_cb,
        .write_descriptor_cb = gattc_write_descriptor_cb,
        .execute_write_cb = gattc_execute_write_cb,
-       .read_remote_rssi_cb = gattc_read_remote_rssi_cb
+       .read_remote_rssi_cb = gattc_read_remote_rssi_cb,
+       .listen_cb = gattc_listen_cb,
 };
 
 /* BT-GATT Server callbacks */
@@ -861,7 +866,7 @@ static void scan_p(int argc, const char **argv)
        VERIFY_CLIENT_IF(2, client_if);
 
        /* start */
-       if (argc >= 3)
+       if (argc >= 4)
                start = atoi(argv[3]);
 
        EXEC(if_gatt->client->scan, client_if, start);
@@ -929,6 +934,27 @@ static void disconnect_p(int argc, const char **argv)
        EXEC(if_gatt->client->disconnect, client_if, &bd_addr, conn_id);
 }
 
+/* listen */
+
+/* Same completion as unregister for now, start stop is not auto completed */
+#define listen_c unregister_client_c
+
+static void listen_p(int argc, const char **argv)
+{
+       int client_if;
+       int start = 1;
+
+       RETURN_IF_NULL(if_gatt);
+
+       VERIFY_CLIENT_IF(2, client_if);
+
+       /* start */
+       if (argc >= 4)
+               start = atoi(argv[3]);
+
+       EXEC(if_gatt->client->listen, client_if, start);
+}
+
 /* refresh */
 
 static void refresh_c(int argc, const char **argv, enum_func *enum_func,
@@ -968,19 +994,21 @@ static void search_service_c(int argc, const char **argv, enum_func *enum_func,
 static void search_service_p(int argc, const char **argv)
 {
        int conn_id;
-       bt_uuid_t filter_uuid;
 
        RETURN_IF_NULL(if_gatt);
 
        VERIFY_CONN_ID(2, conn_id);
 
        /* uuid */
-       if (argc <= 3)
-               memset(&filter_uuid, 0, sizeof(bt_uuid_t));
-       else
-               gatt_str2bt_uuid_t(argv[3], -1, &filter_uuid);
+       if (argc <= 3) {
+               EXEC(if_gatt->client->search_service, conn_id, NULL);
+
+       } else {
+               bt_uuid_t filter_uuid;
 
-       EXEC(if_gatt->client->search_service, conn_id, &filter_uuid);
+               gatt_str2bt_uuid_t(argv[3], -1, &filter_uuid);
+               EXEC(if_gatt->client->search_service, conn_id, &filter_uuid);
+       }
 }
 
 /* get_included_service */
@@ -1410,6 +1438,7 @@ static struct method client_methods[] = {
        STD_METHODCH(get_device_type, "<addr>"),
        STD_METHODCH(test_command,
                        "<cmd> <addr> <uuid> [u1] [u2] [u3] [u4] [u5]"),
+       STD_METHODCH(listen, "<client_if> [1|0]"),
        END_METHOD
 };
 
@@ -1725,7 +1754,38 @@ static void gatts_send_indication_p(int argc, const char *argv[])
 
 static void gatts_send_response_p(int argc, const char *argv[])
 {
-       haltest_warn("%s is not implemented yet\n", __func__);
+       int conn_id;
+       int trans_id;
+       int status;
+       btgatt_response_t data;
+
+       memset(&data, 0, sizeof(data));
+
+       RETURN_IF_NULL(if_gatt);
+
+       VERIFY_CONN_ID(2, conn_id);
+       VERIFY_TRANS_ID(3, trans_id);
+       VERIFY_STATUS(4, status);
+       VERIFY_HANDLE(5, data.attr_value.handle);
+       VERIFY_OFFSET(6, data.attr_value.offset);
+
+       data.attr_value.auth_req = 0;
+       data.attr_value.len = 0;
+
+       if (argc <= 7) {
+               haltest_error("No data specified\n");
+               return;
+       }
+
+       data.attr_value.len = strlen(argv[7]);
+       scan_field(argv[7], data.attr_value.len, data.attr_value.value,
+                                               sizeof(data.attr_value.value));
+
+
+       haltest_info("conn_id %d, trans_id %d, status %d", conn_id, trans_id,
+                                                                       status);
+
+       EXEC(if_gatt->server->send_response, conn_id, trans_id, status, &data);
 }
 
 #define GATTS_METHODH(n, h) METHOD(#n, gatts_##n##_p, NULL, h)
@@ -1741,14 +1801,16 @@ static struct method server_methods[] = {
                        "<server_if> <service_handle> <included_handle>"),
        GATTS_METHODCH(add_characteristic,
                "<server_if> <service_handle> <uuid> <properites> <permissions>"),
-       GATTS_METHODCH(add_descriptor, "<server_if> <uuid> <permissions>"),
+       GATTS_METHODCH(add_descriptor,
+                       "<server_if> <service_handle> <uuid> <permissions>"),
        GATTS_METHODCH(start_service,
                                "<server_if> <service_handle> <transport>"),
        GATTS_METHODCH(stop_service, "<server_if> <service_handle>"),
        GATTS_METHODCH(delete_service, "<server_if> <service_handle>"),
        GATTS_METHODH(send_indication,
                        "<server_if> <attr_handle> <conn_id> <confirm> [<data>]"),
-       GATTS_METHODH(send_response, "<conn_id> <trans_id> <status>"),
+       GATTS_METHODH(send_response,
+               "<conn_id> <trans_id> <status> <handle> <offset> [<data>]"),
        END_METHOD
 };
 
index b8ebc8e..df3fd30 100644 (file)
@@ -128,9 +128,7 @@ static void protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
                                        bthh_protocol_mode_t2str(mode));
 }
 
-/*
- * Callback for get/set_idle_time api.
- */
+/* Callback for get/set_idle_time api. */
 static void idle_time_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,
                                                                int idle_rate)
 {
@@ -178,7 +176,7 @@ static void init_p(int argc, const char **argv)
 
 /* connect */
 
-static void connect_c(int argc, const const char **argv, enum_func *enum_func,
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
                                                                void **user)
 {
        if (argc == 3) {
@@ -229,6 +227,9 @@ static void virtual_unplug_p(int argc, const char **argv)
 
 /* set_info */
 
+/* Same completion as connect_c */
+#define set_info_c connect_c
+
 static void set_info_p(int argc, const char **argv)
 {
        bt_bdaddr_t addr;
@@ -236,15 +237,20 @@ static void set_info_p(int argc, const char **argv)
 
        RETURN_IF_NULL(if_hh);
        VERIFY_ADDR_ARG(2, &addr);
-       /* TODO: set_info does not seem to be called anywhere */
 
+       memset(&hid_info, 0, sizeof(hid_info));
+
+       /*
+        * This command is intentionally not supported. See comment from
+        * bt_hid_info() in android/hidhost.c
+        */
        EXEC(if_hh->set_info, &addr, hid_info);
 }
 
 /* get_protocol */
 
-static void get_protocol_c(int argc, const const char **argv,
-                                       enum_func *enum_func, void **user)
+static void get_protocol_c(int argc, const char **argv, enum_func *enum_func,
+                                                               void **user)
 {
        if (argc == 3) {
                *user = connected_device_addr;
@@ -296,8 +302,8 @@ static void set_protocol_p(int argc, const char **argv)
 
 /* get_report */
 
-static void get_report_c(int argc, const const char **argv,
-                                       enum_func *enum_func, void **user)
+static void get_report_c(int argc, const char **argv, enum_func *enum_func,
+                                                               void **user)
 {
        if (argc == 3) {
                *user = connected_device_addr;
@@ -341,8 +347,8 @@ static void get_report_p(int argc, const char **argv)
 
 /* set_report */
 
-static void set_report_c(int argc, const const char **argv,
-                                       enum_func *enum_func, void **user)
+static void set_report_c(int argc, const char **argv, enum_func *enum_func,
+                                                               void **user)
 {
        if (argc == 3) {
                *user = connected_device_addr;
@@ -377,7 +383,7 @@ static void set_report_p(int argc, const char **argv)
 
 /* send_data */
 
-static void send_data_c(int argc, const const char **argv, enum_func *enum_func,
+static void send_data_c(int argc, const char **argv, enum_func *enum_func,
                                                                void **user)
 {
        if (argc == 3) {
@@ -416,7 +422,7 @@ static struct method methods[] = {
        STD_METHODCH(connect, "<addr>"),
        STD_METHODCH(disconnect, "<addr>"),
        STD_METHODCH(virtual_unplug, "<addr>"),
-       STD_METHOD(set_info),
+       STD_METHODCH(set_info, "<addr>"),
        STD_METHODCH(get_protocol, "<addr> <mode>"),
        STD_METHODCH(set_protocol, "<addr> <mode>"),
        STD_METHODCH(get_report, "<addr> <type> <report_id> <size>"),
diff --git a/android/client/if-hl.c b/android/client/if-hl.c
new file mode 100644 (file)
index 0000000..557a205
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include<stdio.h>
+#include<ctype.h>
+
+#include<hardware/bluetooth.h>
+#include<hardware/bt_hl.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+SINTMAP(bthl_mdep_role_t, -1, "(unknown)")
+       DELEMENT(BTHL_MDEP_ROLE_SOURCE),
+       DELEMENT(BTHL_MDEP_ROLE_SINK),
+ENDMAP
+
+SINTMAP(bthl_channel_type_t, -1, "(unknown)")
+       DELEMENT(BTHL_CHANNEL_TYPE_RELIABLE),
+       DELEMENT(BTHL_CHANNEL_TYPE_STREAMING),
+       DELEMENT(BTHL_CHANNEL_TYPE_ANY),
+ENDMAP
+
+SINTMAP(bthl_app_reg_state_t, -1, "(unknown)")
+       DELEMENT(BTHL_APP_REG_STATE_REG_SUCCESS),
+       DELEMENT(BTHL_APP_REG_STATE_REG_FAILED),
+       DELEMENT(BTHL_APP_REG_STATE_DEREG_SUCCESS),
+       DELEMENT(BTHL_APP_REG_STATE_DEREG_FAILED),
+ENDMAP
+
+SINTMAP(bthl_channel_state_t, -1, "(unknown)")
+       DELEMENT(BTHL_CONN_STATE_CONNECTING),
+       DELEMENT(BTHL_CONN_STATE_CONNECTED),
+       DELEMENT(BTHL_CONN_STATE_DISCONNECTING),
+       DELEMENT(BTHL_CONN_STATE_DISCONNECTED),
+       DELEMENT(BTHL_CONN_STATE_DESTROYED),
+ENDMAP
+
+const bthl_interface_t *if_hl = NULL;
+
+static void app_reg_state_cb(int app_id, bthl_app_reg_state_t state)
+{
+       haltest_info("%s: app_id=%d app_reg_state=%s\n", __func__,
+                               app_id, bthl_app_reg_state_t2str(state));
+}
+
+static void channel_state_cb(int app_id, bt_bdaddr_t *bd_addr,
+                               int mdep_cfg_index, int channel_id,
+                               bthl_channel_state_t state, int fd)
+{
+       char addr[MAX_ADDR_STR_LEN];
+
+       haltest_info("%s: app_id=%d bd_addr=%s mdep_cfg_index=%d\n"
+                       "channel_id=%d channel_state=%s fd=%d\n", __func__,
+                       app_id, bt_bdaddr_t2str(bd_addr, addr), mdep_cfg_index,
+                       channel_id, bthl_channel_state_t2str(state), fd);
+}
+
+static bthl_callbacks_t hl_cbacks = {
+       .size = sizeof(hl_cbacks),
+       .app_reg_state_cb = app_reg_state_cb,
+       .channel_state_cb = channel_state_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_hl);
+
+       EXEC(if_hl->init, &hl_cbacks);
+}
+
+/* register_application */
+
+static void register_application_p(int argc, const char **argv)
+{
+       bthl_reg_param_t reg;
+       uint16_t mdep_argc_init, mdep_argc_off;
+       int app_id = -1;
+       int i;
+
+       RETURN_IF_NULL(if_hl);
+
+       if (argc <= 2) {
+               haltest_error("No app name is specified\n");
+               return;
+       }
+
+       if (argc <= 3) {
+               haltest_error("No provider is specified\n");
+               return;
+       }
+
+       if (argc <= 4) {
+               haltest_error("No service name is specified\n");
+               return;
+       }
+
+       if (argc <= 5) {
+               haltest_error("No service description is specified\n");
+               return;
+       }
+
+       if (argc <= 6) {
+               haltest_error("No num of mdeps is specified\n");
+               return;
+       }
+
+       memset(&reg, 0, sizeof(reg));
+
+       if (argc != ((atoi(argv[6]) * 4) + 7)) {
+               haltest_error("mdep cfg argumetns are not proper\n");
+               return;
+       }
+
+       reg.application_name = argv[2];
+
+       if (strcmp("-", argv[3]))
+               reg.provider_name = argv[3];
+
+       if (strcmp("-", argv[4]))
+               reg.srv_name = argv[4];
+
+       if (strcmp("-", argv[5]))
+               reg.srv_desp = argv[5];
+
+       reg.number_of_mdeps = atoi(argv[6]);
+
+       reg.mdep_cfg = malloc(reg.number_of_mdeps * sizeof(bthl_mdep_cfg_t));
+       mdep_argc_init = 7;
+
+       for (i = 0; i < reg.number_of_mdeps; i++) {
+               mdep_argc_off = mdep_argc_init + (4 * i);
+               reg.mdep_cfg[i].mdep_role =
+                               str2bthl_mdep_role_t(argv[mdep_argc_off]);
+               reg.mdep_cfg[i].data_type = atoi(argv[mdep_argc_off + 1]);
+               reg.mdep_cfg[i].channel_type =
+                       str2bthl_channel_type_t(argv[mdep_argc_off + 2]);
+
+               if (!strcmp("-", argv[mdep_argc_off + 3])) {
+                       reg.mdep_cfg[i].mdep_description = NULL;
+                       continue;
+               }
+
+               reg.mdep_cfg[i].mdep_description = argv[mdep_argc_off + 3];
+       }
+
+       EXEC(if_hl->register_application, &reg, &app_id);
+
+       free(reg.mdep_cfg);
+}
+
+/* unregister_application */
+
+static void unregister_application_p(int argc, const char **argv)
+{
+       uint32_t app_id;
+
+       RETURN_IF_NULL(if_hl);
+
+       if (argc <= 2) {
+               haltest_error("No app id is specified");
+               return;
+       }
+
+       app_id = (uint32_t) atoi(argv[2]);
+
+       EXEC(if_hl->unregister_application, app_id);
+}
+
+/* connect_channel */
+
+static void connect_channel_p(int argc, const char **argv)
+{
+       uint32_t app_id, mdep_cfg_index;
+       int channel_id = -1;
+       bt_bdaddr_t bd_addr;
+
+       RETURN_IF_NULL(if_hl);
+
+       if (argc <= 2) {
+               haltest_error("No app id is specified");
+               return;
+       }
+
+       VERIFY_ADDR_ARG(3, &bd_addr);
+
+       if (argc <= 4) {
+               haltest_error("No mdep cfg index is specified");
+               return;
+       }
+
+       app_id = (uint32_t) atoi(argv[2]);
+       mdep_cfg_index = (uint32_t) atoi(argv[4]);
+
+       EXEC(if_hl->connect_channel, app_id, &bd_addr, mdep_cfg_index,
+                                                               &channel_id);
+}
+
+/* destroy_channel */
+
+static void destroy_channel_p(int argc, const char **argv)
+{
+       uint32_t channel_id;
+
+       RETURN_IF_NULL(if_hl);
+
+       if (argc <= 2) {
+               haltest_error("No channel id is specified");
+               return;
+       }
+
+       channel_id = (uint32_t) atoi(argv[2]);
+
+       EXEC(if_hl->destroy_channel, channel_id);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_hl);
+
+       EXECV(if_hl->cleanup);
+       if_hl = NULL;
+}
+
+static struct method methods[] = {
+       STD_METHOD(init),
+       STD_METHODH(register_application,
+               "<app_name> <provider_name> <srv_name> <srv_descr>\n"
+               "<num_of_mdeps>\n"
+               "[[<mdep_role>] [<data_type>] [<channel_type>] [<mdep_descr>]]"
+               "..."),
+       STD_METHODH(unregister_application, "<app_id>"),
+       STD_METHODH(connect_channel, "<app_id> <bd_addr> <mdep_cfg_index>"),
+       STD_METHODH(destroy_channel, "<channel_id>"),
+       STD_METHOD(cleanup),
+       END_METHOD
+};
+
+const struct interface hl_if = {
+       .name = "hl",
+       .methods = methods
+};
index a83f48b..88da0c7 100644 (file)
@@ -28,6 +28,7 @@
 #include <sys/un.h>
 #include <poll.h>
 
+#include <hardware/audio.h>
 #include <hardware/bluetooth.h>
 #include <hardware/bt_av.h>
 #include <hardware/bt_hh.h>
 #include <hardware/bt_hf.h>
 #include <hardware/bt_hl.h>
 
-#if PLATFORM_SDK_VERSION > 17
 #include <hardware/bt_rc.h>
 #include <hardware/bt_gatt.h>
 #include <hardware/bt_gatt_types.h>
 #include <hardware/bt_gatt_client.h>
 #include <hardware/bt_gatt_server.h>
-#endif
+
+extern audio_hw_device_t *if_audio;
 
 /* Interfaces from hal that can be populated during application lifetime */
 extern const bt_interface_t *if_bluetooth;
 extern const btav_interface_t *if_av;
+extern const btrc_interface_t *if_rc;
 extern const bthf_interface_t *if_hf;
 extern const bthh_interface_t *if_hh;
 extern const btpan_interface_t *if_pan;
+extern const bthl_interface_t *if_hl;
 extern const btsock_interface_t *if_sock;
-#if PLATFORM_SDK_VERSION > 17
 extern const btgatt_interface_t *if_gatt;
 extern const btgatt_server_interface_t *if_gatt_server;
 extern const btgatt_client_interface_t *if_gatt_client;
-#endif
 
 /*
  * Structure defines top level interfaces that can be used in test tool
@@ -66,17 +67,19 @@ struct interface {
        struct method *methods; /* methods available for this interface */
 };
 
+extern const struct interface audio_if;
+extern const struct interface sco_if;
 extern const struct interface bluetooth_if;
 extern const struct interface av_if;
-#if PLATFORM_SDK_VERSION > 17
+extern const struct interface rc_if;
 extern const struct interface gatt_if;
 extern const struct interface gatt_client_if;
 extern const struct interface gatt_server_if;
-#endif
 extern const struct interface pan_if;
 extern const struct interface sock_if;
 extern const struct interface hf_if;
 extern const struct interface hh_if;
+extern const struct interface hl_if;
 
 /* Interfaces that will show up in tool (first part of command line) */
 extern const struct interface *interfaces[];
@@ -124,13 +127,12 @@ struct method {
        const char *help;
 };
 
-int haltest_error(const char *format, ...);
-int haltest_info(const char *format, ...);
-int haltest_warn(const char *format, ...);
+int haltest_error(const char *format, ...)
+                                       __attribute__((format(printf, 1, 2)));
+int haltest_info(const char *format, ...)__attribute__((format(printf, 1, 2)));
+int haltest_warn(const char *format, ...)__attribute__((format(printf, 1, 2)));
 
-/*
- * Enumerator for discovered devices, to be used as tab completion enum_func
- */
+/* Enumerator for discovered devices, to be used as tab completion enum_func */
 const char *enum_devices(void *v, int i);
 const char *interface_name(void *v, int i);
 const char *command_name(void *v, int i);
@@ -147,8 +149,12 @@ const struct method *get_interface_method(const char *iname,
 /* Helper macro for executing function on interface and printing BT_STATUS */
 #define EXEC(f, ...) \
        { \
-               int err = f(__VA_ARGS__); \
-               haltest_info("%s: %s\n", #f, bt_status_t2str(err)); \
+               if (f) { \
+                       int err = f(__VA_ARGS__); \
+                       haltest_info("%s: %s\n", #f, bt_status_t2str(err)); \
+               } else { \
+                       haltest_info("%s is NULL\n", #f); \
+               } \
        }
 
 /* Helper macro for executing void function on interface */
index a11f2a3..bdb36cc 100644 (file)
@@ -80,7 +80,7 @@ static void init_p(int argc, const char **argv)
 
 /* enable */
 
-static void enable_c(int argc, const const char **argv, enum_func *enum_func,
+static void enable_c(int argc, const char **argv, enum_func *enum_func,
                                                                void **user)
 {
        if (argc == 3) {
@@ -121,7 +121,7 @@ static void get_local_role_p(int argc, const char **argv)
 
 /* connect */
 
-static void connect_c(int argc, const const char **argv, enum_func *enum_func,
+static void connect_c(int argc, const char **argv, enum_func *enum_func,
                                                                void **user)
 {
        if (argc == 3) {
@@ -165,8 +165,8 @@ static void connect_p(int argc, const char **argv)
 
 /* disconnect */
 
-static void disconnect_c(int argc, const const char **argv,
-                                       enum_func *enum_func, void **user)
+static void disconnect_c(int argc, const char **argv, enum_func *enum_func,
+                                                               void **user)
 {
        if (argc == 3) {
                *user = last_used_addr;
diff --git a/android/client/if-rc.c b/android/client/if-rc.c
new file mode 100644 (file)
index 0000000..cdf6311
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include<stdio.h>
+#include<ctype.h>
+
+#include<hardware/bluetooth.h>
+#include<hardware/bt_hh.h>
+
+#include "if-main.h"
+#include "pollhandler.h"
+#include "../hal-utils.h"
+
+const btrc_interface_t *if_rc = NULL;
+
+SINTMAP(btrc_play_status_t, -1, "(unknown)")
+       DELEMENT(BTRC_PLAYSTATE_STOPPED),
+       DELEMENT(BTRC_PLAYSTATE_PLAYING),
+       DELEMENT(BTRC_PLAYSTATE_PAUSED),
+       DELEMENT(BTRC_PLAYSTATE_FWD_SEEK),
+       DELEMENT(BTRC_PLAYSTATE_REV_SEEK),
+       DELEMENT(BTRC_PLAYSTATE_ERROR),
+ENDMAP
+
+SINTMAP(btrc_media_attr_t, -1, "(unknown)")
+       DELEMENT(BTRC_MEDIA_ATTR_TITLE),
+       DELEMENT(BTRC_MEDIA_ATTR_ARTIST),
+       DELEMENT(BTRC_MEDIA_ATTR_ALBUM),
+       DELEMENT(BTRC_MEDIA_ATTR_TRACK_NUM),
+       DELEMENT(BTRC_MEDIA_ATTR_NUM_TRACKS),
+       DELEMENT(BTRC_MEDIA_ATTR_GENRE),
+       DELEMENT(BTRC_MEDIA_ATTR_PLAYING_TIME),
+ENDMAP
+
+SINTMAP(btrc_status_t, -1, "(unknown)")
+       DELEMENT(BTRC_STS_BAD_CMD),
+       DELEMENT(BTRC_STS_BAD_PARAM),
+       DELEMENT(BTRC_STS_NOT_FOUND),
+       DELEMENT(BTRC_STS_INTERNAL_ERR),
+       DELEMENT(BTRC_STS_NO_ERROR),
+ENDMAP
+
+SINTMAP(btrc_event_id_t, -1, "(unknown)")
+       DELEMENT(BTRC_EVT_PLAY_STATUS_CHANGED),
+       DELEMENT(BTRC_EVT_TRACK_CHANGE),
+       DELEMENT(BTRC_EVT_TRACK_REACHED_END),
+       DELEMENT(BTRC_EVT_TRACK_REACHED_START),
+       DELEMENT(BTRC_EVT_PLAY_POS_CHANGED),
+       DELEMENT(BTRC_EVT_APP_SETTINGS_CHANGED),
+ENDMAP
+
+SINTMAP(btrc_notification_type_t, -1, "(unknown)")
+       DELEMENT(BTRC_NOTIFICATION_TYPE_INTERIM),
+       DELEMENT(BTRC_NOTIFICATION_TYPE_CHANGED),
+ENDMAP
+
+static char last_addr[MAX_ADDR_STR_LEN];
+
+static void remote_features_cb(bt_bdaddr_t *bd_addr,
+                                       btrc_remote_features_t features)
+{
+       haltest_info("%s: remote_bd_addr=%s features=%u\n", __func__,
+                               bt_bdaddr_t2str(bd_addr, last_addr), features);
+}
+
+static void get_play_status_cb(void)
+{
+       haltest_info("%s\n", __func__);
+}
+
+static void list_player_app_attr_cb(void)
+{
+       haltest_info("%s\n", __func__);
+}
+
+static void list_player_app_values_cb(btrc_player_attr_t attr_id)
+{
+       haltest_info("%s, attr_id=%d\n", __func__, attr_id);
+}
+
+static void get_player_app_value_cb(uint8_t num_attr,
+                                               btrc_player_attr_t *p_attrs)
+{
+       int i;
+
+       haltest_info("%s, num_attr=%d\n", __func__, num_attr);
+
+       for (i = 0; i < num_attr; i++)
+               haltest_info("attribute=%u\n", p_attrs[i]);
+}
+
+static void get_player_app_attrs_text_cb(uint8_t num_attr,
+                                               btrc_player_attr_t *p_attrs)
+{
+       int i;
+
+       haltest_info("%s, num_attr=%d\n", __func__, num_attr);
+
+       for (i = 0; i < num_attr; i++)
+               haltest_info("attribute=%u\n", p_attrs[i]);
+
+}
+
+static void get_player_app_values_text_cb(uint8_t attr_id, uint8_t num_val,
+                                                               uint8_t *p_vals)
+{
+       haltest_info("%s, attr_id=%d num_val=%d values=%p\n", __func__,
+                                               attr_id, num_val, p_vals);
+}
+
+static void set_player_app_value_cb(btrc_player_settings_t *p_vals)
+{
+       int i;
+
+       haltest_info("%s, num_attr=%u\n", __func__, p_vals->num_attr);
+
+       for (i = 0; i < p_vals->num_attr; i++)
+               haltest_info("attr id=%u, values=%u\n", p_vals->attr_ids[i],
+                                                       p_vals->attr_values[i]);
+}
+
+static void get_element_attr_cb(uint8_t num_attr, btrc_media_attr_t *attrs)
+{
+       uint8_t i;
+
+       haltest_info("%s, num_of_attributes=%d\n", __func__, num_attr);
+
+       for (i = 0; i < num_attr; i++)
+               haltest_info("attr id=%s\n", btrc_media_attr_t2str(attrs[i]));
+}
+
+static void register_notification_cb(btrc_event_id_t event_id, uint32_t param)
+{
+       haltest_info("%s, event=%u param=%u\n", __func__, event_id, param);
+}
+
+static void volume_change_cb(uint8_t volume, uint8_t ctype)
+{
+       haltest_info("%s, volume=%d ctype=%d\n", __func__, volume, ctype);
+}
+
+static void passthrough_cmd_cb(int id, int key_state)
+{
+       haltest_info("%s, id=%d key_state=%d\n", __func__, id, key_state);
+}
+
+static btrc_callbacks_t rc_cbacks = {
+       .size = sizeof(rc_cbacks),
+       .remote_features_cb = remote_features_cb,
+       .get_play_status_cb = get_play_status_cb,
+       .list_player_app_attr_cb = list_player_app_attr_cb,
+       .list_player_app_values_cb = list_player_app_values_cb,
+       .get_player_app_value_cb = get_player_app_value_cb,
+       .get_player_app_attrs_text_cb = get_player_app_attrs_text_cb,
+       .get_player_app_values_text_cb = get_player_app_values_text_cb,
+       .set_player_app_value_cb = set_player_app_value_cb,
+       .get_element_attr_cb = get_element_attr_cb,
+       .register_notification_cb = register_notification_cb,
+       .volume_change_cb = volume_change_cb,
+       .passthrough_cmd_cb = passthrough_cmd_cb,
+};
+
+/* init */
+
+static void init_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_rc);
+
+       EXEC(if_rc->init, &rc_cbacks);
+}
+
+/* get_play_status_rsp */
+
+static void get_play_status_rsp_c(int argc, const char **argv,
+                                       enum_func *enum_func, void **user)
+{
+       if (argc == 3) {
+               *user = TYPE_ENUM(btrc_play_status_t);
+               *enum_func = enum_defines;
+       }
+}
+
+static void get_play_status_rsp_p(int argc, const char **argv)
+{
+       btrc_play_status_t play_status;
+       uint32_t song_len, song_pos;
+
+       RETURN_IF_NULL(if_rc);
+
+       if (argc <= 2) {
+               haltest_error("No play status specified");
+               return;
+       }
+
+       if (argc <= 3) {
+               haltest_error("No song length specified");
+               return;
+       }
+
+       if (argc <= 4) {
+               haltest_error("No song position specified");
+               return;
+       }
+
+       play_status = str2btrc_play_status_t(argv[2]);
+       song_len = (uint32_t) atoi(argv[3]);
+       song_pos = (uint32_t) atoi(argv[4]);
+
+       EXEC(if_rc->get_play_status_rsp, play_status, song_len, song_pos);
+}
+
+/* get_element_attr_rsp */
+
+static void get_element_attr_rsp_c(int argc, const char **argv,
+                                       enum_func *enum_func, void **user)
+{
+       if (argc == 4) {
+               *user = TYPE_ENUM(btrc_media_attr_t);
+               *enum_func = enum_defines;
+       }
+}
+
+static void get_element_attr_rsp_p(int argc, const char **argv)
+{
+       uint8_t num_attr;
+       btrc_element_attr_val_t attrs;
+
+       RETURN_IF_NULL(if_rc);
+
+       if (argc <= 2) {
+               haltest_error("No number of attributes specified");
+               return;
+       }
+
+       if (argc <= 4) {
+               haltest_error("No attr id and value specified");
+               return;
+       }
+
+       num_attr = (uint8_t) atoi(argv[2]);
+       attrs.attr_id = str2btrc_media_attr_t(argv[3]);
+       strcpy((char *)attrs.text, argv[4]);
+
+       EXEC(if_rc->get_element_attr_rsp, num_attr, &attrs);
+}
+
+/* set_volume */
+
+static void set_volume_c(int argc, const char **argv,
+                                       enum_func *enum_func, void **user)
+{
+}
+
+static void set_volume_p(int argc, const char **argv)
+{
+       uint8_t volume;
+
+       RETURN_IF_NULL(if_rc);
+
+       if (argc <= 2) {
+               haltest_error("No volume specified");
+               return;
+       }
+
+       volume = (uint8_t) atoi(argv[2]);
+
+       EXEC(if_rc->set_volume, volume);
+}
+
+/* set_player_app_value_rsp */
+
+static void set_player_app_value_rsp_c(int argc, const char **argv,
+                                       enum_func *enum_func, void **user)
+{
+       if (argc == 3) {
+               *user = TYPE_ENUM(btrc_status_t);
+               *enum_func = enum_defines;
+       }
+}
+
+static void set_player_app_value_rsp_p(int argc, const char **argv)
+{
+       btrc_status_t rsp_status;
+
+       RETURN_IF_NULL(if_rc);
+
+       if (argc <= 2) {
+               haltest_error("No response status specified");
+               return;
+       }
+
+       rsp_status = str2btrc_status_t(argv[2]);
+
+       EXEC(if_rc->set_player_app_value_rsp, rsp_status);
+}
+
+/* register_notification_rsp */
+
+static void register_notification_rsp_c(int argc, const char **argv,
+                                       enum_func *enum_func, void **user)
+{
+       if (argc == 3) {
+               *user = TYPE_ENUM(btrc_event_id_t);
+               *enum_func = enum_defines;
+       }
+
+       if (argc == 4) {
+               *user = TYPE_ENUM(btrc_notification_type_t);
+               *enum_func = enum_defines;
+       }
+}
+
+static void register_notification_rsp_p(int argc, const char **argv)
+{
+       btrc_event_id_t event_id;
+       btrc_notification_type_t type;
+       btrc_register_notification_t reg;
+       uint32_t song_pos;
+       uint64_t track;
+
+       RETURN_IF_NULL(if_rc);
+
+       memset(&reg, 0, sizeof(reg));
+       event_id = str2btrc_event_id_t(argv[2]);
+       type = str2btrc_notification_type_t(argv[3]);
+
+       switch (event_id) {
+       case BTRC_EVT_PLAY_STATUS_CHANGED:
+               reg.play_status = str2btrc_play_status_t(argv[4]);
+               break;
+
+       case BTRC_EVT_TRACK_CHANGE:
+               track = strtoull(argv[5], NULL, 10);
+               memcpy(reg.track, &track, sizeof(btrc_uid_t));
+               break;
+
+       case BTRC_EVT_TRACK_REACHED_END:
+       case BTRC_EVT_TRACK_REACHED_START:
+               break;
+
+       case BTRC_EVT_PLAY_POS_CHANGED:
+               song_pos = strtoul(argv[4], NULL, 10);
+               memcpy(&reg.song_pos, &song_pos, sizeof(uint32_t));
+               break;
+
+       case BTRC_EVT_APP_SETTINGS_CHANGED:
+               haltest_error("not supported");
+               return;
+       }
+
+       EXEC(if_rc->register_notification_rsp, event_id, type, &reg);
+}
+
+/* cleanup */
+
+static void cleanup_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_rc);
+
+       EXECV(if_rc->cleanup);
+       if_rc = NULL;
+}
+
+static struct method methods[] = {
+       STD_METHOD(init),
+       STD_METHODCH(get_play_status_rsp,
+                                       "<play_status> <song_len> <song_pos>"),
+       STD_METHODCH(get_element_attr_rsp, "<num_attr> <attrs_id> <value>"),
+       STD_METHODCH(set_player_app_value_rsp, "<rsp_status>"),
+       STD_METHODCH(set_volume, "<volume>"),
+       STD_METHODCH(register_notification_rsp,
+                       "<event_id> <type> <respective_data...>\n"
+                       "BTRC_EVT_PLAY_STATUS_CHANGED <type> <play_status>\n"
+                       "BTRC_EVT_TRACK_CHANGE <type> <track>\n"
+                       "BTRC_EVT_TRACK_REACHED_END <type>\n"
+                       "BTRC_EVT_TRACK_REACHED_START <type>\n"
+                       "BTRC_EVT_PLAY_POS_CHANGED <type> <song_pos>\n"),
+       STD_METHOD(cleanup),
+       END_METHOD
+};
+
+const struct interface rc_if = {
+       .name = "rc",
+       .methods = methods
+};
diff --git a/android/client/if-sco.c b/android/client/if-sco.c
new file mode 100644 (file)
index 0000000..b7f5a80
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "if-main.h"
+#include "../hal-utils.h"
+#include "pthread.h"
+#include "unistd.h"
+#include <math.h>
+
+audio_hw_device_t *if_audio_sco = NULL;
+static struct audio_stream_out *stream_out = NULL;
+
+static size_t buffer_size = 0;
+static pthread_t play_thread = 0;
+static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+enum state {
+       STATE_STOPPED,
+       STATE_STOPPING,
+       STATE_PLAYING,
+       STATE_SUSPENDED,
+       STATE_MAX
+};
+
+SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)")
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY),
+       DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER),
+       DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+       DELEMENT(AUDIO_CHANNEL_OUT_MONO),
+       DELEMENT(AUDIO_CHANNEL_OUT_STEREO),
+       DELEMENT(AUDIO_CHANNEL_OUT_QUAD),
+       DELEMENT(AUDIO_CHANNEL_OUT_SURROUND),
+       DELEMENT(AUDIO_CHANNEL_OUT_5POINT1),
+       DELEMENT(AUDIO_CHANNEL_OUT_7POINT1),
+       DELEMENT(AUDIO_CHANNEL_OUT_ALL),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+       DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT),
+ENDMAP
+
+SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)")
+       DELEMENT(AUDIO_FORMAT_DEFAULT),
+       DELEMENT(AUDIO_FORMAT_PCM),
+       DELEMENT(AUDIO_FORMAT_MP3),
+       DELEMENT(AUDIO_FORMAT_AMR_NB),
+       DELEMENT(AUDIO_FORMAT_AMR_WB),
+       DELEMENT(AUDIO_FORMAT_AAC),
+       DELEMENT(AUDIO_FORMAT_HE_AAC_V1),
+       DELEMENT(AUDIO_FORMAT_HE_AAC_V2),
+       DELEMENT(AUDIO_FORMAT_VORBIS),
+       DELEMENT(AUDIO_FORMAT_MAIN_MASK),
+       DELEMENT(AUDIO_FORMAT_SUB_MASK),
+       DELEMENT(AUDIO_FORMAT_PCM_16_BIT),
+       DELEMENT(AUDIO_FORMAT_PCM_8_BIT),
+       DELEMENT(AUDIO_FORMAT_PCM_32_BIT),
+       DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT),
+ENDMAP
+
+static int current_state = STATE_STOPPED;
+
+#define SAMPLERATE 44100
+static short sample[SAMPLERATE];
+static uint16_t sample_pos;
+
+static void init_p(int argc, const char **argv)
+{
+       int err;
+       const hw_module_t *module;
+       audio_hw_device_t *device;
+
+       err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, "sco", &module);
+       if (err) {
+               haltest_error("hw_get_module_by_class returned %d\n", err);
+               return;
+       }
+
+       err = audio_hw_device_open(module, &device);
+       if (err) {
+               haltest_error("audio_hw_device_open returned %d\n", err);
+               return;
+       }
+
+       if_audio_sco = device;
+}
+
+static int feed_from_file(short *buffer, void *data)
+{
+       FILE *in = data;
+       return fread(buffer, buffer_size, 1, in);
+}
+
+static int feed_from_generator(short *buffer, void *data)
+{
+       size_t i = 0;
+       float volume = 0.5;
+       float *freq = data;
+       float f = 1;
+
+       if (freq)
+               f = *freq;
+
+       /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/
+       for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) {
+               if (sample_pos >= SAMPLERATE)
+                       sample_pos = sample_pos % SAMPLERATE;
+
+               /* Use the same sample for both channels */
+               buffer[i++] = sample[sample_pos] * volume;
+               buffer[i++] = sample[sample_pos] * volume;
+
+               sample_pos += f;
+       }
+
+       return buffer_size;
+}
+
+static void prepare_sample(void)
+{
+       int x;
+       double s;
+
+       haltest_info("Preparing audio sample...\n");
+
+       for (x = 0; x < SAMPLERATE; x++) {
+               /* prepare sinusoidal 1Hz sample */
+               s = (2.0 * 3.14159) * ((double)x / SAMPLERATE);
+               s = sin(s);
+
+               /* remap <-1, 1> to signed 16bit PCM range */
+               sample[x] = s * 32767;
+       }
+
+       sample_pos = 0;
+}
+
+static void *playback_thread(void *data)
+{
+       int (*filbuff_cb) (short*, void*);
+       short buffer[buffer_size / sizeof(short)];
+       size_t len = 0;
+       ssize_t w_len = 0;
+       FILE *in = data;
+       void *cb_data = NULL;
+       float freq = 440.0;
+
+       /* Use file or fall back to generator */
+       if (in) {
+               filbuff_cb = feed_from_file;
+               cb_data = in;
+       } else {
+               prepare_sample();
+               filbuff_cb = feed_from_generator;
+               cb_data = &freq;
+       }
+
+       pthread_mutex_lock(&state_mutex);
+       current_state = STATE_PLAYING;
+       pthread_mutex_unlock(&state_mutex);
+
+       do {
+               pthread_mutex_lock(&state_mutex);
+
+               if (current_state == STATE_STOPPING) {
+                       haltest_info("Detected stopping\n");
+                       pthread_mutex_unlock(&state_mutex);
+                       break;
+               } else if (current_state == STATE_SUSPENDED) {
+                       pthread_mutex_unlock(&state_mutex);
+                       usleep(500);
+                       continue;
+               }
+
+               pthread_mutex_unlock(&state_mutex);
+
+               len = filbuff_cb(buffer, cb_data);
+
+               pthread_mutex_lock(&outstream_mutex);
+               if (!stream_out) {
+                       pthread_mutex_unlock(&outstream_mutex);
+                       break;
+               }
+
+               w_len = stream_out->write(stream_out, buffer, buffer_size);
+               pthread_mutex_unlock(&outstream_mutex);
+       } while (len && w_len > 0);
+
+       if (in)
+               fclose(in);
+
+       pthread_mutex_lock(&state_mutex);
+       current_state = STATE_STOPPED;
+       pthread_mutex_unlock(&state_mutex);
+
+       haltest_info("Done playing.\n");
+
+       return NULL;
+}
+
+static void play_p(int argc, const char **argv)
+{
+       const char *fname = NULL;
+       FILE *in = NULL;
+
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       if (argc < 3) {
+               haltest_error("Invalid audio file path.\n");
+               haltest_info("Using sound generator.\n");
+       } else {
+               fname = argv[2];
+               in = fopen(fname, "r");
+
+               if (in == NULL) {
+                       haltest_error("Cannot open file: %s\n", fname);
+                       return;
+               }
+               haltest_info("Playing file: %s\n", fname);
+       }
+
+       if (buffer_size == 0) {
+               haltest_error("Invalid buffer size. Was stream_out opened?\n");
+               goto fail;
+       }
+
+       pthread_mutex_lock(&state_mutex);
+       if (current_state != STATE_STOPPED) {
+               haltest_error("Already playing or stream suspended!\n");
+               pthread_mutex_unlock(&state_mutex);
+               goto fail;
+       }
+       pthread_mutex_unlock(&state_mutex);
+
+       if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) {
+               haltest_error("Cannot create playback thread!\n");
+               goto fail;
+       }
+
+       return;
+fail:
+       if (in)
+               fclose(in);
+}
+
+static void stop_p(int argc, const char **argv)
+{
+       pthread_mutex_lock(&state_mutex);
+       if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) {
+               pthread_mutex_unlock(&state_mutex);
+               return;
+       }
+
+       current_state = STATE_STOPPING;
+       pthread_mutex_unlock(&state_mutex);
+
+       pthread_mutex_lock(&outstream_mutex);
+       stream_out->common.standby(&stream_out->common);
+       pthread_mutex_unlock(&outstream_mutex);
+
+       haltest_info("Ended %s\n", __func__);
+}
+
+static void open_output_stream_p(int argc, const char **argv)
+{
+       int err;
+
+       RETURN_IF_NULL(if_audio_sco);
+
+       pthread_mutex_lock(&state_mutex);
+       if (current_state == STATE_PLAYING) {
+               haltest_error("Already playing!\n");
+               pthread_mutex_unlock(&state_mutex);
+               return;
+       }
+       pthread_mutex_unlock(&state_mutex);
+
+       err = if_audio_sco->open_output_stream(if_audio_sco,
+                                               0,
+                                               AUDIO_DEVICE_OUT_ALL_SCO,
+                                               AUDIO_OUTPUT_FLAG_NONE,
+                                               NULL,
+                                               &stream_out);
+       if (err < 0) {
+               haltest_error("open output stream returned %d\n", err);
+               return;
+       }
+
+       buffer_size = stream_out->common.get_buffer_size(&stream_out->common);
+       if (buffer_size == 0)
+               haltest_error("Invalid buffer size received!\n");
+       else
+               haltest_info("Using buffer size: %zu\n", buffer_size);
+}
+
+static void close_output_stream_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       stop_p(argc, argv);
+
+       haltest_info("Waiting for playback thread...\n");
+       pthread_join(play_thread, NULL);
+
+       if_audio_sco->close_output_stream(if_audio_sco, stream_out);
+
+       stream_out = NULL;
+       buffer_size = 0;
+}
+
+static void cleanup_p(int argc, const char **argv)
+{
+       int err;
+
+       RETURN_IF_NULL(if_audio_sco);
+
+       pthread_mutex_lock(&state_mutex);
+       if (current_state != STATE_STOPPED) {
+               pthread_mutex_unlock(&state_mutex);
+               close_output_stream_p(0, NULL);
+       } else {
+               pthread_mutex_unlock(&state_mutex);
+       }
+
+       err = audio_hw_device_close(if_audio_sco);
+       if (err < 0) {
+               haltest_error("audio_hw_device_close returned %d\n", err);
+               return;
+       }
+
+       if_audio_sco = NULL;
+}
+
+static void suspend_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       pthread_mutex_lock(&state_mutex);
+       if (current_state != STATE_PLAYING) {
+               pthread_mutex_unlock(&state_mutex);
+               return;
+       }
+       current_state = STATE_SUSPENDED;
+       pthread_mutex_unlock(&state_mutex);
+
+       pthread_mutex_lock(&outstream_mutex);
+       stream_out->common.standby(&stream_out->common);
+       pthread_mutex_unlock(&outstream_mutex);
+}
+
+static void resume_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       pthread_mutex_lock(&state_mutex);
+       if (current_state == STATE_SUSPENDED)
+               current_state = STATE_PLAYING;
+       pthread_mutex_unlock(&state_mutex);
+}
+
+static void get_latency_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       haltest_info("Output audio stream latency: %d\n",
+                                       stream_out->get_latency(stream_out));
+}
+
+static void get_buffer_size_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       haltest_info("Current output buffer size: %zu\n",
+               stream_out->common.get_buffer_size(&stream_out->common));
+}
+
+static void get_channels_p(int argc, const char **argv)
+{
+       audio_channel_mask_t channels;
+
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       channels = stream_out->common.get_channels(&stream_out->common);
+
+       haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels));
+}
+
+static void get_format_p(int argc, const char **argv)
+{
+       audio_format_t format;
+
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       format = stream_out->common.get_format(&stream_out->common);
+
+       haltest_info("Format: %s\n", audio_format_t2str(format));
+}
+
+static void get_sample_rate_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       haltest_info("Current sample rate: %d\n",
+               stream_out->common.get_sample_rate(&stream_out->common));
+}
+
+static void get_parameters_p(int argc, const char **argv)
+{
+       const char *keystr;
+
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       if (argc < 3) {
+               haltest_info("No keys given.\n");
+               keystr = "";
+       } else {
+               keystr = argv[2];
+       }
+
+       haltest_info("Current parameters: %s\n",
+                       stream_out->common.get_parameters(&stream_out->common,
+                                                               keystr));
+}
+
+static void set_parameters_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       if (argc < 3) {
+               haltest_error("No key=value; pairs given.\n");
+               return;
+       }
+
+       stream_out->common.set_parameters(&stream_out->common, argv[2]);
+}
+
+static void set_sample_rate_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio_sco);
+       RETURN_IF_NULL(stream_out);
+
+       if (argc < 3)
+               return;
+
+       stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2]));
+}
+
+static void init_check_p(int argc, const char **argv)
+{
+       RETURN_IF_NULL(if_audio_sco);
+
+       haltest_info("Init check result: %d\n",
+                                       if_audio_sco->init_check(if_audio_sco));
+}
+
+static struct method methods[] = {
+       STD_METHOD(init),
+       STD_METHOD(cleanup),
+       STD_METHOD(open_output_stream),
+       STD_METHOD(close_output_stream),
+       STD_METHODH(play, "<path to pcm file>"),
+       STD_METHOD(stop),
+       STD_METHOD(suspend),
+       STD_METHOD(resume),
+       STD_METHOD(get_latency),
+       STD_METHOD(get_buffer_size),
+       STD_METHOD(get_channels),
+       STD_METHOD(get_format),
+       STD_METHOD(get_sample_rate),
+       STD_METHODH(get_parameters, "<closing>"),
+       STD_METHODH(set_parameters, "<closing=value>"),
+       STD_METHODH(set_sample_rate, "<sample rate>"),
+       STD_METHOD(init_check),
+       END_METHOD
+};
+
+const struct interface sco_if = {
+       .name = "sco",
+       .methods = methods
+};
index 5394a5d..20a1fc8 100644 (file)
@@ -46,7 +46,8 @@ static const char * const uuids[] = {
 static void receive_from_client(struct pollfd *pollfd)
 {
        char buf[16];
-       /* Buffer for lines:
+       /*
+        * Buffer for lines:
         * 41 42 43 20 20 00 31 32 00 07 04 00 00 00 00 00 ABC  .12.....
         */
        char outbuf[sizeof(buf) * 4 + 2];
@@ -154,21 +155,19 @@ static void read_accepted(int fd)
 
        for (cmsgptr = CMSG_FIRSTHDR(&msg);
                cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
-               int *descs;
                int count;
 
                if (cmsgptr->cmsg_level != SOL_SOCKET ||
                        cmsgptr->cmsg_type != SCM_RIGHTS)
                        continue;
 
-               descs = (int *) CMSG_DATA(cmsgptr);
+               memcpy(&accepted_fd, CMSG_DATA(cmsgptr), sizeof(accepted_fd));
                count = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
 
                if (count != 1)
                        haltest_error("Failed to accept descriptors count=%d\n",
                                                                        count);
 
-               accepted_fd = descs[0];
                break;
        }
 
@@ -189,7 +188,7 @@ static void client_connected(struct pollfd *pollfd)
                read_accepted(pollfd->fd);
 }
 
-/** listen */
+/* listen */
 
 static void listen_c(int argc, const char **argv, enum_func *enum_func,
                                                                void **user)
@@ -209,7 +208,7 @@ static void listen_p(int argc, const char **argv)
        const char *service_name;
        bt_uuid_t service_uuid;
        int channel;
-       int sock_fd;
+       int sock_fd = -1;
        int flags;
 
        RETURN_IF_NULL(if_sock);
@@ -261,7 +260,7 @@ static void listen_p(int argc, const char **argv)
        }
 }
 
-/** connect */
+/* connect */
 
 static void connect_c(int argc, const char **argv, enum_func *enum_func,
                                                                void **user)
@@ -283,7 +282,7 @@ static void connect_p(int argc, const char **argv)
        btsock_type_t type;
        bt_uuid_t uuid;
        int channel;
-       int sock_fd;
+       int sock_fd = -1;
        int flags;
 
        /* Address */
index f965620..cf3fd80 100644 (file)
@@ -113,12 +113,10 @@ struct command_completion_args {
        enum_func func; /* enumerating function */
        void *user; /* argument to enumerating function */
        short_help help; /* help function */
-       void *user_help; /* additional data (used by short_help) */
+       const char *user_help; /* additional data (used by short_help) */
 };
 
-/*
- * complete command line
- */
+/* complete command line */
 static void tab_completion(struct command_completion_args *args)
 {
        const char *name = args->typed;
@@ -142,10 +140,7 @@ static void tab_completion(struct command_completion_args *args)
                        continue;
                }
 
-               /*
-                * Prefix matches next time
-                * reduce prefix to common part
-                */
+               /* Prefix matches next time reduce prefix to common part */
                for (j = 0; prefix[j] != 0
                        && prefix[j] == enum_name[j];)
                        ++j;
@@ -289,10 +284,9 @@ static void method_help(struct command_completion_args *args)
 
        if (eb != NULL)
                haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy,
-                               arg1_size, arg1,
-                               sb - (const char *) args->user_help,
-                               args->user_help,
-                               bold, eb - sb, sb, normal, eb);
+                               arg1_size, arg1, (int) (sb - args->user_help),
+                               args->user_help, bold, (int) (eb - sb),
+                               sb, normal, eb);
        else
                haltest_info("%s %-*s%s\n", args->arg->ntcopy,
                        arg1_size, arg1, args->user_help);
@@ -336,7 +330,7 @@ static void param_completion(int argc, const split_arg_t *arg,
        if (args.func != NULL) {
                args.typed = argv[argc - 1];
                args.help = method_help;
-               args.user_help = method ? (void *) method->help : NULL;
+               args.user_help = method ? method->help : NULL;
 
                tab_completion(&args);
        }
index 7951585..8096b19 100644 (file)
@@ -1,24 +1,58 @@
 /*
- * Copyright (C) 2006 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ *  BlueZ - Bluetooth protocol stack for Linux
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
  */
 
+#include <unistd.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#define PROPERTY_VALUE_MAX 32
+
+#define BLUETOOTH_MODE_PROPERTY_NAME "persist.sys.bluetooth.mode"
+
+static inline int property_get(const char *key, char *value,
+                                               const char *default_value)
+{
+       const char *prop = NULL;
+
+       if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_NAME))
+               prop = getenv("BLUETOOTH_MODE");
+
+       if (!prop)
+               prop = default_value;
+
+       if (prop) {
+               strncpy(value, prop, PROPERTY_VALUE_MAX);
+
+               value[PROPERTY_VALUE_MAX - 1] = '\0';
+
+               return strlen(value);
+       }
+
+       return 0;
+}
+
 /* property_set: returns 0 on success, < 0 on failure
 */
 static inline int property_set(const char *key, const char *value)
diff --git a/android/gatt.c b/android/gatt.c
new file mode 100644 (file)
index 0000000..8e0d72a
--- /dev/null
@@ -0,0 +1,5226 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include "ipc.h"
+#include "ipc-common.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+#include "bluetooth.h"
+#include "gatt.h"
+#include "src/log.h"
+#include "hal-msg.h"
+#include "utils.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "btio/btio.h"
+
+/* set according to Android bt_gatt_client.h */
+#define GATT_MAX_ATTR_LEN 600
+
+#define GATT_SUCCESS   0x00000000
+#define GATT_FAILURE   0x00000101
+
+#define BASE_UUID16_OFFSET     12
+
+static const uint8_t BLUETOOTH_UUID[] = {
+       0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80,
+       0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+typedef enum {
+       DEVICE_DISCONNECTED = 0,
+       DEVICE_CONNECT_INIT,            /* connection procedure initiated */
+       DEVICE_CONNECT_READY,           /* dev found during LE scan */
+       DEVICE_CONNECTED,               /* connection has been established */
+} gatt_device_state_t;
+
+static const char const *device_state_str[] = {
+       "DISCONNECTED",
+       "CONNECT INIT",
+       "CONNECT READY",
+       "CONNECTED",
+};
+
+typedef enum {
+       APP_CLIENT,
+       APP_SERVER,
+} gatt_app_type_t;
+
+struct pending_trans_data {
+       unsigned int id;
+       uint8_t opcode;
+};
+
+struct gatt_app {
+       int32_t id;
+       uint8_t uuid[16];
+
+       gatt_app_type_t type;
+
+       /* Valid for client applications */
+       struct queue *notifications;
+
+       /* Transaction data valid for server application */
+       struct pending_trans_data trans_id;
+};
+
+struct element_id {
+       bt_uuid_t uuid;
+       uint8_t instance;
+};
+
+struct descriptor {
+       struct element_id id;
+       uint16_t handle;
+};
+
+struct characteristic {
+       struct element_id id;
+       struct gatt_char ch;
+       uint16_t end_handle;
+
+       struct queue *descriptors;
+};
+
+struct service {
+       struct element_id id;
+       struct gatt_primary prim;
+       struct gatt_included incl;
+
+       bool primary;
+
+       struct queue *chars;
+       struct queue *included; /* Valid only for primary services */
+       bool incl_search_done;
+};
+
+struct notification_data {
+       struct hal_gatt_srvc_id service;
+       struct hal_gatt_gatt_id ch;
+       struct app_connection *conn;
+       guint notif_id;
+       guint ind_id;
+       int ref;
+};
+
+struct gatt_device {
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+
+       gatt_device_state_t state;
+
+       GAttrib *attrib;
+       GIOChannel *att_io;
+       struct queue *services;
+       bool partial_srvc_search;
+
+       bool notify_services_changed;
+
+       guint watch_id;
+       guint server_id;
+
+       int ref;
+       int conn_cnt;
+
+       struct queue *pending_requests;
+};
+
+struct app_connection {
+       struct gatt_device *device;
+       struct gatt_app *app;
+       int32_t id;
+};
+
+static struct ipc *hal_ipc = NULL;
+static bdaddr_t adapter_addr;
+static bool scanning = false;
+static unsigned int advertising_cnt = 0;
+
+static struct queue *gatt_apps = NULL;
+static struct queue *gatt_devices = NULL;
+static struct queue *app_connections = NULL;
+
+static struct queue *listen_apps = NULL;
+static struct gatt_db *gatt_db = NULL;
+
+static GIOChannel *listening_io = NULL;
+
+static void bt_le_discovery_stop_cb(void);
+
+static bool is_bluetooth_uuid(const uint8_t *uuid)
+{
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               /* ignore minimal uuid (16) value */
+               if (i == 12 || i == 13)
+                       continue;
+
+               if (uuid[i] != BLUETOOTH_UUID[i])
+                       return false;
+       }
+
+       return true;
+}
+
+static void android2uuid(const uint8_t *uuid, bt_uuid_t *dst)
+{
+       if (is_bluetooth_uuid(uuid)) {
+               /* copy 16 bit uuid value from full android 128bit uuid */
+               dst->type = BT_UUID16;
+               dst->value.u16 = (uuid[13] << 8) + uuid[12];
+       } else {
+               int i;
+
+               dst->type = BT_UUID128;
+               for (i = 0; i < 16; i++)
+                       dst->value.u128.data[i] = uuid[15 - i];
+       }
+}
+
+static void uuid2android(const bt_uuid_t *src, uint8_t *uuid)
+{
+       bt_uuid_t uu128;
+       uint8_t i;
+
+       if (src->type != BT_UUID128) {
+               bt_uuid_to_uuid128(src, &uu128);
+               src = &uu128;
+       }
+
+       for (i = 0; i < 16; i++)
+               uuid[15 - i] = src->value.u128.data[i];
+}
+
+static void hal_srvc_id_to_element_id(const struct hal_gatt_srvc_id *from,
+                                                       struct element_id *to)
+{
+       to->instance = from->inst_id;
+       android2uuid(from->uuid, &to->uuid);
+}
+
+static void element_id_to_hal_srvc_id(const struct element_id *from,
+                                               uint8_t primary,
+                                               struct hal_gatt_srvc_id *to)
+{
+       to->is_primary = primary;
+       to->inst_id = from->instance;
+       uuid2android(&from->uuid, to->uuid);
+}
+
+static void hal_gatt_id_to_element_id(const struct hal_gatt_gatt_id *from,
+                                                       struct element_id *to)
+{
+       to->instance = from->inst_id;
+       android2uuid(from->uuid, &to->uuid);
+}
+
+static void element_id_to_hal_gatt_id(const struct element_id *from,
+                                               struct hal_gatt_gatt_id *to)
+{
+       to->inst_id = from->instance;
+       uuid2android(&from->uuid, to->uuid);
+}
+
+static void destroy_characteristic(void *data)
+{
+       struct characteristic *chars = data;
+
+       if (!chars)
+               return;
+
+       queue_destroy(chars->descriptors, free);
+       free(chars);
+}
+
+static void destroy_service(void *data)
+{
+       struct service *srvc = data;
+
+       if (!srvc)
+               return;
+
+       queue_destroy(srvc->chars, destroy_characteristic);
+
+       /*
+        * Included services we keep on two queues.
+        * 1. On the same queue with primary services.
+        * 2. On the queue inside primary service.
+        * So we need to free service memory only once but we need to destroy
+        * two queues
+        */
+       if (srvc->primary)
+               queue_destroy(srvc->included, NULL);
+
+       free(srvc);
+}
+
+static bool match_app_by_uuid(const void *data, const void *user_data)
+{
+       const uint8_t *exp_uuid = user_data;
+       const struct gatt_app *client = data;
+
+       return !memcmp(exp_uuid, client->uuid, sizeof(client->uuid));
+}
+
+static bool match_app_by_id(const void *data, const void *user_data)
+{
+       int32_t exp_id = PTR_TO_INT(user_data);
+       const struct gatt_app *client = data;
+
+       return client->id == exp_id;
+}
+
+static struct gatt_app *find_app_by_id(int32_t id)
+{
+       return queue_find(gatt_apps, match_app_by_id, INT_TO_PTR(id));
+}
+
+static bool match_by_value(const void *data, const void *user_data)
+{
+       return data == user_data;
+}
+
+static bool match_device_by_bdaddr(const void *data, const void *user_data)
+{
+       const struct gatt_device *dev = data;
+       const bdaddr_t *addr = user_data;
+
+       return !bacmp(&dev->bdaddr, addr);
+}
+
+static bool match_device_by_state(const void *data, const void *user_data)
+{
+       const struct gatt_device *dev = data;
+
+       if (dev->state != PTR_TO_UINT(user_data))
+               return false;
+
+       return true;
+}
+
+static bool match_pending_device(const void *data, const void *user_data)
+{
+       const struct gatt_device *dev = data;
+
+       if ((dev->state == DEVICE_CONNECT_INIT) ||
+                                       (dev->state == DEVICE_CONNECT_READY))
+               return true;
+
+       return false;
+}
+
+static bool match_connection_by_id(const void *data, const void *user_data)
+{
+       const struct app_connection *conn = data;
+       const int32_t id = PTR_TO_INT(user_data);
+
+       return conn->id == id;
+}
+
+static bool match_connection_by_device_and_app(const void *data,
+                                                       const void *user_data)
+{
+       const struct app_connection *conn = data;
+       const struct app_connection *match = user_data;
+
+       return conn->device == match->device && conn->app == match->app;
+}
+
+static struct app_connection *find_connection_by_id(int32_t conn_id)
+{
+       return queue_find(app_connections, match_connection_by_id,
+                                                       INT_TO_PTR(conn_id));
+}
+
+static bool match_connection_by_device(const void *data, const void *user_data)
+{
+       const struct app_connection *conn = data;
+       const struct gatt_device *dev = user_data;
+
+       return conn->device == dev;
+}
+
+static bool match_connection_by_app(const void *data, const void *user_data)
+{
+       const struct app_connection *conn = data;
+       const struct gatt_app *app = user_data;
+
+       return conn->app == app;
+}
+
+static struct gatt_device *find_device_by_addr(const bdaddr_t *addr)
+{
+       return queue_find(gatt_devices, match_device_by_bdaddr, (void *)addr);
+}
+
+static struct gatt_device *find_pending_device()
+{
+       return queue_find(gatt_devices, match_pending_device, NULL);
+}
+
+static struct gatt_device *find_device_by_state(uint32_t state)
+{
+       return queue_find(gatt_devices, match_device_by_state,
+                                                       UINT_TO_PTR(state));
+}
+
+static bool match_srvc_by_element_id(const void *data, const void *user_data)
+{
+       const struct element_id *exp_id = user_data;
+       const struct service *service = data;
+
+       if (service->id.instance == exp_id->instance)
+               return !bt_uuid_cmp(&service->id.uuid, &exp_id->uuid);
+
+       return false;
+}
+
+static bool match_srvc_by_higher_inst_id(const void *data,
+                                                       const void *user_data)
+{
+       const struct service *s = data;
+       uint8_t inst_id = PTR_TO_INT(user_data);
+
+       /* For now we match inst_id as it is unique */
+       return inst_id < s->id.instance;
+}
+
+static bool match_srvc_by_bt_uuid(const void *data, const void *user_data)
+{
+       const bt_uuid_t *exp_uuid = user_data;
+       const struct service *service = data;
+
+       return !bt_uuid_cmp(exp_uuid, &service->id.uuid);
+}
+
+static bool match_srvc_by_range(const void *data, const void *user_data)
+{
+       const struct service *srvc = data;
+       const struct att_range *range = user_data;
+
+       return !memcmp(&srvc->prim.range, range, sizeof(srvc->prim.range));
+}
+
+static bool match_char_by_higher_inst_id(const void *data,
+                                                       const void *user_data)
+{
+       const struct characteristic *ch = data;
+       uint8_t inst_id = PTR_TO_INT(user_data);
+
+       /* For now we match inst_id as it is unique, we'll match uuids later */
+       return inst_id < ch->id.instance;
+}
+
+static bool match_descr_by_element_id(const void *data, const void *user_data)
+{
+       const struct element_id *exp_id = user_data;
+       const struct descriptor *descr = data;
+
+       if (exp_id->instance == descr->id.instance)
+               return !bt_uuid_cmp(&descr->id.uuid, &exp_id->uuid);
+
+       return false;
+}
+
+static bool match_descr_by_higher_inst_id(const void *data,
+                                                       const void *user_data)
+{
+       const struct descriptor *descr = data;
+       uint8_t instance = PTR_TO_INT(user_data);
+
+       /* For now we match instance as it is unique */
+       return instance < descr->id.instance;
+}
+
+static bool match_notification(const void *a, const void *b)
+{
+       const struct notification_data *a1 = a;
+       const struct notification_data *b1 = b;
+
+       if (a1->conn != b1->conn)
+               return false;
+
+       if (memcmp(&a1->ch, &b1->ch, sizeof(a1->ch)))
+               return false;
+
+       if (memcmp(&a1->service, &b1->service, sizeof(a1->service)))
+               return false;
+
+       return true;
+}
+
+static bool match_char_by_element_id(const void *data, const void *user_data)
+{
+       const struct element_id *exp_id = user_data;
+       const struct characteristic *chars = data;
+
+       if (exp_id->instance == chars->id.instance)
+               return !bt_uuid_cmp(&chars->id.uuid, &exp_id->uuid);
+
+       return false;
+}
+
+static void destroy_notification(void *data)
+{
+       struct notification_data *notification = data;
+       struct gatt_app *app;
+
+       if (--notification->ref)
+               return;
+
+       app = notification->conn->app;
+       queue_remove_if(app->notifications, match_notification, notification);
+       free(notification);
+}
+
+static void unregister_notification(void *data)
+{
+       struct notification_data *notification = data;
+       struct gatt_device *dev = notification->conn->device;
+
+       /*
+        * No device means it was already disconnected and client cleanup was
+        * triggered afterwards, but once client unregisters, device stays if
+        * used by others. Then just unregister single handle.
+        */
+       if (!queue_find(gatt_devices, match_by_value, dev))
+               return;
+
+       if (notification->notif_id && dev)
+               g_attrib_unregister(dev->attrib, notification->notif_id);
+
+       if (notification->ind_id && dev)
+               g_attrib_unregister(dev->attrib, notification->ind_id);
+}
+
+static void device_set_state(struct gatt_device *dev, uint32_t state)
+{
+       char bda[18];
+
+       ba2str(&dev->bdaddr, bda);
+       DBG("gatt: Device %s state changed %s -> %s", bda,
+                       device_state_str[dev->state], device_state_str[state]);
+
+       dev->state = state;
+}
+
+static void connection_cleanup(struct gatt_device *device)
+{
+       if (device->watch_id) {
+               g_source_remove(device->watch_id);
+               device->watch_id = 0;
+       }
+
+       if (device->att_io) {
+               g_io_channel_shutdown(device->att_io, FALSE, NULL);
+               g_io_channel_unref(device->att_io);
+               device->att_io = NULL;
+       }
+
+       if (device->server_id > 0)
+               g_attrib_unregister(device->attrib, device->server_id);
+
+       if (device->attrib) {
+               GAttrib *attrib = device->attrib;
+               device->attrib = NULL;
+               g_attrib_cancel_all(attrib);
+               g_attrib_unref(attrib);
+       }
+
+       /*
+        * If device was in connection_pending or connectable state we
+        * search device list if we should stop the scan.
+        */
+       if (!scanning && (device->state == DEVICE_CONNECT_INIT ||
+                               device->state == DEVICE_CONNECT_READY)) {
+               if (!find_pending_device())
+                       bt_le_discovery_stop(NULL);
+       }
+
+       /* If device is not bonded service cache should be refreshed */
+       if (!bt_device_is_bonded(&device->bdaddr))
+               queue_remove_all(device->services, NULL, NULL, destroy_service);
+
+       device_set_state(device, DEVICE_DISCONNECTED);
+}
+
+static void destroy_gatt_app(void *data)
+{
+       struct gatt_app *app = data;
+
+       /*
+        * First we want to get all notifications and unregister them.
+        * We don't pass unregister_notification to queue_destroy,
+        * because destroy notification performs operations on queue
+        * too. So remove all elements and then destroy queue.
+        */
+
+       if (app->type == APP_CLIENT)
+               while (queue_peek_head(app->notifications)) {
+                       struct notification_data *notification;
+
+                       notification = queue_pop_head(app->notifications);
+                       unregister_notification(notification);
+               }
+
+       queue_destroy(app->notifications, free);
+
+       free(app);
+}
+
+static int register_app(const uint8_t *uuid, gatt_app_type_t app_type)
+{
+       static int32_t application_id = 1;
+       struct gatt_app *app;
+
+       if (queue_find(gatt_apps, match_app_by_uuid, (void *) uuid)) {
+               error("gatt: app uuid is already on list");
+               return 0;
+       }
+
+       app = new0(struct gatt_app, 1);
+       if (!app) {
+               error("gatt: Cannot allocate memory for registering app");
+               return 0;
+       }
+
+       app->type = app_type;
+
+       if (app->type == APP_CLIENT) {
+               app->notifications = queue_new();
+               if (!app->notifications) {
+                       error("gatt: couldn't allocate notifications queue");
+                       destroy_gatt_app(app);
+                       return 0;
+               }
+       }
+
+       memcpy(app->uuid, uuid, sizeof(app->uuid));
+
+       app->id = application_id++;
+
+       if (!queue_push_head(gatt_apps, app)) {
+               error("gatt: Cannot push app on the list");
+               destroy_gatt_app(app);
+               return 0;
+       }
+
+       if ((app->type == APP_SERVER) &&
+                       !queue_push_tail(listen_apps, INT_TO_PTR(app->id))) {
+               error("gatt: Cannot push server on the list");
+               destroy_gatt_app(app);
+               return 0;
+       }
+
+       return app->id;
+}
+
+static void handle_client_register(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_register *cmd = buf;
+       struct hal_ev_gatt_client_register_client ev;
+
+       DBG("");
+
+       memset(&ev, 0, sizeof(ev));
+
+       ev.client_if = register_app(cmd->uuid, APP_CLIENT);
+
+       if (ev.client_if)
+               ev.status = GATT_SUCCESS;
+       else
+               ev.status = GATT_FAILURE;
+
+       /* We should send notification with given in cmd UUID */
+       memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid));
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+                                                       HAL_STATUS_SUCCESS);
+}
+
+static void send_client_disconnection_notify(struct app_connection *connection,
+                                                               int32_t status)
+{
+       struct hal_ev_gatt_client_disconnect ev;
+
+       ev.client_if = connection->app->id;
+       ev.conn_id = connection->id;
+       ev.status = status;
+
+       bdaddr2android(&connection->device->bdaddr, &ev.bda);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
+}
+
+static void send_client_connection_notify(struct app_connection *connection,
+                                                               int32_t status)
+{
+       struct hal_ev_gatt_client_connect ev;
+
+       ev.client_if = connection->app->id;
+       ev.conn_id = connection->id;
+       ev.status = status;
+
+       bdaddr2android(&connection->device->bdaddr, &ev.bda);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT,
+                                                       sizeof(ev), &ev);
+}
+
+static void send_server_connection_notify(struct app_connection *connection,
+                                                               bool connected)
+{
+       struct hal_ev_gatt_server_connection ev;
+
+       ev.server_if = connection->app->id;
+       ev.conn_id = connection->id;
+       ev.connected = connected;
+
+       bdaddr2android(&connection->device->bdaddr, &ev.bdaddr);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev);
+}
+
+static void send_app_disconnect_notify(struct app_connection *connection,
+                                                               int32_t status)
+{
+       if (connection->app->type == APP_CLIENT)
+               send_client_disconnection_notify(connection, status);
+       else
+               send_server_connection_notify(connection, !!status);
+}
+
+static void send_app_connect_notify(struct app_connection *connection,
+                                                               int32_t status)
+{
+       if (connection->app->type == APP_CLIENT)
+               send_client_connection_notify(connection, status);
+       else
+               send_server_connection_notify(connection, !status);
+}
+
+static void disconnect_notify_by_device(void *data, void *user_data)
+{
+       struct app_connection *conn = data;
+       struct gatt_device *dev = user_data;
+
+       if (dev != conn->device)
+               return;
+
+       if (dev->state == DEVICE_CONNECTED)
+               send_app_disconnect_notify(conn, GATT_SUCCESS);
+       else if (dev->state == DEVICE_CONNECT_INIT ||
+                                       dev->state == DEVICE_CONNECT_READY)
+               send_app_connect_notify(conn, GATT_FAILURE);
+}
+
+#define READ_INIT -3
+#define READ_PENDING -2
+#define READ_FAILED -1
+
+struct pending_request {
+       uint16_t handle;
+       int length;
+       uint8_t *value;
+       uint16_t offset;
+
+       uint8_t *filter_value;
+       uint16_t filter_vlen;
+};
+
+static void destroy_pending_request(void *data)
+{
+       struct pending_request *entry = data;
+
+       free(entry->value);
+       free(entry->filter_value);
+       free(entry);
+}
+
+static void destroy_device(void *data)
+{
+       struct gatt_device *dev = data;
+
+       if (!dev)
+               return;
+
+       queue_destroy(dev->services, destroy_service);
+       queue_destroy(dev->pending_requests, destroy_pending_request);
+
+       free(dev);
+}
+
+static struct gatt_device *device_ref(struct gatt_device *device)
+{
+       if (!device)
+               return NULL;
+
+       device->ref++;
+
+       return device;
+}
+
+static void device_unref(struct gatt_device *device)
+{
+       if (!device)
+               return;
+
+       if (--device->ref)
+               return;
+
+       destroy_device(device);
+}
+
+static void destroy_connection(void *data)
+{
+       struct app_connection *conn = data;
+
+       if (!queue_find(gatt_devices, match_by_value, conn->device))
+               goto cleanup;
+
+       conn->device->conn_cnt--;
+       if (conn->device->conn_cnt == 0)
+               connection_cleanup(conn->device);
+
+cleanup:
+       device_unref(conn->device);
+       free(conn);
+}
+
+static void device_disconnect_clients(struct gatt_device *dev)
+{
+       /* Notify disconnection to all clients */
+       queue_foreach(app_connections, disconnect_notify_by_device, dev);
+
+       /* Remove all clients by given device's */
+       queue_remove_all(app_connections, match_connection_by_device, dev,
+                                                       destroy_connection);
+}
+
+static void send_client_primary_notify(void *data, void *user_data)
+{
+       struct hal_ev_gatt_client_search_result ev;
+       struct service *p = data;
+       int32_t conn_id = PTR_TO_INT(user_data);
+
+       /* In service queue we will have also included services */
+       if (!p->primary)
+               return;
+
+       ev.conn_id  = conn_id;
+       element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id);
+
+       uuid2android(&p->id.uuid, ev.srvc_id.uuid);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev);
+}
+
+static struct service *create_service(uint8_t id, bool primary, char *uuid,
+                                                               void *data)
+{
+       struct service *s;
+
+       s = new0(struct service, 1);
+       if (!s) {
+               error("gatt: Cannot allocate memory for gatt_primary");
+               return NULL;
+       }
+
+       s->chars = queue_new();
+       if (!s->chars) {
+               error("gatt: Cannot allocate memory for char cache");
+               free(s);
+               return NULL;
+       }
+
+       if (bt_string_to_uuid(&s->id.uuid, uuid) < 0) {
+               error("gatt: Cannot convert string to uuid");
+               queue_destroy(s->chars, NULL);
+               free(s);
+               return NULL;
+       }
+
+       s->id.instance = id;
+
+       /* Put primary service to our local list */
+       s->primary = primary;
+       if (s->primary) {
+               memcpy(&s->prim, data, sizeof(s->prim));
+       } else {
+               memcpy(&s->incl, data, sizeof(s->incl));
+               return s;
+       }
+
+       /* For primary service allocate queue for included services */
+       s->included = queue_new();
+       if (!s->included) {
+               queue_destroy(s->chars, NULL);
+               free(s);
+               return NULL;
+       }
+
+       return s;
+}
+
+static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
+                                               int rssi, uint16_t eir_len,
+                                                       const void *eir,
+                                                       bool discoverable)
+{
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
+       struct gatt_device *dev;
+       char bda[18];
+
+       if (!scanning || !discoverable)
+               goto connect;
+
+       ba2str(addr, bda);
+       DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir);
+
+       bdaddr2android(addr, ev->bda);
+       ev->rssi = rssi;
+       ev->len = eir_len;
+
+       memcpy(ev->adv_data, eir, ev->len);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                                               HAL_EV_GATT_CLIENT_SCAN_RESULT,
+                                               sizeof(*ev) + ev->len, ev);
+
+connect:
+       dev = find_device_by_addr(addr);
+       if (!dev || (dev->state != DEVICE_CONNECT_INIT))
+               return;
+
+       device_set_state(dev, DEVICE_CONNECT_READY);
+       dev->bdaddr_type = addr_type;
+
+       /*
+        * We are ok to perform connect now. Stop discovery
+        * and once it is stopped continue with creating ACL
+        */
+       bt_le_discovery_stop(bt_le_discovery_stop_cb);
+}
+
+static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct gatt_device *dev = user_data;
+       int sock, err = 0;
+       socklen_t len;
+
+       sock = g_io_channel_unix_get_fd(io);
+       len = sizeof(err);
+       if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len))
+               DBG("%s (%d)", strerror(err), err);
+
+       device_disconnect_clients(dev);
+
+       return FALSE;
+}
+
+struct connect_data {
+       struct gatt_device *dev;
+       int32_t status;
+};
+
+static void send_app_connect_notifications(void *data, void *user_data)
+{
+       struct app_connection *conn = data;
+       struct connect_data *con_data = user_data;
+
+       if (conn->device == con_data->dev)
+               send_app_connect_notify(conn, con_data->status);
+}
+
+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+       struct gatt_device *dev = user_data;
+       struct connect_data data;
+       uint32_t status;
+       GAttrib *attrib;
+
+       if (dev->state != DEVICE_CONNECT_READY) {
+               error("gatt: Device not in a connecting state!?");
+               g_io_channel_shutdown(io, TRUE, NULL);
+               return;
+       }
+
+       g_io_channel_unref(dev->att_io);
+       dev->att_io = NULL;
+
+       if (gerr) {
+               error("gatt: connection failed %s", gerr->message);
+               device_set_state(dev, DEVICE_DISCONNECTED);
+               status = GATT_FAILURE;
+               goto reply;
+       }
+
+       attrib = g_attrib_new(io);
+       if (!attrib) {
+               error("gatt: unable to create new GAttrib instance");
+               device_set_state(dev, DEVICE_DISCONNECTED);
+               status = GATT_FAILURE;
+               goto reply;
+       }
+
+       dev->attrib = attrib;
+       dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                                       disconnected_cb, dev);
+
+       dev->server_id = g_attrib_register(attrib, GATTRIB_ALL_REQS,
+                                               GATTRIB_ALL_HANDLES,
+                                               att_handler, dev, NULL);
+       if (dev->server_id == 0)
+               error("gatt: Could not attach to server");
+
+       device_set_state(dev, DEVICE_CONNECTED);
+
+       status = GATT_SUCCESS;
+
+reply:
+       data.dev = dev;
+       data.status = status;
+       queue_foreach(app_connections, send_app_connect_notifications, &data);
+       device_unref(dev);
+
+       /* Check if we should restart scan */
+       if (scanning)
+               bt_le_discovery_start(le_device_found_handler);
+
+       /* FIXME: What to do if discovery won't start here. */
+}
+
+static int connect_le(struct gatt_device *dev)
+{
+       BtIOSecLevel sec_level;
+       GIOChannel *io;
+       GError *gerr = NULL;
+       char addr[18];
+
+       ba2str(&dev->bdaddr, addr);
+
+       /* There is one connection attempt going on */
+       if (dev->att_io) {
+               info("gatt: connection to dev %s is ongoing", addr);
+               return -EALREADY;
+       }
+
+       DBG("Connection attempt to: %s", addr);
+
+       /* TODO: If we are bonded then we should use higier sec level */
+       sec_level = BT_IO_SEC_LOW;
+
+       /*
+        * This connection will help us catch any PDUs that comes before
+        * pairing finishes
+        */
+       io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr,
+                       BT_IO_OPT_SOURCE_BDADDR,
+                       &adapter_addr,
+                       BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+                       BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+                       BT_IO_OPT_DEST_TYPE, dev->bdaddr_type,
+                       BT_IO_OPT_CID, ATT_CID,
+                       BT_IO_OPT_SEC_LEVEL, sec_level,
+                       BT_IO_OPT_INVALID);
+       if (!io) {
+               error("gatt: Failed bt_io_connect(%s): %s", addr,
+                                                       gerr->message);
+               g_error_free(gerr);
+               return -EIO;
+       }
+
+       /* Keep this, so we can cancel the connection */
+       dev->att_io = io;
+
+       return 0;
+}
+
+static void handle_client_scan(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_scan *cmd = buf;
+       uint8_t status;
+       void *registered;
+
+       DBG("new state %d", cmd->start);
+
+       registered = find_app_by_id(cmd->client_if);
+       if (!registered) {
+               error("gatt: Client not registered");
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+
+       /* Turn off scan */
+       if (!cmd->start) {
+               DBG("Stopping LE SCAN");
+
+               if (scanning) {
+                       bt_le_discovery_stop(NULL);
+                       scanning = false;
+               }
+
+               status = HAL_STATUS_SUCCESS;
+               goto reply;
+       }
+
+       /* Reply success if we already do scan */
+       if (scanning) {
+               status = HAL_STATUS_SUCCESS;
+               goto reply;
+       }
+
+       /* Turn on scan */
+       if (!bt_le_discovery_start(le_device_found_handler)) {
+               error("gatt: LE scan switch failed");
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+       scanning = true;
+       status = HAL_STATUS_SUCCESS;
+
+reply:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
+                                                                       status);
+}
+
+static int connect_next_dev(void)
+{
+       struct gatt_device *dev;
+
+       DBG("");
+
+       dev = find_device_by_state(DEVICE_CONNECT_READY);
+       if (!dev)
+               return -ENODEV;
+
+       return connect_le(dev);
+}
+
+static void bt_le_discovery_stop_cb(void)
+{
+       DBG("");
+
+       /* Check now if there is any device ready to connect */
+       if (connect_next_dev() < 0)
+               bt_le_discovery_start(le_device_found_handler);
+}
+
+static struct gatt_device *create_device(const bdaddr_t *addr)
+{
+       struct gatt_device *dev;
+
+       dev = new0(struct gatt_device, 1);
+       if (!dev)
+               return NULL;
+
+       bacpy(&dev->bdaddr, addr);
+
+       dev->services = queue_new();
+       if (!dev->services) {
+               error("gatt: Failed to allocate memory for client");
+               destroy_device(dev);
+               return NULL;
+       }
+
+       dev->pending_requests = queue_new();
+       if (!dev->pending_requests) {
+               error("gatt: Failed to allocate memory for client");
+               destroy_device(dev);
+               return NULL;
+       }
+
+       if (!queue_push_head(gatt_devices, dev)) {
+               error("gatt: Cannot push device to queue");
+               destroy_device(dev);
+               return NULL;
+       }
+
+       return device_ref(dev);
+}
+
+static struct app_connection *create_connection(struct gatt_device *device,
+                                               struct gatt_app *app)
+{
+       struct app_connection *new_conn;
+       static int32_t last_conn_id = 1;
+
+       /* Check if already connected */
+       new_conn = new0(struct app_connection, 1);
+       if (!new_conn)
+               return NULL;
+
+       /* Make connection id unique to connection record (app, device) pair */
+       new_conn->app = app;
+       new_conn->id = last_conn_id++;
+
+       if (!queue_push_head(app_connections, new_conn)) {
+               error("gatt: Cannot push client on the client queue!?");
+
+               free(new_conn);
+               return NULL;
+       }
+
+       new_conn->device = device_ref(device);
+       new_conn->device->conn_cnt++;
+
+       return new_conn;
+}
+
+static void trigger_disconnection(struct app_connection *connection)
+{
+       /* Notify client */
+       if (queue_remove(app_connections, connection))
+                       send_app_disconnect_notify(connection, GATT_SUCCESS);
+
+       destroy_connection(connection);
+}
+
+static void app_disconnect_devices(struct gatt_app *client)
+{
+       struct app_connection *conn;
+
+       /* find every connection for client record and trigger disconnect */
+       conn = queue_remove_if(app_connections, match_connection_by_app,
+                                                                       client);
+       while (conn) {
+               trigger_disconnection(conn);
+
+               conn = queue_remove_if(app_connections,
+                                       match_connection_by_app, client);
+       }
+}
+
+static bool trigger_connection(struct app_connection *connection)
+{
+       switch (connection->device->state) {
+       case DEVICE_DISCONNECTED:
+               device_set_state(connection->device, DEVICE_CONNECT_INIT);
+               break;
+       case DEVICE_CONNECTED:
+               send_app_connect_notify(connection, GATT_SUCCESS);
+               break;
+       default:
+               break;
+       }
+
+       /* after state change trigger discovering */
+       if (!scanning && (connection->device->state == DEVICE_CONNECT_INIT))
+               if (!bt_le_discovery_start(le_device_found_handler)) {
+                       error("gatt: Could not start scan");
+
+                       return false;
+               }
+
+       return true;
+}
+
+static void handle_client_unregister(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_unregister *cmd = buf;
+       uint8_t status;
+       struct gatt_app *cl;
+
+       DBG("");
+
+       cl = queue_remove_if(gatt_apps, match_app_by_id,
+                                               INT_TO_PTR(cmd->client_if));
+       if (!cl) {
+               error("gatt: client_if=%d not found", cmd->client_if);
+               status = HAL_STATUS_FAILED;
+       } else {
+               /*
+                * Check if there is any connect request or connected device
+                * for this client. If so, remove this client from those lists.
+                */
+               app_disconnect_devices(cl);
+               destroy_gatt_app(cl);
+               status = HAL_STATUS_SUCCESS;
+       }
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_UNREGISTER, status);
+}
+
+static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id)
+{
+       struct app_connection conn_match;
+       struct gatt_device *dev = NULL;
+       struct gatt_app *app;
+
+       /* Check if app is registered */
+       app = find_app_by_id(app_id);
+       if (!app) {
+               error("gatt: Client id %d not found", app_id);
+               return NULL;
+       }
+
+       /* Check if device is known */
+       dev = find_device_by_addr(addr);
+       if (!dev) {
+               error("gatt: Client id %d not found", app_id);
+               return NULL;
+       }
+
+       conn_match.device = dev;
+       conn_match.app = app;
+
+       return queue_find(app_connections, match_connection_by_device_and_app,
+                                                               &conn_match);
+}
+
+static uint8_t handle_connect(int32_t app_id, const bdaddr_t *addr)
+{
+       struct app_connection conn_match;
+       struct app_connection *conn;
+       struct gatt_device *device;
+       struct gatt_app *app;
+
+       DBG("");
+
+       app = find_app_by_id(app_id);
+       if (!app)
+               return HAL_STATUS_FAILED;
+
+       device = find_device_by_addr(addr);
+       if (!device) {
+               device = create_device(addr);
+               if (!device)
+                       return HAL_STATUS_FAILED;
+       }
+
+       conn_match.device = device;
+       conn_match.app = app;
+
+       conn = queue_find(app_connections, match_connection_by_device_and_app,
+                                                               &conn_match);
+       if (!conn) {
+               conn = create_connection(device, app);
+               if (!conn)
+                       return HAL_STATUS_NOMEM;
+       }
+
+       if (!trigger_connection(conn))
+               return HAL_STATUS_FAILED;
+
+       return HAL_STATUS_SUCCESS;
+}
+
+static void handle_client_connect(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_connect *cmd = buf;
+       uint8_t status;
+       bdaddr_t addr;
+
+       DBG("");
+
+       android2bdaddr(&cmd->bdaddr, &addr);
+
+       /* TODO handle is_direct flag */
+
+       status = handle_connect(cmd->client_if, &addr);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+                                                               status);
+}
+
+static void handle_client_disconnect(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_disconnect *cmd = buf;
+       struct app_connection *conn;
+       uint8_t status;
+
+       DBG("");
+
+       /* TODO: should we care to match also bdaddr when conn_id is unique? */
+       conn = find_connection_by_id(cmd->conn_id);
+       if (conn)
+               trigger_disconnection(conn);
+
+       status = HAL_STATUS_SUCCESS;
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_DISCONNECT, status);
+}
+
+static void send_client_listen_notify(int32_t id, int32_t status)
+{
+       struct hal_ev_gatt_client_listen ev;
+
+       /* Server if because of typo in android headers */
+       ev.server_if = id;
+       ev.status = status;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_LISTEN,
+                                                       sizeof(ev), &ev);
+}
+
+struct listen_data {
+       int32_t client_id;
+       bool start;
+};
+
+static void set_advertising_cb(uint8_t status, void *user_data)
+{
+       struct listen_data *l = user_data;
+
+       send_client_listen_notify(l->client_id, status);
+
+       /* In case of success update advertising state*/
+       if (!status)
+               advertising_cnt = l->start ? 1 : 0;
+
+       /*
+        * Let's remove client from the list in two cases
+        * 1. Start failed
+        * 2. Stop succeed
+        */
+       if ((l->start && status) || (!l->start && !status))
+               queue_remove(listen_apps, INT_TO_PTR(l->client_id));
+
+       free(l);
+}
+
+static void handle_client_listen(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_listen *cmd = buf;
+       uint8_t status;
+       struct listen_data *data;
+       bool req_sent = false;
+       void *listening_client;
+
+       DBG("");
+
+       if (!find_app_by_id(cmd->client_if)) {
+               error("gatt: Client not registered");
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+
+       listening_client = queue_find(listen_apps, match_by_value,
+                                               INT_TO_PTR(cmd->client_if));
+       /* Start listening */
+       if (cmd->start) {
+               if (listening_client) {
+                       status = HAL_STATUS_SUCCESS;
+                       goto reply;
+               }
+
+               if (!queue_push_tail(listen_apps,
+                                               INT_TO_PTR(cmd->client_if))) {
+                       error("gatt: Could not put client on listen queue");
+                       status = HAL_STATUS_FAILED;
+                       goto reply;
+               }
+
+               /* If listen is already on just return success*/
+               if (advertising_cnt > 0) {
+                       advertising_cnt++;
+                       status = HAL_STATUS_SUCCESS;
+                       goto reply;
+               }
+       } else {
+               /* Stop listening. Check if client was listening */
+               if (!listening_client) {
+                       error("gatt: This client %d does not listen",
+                                                       cmd->client_if);
+                       status = HAL_STATUS_FAILED;
+                       goto reply;
+               }
+
+               /*
+                * In case there is more listening clients don't stop
+                * advertising
+                */
+               if (advertising_cnt > 1) {
+                       advertising_cnt--;
+                       queue_remove(listen_apps,
+                                               INT_TO_PTR(cmd->client_if));
+                       status = HAL_STATUS_SUCCESS;
+                       goto reply;
+               }
+       }
+
+       data = new0(struct listen_data, 1);
+       if (!data) {
+               error("gatt: Could not allocate memory for listen data");
+               status = HAL_STATUS_NOMEM;
+               goto reply;
+       }
+
+       data->client_id = cmd->client_if;
+       data->start = cmd->start;
+
+       if (!bt_le_set_advertising(cmd->start, set_advertising_cb, data)) {
+               error("gatt: Could not set advertising");
+               status = HAL_STATUS_FAILED;
+               free(data);
+               goto reply;
+       }
+
+       /*
+        * Use this flag to keep in mind that we are waiting for callback with
+        * result
+        */
+       req_sent = true;
+
+       status = HAL_STATUS_SUCCESS;
+
+reply:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+                                                       status);
+
+       /* In case of early success or error, just send notification up */
+       if (!req_sent) {
+               int32_t gatt_status = status == HAL_STATUS_SUCCESS ?
+                                               GATT_SUCCESS : GATT_FAILURE;
+               send_client_listen_notify(cmd->client_if, gatt_status);
+       }
+}
+
+static void handle_client_refresh(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_refresh *cmd = buf;
+       struct gatt_device *dev;
+       uint8_t status;
+       bdaddr_t bda;
+
+       /*
+        * This is Android's framework hidden API call. It seams that no
+        * notification is expected and Bluedroid silently updates device's
+        * cache under the hood. As we use lazy caching ,we can just clear the
+        * cache and we're done.
+        */
+
+       DBG("");
+
+       android2bdaddr(&cmd->bdaddr, &bda);
+       dev = find_device_by_addr(&bda);
+       if (!dev) {
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       queue_remove_all(dev->services, NULL, NULL, destroy_service);
+
+       status = HAL_STATUS_SUCCESS;
+
+done:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
+                                                                       status);
+}
+
+struct discover_srvc_data {
+       bt_uuid_t uuid;
+       struct app_connection *conn;
+};
+
+static void send_client_search_complete_notify(int32_t status, int32_t conn_id)
+{
+       struct hal_ev_gatt_client_search_complete ev;
+
+       ev.status = status;
+       ev.conn_id = conn_id;
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev);
+}
+
+static void discover_srvc_all_cb(uint8_t status, GSList *services,
+                                                               void *user_data)
+{
+       struct discover_srvc_data *cb_data = user_data;
+       struct gatt_device *dev = cb_data->conn->device;
+       int32_t gatt_status;
+       GSList *l;
+       /*
+        * There might be multiply services with same uuid. Therefore make sure
+        * each primary service one has unique instance_id
+        */
+       uint8_t instance_id = queue_length(dev->services);
+
+       DBG("Status %d", status);
+
+       if (status) {
+               error("gatt: Discover all primary services failed: %s",
+                                                       att_ecode2str(status));
+               gatt_status = GATT_FAILURE;
+               goto reply;
+       }
+
+       if (!services) {
+               info("gatt: No primary services found");
+               gatt_status = GATT_SUCCESS;
+               goto reply;
+       }
+
+       for (l = services; l; l = l->next) {
+               struct gatt_primary *prim = l->data;
+               struct service *p;
+
+               if (queue_find(dev->services, match_srvc_by_range,
+                                                               &prim->range))
+                       continue;
+
+               p = create_service(instance_id++, true, prim->uuid, prim);
+               if (!p)
+                       continue;
+
+               if (!queue_push_tail(dev->services, p)) {
+                       error("gatt: Cannot push primary service to the list");
+                       free(p);
+                       continue;
+               }
+
+               DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+                       prim->range.start, prim->range.end, prim->uuid);
+       }
+
+       /*
+        * Send all found services notifications - first cache,
+        * then send notifies
+        */
+       queue_foreach(dev->services, send_client_primary_notify,
+                                               INT_TO_PTR(cb_data->conn->id));
+
+       /* Full search service scanning was performed */
+       dev->partial_srvc_search = false;
+       gatt_status = GATT_SUCCESS;
+
+reply:
+       send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+       free(cb_data);
+}
+
+static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges,
+                                                               void *user_data)
+{
+       struct discover_srvc_data *cb_data = user_data;
+       struct gatt_primary prim;
+       struct service *s;
+       int32_t gatt_status;
+       struct gatt_device *dev = cb_data->conn->device;
+       uint8_t instance_id = queue_length(dev->services);
+
+       DBG("Status %d", status);
+
+       if (status) {
+               error("gatt: Discover pri srvc filtered by uuid failed: %s",
+                                                       att_ecode2str(status));
+               gatt_status = GATT_FAILURE;
+               goto reply;
+       }
+
+       if (!ranges) {
+               info("gatt: No primary services searched by uuid found");
+               gatt_status = GATT_SUCCESS;
+               goto reply;
+       }
+
+       bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid));
+       /*
+        * If multiple instances of the same service (as identified by UUID)
+        * exist, the first instance of the service is returned.
+        */
+       memcpy(&prim.range, ranges->data, sizeof(prim.range));
+
+       s = create_service(instance_id++, true, prim.uuid, &prim);
+       if (!s) {
+               gatt_status = GATT_FAILURE;
+               goto reply;
+       }
+
+       if (!queue_push_tail(dev->services, s)) {
+               error("gatt: Cannot push primary service to the list");
+               gatt_status = GATT_FAILURE;
+               goto reply;
+       }
+
+       send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id));
+
+       DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s",
+               prim.range.start, prim.range.end, prim.uuid);
+
+       /* Partial search service scanning was performed */
+       dev->partial_srvc_search = true;
+       gatt_status = GATT_SUCCESS;
+
+reply:
+       send_client_search_complete_notify(gatt_status, cb_data->conn->id);
+       free(cb_data);
+}
+
+static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid)
+{
+       struct discover_srvc_data *cb_data =
+                                       new0(struct discover_srvc_data, 1);
+
+       if (!cb_data) {
+               error("gatt: Cannot allocate cb data");
+               return 0;
+       }
+
+       cb_data->conn = conn;
+
+       if (uuid) {
+               memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid));
+               return gatt_discover_primary(conn->device->attrib, uuid,
+                                       discover_srvc_by_uuid_cb, cb_data);
+       }
+
+       return gatt_discover_primary(conn->device->attrib, NULL,
+                                               discover_srvc_all_cb, cb_data);
+}
+
+static void handle_client_search_service(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_search_service *cmd = buf;
+       struct app_connection *conn;
+       uint8_t status;
+       struct service *s;
+       bt_uuid_t uuid;
+       guint srvc_search_success;
+
+       DBG("");
+
+       if (len != sizeof(*cmd) + (cmd->filtered ? 16 : 0)) {
+               error("Invalid search service size (%u bytes), terminating",
+                                                                       len);
+               raise(SIGTERM);
+               return;
+       }
+
+       conn = find_connection_by_id(cmd->conn_id);
+       if (!conn) {
+               error("gatt: dev with conn_id=%d not found", cmd->conn_id);
+
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+
+       if (conn->device->state != DEVICE_CONNECTED) {
+               char bda[18];
+
+               ba2str(&conn->device->bdaddr, bda);
+               error("gatt: device %s not connected", bda);
+
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+
+       if (cmd->filtered)
+               android2uuid(cmd->filter_uuid, &uuid);
+
+       /* Services not cached yet */
+       if (queue_isempty(conn->device->services)) {
+               if (cmd->filtered)
+                       srvc_search_success = search_dev_for_srvc(conn, &uuid);
+               else
+                       srvc_search_success = search_dev_for_srvc(conn, NULL);
+
+               if (!srvc_search_success) {
+                       status = HAL_STATUS_FAILED;
+                       goto reply;
+               }
+
+               status = HAL_STATUS_SUCCESS;
+               goto reply;
+       }
+
+       /* Search in cached services for given service */
+       if (cmd->filtered) {
+               /* Search in cache for service by uuid */
+               s = queue_find(conn->device->services, match_srvc_by_bt_uuid,
+                                                                       &uuid);
+
+               if (s) {
+                       send_client_primary_notify(s, INT_TO_PTR(conn->id));
+               } else {
+                       if (!search_dev_for_srvc(conn, &uuid))
+                               status = HAL_STATUS_FAILED;
+
+                       status = HAL_STATUS_SUCCESS;
+                       goto reply;
+               }
+       } else {
+               /* Refresh service cache if only partial search was performed */
+               if (conn->device->partial_srvc_search)
+                       srvc_search_success = search_dev_for_srvc(conn, NULL);
+               else
+                       queue_foreach(conn->device->services,
+                                               send_client_primary_notify,
+                                               INT_TO_PTR(cmd->conn_id));
+       }
+
+       send_client_search_complete_notify(GATT_SUCCESS, conn->id);
+
+       status = HAL_STATUS_SUCCESS;
+
+reply:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status);
+}
+
+static void send_client_incl_service_notify(const struct element_id *srvc_id,
+                                               const struct service *incl,
+                                               int32_t conn_id)
+{
+       struct hal_ev_gatt_client_get_inc_service ev;
+
+       memset(&ev, 0, sizeof(ev));
+
+       ev.conn_id = conn_id;
+
+       element_id_to_hal_srvc_id(srvc_id, 1, &ev.srvc_id);
+
+       if (incl) {
+               element_id_to_hal_srvc_id(&incl->id, 0, &ev.incl_srvc_id);
+               ev.status = GATT_SUCCESS;
+       } else {
+               ev.status = GATT_FAILURE;
+       }
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT ,
+                                       HAL_EV_GATT_CLIENT_GET_INC_SERVICE,
+                                       sizeof(ev), &ev);
+}
+
+struct get_included_data {
+       struct service *prim;
+       struct app_connection *conn;
+};
+
+static int get_inst_id_of_prim_services(const struct gatt_device *dev)
+{
+       struct service *s = queue_peek_tail(dev->services);
+
+       if (s)
+               return s->id.instance;
+
+       return -1;
+}
+
+static void get_included_cb(uint8_t status, GSList *included, void *user_data)
+{
+       struct get_included_data *data = user_data;
+       struct app_connection *conn = data->conn;
+       struct service *service = data->prim;
+       struct service *incl = NULL;
+       int instance_id;
+
+       DBG("");
+
+       free(data);
+
+       if (status) {
+               error("gatt: no included services found");
+               return;
+       }
+
+       /* Remember that we already search included services.*/
+       service->incl_search_done = true;
+
+       /*
+        * There might be multiply services with same uuid. Therefore make sure
+        * each service has unique instance id. Let's take the latest instance
+        * id of primary service and start iterate included services from this
+        * point.
+        */
+       instance_id = get_inst_id_of_prim_services(conn->device);
+       if (instance_id < 0)
+               goto failed;
+
+       for (; included; included = included->next) {
+               struct gatt_included *included_service = included->data;
+
+               incl = create_service(++instance_id, false,
+                                                       included_service->uuid,
+                                                       included_service);
+               if (!incl)
+                       continue;
+
+               /*
+                * Lets keep included service on two queues.
+                * 1. on services queue together with primary service
+                * 2. on special queue inside primary service
+                */
+               if (!queue_push_tail(service->included, incl) ||
+                       !queue_push_tail(conn->device->services, incl)) {
+                       error("gatt: Cannot push incl service to the list");
+                       destroy_service(incl);
+                       continue;
+               }
+       }
+
+       /*
+        * Notify upper layer about first included service.
+        * Android framework will iterate for next one.
+        */
+       incl = queue_peek_head(service->included);
+
+failed:
+       send_client_incl_service_notify(&service->id, incl, conn->id);
+}
+
+static bool search_included_services(struct app_connection *connection,
+                                                       struct service *service)
+{
+       struct get_included_data *data;
+
+       data = new0(struct get_included_data, 1);
+       if (!data) {
+               error("gatt: failed to allocate memory for included_data");
+               return false;
+       }
+
+       data->prim = service;
+       data->conn = connection;
+
+       gatt_find_included(connection->device->attrib,
+                               service->prim.range.start,
+                               service->prim.range.end, get_included_cb, data);
+       return true;
+}
+
+static bool find_service(int32_t conn_id, struct element_id *service_id,
+                                       struct app_connection **connection,
+                                       struct service **service)
+{
+       struct service *srvc;
+       struct app_connection *conn;
+
+       conn = find_connection_by_id(conn_id);
+       if (!conn) {
+               error("gatt: conn_id=%d not found", conn_id);
+               return false;
+       }
+
+       srvc = queue_find(conn->device->services, match_srvc_by_element_id,
+                                                               service_id);
+       if (!srvc) {
+               error("gatt: Service with inst_id: %d not found",
+                                                       service_id->instance);
+               return false;
+       }
+
+       *connection = conn;
+       *service = srvc;
+
+       return true;
+}
+
+static void handle_client_get_included_service(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_get_included_service *cmd = buf;
+       struct app_connection *conn;
+       struct service *prim_service;
+       struct service *incl_service = NULL;
+       struct element_id match_id;
+       struct element_id srvc_id;
+       uint8_t status;
+
+       DBG("");
+
+       hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+
+       if (len != sizeof(*cmd) +
+                       (cmd->continuation ? sizeof(cmd->incl_srvc_id[0]) : 0)) {
+               error("Invalid get incl services size (%u bytes), terminating",
+                                                                       len);
+               raise(SIGTERM);
+               return;
+       }
+
+       hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+       if (!find_service(cmd->conn_id, &match_id, &conn, &prim_service)) {
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+
+       if (!prim_service->incl_search_done) {
+               if (search_included_services(conn, prim_service))
+                       status = HAL_STATUS_SUCCESS;
+               else
+                       status = HAL_STATUS_FAILED;
+
+               ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE,
+                               status);
+               return;
+       }
+
+       /* Try to use cache here */
+       if (!cmd->continuation) {
+               incl_service = queue_peek_head(prim_service->included);
+       } else {
+               uint8_t inst_id = cmd->incl_srvc_id[0].inst_id;
+               incl_service = queue_find(prim_service->included,
+                                               match_srvc_by_higher_inst_id,
+                                               INT_TO_PTR(inst_id));
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+reply:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, status);
+
+       /*
+        * In case of error in handling request we need to send event with
+        * service id of cmd and gatt failure status.
+        */
+       send_client_incl_service_notify(&srvc_id, incl_service, cmd->conn_id);
+}
+
+static void send_client_char_notify(const struct characteristic *ch,
+                                       int32_t conn_id,
+                                       const struct service *service)
+{
+       struct hal_ev_gatt_client_get_characteristic ev;
+
+       memset(&ev, 0, sizeof(ev));
+       ev.status = ch ? GATT_SUCCESS : GATT_FAILURE;
+
+       if (ch) {
+               ev.char_prop = ch->ch.properties;
+               element_id_to_hal_gatt_id(&ch->id, &ev.char_id);
+       }
+
+       ev.conn_id = conn_id;
+       element_id_to_hal_srvc_id(&service->id, service->primary, &ev.srvc_id);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC,
+                                       sizeof(ev), &ev);
+}
+
+static void cache_all_srvc_chars(struct service *srvc, GSList *characteristics)
+{
+       uint16_t inst_id = 0;
+       bt_uuid_t uuid;
+
+       for (; characteristics; characteristics = characteristics->next) {
+               struct characteristic *ch;
+
+               ch = new0(struct characteristic, 1);
+               if (!ch) {
+                       error("gatt: Error while caching characteristic");
+                       continue;
+               }
+
+               ch->descriptors = queue_new();
+               if (!ch->descriptors) {
+                       error("gatt: Error while caching characteristic");
+                       free(ch);
+                       continue;
+               }
+
+               memcpy(&ch->ch, characteristics->data, sizeof(ch->ch));
+
+               bt_string_to_uuid(&uuid, ch->ch.uuid);
+               bt_uuid_to_uuid128(&uuid, &ch->id.uuid);
+
+               /*
+                * For now we increment inst_id and use it as characteristic
+                * handle
+                */
+               ch->id.instance = ++inst_id;
+
+               /* Store end handle to use later for descriptors discovery */
+               if (characteristics->next) {
+                       struct gatt_char *next = characteristics->next->data;
+                       ch->end_handle = next->handle - 1;
+               } else {
+                       ch->end_handle = srvc->primary ? srvc->prim.range.end :
+                                                       srvc->incl.range.end;
+               }
+
+               if (!queue_push_tail(srvc->chars, ch)) {
+                       error("gatt: Error while caching characteristic");
+                       destroy_characteristic(ch);
+               }
+       }
+}
+
+struct discover_char_data {
+       int32_t conn_id;
+       struct service *service;
+};
+
+static void discover_char_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
+{
+       struct discover_char_data *data = user_data;
+       struct service *srvc = data->service;
+
+       if (queue_isempty(srvc->chars))
+               cache_all_srvc_chars(srvc, characteristics);
+
+       send_client_char_notify(queue_peek_head(srvc->chars), data->conn_id,
+                                                                       srvc);
+
+       free(data);
+}
+
+static void handle_client_get_characteristic(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_get_characteristic *cmd = buf;
+       struct characteristic *ch;
+       struct element_id match_id;
+       struct app_connection *conn;
+       struct service *srvc;
+       uint8_t status;
+
+       DBG("");
+
+       if (len != sizeof(*cmd) + (cmd->continuation ? sizeof(cmd->char_id[0]) : 0)) {
+               error("Invalid get characteristic size (%u bytes), terminating",
+                                                                       len);
+               raise(SIGTERM);
+               return;
+       }
+
+       hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+       if (!find_service(cmd->conn_id, &match_id, &conn, &srvc)) {
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       /* Discover all characteristics for services if not cached yet */
+       if (queue_isempty(srvc->chars)) {
+               struct att_range range;
+
+               struct discover_char_data *cb_data =
+                                       new0(struct discover_char_data, 1);
+
+               if (!cb_data) {
+                       error("gatt: Cannot allocate cb data");
+                       status = HAL_STATUS_FAILED;
+                       goto done;
+               }
+
+               cb_data->service = srvc;
+               cb_data->conn_id = conn->id;
+
+               range = srvc->primary ? srvc->prim.range : srvc->incl.range;
+
+               if (!gatt_discover_char(conn->device->attrib, range.start,
+                                               range.end, NULL,
+                                               discover_char_cb, cb_data)) {
+                       free(cb_data);
+
+                       status = HAL_STATUS_FAILED;
+                       goto done;
+               }
+
+               status = HAL_STATUS_SUCCESS;
+               goto done;
+       }
+
+       if (cmd->continuation)
+               ch = queue_find(srvc->chars, match_char_by_higher_inst_id,
+                                       INT_TO_PTR(cmd->char_id[0].inst_id));
+       else
+               ch = queue_peek_head(srvc->chars);
+
+       send_client_char_notify(ch, conn->id, srvc);
+
+       status = HAL_STATUS_SUCCESS;
+
+done:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, status);
+}
+
+static void send_client_descr_notify(int32_t status, int32_t conn_id,
+                                       bool primary,
+                                       const struct element_id *srvc,
+                                       const struct element_id *ch,
+                                       const struct element_id *opt_descr)
+{
+       struct hal_ev_gatt_client_get_descriptor ev;
+
+       memset(&ev, 0, sizeof(ev));
+
+       ev.status = status;
+       ev.conn_id = conn_id;
+
+       element_id_to_hal_srvc_id(srvc, primary, &ev.srvc_id);
+       element_id_to_hal_gatt_id(ch, &ev.char_id);
+
+       if (opt_descr)
+               element_id_to_hal_gatt_id(opt_descr, &ev.descr_id);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_CLIENT_GET_DESCRIPTOR, sizeof(ev), &ev);
+}
+
+struct discover_desc_data {
+       struct app_connection *conn;
+       struct service *srvc;
+       struct characteristic *ch;
+};
+
+static void gatt_discover_desc_cb(guint8 status, GSList *descs,
+                                                       gpointer user_data)
+{
+       struct discover_desc_data *data = user_data;
+       struct app_connection *conn = data->conn;
+       struct service *srvc = data->srvc;
+       struct characteristic *ch = data->ch;
+       struct descriptor *descr;
+       int i = 0;
+
+       if (status != 0) {
+               error("Discover all characteristic descriptors failed [%s]: %s",
+                                       ch->ch.uuid, att_ecode2str(status));
+               goto reply;
+       }
+
+       for ( ; descs; descs = descs->next) {
+               struct gatt_desc *desc = descs->data;
+               bt_uuid_t uuid;
+
+               descr = new0(struct descriptor, 1);
+               if (!descr)
+                       continue;
+
+               bt_string_to_uuid(&uuid, desc->uuid);
+               bt_uuid_to_uuid128(&uuid, &descr->id.uuid);
+
+               descr->id.instance = i++;
+               descr->handle = desc->handle;
+
+               if (!queue_push_tail(ch->descriptors, descr))
+                       free(descr);
+       }
+
+reply:
+       descr = queue_peek_head(ch->descriptors);
+
+       send_client_descr_notify(status, conn->id, srvc->primary, &srvc->id,
+                                               &ch->id,
+                                               descr ? &descr->id : NULL);
+
+       free(data);
+}
+
+static bool build_descr_cache(struct app_connection *connection,
+                                       struct service *srvc,
+                                       struct characteristic *ch)
+{
+       struct discover_desc_data *cb_data;
+       uint16_t start, end;
+
+       /* Clip range to given characteristic */
+       start = ch->ch.value_handle + 1;
+       end = ch->end_handle;
+
+       /* If there are no descriptors, notify with fail status. */
+       if (start > end)
+               return false;
+
+       cb_data = new0(struct discover_desc_data, 1);
+       if (!cb_data)
+               return false;
+
+       cb_data->conn = connection;
+       cb_data->srvc = srvc;
+       cb_data->ch = ch;
+
+       if (!gatt_discover_desc(connection->device->attrib, start, end, NULL,
+                                       gatt_discover_desc_cb, cb_data)) {
+               free(cb_data);
+               return false;
+       }
+
+       return true;
+}
+
+static void handle_client_get_descriptor(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_get_descriptor *cmd = buf;
+       struct descriptor *descr = NULL;
+       struct characteristic *ch;
+       struct service *srvc;
+       struct element_id srvc_id;
+       struct element_id char_id;
+       struct app_connection *conn;
+       int32_t conn_id;
+       uint8_t primary;
+       uint8_t status;
+
+       DBG("");
+
+       if (len != sizeof(*cmd) +
+                       (cmd->continuation ? sizeof(cmd->descr_id[0]) : 0)) {
+               error("gatt: Invalid get descr command (%u bytes), terminating",
+                                                                       len);
+
+               raise(SIGTERM);
+               return;
+       }
+
+       conn_id = cmd->conn_id;
+       primary = cmd->srvc_id.is_primary;
+
+       hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+       hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+       if (!find_service(conn_id, &srvc_id, &conn, &srvc)) {
+               error("gatt: Get descr. could not find service");
+
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+       if (!ch) {
+               error("gatt: Get descr. could not find characteristic");
+
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       if (queue_isempty(ch->descriptors)) {
+               if (build_descr_cache(conn, srvc, ch)) {
+                       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_GET_DESCRIPTOR,
+                                       HAL_STATUS_SUCCESS);
+                       return;
+               }
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+       /* Send from cache */
+       if (cmd->continuation)
+               descr = queue_find(ch->descriptors,
+                                       match_descr_by_higher_inst_id,
+                                       INT_TO_PTR(cmd->descr_id[0].inst_id));
+       else
+               descr = queue_peek_head(ch->descriptors);
+
+failed:
+       send_client_descr_notify(descr ? GATT_SUCCESS : GATT_FAILURE, conn_id,
+                                               primary, &srvc_id, &char_id,
+                                               &descr->id);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, status);
+}
+
+struct char_op_data {
+       int32_t conn_id;
+       const struct element_id *srvc_id;
+       const struct element_id *char_id;
+       uint8_t primary;
+};
+
+static struct char_op_data *create_char_op_data(int32_t conn_id,
+                                               const struct element_id *s_id,
+                                               const struct element_id *ch_id,
+                                               bool primary)
+{
+       struct char_op_data *d;
+
+       d = new0(struct char_op_data, 1);
+       if (!d)
+               return NULL;
+
+       d->conn_id = conn_id;
+       d->srvc_id = s_id;
+       d->char_id = ch_id;
+       d->primary = primary;
+
+       return d;
+}
+
+static void send_client_read_char_notify(int32_t status, const uint8_t *pdu,
+                                               uint16_t len, int32_t conn_id,
+                                               const struct element_id *s_id,
+                                               const struct element_id *ch_id,
+                                               uint8_t primary)
+{
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_gatt_client_read_characteristic *ev = (void *) buf;
+       ssize_t vlen;
+
+       memset(buf, 0, sizeof(buf));
+
+       ev->conn_id = conn_id;
+       ev->status = status;
+
+       element_id_to_hal_srvc_id(s_id, primary, &ev->data.srvc_id);
+       element_id_to_hal_gatt_id(ch_id, &ev->data.char_id);
+
+       if (pdu) {
+               vlen = dec_read_resp(pdu, len, ev->data.value, sizeof(buf));
+               if (vlen < 0) {
+                       error("gatt: Protocol error");
+                       ev->status = GATT_FAILURE;
+               } else {
+                       ev->data.len = vlen;
+               }
+       }
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC,
+                                       sizeof(*ev) + ev->data.len, ev);
+}
+
+static void read_char_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct char_op_data *data = user_data;
+
+       send_client_read_char_notify(status, pdu, len, data->conn_id,
+                                               data->srvc_id, data->char_id,
+                                               data->primary);
+
+       free(data);
+}
+
+static void handle_client_read_characteristic(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_read_characteristic *cmd = buf;
+       struct char_op_data *cb_data;
+       struct characteristic *ch;
+       struct app_connection *conn;
+       struct service *srvc;
+       struct element_id srvc_id;
+       struct element_id char_id;
+       uint8_t status;
+
+       DBG("");
+
+       /* TODO authorization needs to be handled */
+
+       hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+       hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+       if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       /* search characteristics by element id */
+       ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+       if (!ch) {
+               error("gatt: Characteristic with inst_id: %d not found",
+                                                       cmd->char_id.inst_id);
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id,
+                                               cmd->srvc_id.is_primary);
+       if (!cb_data) {
+               error("gatt: Cannot allocate cb data");
+               status = HAL_STATUS_NOMEM;
+               goto failed;
+       }
+
+       if (!gatt_read_char(conn->device->attrib, ch->ch.value_handle,
+                                               read_char_cb, cb_data)) {
+               error("gatt: Cannot read characteristic with inst_id: %d",
+                                                       cmd->char_id.inst_id);
+               status = HAL_STATUS_FAILED;
+               free(cb_data);
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, status);
+
+       /*
+        * We should send notification with service, characteristic id in case
+        * of errors.
+        */
+       if (status != HAL_STATUS_SUCCESS)
+               send_client_read_char_notify(GATT_FAILURE, NULL, 0,
+                                       cmd->conn_id, &srvc_id, &char_id,
+                                       cmd->srvc_id.is_primary);
+}
+
+static void send_client_write_char_notify(int32_t status, int32_t conn_id,
+                                       const struct element_id *srvc_id,
+                                       const struct element_id *char_id,
+                                       uint8_t primary)
+{
+       struct hal_ev_gatt_client_write_characteristic ev;
+
+       memset(&ev, 0, sizeof(ev));
+
+       ev.conn_id = conn_id;
+       ev.status = status;
+
+       element_id_to_hal_srvc_id(srvc_id, primary, &ev.data.srvc_id);
+       element_id_to_hal_gatt_id(char_id, &ev.data.char_id);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC,
+                                       sizeof(ev), &ev);
+}
+
+static void write_char_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct char_op_data *data = user_data;
+
+       send_client_write_char_notify(status, data->conn_id, data->srvc_id,
+                                               data->char_id, data->primary);
+
+       free(data);
+}
+
+static void handle_client_write_characteristic(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_write_characteristic *cmd = buf;
+       struct char_op_data *cb_data = NULL;
+       struct characteristic *ch;
+       struct app_connection *conn;
+       struct service *srvc;
+       struct element_id srvc_id;
+       struct element_id char_id;
+       uint8_t status;
+       guint res;
+
+       DBG("");
+
+       if (len != sizeof(*cmd) + cmd->len) {
+               error("Invalid write char size (%u bytes), terminating", len);
+               raise(SIGTERM);
+               return;
+       }
+
+       hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+       hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+
+       if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       /* search characteristics by instance id */
+       ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+       if (!ch) {
+               error("gatt: Characteristic with inst_id: %d not found",
+                                                       cmd->char_id.inst_id);
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       if (cmd->write_type != GATT_WRITE_TYPE_NO_RESPONSE) {
+               cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id,
+                                               cmd->srvc_id.is_primary);
+               if (!cb_data) {
+                       error("gatt: Cannot allocate call data");
+                       status = HAL_STATUS_NOMEM;
+                       goto failed;
+               }
+       }
+
+       switch (cmd->write_type) {
+       case GATT_WRITE_TYPE_NO_RESPONSE:
+               res = gatt_write_cmd(conn->device->attrib, ch->ch.value_handle,
+                                                       cmd->value, cmd->len,
+                                                       NULL, NULL);
+               break;
+       case GATT_WRITE_TYPE_PREPARE:
+               res = gatt_reliable_write_char(conn->device->attrib,
+                                                       ch->ch.value_handle,
+                                                       cmd->value, cmd->len,
+                                                       write_char_cb, cb_data);
+               break;
+       case GATT_WRITE_TYPE_DEFAULT:
+               res = gatt_write_char(conn->device->attrib, ch->ch.value_handle,
+                                                       cmd->value, cmd->len,
+                                                       write_char_cb, cb_data);
+               break;
+       default:
+               error("gatt: Write type %d unsupported", cmd->write_type);
+               status = HAL_STATUS_UNSUPPORTED;
+               goto failed;
+       }
+
+       if (!res) {
+               error("gatt: Cannot write char. with inst_id: %d",
+                                                       cmd->char_id.inst_id);
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, status);
+
+       /*
+        * We should send notification with service, characteristic id in case
+        * of error and write with no response
+        */
+       if (status != HAL_STATUS_SUCCESS ||
+                       cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE) {
+               int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ?
+                                               GATT_SUCCESS : GATT_FAILURE;
+
+               send_client_write_char_notify(gatt_status, cmd->conn_id,
+                                               &srvc_id, &char_id,
+                                               cmd->srvc_id.is_primary);
+               free(cb_data);
+       }
+}
+
+static void send_client_descr_read_notify(int32_t status, const uint8_t *pdu,
+                                               guint16 len, int32_t conn_id,
+                                               const struct element_id *srvc,
+                                               const struct element_id *ch,
+                                               const struct element_id *descr,
+                                               uint8_t primary)
+{
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_gatt_client_read_descriptor *ev = (void *) buf;
+
+       memset(buf, 0, sizeof(buf));
+
+       ev->status = status;
+       ev->conn_id = conn_id;
+
+       element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id);
+       element_id_to_hal_gatt_id(ch, &ev->data.char_id);
+       element_id_to_hal_gatt_id(descr, &ev->data.descr_id);
+
+       if (len && pdu) {
+               ssize_t ret;
+
+               ret = dec_read_resp(pdu, len, ev->data.value,
+                                                       GATT_MAX_ATTR_LEN);
+               if (ret < 0) {
+                       error("gatt: Protocol error");
+                       ev->status = GATT_FAILURE;
+               } else {
+                       ev->data.len = ret;
+               }
+       }
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_EV_GATT_CLIENT_READ_DESCRIPTOR,
+                                       sizeof(*ev) + ev->data.len, ev);
+}
+
+struct desc_data {
+       int32_t conn_id;
+       const struct element_id *srvc_id;
+       const struct element_id *char_id;
+       const struct element_id *descr_id;
+       uint8_t primary;
+};
+
+static void read_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct desc_data *cb_data = user_data;
+
+       if (status != 0)
+               error("gatt: Discover all char descriptors failed: %s",
+                                                       att_ecode2str(status));
+
+       send_client_descr_read_notify(status, pdu, len, cb_data->conn_id,
+                                       cb_data->srvc_id, cb_data->char_id,
+                                       cb_data->descr_id, cb_data->primary);
+
+       free(cb_data);
+}
+
+static struct desc_data *create_desc_data(int32_t conn_id,
+                                               const struct element_id *s_id,
+                                               const struct element_id *ch_id,
+                                               const struct element_id *d_id,
+                                               uint8_t primary)
+{
+       struct desc_data *d;
+
+       d = new0(struct desc_data, 1);
+       if (!d)
+               return NULL;
+
+       d->conn_id = conn_id;
+       d->srvc_id = s_id;
+       d->char_id = ch_id;
+       d->descr_id = d_id;
+       d->primary = primary;
+
+       return d;
+}
+
+static void handle_client_read_descriptor(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_read_descriptor *cmd = buf;
+       struct desc_data *cb_data;
+       struct characteristic *ch;
+       struct descriptor *descr;
+       struct service *srvc;
+       struct element_id char_id;
+       struct element_id descr_id;
+       struct element_id srvc_id;
+       struct app_connection *conn;
+       int32_t conn_id = 0;
+       uint8_t primary;
+       uint8_t status;
+
+       DBG("");
+
+       conn_id = cmd->conn_id;
+       primary = cmd->srvc_id.is_primary;
+
+       hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+       hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+       hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id);
+
+       if (!find_service(conn_id, &srvc_id, &conn, &srvc)) {
+               error("gatt: Read descr. could not find service");
+
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+       if (!ch) {
+               error("gatt: Read descr. could not find characteristic");
+
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       descr = queue_find(ch->descriptors, match_descr_by_element_id,
+                                                               &descr_id);
+       if (!descr) {
+               error("gatt: Read descr. could not find descriptor");
+
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       cb_data = create_desc_data(conn_id, &srvc->id, &ch->id, &descr->id,
+                                                               primary);
+       if (!cb_data) {
+               error("gatt: Read descr. could not allocate callback data");
+
+               status = HAL_STATUS_NOMEM;
+               goto failed;
+       }
+
+       if (!gatt_read_char(conn->device->attrib, descr->handle, read_desc_cb,
+                                                               cb_data)) {
+               free(cb_data);
+
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       if (status != HAL_STATUS_SUCCESS)
+               send_client_descr_read_notify(GATT_FAILURE, NULL, 0, conn_id,
+                                               &srvc_id, &char_id, &descr_id,
+                                               primary);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, status);
+}
+
+static void send_client_descr_write_notify(int32_t status, int32_t conn_id,
+                                               const struct element_id *srvc,
+                                               const struct element_id *ch,
+                                               const struct element_id *descr,
+                                               uint8_t primary) {
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_gatt_client_write_descriptor *ev = (void *) buf;
+
+       memset(buf, 0, sizeof(buf));
+
+       ev->status = status;
+       ev->conn_id = conn_id;
+
+       element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id);
+       element_id_to_hal_gatt_id(ch, &ev->data.char_id);
+       element_id_to_hal_gatt_id(descr, &ev->data.descr_id);
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR,
+                                       sizeof(*ev), ev);
+}
+
+static void write_descr_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct desc_data *cb_data = user_data;
+
+       if (status)
+               error("gatt: Write descriptors failed: %s",
+                                                       att_ecode2str(status));
+
+       send_client_descr_write_notify(status, cb_data->conn_id,
+                                       cb_data->srvc_id, cb_data->char_id,
+                                       cb_data->descr_id, cb_data->primary);
+
+       free(cb_data);
+}
+
+static void handle_client_write_descriptor(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_write_descriptor *cmd = buf;
+       struct desc_data *cb_data = NULL;
+       struct characteristic *ch;
+       struct descriptor *descr;
+       struct service *srvc;
+       struct element_id srvc_id;
+       struct element_id char_id;
+       struct element_id descr_id;
+       struct app_connection *conn;
+       int32_t conn_id;
+       uint8_t primary;
+       uint8_t status;
+       guint res;
+
+       DBG("");
+
+       if (len != sizeof(*cmd) + cmd->len) {
+               error("Invalid write desriptor command (%u bytes), terminating",
+                                                                       len);
+               raise(SIGTERM);
+               return;
+       }
+
+       primary = cmd->srvc_id.is_primary;
+       conn_id = cmd->conn_id;
+
+       hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id);
+       hal_gatt_id_to_element_id(&cmd->char_id, &char_id);
+       hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id);
+
+       if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) {
+               error("gatt: Write descr. could not find service");
+
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       ch = queue_find(srvc->chars, match_char_by_element_id, &char_id);
+       if (!ch) {
+               error("gatt: Write descr. could not find characteristic");
+
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       descr = queue_find(ch->descriptors, match_descr_by_element_id,
+                                                               &descr_id);
+       if (!descr) {
+               error("gatt: Write descr. could not find descriptor");
+
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       if (cmd->write_type != GATT_WRITE_TYPE_NO_RESPONSE) {
+               cb_data = create_desc_data(conn_id, &srvc->id, &ch->id,
+                                                       &descr->id, primary);
+               if (!cb_data) {
+                       error("gatt: Write descr. could not allocate cb_data");
+
+                       status = HAL_STATUS_NOMEM;
+                       goto failed;
+               }
+       }
+
+       switch (cmd->write_type) {
+       case GATT_WRITE_TYPE_NO_RESPONSE:
+               res = gatt_write_cmd(conn->device->attrib, descr->handle,
+                                       cmd->value, cmd->len, NULL , NULL);
+               break;
+       case GATT_WRITE_TYPE_PREPARE:
+               res = gatt_reliable_write_char(conn->device->attrib,
+                                               descr->handle, cmd->value,
+                                               cmd->len, write_descr_cb,
+                                               cb_data);
+               break;
+       case GATT_WRITE_TYPE_DEFAULT:
+               res = gatt_write_char(conn->device->attrib, descr->handle,
+                                               cmd->value, cmd->len,
+                                               write_descr_cb, cb_data);
+               break;
+       default:
+               error("gatt: Write type %d unsupported", cmd->write_type);
+               status = HAL_STATUS_UNSUPPORTED;
+               goto failed;
+       }
+
+       if (!res) {
+               error("gatt: Write desc, could not write desc");
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       if (status != HAL_STATUS_SUCCESS ||
+                       cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE) {
+               int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ?
+                                               GATT_SUCCESS : GATT_FAILURE;
+
+               send_client_descr_write_notify(gatt_status, conn_id, &srvc_id,
+                                               &char_id, &descr_id, primary);
+               free(cb_data);
+       }
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, status);
+}
+
+static void send_client_write_execute_notify(int32_t id, int32_t status)
+{
+       struct hal_ev_gatt_client_exec_write ev;
+
+       ev.conn_id = id;
+       ev.status = status;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_EV_GATT_CLIENT_EXEC_WRITE,
+                                       sizeof(ev), &ev);
+}
+
+static void write_execute_cb(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       send_client_write_execute_notify(PTR_TO_INT(user_data), status);
+}
+
+static void handle_client_execute_write(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_execute_write *cmd = buf;
+       struct app_connection *conn;
+       uint8_t status;
+       uint8_t flags;
+
+       DBG("");
+
+       conn = find_connection_by_id(cmd->conn_id);
+       if (!conn) {
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+
+       flags = cmd->execute ? ATT_WRITE_ALL_PREP_WRITES :
+                                               ATT_CANCEL_ALL_PREP_WRITES;
+
+       if (!gatt_execute_write(conn->device->attrib, flags, write_execute_cb,
+                                               INT_TO_PTR(cmd->conn_id))) {
+               error("gatt: Could not send execute write");
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+reply:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_OP_GATT_CLIENT_EXECUTE_WRITE, status);
+
+       /* In case of early error send also notification.*/
+       if (status != HAL_STATUS_SUCCESS)
+               send_client_write_execute_notify(cmd->conn_id, GATT_FAILURE);
+}
+
+static void handle_notification(const uint8_t *pdu, uint16_t len,
+                                                       gpointer user_data)
+{
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_gatt_client_notify *ev = (void *) buf;
+       struct notification_data *notification = user_data;
+       uint8_t data_offset = sizeof(uint8_t) + sizeof(uint16_t);
+
+       if (len < data_offset)
+               return;
+
+       memcpy(&ev->char_id, &notification->ch, sizeof(ev->char_id));
+       memcpy(&ev->srvc_id, &notification->service, sizeof(ev->srvc_id));
+       bdaddr2android(&notification->conn->device->bdaddr, &ev->bda);
+       ev->conn_id = notification->conn->id;
+       ev->is_notify = pdu[0] == ATT_OP_HANDLE_NOTIFY;
+
+       /* We have to cut opcode and handle from data */
+       ev->len = len - data_offset;
+       memcpy(ev->value, pdu + data_offset, len - data_offset);
+
+       if (!ev->is_notify) {
+               uint8_t *res;
+               uint16_t len;
+               size_t plen;
+
+               res = g_attrib_get_buffer(
+                               notification->conn->device->attrib,
+                               &plen);
+               len = enc_confirmation(res, plen);
+               if (len > 0)
+                       g_attrib_send(notification->conn->device->attrib,
+                                               0, res, len, NULL, NULL, NULL);
+       }
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_NOTIFY,
+                                               sizeof(*ev) + ev->len, ev);
+}
+
+static void send_register_for_notification_ev(int32_t id, int32_t registered,
+                                       int32_t status,
+                                       const struct hal_gatt_srvc_id *srvc,
+                                       const struct hal_gatt_gatt_id *ch)
+{
+       struct hal_ev_gatt_client_reg_for_notif ev;
+
+       ev.conn_id = id;
+       ev.status = status;
+       ev.registered = registered;
+       memcpy(&ev.srvc_id, srvc, sizeof(ev.srvc_id));
+       memcpy(&ev.char_id, ch, sizeof(ev.char_id));
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF, sizeof(ev), &ev);
+}
+
+static void handle_client_register_for_notification(const void *buf,
+                                                               uint16_t len)
+{
+       const struct hal_cmd_gatt_client_register_for_notification *cmd = buf;
+       struct notification_data *notification;
+       struct characteristic *c;
+       struct element_id match_id;
+       struct app_connection *conn;
+       int32_t conn_id = 0;
+       struct service *service;
+       uint8_t status;
+       int32_t gatt_status;
+       bdaddr_t addr;
+
+       DBG("");
+
+       android2bdaddr(&cmd->bdaddr, &addr);
+
+       conn = find_conn(&addr, cmd->client_if);
+       if (!conn) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       conn_id = conn->id;
+
+       hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id);
+       service = queue_find(conn->device->services, match_srvc_by_element_id,
+                                                               &match_id);
+       if (!service) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       hal_gatt_id_to_element_id(&cmd->char_id, &match_id);
+       c = queue_find(service->chars, match_char_by_element_id, &match_id);
+       if (!c) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       notification = new0(struct notification_data, 1);
+       if (!notification) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       memcpy(&notification->ch, &cmd->char_id, sizeof(notification->ch));
+       memcpy(&notification->service, &cmd->srvc_id,
+                                               sizeof(notification->service));
+       notification->conn = conn;
+
+       if (queue_find(conn->app->notifications, match_notification,
+                                                               notification)) {
+               free(notification);
+               status = HAL_STATUS_SUCCESS;
+               goto failed;
+       }
+
+       notification->notif_id = g_attrib_register(conn->device->attrib,
+                                                       ATT_OP_HANDLE_NOTIFY,
+                                                       c->ch.value_handle,
+                                                       handle_notification,
+                                                       notification,
+                                                       destroy_notification);
+       if (!notification->notif_id) {
+               free(notification);
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       notification->ind_id = g_attrib_register(conn->device->attrib,
+                                                       ATT_OP_HANDLE_IND,
+                                                       c->ch.value_handle,
+                                                       handle_notification,
+                                                       notification,
+                                                       destroy_notification);
+       if (!notification->ind_id) {
+               g_attrib_unregister(conn->device->attrib,
+                                                       notification->notif_id);
+               free(notification);
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       /*
+        * Because same data - notification - is shared by two handlers, we
+        * introduce ref counter to be sure that data can be freed with no risk.
+        * Counter is decremented in destroy_notification.
+        */
+       notification->ref = 2;
+
+       if (!queue_push_tail(conn->app->notifications, notification)) {
+               unregister_notification(notification);
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+       send_register_for_notification_ev(conn_id, 1, gatt_status,
+                                               &cmd->srvc_id, &cmd->char_id);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION, status);
+}
+
+static void handle_client_deregister_for_notification(const void *buf,
+                                                               uint16_t len)
+{
+       const struct hal_cmd_gatt_client_deregister_for_notification *cmd = buf;
+       struct notification_data *notification, notif;
+       struct app_connection *conn;
+       int32_t conn_id = 0;
+       uint8_t status;
+       int32_t gatt_status;
+       bdaddr_t addr;
+
+       DBG("");
+
+       android2bdaddr(&cmd->bdaddr, &addr);
+
+       conn = find_conn(&addr, cmd->client_if);
+       if (!conn) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       conn_id = conn->id;
+
+       memcpy(&notif.ch, &cmd->char_id, sizeof(notif.ch));
+       memcpy(&notif.service, &cmd->srvc_id, sizeof(notif.service));
+       notif.conn = conn;
+
+       notification = queue_find(conn->app->notifications,
+                                               match_notification, &notif);
+       if (!notification) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       unregister_notification(notification);
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+       send_register_for_notification_ev(conn_id, 0, gatt_status,
+                                               &cmd->srvc_id, &cmd->char_id);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION, status);
+}
+
+static void send_client_remote_rssi_notify(int32_t client_if,
+                                               const bdaddr_t *addr,
+                                               int32_t rssi, int32_t status)
+{
+       struct hal_ev_gatt_client_read_remote_rssi ev;
+
+       ev.client_if = client_if;
+       bdaddr2android(addr, &ev.address);
+       ev.rssi = rssi;
+       ev.status = status;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI, sizeof(ev), &ev);
+}
+
+static void read_remote_rssi_cb(uint8_t status, const bdaddr_t *addr,
+                                               int8_t rssi, void *user_data)
+{
+       int32_t client_if = PTR_TO_INT(user_data);
+       int32_t gatt_status = status ? GATT_FAILURE : GATT_SUCCESS;
+
+       send_client_remote_rssi_notify(client_if, addr, rssi, gatt_status);
+}
+
+static void handle_client_read_remote_rssi(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_read_remote_rssi *cmd = buf;
+       uint8_t status;
+       bdaddr_t bdaddr;
+
+       DBG("");
+
+       if (!find_app_by_id(cmd->client_if)) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       android2bdaddr(cmd->bdaddr, &bdaddr);
+       if (!bt_read_device_rssi(&bdaddr, read_remote_rssi_cb,
+                                               INT_TO_PTR(cmd->client_if))) {
+               error("gatt: Could not read RSSI");
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, status);
+
+       if (status != HAL_STATUS_SUCCESS)
+               send_client_remote_rssi_notify(cmd->client_if, &bdaddr, 0,
+                                                               GATT_FAILURE);
+}
+
+static void handle_client_get_device_type(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_get_device_type *cmd = buf;
+       struct hal_rsp_gatt_client_get_device_type rsp;
+       bdaddr_t bdaddr;
+
+       DBG("");
+
+       android2bdaddr(cmd->bdaddr, &bdaddr);
+
+       rsp.type = bt_get_device_android_type(&bdaddr);
+
+       ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE,
+                                       sizeof(rsp), &rsp, -1);
+}
+
+static void handle_client_set_adv_data(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_client_set_adv_data *cmd = buf;
+       uint8_t status;
+
+       if (len != sizeof(*cmd) + cmd->manufacturer_len) {
+               error("Invalid set adv data command (%u bytes), terminating",
+                                                                       len);
+               raise(SIGTERM);
+               return;
+       }
+
+       DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d manufacturer=%u",
+               cmd->set_scan_rsp, cmd->include_name, cmd->include_txpower,
+               cmd->min_interval, cmd->max_interval, cmd->appearance,
+               cmd->manufacturer_len);
+
+       /*
+        * TODO
+        * Currently kernel is setting all except for vendor data.
+        * This should be implemented when kernel supports it.
+        */
+
+       if (cmd->manufacturer_len) {
+               error("gatt: Manufacturer advertising data not supported");
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_CLIENT_SET_ADV_DATA, status);
+}
+
+static void handle_client_test_command(const void *buf, uint16_t len)
+{
+       DBG("");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_OP_GATT_CLIENT_TEST_COMMAND, HAL_STATUS_FAILED);
+}
+
+static void handle_server_register(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_register *cmd = buf;
+       struct hal_ev_gatt_server_register ev;
+
+       DBG("");
+
+       memset(&ev, 0, sizeof(ev));
+
+       ev.server_if = register_app(cmd->uuid, APP_SERVER);
+
+       if (ev.server_if)
+               ev.status = GATT_SUCCESS;
+       else
+               ev.status = GATT_FAILURE;
+
+       memcpy(ev.uuid, cmd->uuid, sizeof(ev.uuid));
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_EV_GATT_SERVER_REGISTER, sizeof(ev), &ev);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+                                                       HAL_STATUS_SUCCESS);
+}
+
+static void handle_server_unregister(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_unregister *cmd = buf;
+       uint8_t status;
+       struct gatt_app *server;
+
+       DBG("");
+
+       server = find_app_by_id(cmd->server_if);
+       if (!server) {
+               error("gatt: server_if=%d not found", cmd->server_if);
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       destroy_gatt_app(server);
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_SERVER_UNREGISTER, status);
+}
+
+static void handle_server_connect(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_connect *cmd = buf;
+       uint8_t status;
+       bdaddr_t addr;
+
+       DBG("");
+
+       android2bdaddr(&cmd->bdaddr, &addr);
+
+       status = handle_connect(cmd->server_if, &addr);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+                                                               status);
+}
+
+static void handle_server_disconnect(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_disconnect *cmd = buf;
+       struct app_connection *conn;
+       uint8_t status;
+
+       DBG("");
+
+       /* TODO: should we care to match also bdaddr when conn_id is unique? */
+       conn = find_connection_by_id(cmd->conn_id);
+       if (conn)
+               trigger_disconnection(conn);
+
+       status = HAL_STATUS_SUCCESS;
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_SERVER_DISCONNECT, status);
+}
+
+static void handle_server_add_service(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_add_service *cmd = buf;
+       struct hal_ev_gatt_server_service_added ev;
+       struct gatt_app *server;
+       uint8_t status;
+       bt_uuid_t uuid;
+
+       DBG("");
+
+       memset(&ev, 0, sizeof(ev));
+
+       server = find_app_by_id(cmd->server_if);
+       if (!server) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       android2uuid(cmd->srvc_id.uuid, &uuid);
+
+       ev.srvc_handle = gatt_db_add_service(gatt_db, &uuid,
+                                                       cmd->srvc_id.is_primary,
+                                                       cmd->num_handles);
+       if (!ev.srvc_handle) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+       ev.srvc_id = cmd->srvc_id;
+       ev.server_if = cmd->server_if;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_SERVER_SERVICE_ADDED, sizeof(ev), &ev);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_SERVER_ADD_SERVICE, status);
+}
+
+static void handle_server_add_included_service(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_add_inc_service *cmd = buf;
+       struct hal_ev_gatt_server_inc_srvc_added ev;
+       struct gatt_app *server;
+       uint8_t status;
+
+       DBG("");
+
+       memset(&ev, 0, sizeof(ev));
+
+       server = find_app_by_id(cmd->server_if);
+       if (!server) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       ev.incl_srvc_handle = gatt_db_add_included_service(gatt_db,
+                                                       cmd->service_handle,
+                                                       cmd->included_handle);
+       if (!ev.incl_srvc_handle) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+failed:
+       ev.srvc_handle = cmd->service_handle;
+       ev.status = status;
+       ev.server_if = cmd->server_if;
+       ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_SERVER_INC_SRVC_ADDED, sizeof(ev), &ev);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status);
+}
+
+static bool is_service(const bt_uuid_t *type)
+{
+       bt_uuid_t uuid;
+
+       bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+       if (!bt_uuid_cmp(&uuid, type))
+               return true;
+
+       bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+       if (!bt_uuid_cmp(&uuid, type))
+               return true;
+
+       return false;
+}
+
+static void send_dev_pending_response(struct gatt_device *device,
+                                                               uint8_t opcode)
+{
+       uint8_t rsp[ATT_DEFAULT_LE_MTU];
+       struct pending_request *val;
+       uint16_t len = 0;
+       uint8_t error = 0;
+
+       switch (opcode) {
+       case ATT_OP_READ_BY_TYPE_REQ: {
+               struct att_data_list *adl;
+               int iterator = 0;
+               int length;
+               struct queue *temp;
+
+
+               temp = queue_new();
+               if (!temp)
+                       goto done;
+
+               val = queue_pop_head(device->pending_requests);
+               if (!val) {
+                       queue_destroy(temp, NULL);
+                       error = ATT_ECODE_ATTR_NOT_FOUND;
+                       goto done;
+               }
+
+               length = val->length;
+
+               while (val && val->length == length) {
+                       queue_push_tail(temp, val);
+                       val = queue_pop_head(device->pending_requests);
+               }
+
+               adl = att_data_list_alloc(queue_length(temp), sizeof(uint16_t) +
+                                                                       length);
+
+               val = queue_pop_head(temp);
+               while (val) {
+                       uint8_t *value = adl->data[iterator++];
+
+                       put_le16(val->handle, value);
+                       memcpy(&value[2], val->value, val->length);
+
+                       destroy_pending_request(val);
+                       val = queue_pop_head(temp);
+               }
+
+               len = enc_read_by_type_resp(adl, rsp, sizeof(rsp));
+
+               att_data_list_free(adl);
+               queue_destroy(temp, destroy_pending_request);
+
+               break;
+       }
+       case ATT_OP_READ_BLOB_REQ:
+               val = queue_pop_head(device->pending_requests);
+               if (!val || val->length < 0) {
+                       error = ATT_ECODE_ATTR_NOT_FOUND;
+                       goto done;
+               }
+
+               len = enc_read_blob_resp(val->value, val->length, val->offset,
+                                                       rsp, sizeof(rsp));
+               destroy_pending_request(val);
+               break;
+       case ATT_OP_READ_REQ:
+               val = queue_pop_head(device->pending_requests);
+               if (!val || val->length < 0) {
+                       error = ATT_ECODE_ATTR_NOT_FOUND;
+                       goto done;
+               }
+
+               len = enc_read_resp(val->value, val->length, rsp, sizeof(rsp));
+               destroy_pending_request(val);
+               break;
+       case ATT_OP_READ_BY_GROUP_REQ: {
+               struct att_data_list *adl;
+               int iterator = 0;
+               int length;
+               struct queue *temp;
+
+               temp = queue_new();
+               if (!temp)
+                       goto done;
+
+               val = queue_pop_head(device->pending_requests);
+               if (!val) {
+                       queue_destroy(temp, NULL);
+                       error = ATT_ECODE_ATTR_NOT_FOUND;
+                       goto done;
+               }
+
+               length = val->length;
+
+               while (val && val->length == length) {
+                       queue_push_tail(temp, val);
+                       val = queue_pop_head(device->pending_requests);
+               }
+
+               adl = att_data_list_alloc(queue_length(temp),
+                                               2 * sizeof(uint16_t) + length);
+
+               val = queue_pop_head(temp);
+               while (val) {
+                       uint8_t *value = adl->data[iterator++];
+                       uint16_t end_handle;
+
+                       end_handle = gatt_db_get_end_handle(gatt_db,
+                                                               val->handle);
+
+                       put_le16(val->handle, value);
+                       put_le16(end_handle, &value[2]);
+                       memcpy(&value[4], val->value, val->length);
+
+                       destroy_pending_request(val);
+                       val = queue_pop_head(temp);
+               }
+
+               len = enc_read_by_grp_resp(adl, rsp, sizeof(rsp));
+
+               att_data_list_free(adl);
+               queue_destroy(temp, destroy_pending_request);
+
+               break;
+       }
+       case ATT_OP_FIND_BY_TYPE_REQ: {
+               GSList *list = NULL;
+
+               val = queue_pop_head(device->pending_requests);
+               while (val) {
+                       struct att_range *range;
+                       const bt_uuid_t *type;
+
+                       /* Its find by type and value - filter by value here */
+                       if ((val->length != val->filter_vlen) ||
+                               memcmp(val->value, val->filter_value,
+                                                               val->length)) {
+
+                               destroy_pending_request(val);
+                               val = queue_pop_head(device->pending_requests);
+                               continue;
+                       }
+
+                       range = new0(struct att_range, 1);
+                       if (!range) {
+                               destroy_pending_request(val);
+                               error = ATT_ECODE_INSUFF_RESOURCES;
+                               break;
+                       }
+
+                       range->start = val->handle;
+                       range->end = range->start;
+
+                       /* Get proper end handle if its group type */
+                       type = gatt_db_get_attribute_type(gatt_db, val->handle);
+                       if (is_service(type))
+                               range->end = gatt_db_get_end_handle(gatt_db,
+                                                               val->handle);
+
+                       list = g_slist_append(list, range);
+
+                       destroy_pending_request(val);
+                       val = queue_pop_head(device->pending_requests);
+               }
+
+               if (list && !error)
+                       len = enc_find_by_type_resp(list, rsp, sizeof(rsp));
+               else
+                       error = ATT_ECODE_ATTR_NOT_FOUND;
+
+               g_slist_free_full(list, free);
+
+               break;
+       }
+       case ATT_OP_EXEC_WRITE_REQ:
+               val = queue_pop_head(device->pending_requests);
+               if (!val) {
+                       error = ATT_ECODE_ATTR_NOT_FOUND;
+                       break;
+               }
+
+               len = enc_exec_write_resp(rsp);
+               destroy_pending_request(val);
+               break;
+       case ATT_OP_WRITE_REQ:
+               val = queue_pop_head(device->pending_requests);
+               if (!val) {
+                       error = ATT_ECODE_ATTR_NOT_FOUND;
+                       break;
+               }
+
+               len = enc_write_resp(rsp);
+               destroy_pending_request(val);
+               break;
+       case ATT_OP_PREP_WRITE_REQ:
+               val = queue_pop_head(device->pending_requests);
+               if (!val) {
+                       error = ATT_ECODE_ATTR_NOT_FOUND;
+                       break;
+               }
+
+               len = enc_prep_write_resp(val->handle, val->offset, val->value,
+                                               val->length, rsp, sizeof(rsp));
+               destroy_pending_request(val);
+               break;
+       default:
+               break;
+       }
+
+done:
+       if (!len)
+               len = enc_error_resp(opcode, 0x0000, error, rsp,
+                                                       ATT_DEFAULT_LE_MTU);
+
+       g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
+
+       queue_remove_all(device->pending_requests, NULL, NULL,
+                                               destroy_pending_request);
+}
+
+struct request_processing_data {
+       uint8_t opcode;
+       struct gatt_device *device;
+};
+
+static bool match_pending_dev_request(const void *data, const void *user_data)
+{
+       const struct pending_request *pending_request = data;
+
+       return pending_request->length == READ_PENDING;
+}
+
+static bool match_dev_request_by_handle(const void *data, const void *user_data)
+{
+       const struct pending_request *handle_data = data;
+       uint16_t handle = PTR_TO_UINT(user_data);
+
+       return handle_data->handle == handle;
+}
+
+static void read_requested_attributes(void *data, void *user_data)
+{
+       struct pending_request *resp_data = data;
+       struct request_processing_data *process_data = user_data;
+       uint8_t *value;
+       int value_len;
+
+       if (!gatt_db_read(gatt_db, resp_data->handle,
+                                               resp_data->offset,
+                                               process_data->opcode,
+                                               &process_data->device->bdaddr,
+                                               &value, &value_len)) {
+               resp_data->length = READ_FAILED;
+               return;
+       }
+
+       /* We have value here already if no callback will be called */
+       if (value_len >= 0) {
+               resp_data->value = malloc0(value_len);
+               if (!resp_data->value) {
+                       /* If data cannot be copied, act like when read fails */
+                       resp_data->length = READ_FAILED;
+                       return;
+               }
+
+               memcpy(resp_data->value, value, value_len);
+               resp_data->length = value_len;
+       } else if (resp_data->length == READ_INIT) {
+               resp_data->length = READ_PENDING;
+       }
+}
+
+static void process_dev_pending_requests(struct gatt_device *device,
+                                                       uint8_t att_opcode)
+{
+       struct request_processing_data process_data;
+
+       process_data.device = device;
+       process_data.opcode = att_opcode;
+
+       /* Process pending requests and prepare response */
+       queue_foreach(device->pending_requests, read_requested_attributes,
+                                                               &process_data);
+
+       if (!queue_find(device->pending_requests,
+                                       match_pending_dev_request, NULL))
+               send_dev_pending_response(device, att_opcode);
+}
+
+static void send_gatt_response(uint8_t opcode, uint16_t handle,
+                                       uint16_t offset, uint8_t status,
+                                       uint16_t len, const uint8_t *data,
+                                       bdaddr_t *bdaddr)
+{
+       struct gatt_device *dev;
+       struct pending_request *entry;
+
+       dev = find_device_by_addr(bdaddr);
+       if (!dev) {
+               error("gatt: send_gatt_response, could not find dev");
+               goto done;
+       }
+
+       if (status)
+               goto done;
+
+       entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
+                                                       UINT_TO_PTR(handle));
+       if (!entry) {
+               DBG("No pending response found! Bogus android response?");
+               return;
+       }
+
+       entry->handle = handle;
+       entry->offset = offset;
+       entry->length = len;
+
+       if (!len)
+               goto done;
+
+       entry->value = malloc0(len);
+       if (!entry->value) {
+               /* send_dev_pending_request on empty queue sends error resp. */
+               queue_remove(dev->pending_requests, entry);
+               destroy_pending_request(entry);
+
+               goto done;
+       }
+
+       memcpy(entry->value, data, len);
+
+done:
+       if (!queue_find(dev->pending_requests, match_pending_dev_request, NULL))
+               send_dev_pending_response(dev, opcode);
+}
+
+static void set_trans_id(struct gatt_app *app, unsigned int id, int8_t opcode)
+{
+       app->trans_id.id = id;
+       app->trans_id.opcode = opcode;
+}
+
+static void clear_trans_id(struct gatt_app *app)
+{
+       app->trans_id.id = 0;
+       app->trans_id.opcode = 0;
+}
+
+static void read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode,
+                                       bdaddr_t *bdaddr, void *user_data)
+{
+       struct hal_ev_gatt_server_request_read ev;
+       struct gatt_app *app;
+       struct app_connection *conn;
+       int32_t id = PTR_TO_INT(user_data);
+       static int32_t trans_id = 1;
+
+       app = find_app_by_id(id);
+       if (!app) {
+               error("gatt: read_cb, cound not found app id");
+               goto failed;
+       }
+
+       conn = find_conn(bdaddr, app->id);
+       if (!conn) {
+               error("gatt: read_cb, cound not found connection");
+               goto failed;
+       }
+
+       memset(&ev, 0, sizeof(ev));
+
+       /* Store the request data, complete callback and transaction id */
+       set_trans_id(app, trans_id++, att_opcode);
+
+       bdaddr2android(bdaddr, ev.bdaddr);
+       ev.conn_id = conn->id;
+       ev.attr_handle = handle;
+       ev.offset = offset;
+       ev.is_long = att_opcode == ATT_OP_READ_BLOB_REQ;
+       ev.trans_id = app->trans_id.id;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                                       HAL_EV_GATT_SERVER_REQUEST_READ,
+                                       sizeof(ev), &ev);
+
+       return;
+
+failed:
+       send_gatt_response(att_opcode, handle, 0, ATT_ECODE_UNLIKELY, 0,
+                                                       NULL, bdaddr);
+}
+
+static void write_cb(uint16_t handle, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t att_opcode, bdaddr_t *bdaddr,
+                                       void *user_data)
+{
+       struct hal_ev_gatt_server_request_write ev;
+       struct gatt_app *app;
+       int32_t id = PTR_TO_INT(user_data);
+       static int32_t trans_id = 1;
+       struct app_connection *conn;
+
+       app = find_app_by_id(id);
+       if (!app) {
+               error("gatt: write_cb could not found app id");
+               goto failed;
+       }
+
+       conn = find_conn(bdaddr, app->id);
+       if (!conn) {
+               error("gatt: write_cb could not found connection");
+               goto failed;
+       }
+
+       /* Store the request data, complete callback and transaction id */
+       set_trans_id(app, trans_id++, att_opcode);
+
+       /* TODO figure it out */
+       if (att_opcode == ATT_OP_EXEC_WRITE_REQ)
+               goto failed;
+
+       memset(&ev, 0, sizeof(ev));
+
+       bdaddr2android(bdaddr, ev.bdaddr);
+       ev.attr_handle = handle;
+       ev.offset = offset;
+
+       ev.conn_id = conn->id;
+       ev.trans_id = app->trans_id.id;
+
+       ev.is_prep = att_opcode == ATT_OP_PREP_WRITE_REQ;
+       ev.need_rsp = att_opcode == ATT_OP_WRITE_REQ;
+
+       ev.length = len;
+       memcpy(&ev.value, value, len);
+
+       return;
+
+failed:
+       send_gatt_response(att_opcode, handle, 0, ATT_ECODE_UNLIKELY, 0, NULL,
+                                                               bdaddr);
+}
+
+static void handle_server_add_characteristic(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_add_characteristic *cmd = buf;
+       struct hal_ev_gatt_server_characteristic_added ev;
+       struct gatt_app *server;
+       bt_uuid_t uuid;
+       uint8_t status;
+       int32_t app_id = cmd->server_if;
+
+       DBG("");
+
+       memset(&ev, 0, sizeof(ev));
+
+       server = find_app_by_id(app_id);
+       if (!server) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       android2uuid(cmd->uuid, &uuid);
+
+       /*FIXME: Handle properties. Register callback if needed. */
+       ev.char_handle = gatt_db_add_characteristic(gatt_db,
+                                                       cmd->service_handle,
+                                                       &uuid, cmd->permissions,
+                                                       cmd->properties,
+                                                       read_cb, write_cb,
+                                                       INT_TO_PTR(app_id));
+       if (!ev.char_handle)
+               status = HAL_STATUS_FAILED;
+       else
+               status = HAL_STATUS_SUCCESS;
+
+failed:
+       ev.srvc_handle = cmd->service_handle;
+       ev.status = status;
+       ev.server_if = app_id;
+       ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+       memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_EV_GATT_SERVER_CHAR_ADDED, sizeof(ev), &ev);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, status);
+}
+
+static void handle_server_add_descriptor(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_add_descriptor *cmd = buf;
+       struct hal_ev_gatt_server_descriptor_added ev;
+       struct gatt_app *server;
+       bt_uuid_t uuid;
+       uint8_t status;
+       int32_t app_id = cmd->server_if;
+
+       DBG("");
+
+       memset(&ev, 0, sizeof(ev));
+
+       server = find_app_by_id(app_id);
+       if (!server) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       android2uuid(cmd->uuid, &uuid);
+
+       /*FIXME: Handle properties. Register callback if needed. */
+       ev.descr_handle = gatt_db_add_char_descriptor(gatt_db,
+                                                       cmd->service_handle,
+                                                       &uuid, cmd->permissions,
+                                                       read_cb, write_cb,
+                                                       INT_TO_PTR(app_id));
+       if (!ev.descr_handle)
+               status = HAL_STATUS_FAILED;
+       else
+               status = HAL_STATUS_SUCCESS;
+
+failed:
+       ev.server_if = app_id;
+       ev.srvc_handle = cmd->service_handle;
+       memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid));
+       ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED, sizeof(ev), &ev);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, status);
+}
+
+static void handle_server_start_service(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_start_service *cmd = buf;
+       struct hal_ev_gatt_server_service_started ev;
+       struct gatt_app *server;
+       uint8_t status;
+
+       DBG("");
+
+       memset(&ev, 0, sizeof(ev));
+
+       server = find_app_by_id(cmd->server_if);
+       if (!server) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       /* TODO: support BR/EDR (cmd->transport) */
+
+       if (!gatt_db_service_set_active(gatt_db, cmd->service_handle, true)) {
+               /* we ignore service now */
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+       ev.server_if = cmd->server_if;
+       ev.srvc_handle = cmd->service_handle;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_SERVER_SERVICE_STARTED, sizeof(ev), &ev);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_SERVER_START_SERVICE, status);
+}
+
+static void handle_server_stop_service(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_stop_service *cmd = buf;
+       struct hal_ev_gatt_server_service_stopped ev;
+       struct gatt_app *server;
+       uint8_t status;
+
+       DBG("");
+
+       memset(&ev, 0, sizeof(ev));
+
+       server = find_app_by_id(cmd->server_if);
+       if (!server) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       if (!gatt_db_service_set_active(gatt_db, cmd->service_handle, false))
+               status = HAL_STATUS_FAILED;
+       else
+               status = HAL_STATUS_SUCCESS;
+
+failed:
+       ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+       ev.server_if = cmd->server_if;
+       ev.srvc_handle = cmd->service_handle;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_SERVER_SERVICE_STOPPED, sizeof(ev), &ev);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_SERVER_STOP_SERVICE, status);
+}
+
+static void handle_server_delete_service(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_delete_service *cmd = buf;
+       struct hal_ev_gatt_server_service_deleted ev;
+       struct gatt_app *server;
+       uint8_t status;
+
+       DBG("");
+
+       memset(&ev, 0, sizeof(ev));
+
+       server = find_app_by_id(cmd->server_if);
+       if (!server) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       if (!gatt_db_remove_service(gatt_db, cmd->service_handle)) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE;
+       ev.srvc_handle = cmd->service_handle;
+       ev.server_if = cmd->server_if;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_EV_GATT_SERVER_SERVICE_DELETED, sizeof(ev), &ev);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_SERVER_DELETE_SERVICE, status);
+}
+
+static void handle_server_send_indication(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_send_indication *cmd = buf;
+       uint8_t pdu[ATT_DEFAULT_LE_MTU];
+       struct app_connection *conn;
+       uint8_t status;
+       uint16_t length;
+
+       DBG("");
+
+       conn = find_connection_by_id(cmd->conn_id);
+       if (!conn) {
+               error("gatt: Could not find connection");
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+
+       if (cmd->confirm)
+               /* TODO: Add data to track confirmation for this request */
+               length = enc_indication(cmd->attribute_handle,
+                                       (uint8_t *)cmd->value, cmd->len,
+                                       pdu, sizeof(pdu));
+       else
+               length = enc_notification(cmd->attribute_handle,
+                                               (uint8_t *)cmd->value, cmd->len,
+                                               pdu, sizeof(pdu));
+
+       g_attrib_send(conn->device->attrib, 0, pdu, length, NULL, NULL, NULL);
+
+       status = HAL_STATUS_SUCCESS;
+
+reply:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_SERVER_SEND_INDICATION, status);
+}
+
+static void handle_server_send_response(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_gatt_server_send_response *cmd = buf;
+       struct app_connection *conn;
+       struct gatt_app *app;
+       uint8_t status;
+
+       DBG("");
+
+       conn = find_connection_by_id(cmd->conn_id);
+       if (!conn) {
+               error("gatt: could not found connection");
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+
+       app = conn->app;
+
+       if ((unsigned int)cmd->trans_id != app->trans_id.id) {
+               error("gatt: transaction ID mismatch (%d!=%d)",
+                                       cmd->trans_id, app->trans_id.id);
+
+               status = HAL_STATUS_FAILED;
+               goto reply;
+       }
+
+       send_gatt_response(conn->app->trans_id.opcode, cmd->handle, cmd->offset,
+                                       cmd->status, cmd->len, cmd->data,
+                                       &conn->device->bdaddr);
+
+       /* Clean request data */
+       clear_trans_id(app);
+
+       status = HAL_STATUS_SUCCESS;
+
+reply:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
+                       HAL_OP_GATT_SERVER_SEND_RESPONSE, status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+       /* HAL_OP_GATT_CLIENT_REGISTER */
+       { handle_client_register, false,
+               sizeof(struct hal_cmd_gatt_client_register) },
+       /* HAL_OP_GATT_CLIENT_UNREGISTER */
+       { handle_client_unregister, false,
+               sizeof(struct hal_cmd_gatt_client_unregister) },
+       /* HAL_OP_GATT_CLIENT_SCAN */
+       { handle_client_scan, false,
+               sizeof(struct hal_cmd_gatt_client_scan) },
+       /* HAL_OP_GATT_CLIENT_CONNECT */
+       { handle_client_connect, false,
+               sizeof(struct hal_cmd_gatt_client_connect) },
+       /* HAL_OP_GATT_CLIENT_DISCONNECT */
+       { handle_client_disconnect, false,
+               sizeof(struct hal_cmd_gatt_client_disconnect) },
+       /* HAL_OP_GATT_CLIENT_LISTEN */
+       { handle_client_listen, false,
+               sizeof(struct hal_cmd_gatt_client_listen) },
+       /* HAL_OP_GATT_CLIENT_REFRESH */
+       { handle_client_refresh, false,
+               sizeof(struct hal_cmd_gatt_client_refresh) },
+       /* HAL_OP_GATT_CLIENT_SEARCH_SERVICE */
+       { handle_client_search_service, true,
+               sizeof(struct hal_cmd_gatt_client_search_service) },
+       /* HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE */
+       { handle_client_get_included_service, true,
+               sizeof(struct hal_cmd_gatt_client_get_included_service) },
+       /* HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC */
+       { handle_client_get_characteristic, true,
+               sizeof(struct hal_cmd_gatt_client_get_characteristic) },
+       /* HAL_OP_GATT_CLIENT_GET_DESCRIPTOR */
+       { handle_client_get_descriptor, true,
+               sizeof(struct hal_cmd_gatt_client_get_descriptor) },
+       /* HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC */
+       { handle_client_read_characteristic, false,
+               sizeof(struct hal_cmd_gatt_client_read_characteristic) },
+       /* HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC */
+       { handle_client_write_characteristic, true,
+               sizeof(struct hal_cmd_gatt_client_write_characteristic) },
+       /* HAL_OP_GATT_CLIENT_READ_DESCRIPTOR */
+       { handle_client_read_descriptor, false,
+               sizeof(struct hal_cmd_gatt_client_read_descriptor) },
+       /* HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR */
+       { handle_client_write_descriptor, true,
+               sizeof(struct hal_cmd_gatt_client_write_descriptor) },
+       /* HAL_OP_GATT_CLIENT_EXECUTE_WRITE */
+       { handle_client_execute_write, false,
+               sizeof(struct hal_cmd_gatt_client_execute_write)},
+       /* HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION */
+       { handle_client_register_for_notification, false,
+               sizeof(struct hal_cmd_gatt_client_register_for_notification) },
+       /* HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION */
+       { handle_client_deregister_for_notification, false,
+               sizeof(struct hal_cmd_gatt_client_deregister_for_notification) },
+       /* HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI */
+       { handle_client_read_remote_rssi, false,
+               sizeof(struct hal_cmd_gatt_client_read_remote_rssi) },
+       /* HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE */
+       { handle_client_get_device_type, false,
+               sizeof(struct hal_cmd_gatt_client_get_device_type) },
+       /* HAL_OP_GATT_CLIENT_SET_ADV_DATA */
+       { handle_client_set_adv_data, true,
+               sizeof(struct hal_cmd_gatt_client_set_adv_data) },
+       /* HAL_OP_GATT_CLIENT_TEST_COMMAND */
+       { handle_client_test_command, false,
+               sizeof(struct hal_cmd_gatt_client_test_command) },
+       /* HAL_OP_GATT_SERVER_REGISTER */
+       { handle_server_register, false,
+               sizeof(struct hal_cmd_gatt_server_register) },
+       /* HAL_OP_GATT_SERVER_UNREGISTER */
+       { handle_server_unregister, false,
+               sizeof(struct hal_cmd_gatt_server_unregister) },
+       /* HAL_OP_GATT_SERVER_CONNECT */
+       { handle_server_connect, false,
+               sizeof(struct hal_cmd_gatt_server_connect) },
+       /* HAL_OP_GATT_SERVER_DISCONNECT */
+       { handle_server_disconnect, false,
+               sizeof(struct hal_cmd_gatt_server_disconnect) },
+       /* HAL_OP_GATT_SERVER_ADD_SERVICE */
+       { handle_server_add_service, false,
+               sizeof(struct hal_cmd_gatt_server_add_service) },
+       /* HAL_OP_GATT_SERVER_ADD_INC_SERVICE */
+       { handle_server_add_included_service, false,
+               sizeof(struct hal_cmd_gatt_server_add_inc_service) },
+       /* HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC */
+       { handle_server_add_characteristic, false,
+               sizeof(struct hal_cmd_gatt_server_add_characteristic) },
+       /* HAL_OP_GATT_SERVER_ADD_DESCRIPTOR */
+       { handle_server_add_descriptor, false,
+               sizeof(struct hal_cmd_gatt_server_add_descriptor) },
+       /* HAL_OP_GATT_SERVER_START_SERVICE */
+       { handle_server_start_service, false,
+               sizeof(struct hal_cmd_gatt_server_start_service) },
+       /* HAL_OP_GATT_SERVER_STOP_SERVICE */
+       { handle_server_stop_service, false,
+               sizeof(struct hal_cmd_gatt_server_stop_service) },
+       /* HAL_OP_GATT_SERVER_DELETE_SERVICE */
+       { handle_server_delete_service, false,
+               sizeof(struct hal_cmd_gatt_server_delete_service) },
+       /* HAL_OP_GATT_SERVER_SEND_INDICATION */
+       { handle_server_send_indication, true,
+               sizeof(struct hal_cmd_gatt_server_send_indication) },
+       /* HAL_OP_GATT_SERVER_SEND_RESPONSE */
+       { handle_server_send_response, true,
+               sizeof(struct hal_cmd_gatt_server_send_response) },
+};
+
+static uint8_t read_by_group_type(const uint8_t *cmd, uint16_t cmd_len,
+                                               uint8_t *rsp, size_t rsp_size,
+                                               uint16_t *length,
+                                               struct gatt_device *device)
+{
+       uint16_t start, end;
+       int len;
+       bt_uuid_t uuid;
+       struct queue *q;
+
+       len = dec_read_by_grp_req(cmd, cmd_len, &start, &end, &uuid);
+       if (!len)
+               return ATT_ECODE_INVALID_PDU;
+
+       q = queue_new();
+       if (!q)
+               return ATT_ECODE_INSUFF_RESOURCES;
+
+       gatt_db_read_by_group_type(gatt_db, start, end, uuid, q);
+
+       if (queue_isempty(q)) {
+               queue_destroy(q, NULL);
+               return ATT_ECODE_ATTR_NOT_FOUND;
+       }
+
+       while (queue_peek_head(q)) {
+               uint16_t handle = PTR_TO_UINT(queue_pop_head(q));
+               uint8_t *value;
+               int value_len;
+               struct pending_request *entry;
+
+               entry = new0(struct pending_request, 1);
+               if (!entry) {
+                       queue_destroy(q, destroy_pending_request);
+                       return ATT_ECODE_UNLIKELY;
+               }
+
+               entry->handle = handle;
+
+               if (!gatt_db_read(gatt_db, handle, 0, ATT_OP_READ_BY_GROUP_REQ,
+                                       &device->bdaddr, &value, &value_len))
+                       break;
+
+               entry->value = malloc0(value_len);
+               if (!entry->value) {
+                       queue_destroy(q, destroy_pending_request);
+                       return ATT_ECODE_UNLIKELY;
+               }
+
+               memcpy(entry->value, value, value_len);
+               entry->length = value_len;
+
+               if (!queue_push_tail(device->pending_requests, entry)) {
+                       queue_remove_all(device->pending_requests, NULL, NULL,
+                                               destroy_pending_request);
+                       queue_destroy(q, NULL);
+                       return ATT_ECODE_UNLIKELY;
+               }
+       }
+
+       queue_destroy(q, NULL);
+
+       send_dev_pending_response(device, cmd[0]);
+
+       return 0;
+}
+
+static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len,
+                                               struct gatt_device *device)
+{
+       uint16_t start, end;
+       uint16_t len;
+       bt_uuid_t uuid;
+       struct queue *q;
+
+       DBG("");
+
+       len = dec_read_by_type_req(cmd, cmd_len, &start, &end, &uuid);
+       if (!len)
+               return ATT_ECODE_INVALID_PDU;
+
+       q = queue_new();
+       if (!q)
+               return ATT_ECODE_INSUFF_RESOURCES;
+
+       gatt_db_read_by_type(gatt_db, start, end, uuid, q);
+
+       if (queue_isempty(q)) {
+               queue_destroy(q, NULL);
+               return ATT_ECODE_ATTR_NOT_FOUND;
+       }
+
+       while (queue_peek_head(q)) {
+               struct pending_request *data;
+               uint16_t handle = PTR_TO_UINT(queue_pop_head(q));
+
+               data = new0(struct pending_request, 1);
+               if (!data) {
+                       queue_destroy(q, NULL);
+                       return ATT_ECODE_INSUFF_RESOURCES;
+               }
+
+               data->length = READ_INIT;
+               data->handle = handle;
+               queue_push_tail(device->pending_requests, data);
+       }
+
+       queue_destroy(q, NULL);
+
+       process_dev_pending_requests(device, ATT_OP_READ_BY_TYPE_REQ);
+
+       return 0;
+}
+
+static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
+                                                       struct gatt_device *dev)
+{
+       uint16_t handle;
+       uint16_t len;
+       uint16_t offset;
+       struct pending_request *data;
+
+       DBG("");
+
+       switch (cmd[0]) {
+       case ATT_OP_READ_BLOB_REQ:
+               len = dec_read_blob_req(cmd, cmd_len, &handle, &offset);
+               if (!len)
+                       return ATT_ECODE_INVALID_PDU;
+               break;
+       case ATT_OP_READ_REQ:
+               len = dec_read_req(cmd, cmd_len, &handle);
+               if (!len)
+                       return ATT_ECODE_INVALID_PDU;
+               offset = 0;
+               break;
+       default:
+               error("gatt: Unexpected read type 0x%02x", cmd[0]);
+               return ATT_ECODE_REQ_NOT_SUPP;
+       }
+
+       data = new0(struct pending_request, 1);
+       if (!data)
+               return ATT_ECODE_INSUFF_RESOURCES;
+
+       data->offset = offset;
+       data->handle = handle;
+       data->length = READ_INIT;
+       if (!queue_push_tail(dev->pending_requests, data)) {
+               free(data);
+               return ATT_ECODE_INSUFF_RESOURCES;
+       }
+
+       process_dev_pending_requests(dev, cmd[0]);
+
+       return 0;
+}
+
+static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len,
+                                       uint8_t *rsp, size_t rsp_size,
+                                       struct gatt_device *dev,
+                                       uint16_t *length)
+{
+       uint16_t mtu, imtu, omtu;
+       GIOChannel *io;
+       GError *gerr = NULL;
+       uint16_t len;
+
+       DBG("");
+
+       len = dec_mtu_req(cmd, cmd_len, &mtu);
+       if (!len)
+               return ATT_ECODE_INVALID_PDU;
+
+       if (mtu < ATT_DEFAULT_LE_MTU)
+               return ATT_ECODE_REQ_NOT_SUPP;
+
+       io = g_attrib_get_channel(dev->attrib);
+
+       bt_io_get(io, &gerr,
+                       BT_IO_OPT_IMTU, &imtu,
+                       BT_IO_OPT_OMTU, &omtu,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("bt_io_get: %s", gerr->message);
+               g_error_free(gerr);
+               return ATT_ECODE_UNLIKELY;
+       }
+
+       /* Limit OMTU to received value */
+       mtu = MIN(mtu, omtu);
+       g_attrib_set_mtu(dev->attrib, mtu);
+
+       /* Respond with our IMTU */
+       len = enc_mtu_resp(imtu, rsp, rsp_size);
+       if (!len)
+               return ATT_ECODE_UNLIKELY;
+
+       *length = len;
+
+       return 0;
+}
+
+static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len,
+                                               uint8_t *rsp, size_t rsp_size,
+                                               uint16_t *length)
+{
+       struct queue *q;
+       struct att_data_list *adl;
+       int iterator = 0;
+       uint16_t start, end;
+       uint16_t len;
+
+       DBG("");
+
+       len = dec_find_info_req(cmd, cmd_len, &start, &end);
+       if (!len)
+               return ATT_ECODE_INVALID_PDU;
+
+       q = queue_new();
+       if (!q)
+               return ATT_ECODE_UNLIKELY;
+
+       gatt_db_find_information(gatt_db, start, end, q);
+
+       if (queue_isempty(q)) {
+               queue_destroy(q, NULL);
+               return ATT_ECODE_ATTR_NOT_FOUND;
+       }
+
+       len = queue_length(q);
+       adl = att_data_list_alloc(len, 2 * sizeof(uint16_t));
+       if (!adl) {
+               queue_destroy(q, NULL);
+               return ATT_ECODE_INSUFF_RESOURCES;
+       }
+
+       while (queue_peek_head(q)) {
+               uint8_t *value;
+               const bt_uuid_t *type;
+               uint16_t handle = PTR_TO_UINT(queue_pop_head(q));
+
+               type = gatt_db_get_attribute_type(gatt_db, handle);
+               if (!type)
+                       break;
+
+               value = adl->data[iterator++];
+
+               put_le16(handle, value);
+               memcpy(&value[2], &type->value.u16, bt_uuid_len(type));
+
+       }
+
+       if (!adl) {
+               queue_destroy(q, NULL);
+               return ATT_ECODE_INSUFF_RESOURCES;
+       }
+
+       len = enc_find_info_resp(ATT_FIND_INFO_RESP_FMT_16BIT, adl, rsp,
+                                                               rsp_size);
+       if (!len)
+               return ATT_ECODE_UNLIKELY;
+
+       *length = len;
+       att_data_list_free(adl);
+       queue_destroy(q, free);
+
+       return 0;
+}
+
+static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
+                                               struct gatt_device *device)
+{
+       uint8_t search_value[ATT_DEFAULT_LE_MTU];
+       size_t search_vlen;
+       uint16_t start, end;
+       uint16_t handle;
+       struct queue *q;
+       bt_uuid_t uuid;
+       uint16_t len;
+
+       DBG("");
+
+       len = dec_find_by_type_req(cmd, cmd_len, &start, &end, &uuid,
+                                               search_value, &search_vlen);
+       if (!len)
+               return ATT_ECODE_INVALID_PDU;
+
+       q = queue_new();
+       if (!q)
+               return ATT_ECODE_UNLIKELY;
+
+       gatt_db_find_by_type(gatt_db, start, end, &uuid, q);
+
+       handle = PTR_TO_UINT(queue_pop_head(q));
+       while (handle) {
+               struct pending_request *data;
+
+               data = new0(struct pending_request, 1);
+               if (!data) {
+                       queue_destroy(q, NULL);
+                       return ATT_ECODE_INSUFF_RESOURCES;
+               }
+
+               data->filter_value = malloc0(search_vlen);
+               if (!data) {
+                       destroy_pending_request(data);
+                       queue_destroy(q, NULL);
+                       return ATT_ECODE_INSUFF_RESOURCES;
+               }
+
+               data->length = READ_INIT;
+               data->handle = handle;
+               data->filter_vlen = search_vlen;
+               memcpy(data->filter_value, search_value, search_vlen);
+
+               queue_push_tail(device->pending_requests, data);
+
+               handle = PTR_TO_UINT(queue_pop_head(q));
+       }
+
+       queue_destroy(q, NULL);
+
+       process_dev_pending_requests(device, ATT_OP_FIND_BY_TYPE_REQ);
+
+       return 0;
+}
+
+static uint8_t write_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
+                                               struct gatt_device *dev)
+{
+       uint8_t value[ATT_DEFAULT_LE_MTU];
+       uint16_t handle;
+       uint16_t len;
+       size_t vlen;
+
+       len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen);
+       if (!len)
+               return ATT_ECODE_INVALID_PDU;
+
+       if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0],
+                                                               &dev->bdaddr))
+               return ATT_ECODE_UNLIKELY;
+
+       return 0;
+}
+
+static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len,
+                                               struct gatt_device *dev)
+{
+       uint8_t value[ATT_DEFAULT_LE_MTU];
+       uint16_t handle;
+       uint16_t len;
+       size_t vlen;
+
+       len = dec_write_req(cmd, cmd_len, &handle, value, &vlen);
+       if (!len)
+               return ATT_ECODE_INVALID_PDU;
+
+       if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0],
+                                                               &dev->bdaddr))
+               return ATT_ECODE_UNLIKELY;
+
+       return 0;
+}
+
+static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len,
+                                               struct gatt_device *dev)
+{
+       uint8_t value[ATT_DEFAULT_LE_MTU];
+       uint16_t handle;
+       uint16_t offset;
+       uint16_t len;
+       size_t vlen;
+
+       len = dec_prep_write_req(cmd, cmd_len, &handle, &offset,
+                                               value, &vlen);
+       if (!len)
+               return ATT_ECODE_INVALID_PDU;
+
+       if (!gatt_db_write(gatt_db, handle, offset, value, vlen, cmd[0],
+                                                               &dev->bdaddr))
+               return ATT_ECODE_UNLIKELY;
+
+       return 0;
+}
+
+static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
+{
+       struct gatt_device *dev = user_data;
+       uint8_t opdu[ATT_DEFAULT_LE_MTU];
+       uint8_t status;
+       uint16_t length = 0;
+       size_t vlen;
+       uint8_t *value = g_attrib_get_buffer(dev->attrib, &vlen);
+
+       DBG("op 0x%02x", ipdu[0]);
+
+       if (len > vlen) {
+               error("gatt: Too much data on ATT socket %p", value);
+               status = ATT_ECODE_INVALID_PDU;
+               goto done;
+       }
+
+       switch (ipdu[0]) {
+       case ATT_OP_READ_BY_GROUP_REQ:
+               status = read_by_group_type(ipdu, len, opdu, sizeof(opdu),
+                                                               &length, dev);
+               break;
+       case ATT_OP_READ_BY_TYPE_REQ:
+               status = read_by_type(ipdu, len, dev);
+               break;
+       case ATT_OP_READ_REQ:
+       case ATT_OP_READ_BLOB_REQ:
+               status = read_request(ipdu, len, dev);
+               break;
+       case ATT_OP_MTU_REQ:
+               status = mtu_att_handle(ipdu, len, opdu, sizeof(opdu), dev,
+                                                               &length);
+               break;
+       case ATT_OP_FIND_INFO_REQ:
+               status = find_info_handle(ipdu, len, opdu, sizeof(opdu),
+                                                               &length);
+               break;
+       case ATT_OP_WRITE_REQ:
+               status = write_req_request(ipdu, len, dev);
+               if (!status)
+                       return;
+               break;
+       case ATT_OP_WRITE_CMD:
+               status = write_cmd_request(ipdu, len, dev);
+               if (!status)
+                       return;
+               break;
+       case ATT_OP_PREP_WRITE_REQ:
+               status = write_prep_request(ipdu, len, dev);
+               if (!status)
+                       return;
+               break;
+       case ATT_OP_FIND_BY_TYPE_REQ:
+               status = find_by_type_request(ipdu, len, dev);
+               break;
+       case ATT_OP_EXEC_WRITE_REQ:
+               /* TODO */
+       case ATT_OP_HANDLE_CNF:
+       case ATT_OP_HANDLE_IND:
+       case ATT_OP_HANDLE_NOTIFY:
+       case ATT_OP_READ_MULTI_REQ:
+       default:
+               DBG("Unsupported request 0x%02x", ipdu[0]);
+               status = ATT_ECODE_REQ_NOT_SUPP;
+               goto done;
+       }
+
+done:
+       if (status)
+               length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
+                                                       ATT_DEFAULT_LE_MTU);
+
+       if (length)
+               g_attrib_send(dev->attrib, 0, opdu, length, NULL, NULL, NULL);
+}
+
+static void create_listen_connections(void *data, void *user_data)
+{
+       struct gatt_device *dev = user_data;
+       int32_t id = PTR_TO_INT(data);
+       struct gatt_app *app;
+
+       app = find_app_by_id(id);
+       if (app)
+               create_connection(dev, app);
+}
+
+static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
+{
+       struct gatt_device *dev;
+       uint8_t dst_type;
+       bdaddr_t dst;
+       struct connect_data data;
+
+       DBG("");
+
+       if (gerr) {
+               error("gatt: %s", gerr->message);
+               g_error_free(gerr);
+               return;
+       }
+
+       bt_io_get(io, &gerr,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_DEST_TYPE, &dst_type,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("gatt: bt_io_get: %s", gerr->message);
+               g_error_free(gerr);
+               return;
+       }
+
+       /* TODO Handle collision */
+       dev = find_device_by_addr(&dst);
+       if (!dev) {
+               dev = create_device(&dst);
+               if (!dev) {
+                       error("gatt: Could not create device");
+                       return;
+               }
+
+               dev->bdaddr_type = dst_type;
+       } else {
+               if (dev->state != DEVICE_DISCONNECTED) {
+                       char addr[18];
+
+                       ba2str(&dst, addr);
+                       info("gatt: Rejecting incoming connection from %s",
+                                                                       addr);
+                       return;
+               }
+       }
+
+       dev->attrib = g_attrib_new(io);
+       if (!dev->attrib) {
+               error("gatt: unable to create new GAttrib instance");
+               destroy_device(dev);
+               return;
+       }
+       dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                                       disconnected_cb, dev);
+
+       queue_foreach(listen_apps, create_listen_connections, dev);
+
+       data.dev = dev;
+       data.status = GATT_SUCCESS;
+       device_set_state(dev, DEVICE_CONNECTED);
+
+       queue_foreach(app_connections, send_app_connect_notifications, &data);
+
+       dev->server_id = g_attrib_register(dev->attrib, GATTRIB_ALL_REQS,
+                                               GATTRIB_ALL_HANDLES,
+                                               att_handler, dev, NULL);
+       if (dev->server_id == 0)
+               error("gatt: Could not attach to server");
+}
+
+struct gap_srvc_handles {
+       uint16_t srvc;
+
+       /* Characteristics */
+       uint16_t dev_name;
+       uint16_t appear;
+       uint16_t priv;
+};
+
+static struct gap_srvc_handles gap_srvc_data;
+
+#define APPEARANCE_GENERIC_PHONE 0x0040
+#define PERIPHERAL_PRIVACY_DISABLE 0x00
+
+static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode,
+                                       bdaddr_t *bdaddr, void *user_data)
+{
+       struct pending_request *entry;
+       struct gatt_device *dev;
+
+       DBG("");
+
+       dev = find_device_by_addr(bdaddr);
+       if (!dev) {
+               error("gatt: Could not find device ?!");
+               return;
+       }
+
+       entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
+                                                       UINT_TO_PTR(handle));
+       if (!entry)
+               return;
+
+       if (handle == gap_srvc_data.dev_name) {
+               const char *name = bt_get_adapter_name();
+
+               entry->value = malloc0(strlen(name));
+               if (!entry->value) {
+                       queue_remove(dev->pending_requests, entry);
+                       free(entry);
+                       return;
+               }
+
+               entry->length = strlen(name);
+               memcpy(entry->value, bt_get_adapter_name(), entry->length);
+       } else if (handle == gap_srvc_data.appear) {
+               entry->value = malloc0(2);
+               if (!entry->value) {
+                       queue_remove(dev->pending_requests, entry);
+                       free(entry);
+                       return;
+               }
+
+               put_le16(APPEARANCE_GENERIC_PHONE, entry->value);
+               entry->length = sizeof(uint8_t) * 2;
+       } else if (handle == gap_srvc_data.priv) {
+               entry->value = malloc0(1);
+               if (!entry->value) {
+                       queue_remove(dev->pending_requests, entry);
+                       free(entry);
+                       return;
+               }
+
+               *entry->value = PERIPHERAL_PRIVACY_DISABLE;
+               entry->length = sizeof(uint8_t);
+       }
+
+       entry->offset = offset;
+}
+
+static void register_gap_service(void)
+{
+       bt_uuid_t uuid;
+
+       /* GAP UUID */
+       bt_uuid16_create(&uuid, 0x1800);
+       gap_srvc_data.srvc = gatt_db_add_service(gatt_db, &uuid, true, 7);
+
+       /* Device name characteristic */
+       bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+       gap_srvc_data.dev_name =
+                       gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
+                                                       &uuid, 0,
+                                                       GATT_CHR_PROP_READ,
+                                                       gap_read_cb, NULL,
+                                                       NULL);
+
+       /* Appearance */
+       bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+       gap_srvc_data.appear =
+                       gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
+                                                       &uuid, 0,
+                                                       GATT_CHR_PROP_READ,
+                                                       gap_read_cb, NULL,
+                                                       NULL);
+
+       /* Pripheral privacy flag */
+       bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG);
+       gap_srvc_data.priv =
+                       gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc,
+                                                       &uuid, 0,
+                                                       GATT_CHR_PROP_READ,
+                                                       gap_read_cb, NULL,
+                                                       NULL);
+
+       gatt_db_service_set_active(gatt_db, gap_srvc_data.srvc , true);
+}
+
+/* TODO: Get those data from device possible via androig/bluetooth.c */
+static struct device_info {
+       const char *manufacturer_name;
+       const char *system_id;
+       const char *model_number;
+       const char *serial_number;
+       const char *firmware_rev;
+       const char *hardware_rev;
+       const char *software_rev;
+} device_info = {
+       .manufacturer_name =    "BlueZ",
+       .system_id =            "BlueZ for Android",
+       .model_number =         "model no",
+       .serial_number =        "serial no",
+       .firmware_rev =         "firmware rev",
+       .hardware_rev =         "hardware rev",
+       .software_rev =         "software rev",
+};
+
+static void device_info_read_cb(uint16_t handle, uint16_t offset,
+                                       uint8_t att_opcode, bdaddr_t *bdaddr,
+                                       void *user_data)
+{
+       struct pending_request *entry;
+       struct gatt_device *dev;
+       char *buf = user_data;
+
+       dev = find_device_by_addr(bdaddr);
+       if (!dev) {
+               error("gatt: Could not find device ?!");
+               return;
+       }
+
+       entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
+                                                       UINT_TO_PTR(handle));
+       if (!entry)
+               return;
+
+       entry->value = malloc0(strlen(buf));
+       if (!entry->value) {
+               queue_remove(dev->pending_requests, entry);
+               free(entry);
+               return;
+       }
+
+       entry->length = strlen(buf);
+       memcpy(entry->value, buf, entry->length);
+       entry->offset = offset;
+}
+
+static void register_device_info_service(void)
+{
+       bt_uuid_t uuid;
+       uint16_t srvc_handle;
+
+       DBG("");
+
+       /* Device Information Service */
+       bt_uuid16_create(&uuid, 0x180a);
+       srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 15);
+
+       /* User data are not const hence (void *) cast is used */
+       bt_uuid16_create(&uuid, GATT_CHARAC_SYSTEM_ID);
+       gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+                                       GATT_CHR_PROP_READ,
+                                       device_info_read_cb, NULL,
+                                       (void *) device_info.system_id);
+
+       bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING);
+       gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+                                       GATT_CHR_PROP_READ,
+                                       device_info_read_cb, NULL,
+                                       (void *) device_info.model_number);
+
+       bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING);
+       gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+                                       GATT_CHR_PROP_READ,
+                                       device_info_read_cb, NULL,
+                                       (void *) device_info.serial_number);
+
+       bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING);
+       gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+                                       GATT_CHR_PROP_READ,
+                                       device_info_read_cb, NULL,
+                                       (void *) device_info.firmware_rev);
+
+       bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING);
+       gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+                                       GATT_CHR_PROP_READ,
+                                       device_info_read_cb, NULL,
+                                       (void *) device_info.hardware_rev);
+
+       bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING);
+       gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+                                       GATT_CHR_PROP_READ,
+                                       device_info_read_cb, NULL,
+                                       (void *) device_info.software_rev);
+
+       bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING);
+       gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+                                       GATT_CHR_PROP_READ,
+                                       device_info_read_cb, NULL,
+                                       (void *) device_info.manufacturer_name);
+
+       gatt_db_service_set_active(gatt_db, srvc_handle, true);
+}
+
+static void gatt_srvc_change_register_cb(uint16_t handle, uint16_t offset,
+                                               const uint8_t *val, size_t len,
+                                               uint8_t att_opcode,
+                                               bdaddr_t *bdaddr,
+                                               void *user_data)
+{
+       uint8_t pdu[ATT_DEFAULT_LE_MTU];
+       struct gatt_device *dev;
+       uint16_t length;
+
+       dev = find_device_by_addr(bdaddr);
+       if (!dev) {
+               error("gatt: Could not find device ?!");
+               return;
+       }
+
+       /* TODO handle CCC */
+
+       /* Set services changed notification flag */
+       dev->notify_services_changed = !!(*val);
+
+       length = enc_write_resp(pdu);
+
+       g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL);
+}
+
+static void register_gatt_service(void)
+{
+       bt_uuid_t uuid;
+       uint16_t srvc_handle;
+
+       DBG("");
+
+       bt_uuid16_create(&uuid, 0x1801);
+       srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 4);
+
+       bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+       gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, 0,
+                                       GATT_CHR_PROP_INDICATE, NULL, NULL,
+                                       NULL);
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       gatt_db_add_char_descriptor(gatt_db, srvc_handle, &uuid, 0, NULL,
+                                       gatt_srvc_change_register_cb, NULL);
+
+       gatt_db_service_set_active(gatt_db, srvc_handle, true);
+}
+
+static bool start_listening_io(void)
+{
+       GError *gerr = NULL;
+
+       /* For now only listen on BLE */
+       listening_io = bt_io_listen(connect_event, NULL,
+                                       &listening_io, NULL, &gerr,
+                                       BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+                                       BT_IO_OPT_CID, ATT_CID,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+                                       BT_IO_OPT_INVALID);
+       if (!listening_io) {
+               error("gatt: Failed to start listening IO (%s)", gerr->message);
+               g_error_free(gerr);
+               return false;
+       }
+
+       return true;
+}
+
+bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
+{
+       DBG("");
+
+       if (!start_listening_io())
+               return false;
+
+       gatt_devices = queue_new();
+       gatt_apps = queue_new();
+       app_connections = queue_new();
+       listen_apps = queue_new();
+       gatt_db = gatt_db_new();
+
+       if (!gatt_devices || !gatt_apps || !listen_apps ||
+                                               !app_connections || !gatt_db) {
+               error("gatt: Failed to allocate memory for queues");
+
+               queue_destroy(gatt_apps, NULL);
+               gatt_apps = NULL;
+
+               queue_destroy(gatt_devices, NULL);
+               gatt_devices = NULL;
+
+               queue_destroy(app_connections, NULL);
+               app_connections = NULL;
+
+               queue_destroy(listen_apps, NULL);
+               listen_apps = NULL;
+
+               gatt_db_destroy(gatt_db);
+               gatt_db = NULL;
+
+               g_io_channel_unref(listening_io);
+               listening_io = NULL;
+
+               return false;
+       }
+
+       bacpy(&adapter_addr, addr);
+
+       hal_ipc = ipc;
+
+       ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
+                                               G_N_ELEMENTS(cmd_handlers));
+
+       register_gap_service();
+       register_device_info_service();
+       register_gatt_service();
+
+       return true;
+}
+
+void bt_gatt_unregister(void)
+{
+       DBG("");
+
+       ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
+       hal_ipc = NULL;
+
+       queue_destroy(gatt_apps, destroy_gatt_app);
+       gatt_apps = NULL;
+
+       queue_destroy(app_connections, destroy_connection);
+       app_connections = NULL;
+
+       queue_destroy(gatt_devices, destroy_device);
+       gatt_devices = NULL;
+
+       queue_destroy(listen_apps, NULL);
+       listen_apps = NULL;
+
+       gatt_db_destroy(gatt_db);
+       gatt_db = NULL;
+
+       g_io_channel_unref(listening_io);
+       listening_io = NULL;
+}
diff --git a/android/gatt.h b/android/gatt.h
new file mode 100644 (file)
index 0000000..d4392d9
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr);
+void bt_gatt_unregister(void);
index c898995..68888b2 100644 (file)
@@ -48,8 +48,10 @@ static void handle_audio_state(void *buf, uint16_t len)
                cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr));
 }
 
-/* handlers will be called from notification thread context,
- * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
 static const struct hal_ipc_handler ev_handlers[] = {
        {       /* HAL_EV_A2DP_CONN_STATE */
                .handler = handle_conn_state,
@@ -109,6 +111,7 @@ static bt_status_t init(btav_callbacks_t *callbacks)
                                sizeof(ev_handlers)/sizeof(ev_handlers[0]));
 
        cmd.service_id = HAL_SERVICE_ID_A2DP;
+       cmd.mode = HAL_MODE_DEFAULT;
 
        ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
                                        sizeof(cmd), &cmd, 0, NULL, NULL);
diff --git a/android/hal-audio.c b/android/hal-audio.c
new file mode 100644 (file)
index 0000000..1c889cc
--- /dev/null
@@ -0,0 +1,1898 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+
+#include <sbc/sbc.h>
+
+#include "audio-msg.h"
+#include "ipc-common.h"
+#include "hal-log.h"
+#include "hal-msg.h"
+#include "../profiles/audio/a2dp-codecs.h"
+#include "../src/shared/util.h"
+
+#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25
+
+#define FIXED_BUFFER_SIZE (20 * 512)
+
+#define MAX_FRAMES_IN_PAYLOAD 15
+
+#define MAX_DELAY      100000 /* 100ms */
+
+#define SBC_QUALITY_MIN_BITPOOL        33
+#define SBC_QUALITY_STEP       5
+
+static const uint8_t a2dp_src_uuid[] = {
+               0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00,
+               0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
+
+static int listen_sk = -1;
+static int audio_sk = -1;
+
+static pthread_t ipc_th = 0;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+#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
+
+struct media_packet {
+       struct rtp_header hdr;
+       struct rtp_payload payload;
+       uint8_t data[0];
+};
+
+struct audio_input_config {
+       uint32_t rate;
+       uint32_t channels;
+       audio_format_t format;
+};
+
+struct sbc_data {
+       a2dp_sbc_t sbc;
+
+       sbc_t enc;
+
+       uint16_t payload_len;
+
+       size_t in_frame_len;
+       size_t in_buf_size;
+
+       size_t out_frame_len;
+
+       unsigned frame_duration;
+       unsigned frames_per_packet;
+};
+
+static void timespec_add(struct timespec *base, uint64_t time_us,
+                                                       struct timespec *res)
+{
+       res->tv_sec = base->tv_sec + time_us / 1000000;
+       res->tv_nsec = base->tv_nsec + (time_us % 1000000) * 1000;
+
+       if (res->tv_nsec >= 1000000000) {
+               res->tv_sec++;
+               res->tv_nsec -= 1000000000;
+       }
+}
+
+static void timespec_diff(struct timespec *a, struct timespec *b,
+                                                       struct timespec *res)
+{
+       res->tv_sec = a->tv_sec - b->tv_sec;
+       res->tv_nsec = a->tv_nsec - b->tv_nsec;
+
+       if (res->tv_nsec < 0) {
+               res->tv_sec--;
+               res->tv_nsec += 1000000000; /* 1sec */
+       }
+}
+
+static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b)
+{
+       struct timespec res;
+
+       timespec_diff(a, b, &res);
+
+       return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll;
+}
+
+#if defined(ANDROID)
+/*
+ * Bionic does not have clock_nanosleep() prototype in time.h even though
+ * it provides its implementation.
+ */
+extern int clock_nanosleep(clockid_t clock_id, int flags,
+                                       const struct timespec *request,
+                                       struct timespec *remain);
+#endif
+
+static int sbc_get_presets(struct audio_preset *preset, size_t *len);
+static int sbc_codec_init(struct audio_preset *preset, uint16_t mtu,
+                                                       void **codec_data);
+static int sbc_cleanup(void *codec_data);
+static int sbc_get_config(void *codec_data, struct audio_input_config *config);
+static size_t sbc_get_buffer_size(void *codec_data);
+static size_t sbc_get_mediapacket_duration(void *codec_data);
+static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+                                       size_t len, struct media_packet *mp,
+                                       size_t mp_data_len, size_t *written);
+static bool sbc_update_qos(void *codec_data, uint8_t op);
+
+#define QOS_POLICY_DEFAULT     0x00
+#define QOS_POLICY_DECREASE    0x01
+
+struct audio_codec {
+       uint8_t type;
+
+       int (*get_presets) (struct audio_preset *preset, size_t *len);
+
+       int (*init) (struct audio_preset *preset, uint16_t mtu,
+                               void **codec_data);
+       int (*cleanup) (void *codec_data);
+       int (*get_config) (void *codec_data,
+                                       struct audio_input_config *config);
+       size_t (*get_buffer_size) (void *codec_data);
+       size_t (*get_mediapacket_duration) (void *codec_data);
+       ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer,
+                                       size_t len, struct media_packet *mp,
+                                       size_t mp_data_len, size_t *written);
+       bool (*update_qos) (void *codec_data, uint8_t op);
+};
+
+static const struct audio_codec audio_codecs[] = {
+       {
+               .type = A2DP_CODEC_SBC,
+
+               .get_presets = sbc_get_presets,
+
+               .init = sbc_codec_init,
+               .cleanup = sbc_cleanup,
+               .get_config = sbc_get_config,
+               .get_buffer_size = sbc_get_buffer_size,
+               .get_mediapacket_duration = sbc_get_mediapacket_duration,
+               .encode_mediapacket = sbc_encode_mediapacket,
+               .update_qos = sbc_update_qos,
+       }
+};
+
+#define NUM_CODECS (sizeof(audio_codecs) / sizeof(audio_codecs[0]))
+
+#define MAX_AUDIO_ENDPOINTS NUM_CODECS
+
+struct audio_endpoint {
+       uint8_t id;
+       const struct audio_codec *codec;
+       void *codec_data;
+       int fd;
+
+       struct media_packet *mp;
+       size_t mp_data_len;
+
+       uint16_t seq;
+       uint32_t samples;
+       struct timespec start;
+
+       bool resync;
+};
+
+static struct audio_endpoint audio_endpoints[MAX_AUDIO_ENDPOINTS];
+
+enum a2dp_state_t {
+       AUDIO_A2DP_STATE_NONE,
+       AUDIO_A2DP_STATE_STANDBY,
+       AUDIO_A2DP_STATE_SUSPENDED,
+       AUDIO_A2DP_STATE_STARTED
+};
+
+struct a2dp_stream_out {
+       struct audio_stream_out stream;
+
+       struct audio_endpoint *ep;
+       enum a2dp_state_t audio_state;
+       struct audio_input_config cfg;
+
+       uint8_t *downmix_buf;
+};
+
+struct a2dp_audio_dev {
+       struct audio_hw_device dev;
+       struct a2dp_stream_out *out;
+};
+
+static const a2dp_sbc_t sbc_presets[] = {
+       {
+               .frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000,
+               .channel_mode = SBC_CHANNEL_MODE_MONO |
+                               SBC_CHANNEL_MODE_DUAL_CHANNEL |
+                               SBC_CHANNEL_MODE_STEREO |
+                               SBC_CHANNEL_MODE_JOINT_STEREO,
+               .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8,
+               .allocation_method = SBC_ALLOCATION_SNR |
+                                       SBC_ALLOCATION_LOUDNESS,
+               .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
+                               SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16,
+               .min_bitpool = MIN_BITPOOL,
+               .max_bitpool = MAX_BITPOOL
+       },
+       {
+               .frequency = SBC_SAMPLING_FREQ_44100,
+               .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+               .subbands = SBC_SUBBANDS_8,
+               .allocation_method = SBC_ALLOCATION_LOUDNESS,
+               .block_length = SBC_BLOCK_LENGTH_16,
+               .min_bitpool = MIN_BITPOOL,
+               .max_bitpool = MAX_BITPOOL
+       },
+       {
+               .frequency = SBC_SAMPLING_FREQ_48000,
+               .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO,
+               .subbands = SBC_SUBBANDS_8,
+               .allocation_method = SBC_ALLOCATION_LOUDNESS,
+               .block_length = SBC_BLOCK_LENGTH_16,
+               .min_bitpool = MIN_BITPOOL,
+               .max_bitpool = MAX_BITPOOL
+       },
+};
+
+static int sbc_get_presets(struct audio_preset *preset, size_t *len)
+{
+       int i;
+       int count;
+       size_t new_len = 0;
+       uint8_t *ptr = (uint8_t *) preset;
+       size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t);
+
+       count = sizeof(sbc_presets) / sizeof(sbc_presets[0]);
+
+       for (i = 0; i < count; i++) {
+               preset = (struct audio_preset *) ptr;
+
+               if (new_len + preset_size > *len)
+                       break;
+
+               preset->len = sizeof(a2dp_sbc_t);
+               memcpy(preset->data, &sbc_presets[i], preset->len);
+
+               new_len += preset_size;
+               ptr += preset_size;
+       }
+
+       *len = new_len;
+
+       return i;
+}
+
+static int sbc_freq2int(uint8_t freq)
+{
+       switch (freq) {
+       case SBC_SAMPLING_FREQ_16000:
+               return 16000;
+       case SBC_SAMPLING_FREQ_32000:
+               return 32000;
+       case SBC_SAMPLING_FREQ_44100:
+               return 44100;
+       case SBC_SAMPLING_FREQ_48000:
+               return 48000;
+       default:
+               return 0;
+       }
+}
+
+static const char *sbc_mode2str(uint8_t mode)
+{
+       switch (mode) {
+       case SBC_CHANNEL_MODE_MONO:
+               return "Mono";
+       case SBC_CHANNEL_MODE_DUAL_CHANNEL:
+               return "DualChannel";
+       case SBC_CHANNEL_MODE_STEREO:
+               return "Stereo";
+       case SBC_CHANNEL_MODE_JOINT_STEREO:
+               return "JointStereo";
+       default:
+               return "(unknown)";
+       }
+}
+
+static int sbc_blocks2int(uint8_t blocks)
+{
+       switch (blocks) {
+       case SBC_BLOCK_LENGTH_4:
+               return 4;
+       case SBC_BLOCK_LENGTH_8:
+               return 8;
+       case SBC_BLOCK_LENGTH_12:
+               return 12;
+       case SBC_BLOCK_LENGTH_16:
+               return 16;
+       default:
+               return 0;
+       }
+}
+
+static int sbc_subbands2int(uint8_t subbands)
+{
+       switch (subbands) {
+       case SBC_SUBBANDS_4:
+               return 4;
+       case SBC_SUBBANDS_8:
+               return 8;
+       default:
+               return 0;
+       }
+}
+
+static const char *sbc_allocation2str(uint8_t allocation)
+{
+       switch (allocation) {
+       case SBC_ALLOCATION_SNR:
+               return "SNR";
+       case SBC_ALLOCATION_LOUDNESS:
+               return "Loudness";
+       default:
+               return "(unknown)";
+       }
+}
+
+static void sbc_init_encoder(struct sbc_data *sbc_data)
+{
+       a2dp_sbc_t *in = &sbc_data->sbc;
+       sbc_t *out = &sbc_data->enc;
+
+       sbc_init_a2dp(out, 0L, in, sizeof(*in));
+
+       out->endian = SBC_LE;
+       out->bitpool = in->max_bitpool;
+
+       DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d "
+                       "allocation=%s bitpool=%d-%d",
+                       sbc_freq2int(in->frequency),
+                       sbc_mode2str(in->channel_mode),
+                       sbc_blocks2int(in->block_length),
+                       sbc_subbands2int(in->subbands),
+                       sbc_allocation2str(in->allocation_method),
+                       in->min_bitpool, in->max_bitpool);
+}
+
+static void sbc_codec_calculate(struct sbc_data *sbc_data)
+{
+       size_t in_frame_len;
+       size_t out_frame_len;
+       size_t num_frames;
+
+       in_frame_len = sbc_get_codesize(&sbc_data->enc);
+       out_frame_len = sbc_get_frame_length(&sbc_data->enc);
+       num_frames = sbc_data->payload_len / out_frame_len;
+
+       sbc_data->in_frame_len = in_frame_len;
+       sbc_data->in_buf_size = num_frames * in_frame_len;
+
+       sbc_data->out_frame_len = out_frame_len;
+
+       sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc);
+       sbc_data->frames_per_packet = num_frames;
+
+       DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu",
+                               in_frame_len, out_frame_len, num_frames);
+}
+
+static int sbc_codec_init(struct audio_preset *preset, uint16_t payload_len,
+                                                       void **codec_data)
+{
+       struct sbc_data *sbc_data;
+
+       if (preset->len != sizeof(a2dp_sbc_t)) {
+               error("SBC: preset size mismatch");
+               return AUDIO_STATUS_FAILED;
+       }
+
+       sbc_data = calloc(sizeof(struct sbc_data), 1);
+       if (!sbc_data)
+               return AUDIO_STATUS_FAILED;
+
+       memcpy(&sbc_data->sbc, preset->data, preset->len);
+
+       sbc_init_encoder(sbc_data);
+
+       sbc_data->payload_len = payload_len;
+
+       sbc_codec_calculate(sbc_data);
+
+       *codec_data = sbc_data;
+
+       return AUDIO_STATUS_SUCCESS;
+}
+
+static int sbc_cleanup(void *codec_data)
+{
+       struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+       sbc_finish(&sbc_data->enc);
+       free(codec_data);
+
+       return AUDIO_STATUS_SUCCESS;
+}
+
+static int sbc_get_config(void *codec_data, struct audio_input_config *config)
+{
+       struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+       switch (sbc_data->sbc.frequency) {
+       case SBC_SAMPLING_FREQ_16000:
+               config->rate = 16000;
+               break;
+       case SBC_SAMPLING_FREQ_32000:
+               config->rate = 32000;
+               break;
+       case SBC_SAMPLING_FREQ_44100:
+               config->rate = 44100;
+               break;
+       case SBC_SAMPLING_FREQ_48000:
+               config->rate = 48000;
+               break;
+       default:
+               return AUDIO_STATUS_FAILED;
+       }
+       config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ?
+                               AUDIO_CHANNEL_OUT_MONO :
+                               AUDIO_CHANNEL_OUT_STEREO;
+       config->format = AUDIO_FORMAT_PCM_16_BIT;
+
+       return AUDIO_STATUS_SUCCESS;
+}
+
+static size_t sbc_get_buffer_size(void *codec_data)
+{
+       struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+       return sbc_data->in_buf_size;
+}
+
+static size_t sbc_get_mediapacket_duration(void *codec_data)
+{
+       struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+
+       return sbc_data->frame_duration * sbc_data->frames_per_packet;
+}
+
+static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer,
+                                       size_t len, struct media_packet *mp,
+                                       size_t mp_data_len, size_t *written)
+{
+       struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+       size_t consumed = 0;
+       size_t encoded = 0;
+       uint8_t frame_count = 0;
+
+       while (len - consumed >= sbc_data->in_frame_len &&
+                       mp_data_len - encoded >= sbc_data->out_frame_len &&
+                       frame_count < MAX_FRAMES_IN_PAYLOAD) {
+               ssize_t read;
+               ssize_t written = 0;
+
+               read = sbc_encode(&sbc_data->enc, buffer + consumed,
+                               sbc_data->in_frame_len, mp->data + encoded,
+                               mp_data_len - encoded, &written);
+
+               if (read < 0) {
+                       error("SBC: failed to encode block at frame %d (%zd)",
+                                                       frame_count, read);
+                       break;
+               }
+
+               frame_count++;
+               consumed += read;
+               encoded += written;
+       }
+
+       *written = encoded;
+       mp->payload.frame_count = frame_count;
+
+       return consumed;
+}
+
+static bool sbc_update_qos(void *codec_data, uint8_t op)
+{
+       struct sbc_data *sbc_data = (struct sbc_data *) codec_data;
+       uint8_t curr_bitpool = sbc_data->enc.bitpool;
+       uint8_t new_bitpool = curr_bitpool;
+
+       switch (op) {
+       case QOS_POLICY_DEFAULT:
+               new_bitpool = sbc_data->sbc.max_bitpool;
+               break;
+
+       case QOS_POLICY_DECREASE:
+               if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) {
+                       new_bitpool = curr_bitpool - SBC_QUALITY_STEP;
+                       if (new_bitpool < SBC_QUALITY_MIN_BITPOOL)
+                               new_bitpool = SBC_QUALITY_MIN_BITPOOL;
+               }
+               break;
+       }
+
+       if (new_bitpool == curr_bitpool)
+               return false;
+
+       sbc_data->enc.bitpool = new_bitpool;
+
+       sbc_codec_calculate(sbc_data);
+
+       info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool);
+
+       return true;
+}
+
+static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
+                       void *param, size_t *rsp_len, void *rsp, int *fd)
+{
+       ssize_t ret;
+       struct msghdr msg;
+       struct iovec iv[2];
+       struct ipc_hdr cmd;
+       char cmsgbuf[CMSG_SPACE(sizeof(int))];
+       struct ipc_status s;
+       size_t s_len = sizeof(s);
+
+       pthread_mutex_lock(&sk_mutex);
+
+       if (audio_sk < 0) {
+               error("audio: Invalid cmd socket passed to audio_ipc_cmd");
+               goto failed;
+       }
+
+       if (!rsp || !rsp_len) {
+               memset(&s, 0, s_len);
+               rsp_len = &s_len;
+               rsp = &s;
+       }
+
+       memset(&msg, 0, sizeof(msg));
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.service_id = service_id;
+       cmd.opcode = opcode;
+       cmd.len = len;
+
+       iv[0].iov_base = &cmd;
+       iv[0].iov_len = sizeof(cmd);
+
+       iv[1].iov_base = param;
+       iv[1].iov_len = len;
+
+       msg.msg_iov = iv;
+       msg.msg_iovlen = 2;
+
+       ret = sendmsg(audio_sk, &msg, 0);
+       if (ret < 0) {
+               error("audio: Sending command failed:%s", strerror(errno));
+               goto failed;
+       }
+
+       /* socket was shutdown */
+       if (ret == 0) {
+               error("audio: Command socket closed");
+               goto failed;
+       }
+
+       memset(&msg, 0, sizeof(msg));
+       memset(&cmd, 0, sizeof(cmd));
+
+       iv[0].iov_base = &cmd;
+       iv[0].iov_len = sizeof(cmd);
+
+       iv[1].iov_base = rsp;
+       iv[1].iov_len = *rsp_len;
+
+       msg.msg_iov = iv;
+       msg.msg_iovlen = 2;
+
+       if (fd) {
+               memset(cmsgbuf, 0, sizeof(cmsgbuf));
+               msg.msg_control = cmsgbuf;
+               msg.msg_controllen = sizeof(cmsgbuf);
+       }
+
+       ret = recvmsg(audio_sk, &msg, 0);
+       if (ret < 0) {
+               error("audio: Receiving command response failed:%s",
+                                                       strerror(errno));
+               goto failed;
+       }
+
+       if (ret < (ssize_t) sizeof(cmd)) {
+               error("audio: Too small response received(%zd bytes)", ret);
+               goto failed;
+       }
+
+       if (cmd.service_id != service_id) {
+               error("audio: Invalid service id (%u vs %u)", cmd.service_id,
+                                                               service_id);
+               goto failed;
+       }
+
+       if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+               error("audio: Malformed response received(%zd bytes)", ret);
+               goto failed;
+       }
+
+       if (cmd.opcode != opcode && cmd.opcode != AUDIO_OP_STATUS) {
+               error("audio: Invalid opcode received (%u vs %u)",
+                                               cmd.opcode, opcode);
+               goto failed;
+       }
+
+       if (cmd.opcode == AUDIO_OP_STATUS) {
+               struct ipc_status *s = rsp;
+
+               if (sizeof(*s) != cmd.len) {
+                       error("audio: Invalid status length");
+                       goto failed;
+               }
+
+               if (s->code == AUDIO_STATUS_SUCCESS) {
+                       error("audio: Invalid success status response");
+                       goto failed;
+               }
+
+               pthread_mutex_unlock(&sk_mutex);
+
+               return s->code;
+       }
+
+       pthread_mutex_unlock(&sk_mutex);
+
+       /* Receive auxiliary data in msg */
+       if (fd) {
+               struct cmsghdr *cmsg;
+
+               *fd = -1;
+
+               for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+                                       cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+                       if (cmsg->cmsg_level == SOL_SOCKET
+                                       && cmsg->cmsg_type == SCM_RIGHTS) {
+                               memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+                               break;
+                       }
+               }
+
+               if (*fd < 0)
+                       goto failed;
+       }
+
+       if (rsp_len)
+               *rsp_len = cmd.len;
+
+       return AUDIO_STATUS_SUCCESS;
+
+failed:
+       /* Some serious issue happen on IPC - recover */
+       shutdown(audio_sk, SHUT_RDWR);
+       pthread_mutex_unlock(&sk_mutex);
+
+       return AUDIO_STATUS_FAILED;
+}
+
+static int ipc_open_cmd(const struct audio_codec *codec)
+{
+       uint8_t buf[BLUEZ_AUDIO_MTU];
+       struct audio_cmd_open *cmd = (struct audio_cmd_open *) buf;
+       struct audio_rsp_open rsp;
+       size_t cmd_len = sizeof(buf) - sizeof(*cmd);
+       size_t rsp_len = sizeof(rsp);
+       int result;
+
+       DBG("");
+
+       memcpy(cmd->uuid, a2dp_src_uuid, sizeof(a2dp_src_uuid));
+
+       cmd->codec = codec->type;
+       cmd->presets = codec->get_presets(cmd->preset, &cmd_len);
+
+       cmd_len += sizeof(*cmd);
+
+       result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN, cmd_len, cmd,
+                               &rsp_len, &rsp, NULL);
+
+       if (result != AUDIO_STATUS_SUCCESS)
+               return 0;
+
+       return rsp.id;
+}
+
+static int ipc_close_cmd(uint8_t endpoint_id)
+{
+       struct audio_cmd_close cmd;
+       int result;
+
+       DBG("");
+
+       cmd.id = endpoint_id;
+
+       result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE,
+                               sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+       return result;
+}
+
+static int ipc_open_stream_cmd(uint8_t endpoint_id, uint16_t *mtu, int *fd,
+                                               struct audio_preset **caps)
+{
+       char buf[BLUEZ_AUDIO_MTU];
+       struct audio_cmd_open_stream cmd;
+       struct audio_rsp_open_stream *rsp =
+                                       (struct audio_rsp_open_stream *) &buf;
+       size_t rsp_len = sizeof(buf);
+       int result;
+
+       DBG("");
+
+       if (!caps)
+               return AUDIO_STATUS_FAILED;
+
+       cmd.id = endpoint_id;
+
+       result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM,
+                               sizeof(cmd), &cmd, &rsp_len, rsp, fd);
+       if (result == AUDIO_STATUS_SUCCESS) {
+               size_t buf_len = sizeof(struct audio_preset) +
+                                       rsp->preset[0].len;
+               *mtu = rsp->mtu;
+               *caps = malloc(buf_len);
+               memcpy(*caps, &rsp->preset, buf_len);
+       } else {
+               *caps = NULL;
+       }
+
+       return result;
+}
+
+static int ipc_close_stream_cmd(uint8_t endpoint_id)
+{
+       struct audio_cmd_close_stream cmd;
+       int result;
+
+       DBG("");
+
+       cmd.id = endpoint_id;
+
+       result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM,
+                                       sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+       return result;
+}
+
+static int ipc_resume_stream_cmd(uint8_t endpoint_id)
+{
+       struct audio_cmd_resume_stream cmd;
+       int result;
+
+       DBG("");
+
+       cmd.id = endpoint_id;
+
+       result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM,
+                                       sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+       return result;
+}
+
+static int ipc_suspend_stream_cmd(uint8_t endpoint_id)
+{
+       struct audio_cmd_suspend_stream cmd;
+       int result;
+
+       DBG("");
+
+       cmd.id = endpoint_id;
+
+       result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM,
+                                       sizeof(cmd), &cmd, NULL, NULL, NULL);
+
+       return result;
+}
+
+static int register_endpoints(void)
+{
+       struct audio_endpoint *ep = &audio_endpoints[0];
+       size_t i;
+
+       for (i = 0; i < NUM_CODECS; i++, ep++) {
+               const struct audio_codec *codec = &audio_codecs[i];
+
+               ep->id = ipc_open_cmd(codec);
+
+               if (!ep->id)
+                       return AUDIO_STATUS_FAILED;
+
+               ep->codec = codec;
+               ep->codec_data = NULL;
+               ep->fd = -1;
+       }
+
+       return AUDIO_STATUS_SUCCESS;
+}
+
+static void unregister_endpoints(void)
+{
+       size_t i;
+
+       for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++) {
+               struct audio_endpoint *ep = &audio_endpoints[i];
+
+               if (ep->id) {
+                       ipc_close_cmd(ep->id);
+                       memset(ep, 0, sizeof(*ep));
+               }
+       }
+}
+
+static bool open_endpoint(struct audio_endpoint *ep,
+                                               struct audio_input_config *cfg)
+{
+       struct audio_preset *preset;
+       const struct audio_codec *codec;
+       uint16_t mtu;
+       uint16_t payload_len;
+       int fd;
+
+       if (ipc_open_stream_cmd(ep->id, &mtu, &fd, &preset) !=
+                                                       AUDIO_STATUS_SUCCESS)
+               return false;
+
+       DBG("mtu=%u", mtu);
+
+       payload_len = mtu - sizeof(*ep->mp);
+
+       ep->fd = fd;
+
+       codec = ep->codec;
+       codec->init(preset, payload_len, &ep->codec_data);
+       codec->get_config(ep->codec_data, cfg);
+
+       ep->mp = calloc(mtu, 1);
+       if (!ep->mp)
+               goto failed;
+       ep->mp->hdr.v = 2;
+       ep->mp->hdr.pt = 1;
+       ep->mp->hdr.ssrc = htonl(1);
+
+       ep->mp_data_len = payload_len;
+
+       free(preset);
+
+       return true;
+
+failed:
+       close(fd);
+       free(preset);
+
+       return false;
+}
+
+static void close_endpoint(struct audio_endpoint *ep)
+{
+       ipc_close_stream_cmd(ep->id);
+       if (ep->fd >= 0) {
+               close(ep->fd);
+               ep->fd = -1;
+       }
+
+       free(ep->mp);
+
+       ep->codec->cleanup(ep->codec_data);
+       ep->codec_data = NULL;
+}
+
+static bool resume_endpoint(struct audio_endpoint *ep)
+{
+       if (ipc_resume_stream_cmd(ep->id) != AUDIO_STATUS_SUCCESS)
+               return false;
+
+       ep->samples = 0;
+       ep->resync = false;
+
+       ep->codec->update_qos(ep->codec_data, QOS_POLICY_DEFAULT);
+
+       return true;
+}
+
+static void downmix_to_mono(struct a2dp_stream_out *out, const uint8_t *buffer,
+                                                               size_t bytes)
+{
+       const int16_t *input = (const void *) buffer;
+       int16_t *output = (void *) out->downmix_buf;
+       size_t i;
+
+       for (i = 0; i < bytes / 2; i++) {
+               int16_t l = le16_to_cpu(get_unaligned(&input[i * 2]));
+               int16_t r = le16_to_cpu(get_unaligned(&input[i * 2 + 1]));
+
+               put_unaligned(cpu_to_le16((l + r) / 2), &output[i]);
+       }
+}
+
+static bool wait_for_endpoint(struct audio_endpoint *ep, bool *writable)
+{
+       int ret;
+
+       while (true) {
+               struct pollfd pollfd;
+
+               pollfd.fd = ep->fd;
+               pollfd.events = POLLOUT;
+               pollfd.revents = 0;
+
+               ret = poll(&pollfd, 1, 500);
+
+               if (ret >= 0) {
+                       *writable = !!(pollfd.revents & POLLOUT);
+                       break;
+               }
+
+               if (errno != EINTR) {
+                       ret = errno;
+                       error("poll failed (%d)", ret);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static bool write_to_endpoint(struct audio_endpoint *ep, size_t bytes)
+{
+       struct media_packet *mp = (struct media_packet *) ep->mp;
+       int ret;
+
+       while (true) {
+               ret = write(ep->fd, mp, sizeof(*mp) + bytes);
+
+               if (ret >= 0)
+                       break;
+
+               /*
+                * this should not happen so let's issue warning, but do not
+                * fail, we can try to write next packet
+                */
+               if (errno == EAGAIN) {
+                       ret = errno;
+                       warn("write failed (%d)", ret);
+                       break;
+               }
+
+               if (errno != EINTR) {
+                       ret = errno;
+                       error("write failed (%d)", ret);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static bool write_data(struct a2dp_stream_out *out, const void *buffer,
+                                                               size_t bytes)
+{
+       struct audio_endpoint *ep = out->ep;
+       struct media_packet *mp = (struct media_packet *) ep->mp;
+       size_t free_space = ep->mp_data_len;
+       size_t consumed = 0;
+
+       while (consumed < bytes) {
+               size_t written = 0;
+               ssize_t read;
+               uint32_t samples;
+               int ret;
+               struct timespec current;
+               uint64_t audio_sent, audio_passed;
+               bool do_write = false;
+
+               /*
+                * prepare media packet in advance so we don't waste time after
+                * wakeup
+                */
+               mp->hdr.sequence_number = htons(ep->seq++);
+               mp->hdr.timestamp = htonl(ep->samples);
+               read = ep->codec->encode_mediapacket(ep->codec_data,
+                                               buffer + consumed,
+                                               bytes - consumed, mp,
+                                               free_space, &written);
+
+               /*
+                * not much we can do here, let's just ignore remaining
+                * data and continue
+                */
+               if (read <= 0)
+                       return true;
+
+               /* calculate where are we and where we should be */
+               clock_gettime(CLOCK_MONOTONIC, &current);
+               if (!ep->samples)
+                       memcpy(&ep->start, &current, sizeof(ep->start));
+               audio_sent = ep->samples * 1000000ll / out->cfg.rate;
+               audio_passed = timespec_diff_us(&current, &ep->start);
+
+               /*
+                * if we're ahead of stream then wait for next write point,
+                * if we're lagging more than 100ms then stop writing and just
+                * skip data until we're back in sync
+                */
+               if (audio_sent > audio_passed) {
+                       struct timespec anchor;
+
+                       ep->resync = false;
+
+                       timespec_add(&ep->start, audio_sent, &anchor);
+
+                       while (true) {
+                               ret = clock_nanosleep(CLOCK_MONOTONIC,
+                                                       TIMER_ABSTIME, &anchor,
+                                                       NULL);
+
+                               if (!ret)
+                                       break;
+
+                               if (ret != EINTR) {
+                                       error("clock_nanosleep failed (%d)",
+                                                                       ret);
+                                       return false;
+                               }
+                       }
+               } else if (!ep->resync) {
+                       uint64_t diff = audio_passed - audio_sent;
+
+                       if (diff > MAX_DELAY) {
+                               warn("lag is %jums, resyncing", diff / 1000);
+
+                               ep->codec->update_qos(ep->codec_data,
+                                                       QOS_POLICY_DECREASE);
+                               ep->resync = true;
+                       }
+               }
+
+               /* in resync mode we'll just drop mediapackets */
+               if (!ep->resync) {
+                       /* wait some time for socket to be ready for write,
+                        * but we'll just skip writing data if timeout occurs
+                        */
+                       if (!wait_for_endpoint(ep, &do_write))
+                               return false;
+
+                       if (do_write)
+                               if (!write_to_endpoint(ep, written))
+                                       return false;
+               }
+
+               /*
+                * AudioFlinger provides 16bit PCM, so sample size is 2 bytes
+                * multiplied by number of channels. Number of channels is
+                * simply number of bits set in channels mask.
+                */
+               samples = read / (2 * popcount(out->cfg.channels));
+               ep->samples += samples;
+               consumed += read;
+       }
+
+       return true;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+                                                               size_t bytes)
+{
+       struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+       const void *in_buf = buffer;
+       size_t in_len = bytes;
+
+       /* just return in case we're closing */
+       if (out->audio_state == AUDIO_A2DP_STATE_NONE)
+               return -1;
+
+       /* We can auto-start only from standby */
+       if (out->audio_state == AUDIO_A2DP_STATE_STANDBY) {
+               DBG("stream in standby, auto-start");
+
+               if (!resume_endpoint(out->ep))
+                       return -1;
+
+               out->audio_state = AUDIO_A2DP_STATE_STARTED;
+       }
+
+       if (out->audio_state != AUDIO_A2DP_STATE_STARTED) {
+               error("audio: stream not started");
+               return -1;
+       }
+
+       if (out->ep->fd < 0) {
+               error("audio: no transport socket");
+               return -1;
+       }
+
+       /*
+        * currently Android audioflinger is not able to provide mono stream on
+        * A2DP output so down mixing needs to be done in hal-audio plugin.
+        *
+        * for reference see
+        * AudioFlinger::PlaybackThread::readOutputParameters()
+        * frameworks/av/services/audioflinger/Threads.cpp:1631
+        */
+       if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
+               if (!out->downmix_buf) {
+                       error("audio: downmix buffer not initialized");
+                       return -1;
+               }
+
+               downmix_to_mono(out, buffer, bytes);
+
+               in_buf = out->downmix_buf;
+               in_len = bytes / 2;
+       }
+
+       if (!write_data(out, in_buf, in_len))
+               return -1;
+
+       return bytes;
+}
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+       struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+       DBG("");
+
+       return out->cfg.rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+       struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+       DBG("");
+
+       if (rate != out->cfg.rate) {
+               warn("audio: cannot set sample rate to %d", rate);
+               return -1;
+       }
+
+       return 0;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+       DBG("");
+
+       /*
+        * We should return proper buffer size calculated by codec (so each
+        * input buffer is encoded into single media packed) but this does not
+        * work well with AudioFlinger and causes problems. For this reason we
+        * use magic value here and out_write code takes care of splitting
+        * input buffer into multiple media packets.
+        */
+       return FIXED_BUFFER_SIZE;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+       DBG("");
+
+       /*
+        * AudioFlinger can only provide stereo stream, so we return it here and
+        * later we'll downmix this to mono in case codec requires it
+        */
+
+       return AUDIO_CHANNEL_OUT_STEREO;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+       struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+       DBG("");
+
+       return out->cfg.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+       struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+       DBG("");
+
+       if (out->audio_state == AUDIO_A2DP_STATE_STARTED) {
+               if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+                       return -1;
+               out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+       }
+
+       return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+       struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+       char *kvpair;
+       char *str;
+       char *saveptr;
+       bool enter_suspend = false;
+       bool exit_suspend = false;
+
+       DBG("%s", kvpairs);
+
+       str = strdup(kvpairs);
+       if (!str)
+               return -ENOMEM;
+
+       kvpair = strtok_r(str, ";", &saveptr);
+
+       for (; kvpair && *kvpair; kvpair = strtok_r(NULL, ";", &saveptr)) {
+               char *keyval;
+
+               keyval = strchr(kvpair, '=');
+               if (!keyval)
+                       continue;
+
+               *keyval = '\0';
+               keyval++;
+
+               if (!strcmp(kvpair, "closing")) {
+                       if (!strcmp(keyval, "true"))
+                               out->audio_state = AUDIO_A2DP_STATE_NONE;
+               } else if (!strcmp(kvpair, "A2dpSuspended")) {
+                       if (!strcmp(keyval, "true"))
+                               enter_suspend = true;
+                       else
+                               exit_suspend = true;
+               }
+       }
+
+       free(str);
+
+       if (enter_suspend && out->audio_state == AUDIO_A2DP_STATE_STARTED) {
+               if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS)
+                       return -1;
+               out->audio_state = AUDIO_A2DP_STATE_SUSPENDED;
+       }
+
+       if (exit_suspend && out->audio_state == AUDIO_A2DP_STATE_SUSPENDED)
+               out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+
+       return 0;
+}
+
+static char *out_get_parameters(const struct audio_stream *stream,
+                                                       const char *keys)
+{
+       DBG("");
+       return strdup("");
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+       struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+       struct audio_endpoint *ep = out->ep;
+       size_t pkt_duration;
+
+       DBG("");
+
+       pkt_duration = ep->codec->get_mediapacket_duration(ep->codec_data);
+
+       return FIXED_A2DP_PLAYBACK_LATENCY_MS + pkt_duration / 1000;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+                                                               float right)
+{
+       DBG("");
+       /* volume controlled in audioflinger mixer (digital) */
+       return -ENOSYS;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+                                                       uint32_t *dsp_frames)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream,
+                                                       effect_handle_t effect)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream,
+                                                       effect_handle_t effect)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static uint32_t in_get_channels(const struct audio_stream *stream)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static char *in_get_parameters(const struct audio_stream *stream,
+                                                       const char *keys)
+{
+       DBG("");
+       return strdup("");
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
+                                                               size_t bytes)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream,
+                                                       effect_handle_t effect)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream,
+                                                       effect_handle_t effect)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int audio_open_output_stream(struct audio_hw_device *dev,
+                                       audio_io_handle_t handle,
+                                       audio_devices_t devices,
+                                       audio_output_flags_t flags,
+                                       struct audio_config *config,
+                                       struct audio_stream_out **stream_out)
+
+{
+       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+       struct a2dp_stream_out *out;
+
+       out = calloc(1, sizeof(struct a2dp_stream_out));
+       if (!out)
+               return -ENOMEM;
+
+       DBG("");
+
+       out->stream.common.get_sample_rate = out_get_sample_rate;
+       out->stream.common.set_sample_rate = out_set_sample_rate;
+       out->stream.common.get_buffer_size = out_get_buffer_size;
+       out->stream.common.get_channels = out_get_channels;
+       out->stream.common.get_format = out_get_format;
+       out->stream.common.set_format = out_set_format;
+       out->stream.common.standby = out_standby;
+       out->stream.common.dump = out_dump;
+       out->stream.common.set_parameters = out_set_parameters;
+       out->stream.common.get_parameters = out_get_parameters;
+       out->stream.common.add_audio_effect = out_add_audio_effect;
+       out->stream.common.remove_audio_effect = out_remove_audio_effect;
+       out->stream.get_latency = out_get_latency;
+       out->stream.set_volume = out_set_volume;
+       out->stream.write = out_write;
+       out->stream.get_render_position = out_get_render_position;
+
+       /* TODO: for now we always use endpoint 0 */
+       out->ep = &audio_endpoints[0];
+
+       if (!open_endpoint(out->ep, &out->cfg))
+               goto fail;
+
+       DBG("rate=%d channels=%d format=%d", out->cfg.rate,
+                                       out->cfg.channels, out->cfg.format);
+
+       if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) {
+               out->downmix_buf = malloc(FIXED_BUFFER_SIZE / 2);
+               if (!out->downmix_buf)
+                       goto fail;
+       }
+
+       *stream_out = &out->stream;
+       a2dp_dev->out = out;
+
+       out->audio_state = AUDIO_A2DP_STATE_STANDBY;
+
+       return 0;
+
+fail:
+       error("audio: cannot open output stream");
+       free(out);
+       *stream_out = NULL;
+       return -EIO;
+}
+
+static void audio_close_output_stream(struct audio_hw_device *dev,
+                                       struct audio_stream_out *stream)
+{
+       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+       struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream;
+
+       DBG("");
+
+       close_endpoint(a2dp_dev->out->ep);
+
+       free(out->downmix_buf);
+
+       free(stream);
+       a2dp_dev->out = NULL;
+}
+
+static int audio_set_parameters(struct audio_hw_device *dev,
+                                                       const char *kvpairs)
+{
+       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev;
+       struct a2dp_stream_out *out = a2dp_dev->out;
+
+       DBG("");
+
+       if (!out)
+               return 0;
+
+       return out->stream.common.set_parameters((struct audio_stream *) out,
+                                                               kvpairs);
+}
+
+static char *audio_get_parameters(const struct audio_hw_device *dev,
+                                                       const char *keys)
+{
+       DBG("");
+       return strdup("");
+}
+
+static int audio_init_check(const struct audio_hw_device *dev)
+{
+       DBG("");
+       return 0;
+}
+
+static int audio_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int audio_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int audio_set_mode(struct audio_hw_device *dev, int mode)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int audio_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int audio_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static size_t audio_get_input_buffer_size(const struct audio_hw_device *dev,
+                                       const struct audio_config *config)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int audio_open_input_stream(struct audio_hw_device *dev,
+                                       audio_io_handle_t handle,
+                                       audio_devices_t devices,
+                                       struct audio_config *config,
+                                       struct audio_stream_in **stream_in)
+{
+       struct audio_stream_in *in;
+
+       DBG("");
+
+       in = calloc(1, sizeof(struct audio_stream_in));
+       if (!in)
+               return -ENOMEM;
+
+       in->common.get_sample_rate = in_get_sample_rate;
+       in->common.set_sample_rate = in_set_sample_rate;
+       in->common.get_buffer_size = in_get_buffer_size;
+       in->common.get_channels = in_get_channels;
+       in->common.get_format = in_get_format;
+       in->common.set_format = in_set_format;
+       in->common.standby = in_standby;
+       in->common.dump = in_dump;
+       in->common.set_parameters = in_set_parameters;
+       in->common.get_parameters = in_get_parameters;
+       in->common.add_audio_effect = in_add_audio_effect;
+       in->common.remove_audio_effect = in_remove_audio_effect;
+       in->set_gain = in_set_gain;
+       in->read = in_read;
+       in->get_input_frames_lost = in_get_input_frames_lost;
+
+       *stream_in = in;
+
+       return 0;
+}
+
+static void audio_close_input_stream(struct audio_hw_device *dev,
+                                       struct audio_stream_in *stream_in)
+{
+       DBG("");
+       free(stream_in);
+}
+
+static int audio_dump(const audio_hw_device_t *device, int fd)
+{
+       DBG("");
+       return -ENOSYS;
+}
+
+static int audio_close(hw_device_t *device)
+{
+       struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device;
+
+       DBG("");
+
+       unregister_endpoints();
+
+       shutdown(listen_sk, SHUT_RDWR);
+       shutdown(audio_sk, SHUT_RDWR);
+
+       pthread_join(ipc_th, NULL);
+
+       close(listen_sk);
+       listen_sk = -1;
+
+       free(a2dp_dev);
+       return 0;
+}
+
+static void *ipc_handler(void *data)
+{
+       bool done = false;
+       struct pollfd pfd;
+       int sk;
+
+       DBG("");
+
+       while (!done) {
+               DBG("Waiting for connection ...");
+
+               sk = accept(listen_sk, NULL, NULL);
+               if (sk < 0) {
+                       int err = errno;
+
+                       if (err == EINTR)
+                               continue;
+
+                       if (err != ECONNABORTED && err != EINVAL)
+                               error("audio: Failed to accept socket: %d (%s)",
+                                                       err, strerror(err));
+
+                       break;
+               }
+
+               pthread_mutex_lock(&sk_mutex);
+               audio_sk = sk;
+               pthread_mutex_unlock(&sk_mutex);
+
+               DBG("Audio IPC: Connected");
+
+               if (register_endpoints() != AUDIO_STATUS_SUCCESS) {
+                       error("audio: Failed to register endpoints");
+
+                       unregister_endpoints();
+
+                       pthread_mutex_lock(&sk_mutex);
+                       shutdown(audio_sk, SHUT_RDWR);
+                       close(audio_sk);
+                       audio_sk = -1;
+                       pthread_mutex_unlock(&sk_mutex);
+
+                       continue;
+               }
+
+               memset(&pfd, 0, sizeof(pfd));
+               pfd.fd = audio_sk;
+               pfd.events = POLLHUP | POLLERR | POLLNVAL;
+
+               /* Check if socket is still alive. Empty while loop.*/
+               while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
+
+               if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
+                       info("Audio HAL: Socket closed");
+
+                       pthread_mutex_lock(&sk_mutex);
+                       close(audio_sk);
+                       audio_sk = -1;
+                       pthread_mutex_unlock(&sk_mutex);
+               }
+       }
+
+       /* audio_sk is closed at this point, just cleanup endpoints states */
+       memset(audio_endpoints, 0, sizeof(audio_endpoints));
+
+       info("Closing Audio IPC thread");
+       return NULL;
+}
+
+static int audio_ipc_init(void)
+{
+       struct sockaddr_un addr;
+       int err;
+       int sk;
+
+       DBG("");
+
+       sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+       if (sk < 0) {
+               err = -errno;
+               error("audio: Failed to create socket: %d (%s)", -err,
+                                                               strerror(-err));
+               return err;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+
+       memcpy(addr.sun_path, BLUEZ_AUDIO_SK_PATH,
+                                       sizeof(BLUEZ_AUDIO_SK_PATH));
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               err = -errno;
+               error("audio: Failed to bind socket: %d (%s)", -err,
+                                                               strerror(-err));
+               goto failed;
+       }
+
+       if (listen(sk, 1) < 0) {
+               err = -errno;
+               error("audio: Failed to listen on the socket: %d (%s)", -err,
+                                                               strerror(-err));
+               goto failed;
+       }
+
+       listen_sk = sk;
+
+       err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
+       if (err) {
+               err = -err;
+               ipc_th = 0;
+               error("audio: Failed to start Audio IPC thread: %d (%s)",
+                                                       -err, strerror(-err));
+               goto failed;
+       }
+
+       return 0;
+
+failed:
+       close(sk);
+       return err;
+}
+
+static int audio_open(const hw_module_t *module, const char *name,
+                                                       hw_device_t **device)
+{
+       struct a2dp_audio_dev *a2dp_dev;
+       int err;
+
+       DBG("");
+
+       if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+               error("audio: interface %s not matching [%s]", name,
+                                               AUDIO_HARDWARE_INTERFACE);
+               return -EINVAL;
+       }
+
+       err = audio_ipc_init();
+       if (err < 0)
+               return err;
+
+       a2dp_dev = calloc(1, sizeof(struct a2dp_audio_dev));
+       if (!a2dp_dev)
+               return -ENOMEM;
+
+       a2dp_dev->dev.common.tag = HARDWARE_DEVICE_TAG;
+       a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
+       a2dp_dev->dev.common.module = (struct hw_module_t *) module;
+       a2dp_dev->dev.common.close = audio_close;
+
+       a2dp_dev->dev.init_check = audio_init_check;
+       a2dp_dev->dev.set_voice_volume = audio_set_voice_volume;
+       a2dp_dev->dev.set_master_volume = audio_set_master_volume;
+       a2dp_dev->dev.set_mode = audio_set_mode;
+       a2dp_dev->dev.set_mic_mute = audio_set_mic_mute;
+       a2dp_dev->dev.get_mic_mute = audio_get_mic_mute;
+       a2dp_dev->dev.set_parameters = audio_set_parameters;
+       a2dp_dev->dev.get_parameters = audio_get_parameters;
+       a2dp_dev->dev.get_input_buffer_size = audio_get_input_buffer_size;
+       a2dp_dev->dev.open_output_stream = audio_open_output_stream;
+       a2dp_dev->dev.close_output_stream = audio_close_output_stream;
+       a2dp_dev->dev.open_input_stream = audio_open_input_stream;
+       a2dp_dev->dev.close_input_stream = audio_close_input_stream;
+       a2dp_dev->dev.dump = audio_dump;
+
+       /*
+        * Note that &a2dp_dev->dev.common is the same pointer as a2dp_dev.
+        * This results from the structure of following structs:a2dp_audio_dev,
+        * audio_hw_device. We will rely on this later in the code.
+        */
+       *device = &a2dp_dev->dev.common;
+
+       return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+       .open = audio_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+       .common = {
+               .tag = HARDWARE_MODULE_TAG,
+               .version_major = 1,
+               .version_minor = 0,
+               .id = AUDIO_HARDWARE_MODULE_ID,
+               .name = "A2DP Bluez HW HAL",
+               .author = "Intel Corporation",
+               .methods = &hal_module_methods,
+       },
+};
diff --git a/android/hal-avrcp.c b/android/hal-avrcp.c
new file mode 100644 (file)
index 0000000..5e07366
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const btrc_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+       return cbs != NULL;
+}
+
+static void handle_remote_features(void *buf, uint16_t len)
+{
+       struct hal_ev_avrcp_remote_features *ev = buf;
+
+       if (cbs->remote_features_cb)
+               cbs->remote_features_cb((bt_bdaddr_t *) (ev->bdaddr),
+                                                               ev->features);
+}
+
+static void handle_get_play_status(void *buf, uint16_t len)
+{
+       if (cbs->get_play_status_cb)
+               cbs->get_play_status_cb();
+}
+
+static void handle_list_player_attrs(void *buf, uint16_t len)
+{
+       if (cbs->list_player_app_attr_cb)
+               cbs->list_player_app_attr_cb();
+}
+
+static void handle_list_player_values(void *buf, uint16_t len)
+{
+       struct hal_ev_avrcp_list_player_values *ev = buf;
+
+       if (cbs->list_player_app_values_cb)
+               cbs->list_player_app_values_cb(ev->attr);
+}
+
+static void handle_get_player_values(void *buf, uint16_t len)
+{
+       struct hal_ev_avrcp_get_player_values *ev = buf;
+       btrc_player_attr_t attrs[4];
+       int i;
+
+       if (!cbs->get_player_app_value_cb)
+               return;
+
+       /* Convert uint8_t array to btrc_player_attr_t array */
+       for (i = 0; i < ev->number; i++)
+               attrs[i] = ev->attrs[i];
+
+       cbs->get_player_app_value_cb(ev->number, attrs);
+}
+
+static void handle_get_player_attrs_text(void *buf, uint16_t len)
+{
+       struct hal_ev_avrcp_get_player_attrs_text *ev = buf;
+       btrc_player_attr_t attrs[4];
+       int i;
+
+       if (!cbs->get_player_app_attrs_text_cb)
+               return;
+
+       /* Convert uint8_t array to btrc_player_attr_t array */
+       for (i = 0; i < ev->number; i++)
+               attrs[i] = ev->attrs[i];
+
+       cbs->get_player_app_attrs_text_cb(ev->number, attrs);
+}
+
+static void handle_get_player_values_text(void *buf, uint16_t len)
+{
+       struct hal_ev_avrcp_get_player_values_text *ev = buf;
+
+       if (cbs->get_player_app_values_text_cb)
+               cbs->get_player_app_values_text_cb(ev->attr, ev->number,
+                                                               ev->values);
+}
+
+static void handle_set_player_value(void *buf, uint16_t len)
+{
+       struct hal_ev_avrcp_set_player_values *ev = buf;
+       struct hal_avrcp_player_attr_value *attrs;
+       btrc_player_settings_t values;
+       int i;
+
+       if (!cbs->set_player_app_value_cb)
+               return;
+
+       attrs = (struct hal_avrcp_player_attr_value *) ev->attrs;
+
+       /* Convert to btrc_player_settings_t */
+       values.num_attr = ev->number;
+       for (i = 0; i < ev->number; i++) {
+               values.attr_ids[i] = attrs[i].attr;
+               values.attr_values[i] = attrs[i].value;
+       }
+
+       cbs->set_player_app_value_cb(&values);
+}
+
+static void handle_get_element_attrs(void *buf, uint16_t len)
+{
+       struct hal_ev_avrcp_get_element_attrs *ev = buf;
+       btrc_media_attr_t attrs[BTRC_MAX_APP_SETTINGS];
+       int i;
+
+       if (!cbs->get_element_attr_cb)
+               return;
+
+       /* Convert uint8_t array to btrc_media_attr_t array */
+       for (i = 0; i < ev->number; i++)
+               attrs[i] = ev->attrs[i];
+
+       cbs->get_element_attr_cb(ev->number, attrs);
+}
+
+static void handle_register_notification(void *buf, uint16_t len)
+{
+       struct hal_ev_avrcp_register_notification *ev = buf;
+
+       if (cbs->register_notification_cb)
+               cbs->register_notification_cb(ev->event, ev->param);
+}
+
+static void handle_volume_changed(void *buf, uint16_t len)
+{
+       struct hal_ev_avrcp_volume_changed *ev = buf;
+
+       if (cbs->volume_change_cb)
+               cbs->volume_change_cb(ev->volume, ev->type);
+}
+
+static void handle_passthrough_cmd(void *buf, uint16_t len)
+{
+       struct hal_ev_avrcp_passthrough_cmd *ev = buf;
+
+       if (cbs->passthrough_cmd_cb)
+               cbs->passthrough_cmd_cb(ev->id, ev->state);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+       /* HAL_EV_AVRCP_REMOTE_FEATURES */
+       { handle_remote_features, false,
+                       sizeof(struct hal_ev_avrcp_remote_features) },
+       /* HAL_EV_AVRCP_GET_PLAY_STATUS */
+       { handle_get_play_status, false, 0 },
+       /* HAL_EV_AVRCP_LIST_PLAYER_ATTRS */
+       { handle_list_player_attrs, false, 0 },
+       /* HAL_EV_AVRCP_LIST_PLAYER_VALUES */
+       { handle_list_player_values, false,
+                       sizeof(struct hal_ev_avrcp_list_player_values) },
+       /* HAL_EV_AVRCP_GET_PLAYER_VALUES */
+       { handle_get_player_values, true,
+                       sizeof(struct hal_ev_avrcp_get_player_values) },
+       /* HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT */
+       { handle_get_player_attrs_text, true,
+                       sizeof(struct hal_ev_avrcp_get_player_attrs_text) },
+       /* HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT */
+       { handle_get_player_values_text, true,
+                       sizeof(struct hal_ev_avrcp_get_player_values_text) },
+       /* HAL_EV_AVRCP_SET_PLAYER_VALUES */
+       { handle_set_player_value, true,
+                       sizeof(struct hal_ev_avrcp_set_player_values) },
+       /* HAL_EV_AVRCP_GET_ELEMENT_ATTRS */
+       { handle_get_element_attrs, true,
+                       sizeof(struct hal_ev_avrcp_get_element_attrs) },
+       /* HAL_EV_AVRCP_REGISTER_NOTIFICATION */
+       { handle_register_notification, false,
+                       sizeof(struct hal_ev_avrcp_register_notification) },
+       /* HAL_EV_AVRCP_VOLUME_CHANGED */
+       { handle_volume_changed, false,
+                       sizeof(struct hal_ev_avrcp_volume_changed) },
+       /* HAL_EV_AVRCP_PASSTHROUGH_CMD */
+       { handle_passthrough_cmd, false,
+                       sizeof(struct hal_ev_avrcp_passthrough_cmd) },
+};
+
+static bt_status_t init(btrc_callbacks_t *callbacks)
+{
+       struct hal_cmd_register_module cmd;
+       int ret;
+
+       DBG("");
+
+       if (interface_ready())
+               return BT_STATUS_DONE;
+
+       cbs = callbacks;
+
+       hal_ipc_register(HAL_SERVICE_ID_AVRCP, ev_handlers,
+                               sizeof(ev_handlers) / sizeof(ev_handlers[0]));
+
+       cmd.service_id = HAL_SERVICE_ID_AVRCP;
+       cmd.mode = HAL_MODE_DEFAULT;
+
+       ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       if (ret != BT_STATUS_SUCCESS) {
+               cbs = NULL;
+               hal_ipc_unregister(HAL_SERVICE_ID_AVRCP);
+       }
+
+       return ret;
+}
+
+static bt_status_t get_play_status_rsp(btrc_play_status_t status,
+                                       uint32_t song_len, uint32_t song_pos)
+{
+       struct hal_cmd_avrcp_get_play_status cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.status = status;
+       cmd.duration = song_len;
+       cmd.position = song_pos;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAY_STATUS,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t list_player_app_attr_rsp(int num_attr,
+                                               btrc_player_attr_t *p_attrs)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_avrcp_list_player_attrs *cmd = (void *) buf;
+       size_t len;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (num_attr < 0)
+               return BT_STATUS_PARM_INVALID;
+
+       len = sizeof(*cmd) + num_attr;
+       if (len > IPC_MTU)
+               return BT_STATUS_PARM_INVALID;
+
+       cmd->number = num_attr;
+       memcpy(cmd->attrs, p_attrs, num_attr);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_LIST_PLAYER_ATTRS,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t list_player_app_value_rsp(int num_val, uint8_t *p_vals)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_avrcp_list_player_values *cmd = (void *) buf;
+       size_t len;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (num_val < 0)
+               return BT_STATUS_PARM_INVALID;
+
+       len = sizeof(*cmd) + num_val;
+
+       if (len > IPC_MTU)
+               return BT_STATUS_PARM_INVALID;
+
+       cmd->number = num_val;
+       memcpy(cmd->values, p_vals, num_val);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_LIST_PLAYER_VALUES,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t get_player_app_value_rsp(btrc_player_settings_t *p_vals)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_avrcp_get_player_attrs *cmd = (void *) buf;
+       size_t len, attrs_len;
+       int i;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (!p_vals)
+               return BT_STATUS_PARM_INVALID;
+
+       attrs_len = p_vals->num_attr *
+                               sizeof(struct hal_avrcp_player_attr_value);
+       len = sizeof(*cmd) + attrs_len;
+
+       if (len > IPC_MTU)
+               return BT_STATUS_PARM_INVALID;
+
+       cmd->number = p_vals->num_attr;
+
+       for (i = 0; i < p_vals->num_attr; i++) {
+               cmd->attrs[i].attr = p_vals->attr_ids[i];
+               cmd->attrs[i].value = p_vals->attr_values[i];
+       }
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_GET_PLAYER_ATTRS,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static int write_text(uint8_t *ptr, uint8_t id, uint8_t *text, size_t *len)
+{
+       struct hal_avrcp_player_setting_text *value = (void *) ptr;
+       size_t attr_len = sizeof(*value);
+
+       if (attr_len + *len > IPC_MTU)
+               return 0;
+
+       value->id = id;
+       value->len = strnlen((const char *) text, BTRC_MAX_ATTR_STR_LEN);
+
+       *len += attr_len;
+
+       if (value->len + *len > IPC_MTU)
+               value->len = IPC_MTU - *len;
+
+       memcpy(value->text, text, value->len);
+
+       *len += value->len;
+
+       return attr_len + value->len;
+}
+
+static uint8_t write_player_setting_text(uint8_t *ptr, uint8_t num_attr,
+                                       btrc_player_setting_text_t *p_attrs,
+                                       size_t *len)
+{
+       int i;
+
+       for (i = 0; i < num_attr && *len < IPC_MTU; i++) {
+               int ret;
+
+               ret = write_text(ptr, p_attrs[i].id, p_attrs[i].text, len);
+               if (ret == 0)
+                       break;
+
+               ptr += ret;
+       }
+
+       return i;
+}
+
+static bt_status_t get_player_app_attr_text_rsp(int num_attr,
+                                       btrc_player_setting_text_t *p_attrs)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_avrcp_get_player_attrs_text *cmd = (void *) buf;
+       uint8_t *ptr;
+       size_t len;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (num_attr < 0 || num_attr > BTRC_MAX_APP_SETTINGS)
+               return BT_STATUS_PARM_INVALID;
+
+       len = sizeof(*cmd);
+       ptr = (uint8_t *) &cmd->attrs[0];
+       cmd->number = write_player_setting_text(ptr, num_attr, p_attrs, &len);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t get_player_app_value_text_rsp(int num_val,
+                                       btrc_player_setting_text_t *p_vals)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_avrcp_get_player_values_text *cmd = (void *) buf;
+       uint8_t *ptr;
+       size_t len;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (num_val < 0)
+               return BT_STATUS_PARM_INVALID;
+
+       len = sizeof(*cmd);
+       ptr = (uint8_t *) &cmd->values[0];
+       cmd->number = write_player_setting_text(ptr, num_val, p_vals, &len);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static uint8_t write_element_attr_text(uint8_t *ptr, uint8_t num_attr,
+                                       btrc_element_attr_val_t *p_attrs,
+                                       size_t *len)
+{
+       int i;
+
+       for (i = 0; i < num_attr && *len < IPC_MTU; i++) {
+               int ret;
+
+               ret = write_text(ptr, p_attrs[i].attr_id, p_attrs[i].text, len);
+               if (ret == 0)
+                       break;
+
+               ptr += ret;
+       }
+
+       return i;
+}
+
+static bt_status_t get_element_attr_rsp(uint8_t num_attr,
+                                       btrc_element_attr_val_t *p_attrs)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf;
+       size_t len;
+       uint8_t *ptr;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       len = sizeof(*cmd);
+       ptr = (uint8_t *) &cmd->values[0];
+       cmd->number = write_element_attr_text(ptr, num_attr, p_attrs, &len);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t set_player_app_value_rsp(btrc_status_t rsp_status)
+{
+       struct hal_cmd_avrcp_set_player_attrs_value cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.status = rsp_status;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t play_status_changed_rsp(btrc_notification_type_t type,
+                                               btrc_play_status_t *play_status)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+       size_t len;
+
+       cmd->event = BTRC_EVT_PLAY_STATUS_CHANGED;
+       cmd->type = type;
+       cmd->len = 1;
+       memcpy(cmd->data, play_status, cmd->len);
+
+       len = sizeof(*cmd) + cmd->len;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t track_change_rsp(btrc_notification_type_t type,
+                                                       btrc_uid_t *track)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+       size_t len;
+
+       cmd->event = BTRC_EVT_TRACK_CHANGE;
+       cmd->type = type;
+       cmd->len = sizeof(*track);
+       memcpy(cmd->data, track, cmd->len);
+
+       len = sizeof(*cmd) + cmd->len;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t track_reached_end_rsp(btrc_notification_type_t type)
+{
+       struct hal_cmd_avrcp_register_notification cmd;
+
+       cmd.event = BTRC_EVT_TRACK_REACHED_END;
+       cmd.type = type;
+       cmd.len = 0;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t track_reached_start_rsp(btrc_notification_type_t type)
+{
+       struct hal_cmd_avrcp_register_notification cmd;
+
+       cmd.event = BTRC_EVT_TRACK_REACHED_START;
+       cmd.type = type;
+       cmd.len = 0;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t play_pos_changed_rsp(btrc_notification_type_t type,
+                                                       uint32_t *song_pos)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+       size_t len;
+
+       cmd->event = BTRC_EVT_PLAY_POS_CHANGED;
+       cmd->type = type;
+       cmd->len = sizeof(*song_pos);
+       memcpy(cmd->data, song_pos, cmd->len);
+
+       len = sizeof(*cmd) + cmd->len;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t settings_changed_rsp(btrc_notification_type_t type,
+                                       btrc_player_settings_t *player_setting)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_avrcp_register_notification *cmd = (void *) buf;
+       struct hal_avrcp_player_attr_value *attrs;
+       size_t len, param_len;
+       int i;
+
+       param_len = player_setting->num_attr * sizeof(*attrs);
+       len = sizeof(*cmd) + param_len;
+
+       if (len > IPC_MTU)
+               return BT_STATUS_PARM_INVALID;
+
+       cmd->event = BTRC_EVT_APP_SETTINGS_CHANGED;
+       cmd->type = type;
+       cmd->len = param_len;
+
+       attrs = (struct hal_avrcp_player_attr_value *) &cmd->data[0];
+       for (i = 0; i < player_setting->num_attr; i++) {
+               attrs[i].attr = player_setting->attr_ids[i];
+               attrs[i].value = player_setting->attr_values[i];
+       }
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP,
+                                       HAL_OP_AVRCP_REGISTER_NOTIFICATION,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t register_notification_rsp(btrc_event_id_t event_id,
+                                       btrc_notification_type_t type,
+                                       btrc_register_notification_t *p_param)
+{
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       switch (event_id) {
+       case BTRC_EVT_PLAY_STATUS_CHANGED:
+               return play_status_changed_rsp(type, &p_param->play_status);
+       case BTRC_EVT_TRACK_CHANGE:
+               return track_change_rsp(type, &p_param->track);
+       case BTRC_EVT_TRACK_REACHED_END:
+               return track_reached_end_rsp(type);
+       case BTRC_EVT_TRACK_REACHED_START:
+               return track_reached_start_rsp(type);
+       case BTRC_EVT_PLAY_POS_CHANGED:
+               return play_pos_changed_rsp(type, &p_param->song_pos);
+       case BTRC_EVT_APP_SETTINGS_CHANGED:
+               return settings_changed_rsp(type, &p_param->player_setting);
+       default:
+               return BT_STATUS_PARM_INVALID;
+       }
+}
+
+static bt_status_t set_volume(uint8_t volume)
+{
+       struct hal_cmd_avrcp_set_volume cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.value = volume;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static void cleanup()
+{
+       struct hal_cmd_unregister_module cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return;
+
+       cbs = NULL;
+
+       cmd.service_id = HAL_SERVICE_ID_AVRCP;
+
+       hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       hal_ipc_unregister(HAL_SERVICE_ID_AVRCP);
+}
+
+static btrc_interface_t iface = {
+       .size = sizeof(iface),
+       .init = init,
+       .get_play_status_rsp = get_play_status_rsp,
+       .list_player_app_attr_rsp = list_player_app_attr_rsp,
+       .list_player_app_value_rsp = list_player_app_value_rsp,
+       .get_player_app_value_rsp = get_player_app_value_rsp,
+       .get_player_app_attr_text_rsp = get_player_app_attr_text_rsp,
+       .get_player_app_value_text_rsp = get_player_app_value_text_rsp,
+       .get_element_attr_rsp = get_element_attr_rsp,
+       .set_player_app_value_rsp = set_player_app_value_rsp,
+       .register_notification_rsp = register_notification_rsp,
+       .set_volume = set_volume,
+       .cleanup = cleanup
+};
+
+btrc_interface_t *bt_get_avrcp_interface()
+{
+       return &iface;
+}
index 7cac15c..2155978 100644 (file)
 #include <stdbool.h>
 #include <string.h>
 
+#include <cutils/properties.h>
+
 #include "hal-log.h"
 #include "hal.h"
 #include "hal-msg.h"
+#include "ipc-common.h"
 #include "hal-ipc.h"
 #include "hal-utils.h"
 
+#define MODE_PROPERTY_NAME "persist.sys.bluetooth.mode"
+
 static const bt_callbacks_t *bt_hal_cbacks = NULL;
 
 #define enum_prop_to_hal(prop, hal_prop, type) do { \
@@ -109,7 +114,7 @@ static void adapter_prop_from_hal(const bt_property_t *property, uint8_t *type,
        /* type match IPC type */
        *type = property->type;
 
-       switch(property->type) {
+       switch (property->type) {
        case HAL_PROP_ADAPTER_SCAN_MODE:
                enum_prop_from_hal(property, len, val, bt_scan_mode_t);
                break;
@@ -141,8 +146,37 @@ static void device_props_to_hal(bt_property_t *send_props,
                        enum_prop_to_hal(send_props[i], prop,
                                                        bt_device_type_t);
                        break;
-               case HAL_PROP_DEVICE_SERVICE_REC:
                case HAL_PROP_DEVICE_VERSION_INFO:
+               {
+                       static bt_remote_version_t e;
+                       const struct hal_prop_device_info *p;
+
+                       send_props[i].val = &e;
+                       send_props[i].len = sizeof(e);
+
+                       p = (struct hal_prop_device_info *) prop->val;
+
+                       e.manufacturer = p->manufacturer;
+                       e.sub_ver = p->sub_version;
+                       e.version = p->version;
+               }
+                       break;
+               case HAL_PROP_DEVICE_SERVICE_REC:
+               {
+                       static bt_service_record_t e;
+                       const struct hal_prop_device_service_rec *p;
+
+                       send_props[i].val = &e;
+                       send_props[i].len = sizeof(e);
+
+                       p = (struct hal_prop_device_service_rec *) prop->val;
+
+                       memset(&e, 0, sizeof(e));
+                       memcpy(&e.channel, &p->channel, sizeof(e.channel));
+                       memcpy(e.uuid.uu, p->uuid, sizeof(e.uuid.uu));
+                       memcpy(e.name, p->name, p->name_len);
+               }
+                       break;
                default:
                        send_props[i].len = prop->len;
                        send_props[i].val = prop->val;
@@ -307,8 +341,20 @@ static void handle_dut_mode_receive(void *buf, uint16_t len)
                bt_hal_cbacks->dut_mode_recv_cb(ev->opcode, ev->data, ev->len);
 }
 
-/* handlers will be called from notification thread context,
- * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */
+static void handle_le_test_mode(void *buf, uint16_t len)
+{
+       struct hal_ev_le_test_mode *ev = buf;
+
+       DBG("");
+
+       if (bt_hal_cbacks->le_test_mode_cb)
+               bt_hal_cbacks->le_test_mode_cb(ev->status, ev->num_packets);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
 static const struct hal_ipc_handler ev_handlers[] = {
        {       /* HAL_EV_ADAPTER_STATE_CHANGED */
                .handler = handle_adapter_state_changed,
@@ -363,8 +409,28 @@ static const struct hal_ipc_handler ev_handlers[] = {
                .var_len = true,
                .data_len = sizeof(struct hal_ev_dut_mode_receive),
        },
+       {       /* HAL_EV_LE_TEST_MODE */
+               .handler = handle_le_test_mode,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_le_test_mode),
+       }
 };
 
+static uint8_t get_mode(void)
+{
+       char value[PROPERTY_VALUE_MAX];
+
+       if (property_get(MODE_PROPERTY_NAME, value, "") > 0 &&
+                                       (!strcasecmp(value, "bredr")))
+               return HAL_MODE_BREDR;
+
+       if (property_get(MODE_PROPERTY_NAME, value, "") > 0 &&
+                                       (!strcasecmp(value, "le")))
+               return HAL_MODE_LE;
+
+       return HAL_MODE_DEFAULT;
+}
+
 static int init(bt_callbacks_t *callbacks)
 {
        struct hal_cmd_register_module cmd;
@@ -386,6 +452,7 @@ static int init(bt_callbacks_t *callbacks)
        }
 
        cmd.service_id = HAL_SERVICE_ID_BLUETOOTH;
+       cmd.mode = get_mode();
 
        status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
                                        sizeof(cmd), &cmd, NULL, NULL, NULL);
@@ -394,7 +461,8 @@ static int init(bt_callbacks_t *callbacks)
                goto fail;
        }
 
-       cmd.service_id = HAL_SERVICE_ID_SOCK;
+       cmd.service_id = HAL_SERVICE_ID_SOCKET;
+       cmd.mode = HAL_MODE_DEFAULT;
 
        status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
                                        sizeof(cmd), &cmd, NULL, NULL, NULL);
@@ -479,8 +547,9 @@ static int get_adapter_property(bt_property_type_t type)
 
 static int set_adapter_property(const bt_property_t *property)
 {
-       char buf[sizeof(struct hal_cmd_set_adapter_prop) + property->len];
+       char buf[IPC_MTU];
        struct hal_cmd_set_adapter_prop *cmd = (void *) buf;
+       size_t len;
 
        DBG("prop: %s", btproperty2str(property));
 
@@ -489,8 +558,10 @@ static int set_adapter_property(const bt_property_t *property)
 
        adapter_prop_from_hal(property, &cmd->type, &cmd->len, cmd->val);
 
+       len = sizeof(*cmd) + cmd->len;
+
        return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP,
-                               sizeof(*cmd) + cmd->len, cmd, 0, NULL, NULL);
+                                               len, cmd, 0, NULL, NULL);
 }
 
 static int get_remote_device_properties(bt_bdaddr_t *remote_addr)
@@ -533,8 +604,9 @@ static int get_remote_device_property(bt_bdaddr_t *remote_addr,
 static int set_remote_device_property(bt_bdaddr_t *remote_addr,
                                                const bt_property_t *property)
 {
-       struct hal_cmd_set_remote_device_prop *cmd;
-       uint8_t buf[sizeof(*cmd) + property->len];
+       char buf[IPC_MTU];
+       struct hal_cmd_set_remote_device_prop *cmd = (void *) buf;
+       size_t len;
 
        DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr),
                                bt_property_type_t2str(property->type));
@@ -542,8 +614,6 @@ static int set_remote_device_property(bt_bdaddr_t *remote_addr,
        if (!interface_ready())
                return BT_STATUS_NOT_READY;
 
-       cmd = (void *) buf;
-
        memcpy(cmd->bdaddr, remote_addr, sizeof(cmd->bdaddr));
 
        /* type match IPC type */
@@ -551,9 +621,11 @@ static int set_remote_device_property(bt_bdaddr_t *remote_addr,
        cmd->len = property->len;
        memcpy(cmd->val, property->val, property->len);
 
+       len = sizeof(*cmd) + cmd->len;
+
        return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
                                        HAL_OP_SET_REMOTE_DEVICE_PROP,
-                                       sizeof(buf), cmd, 0, NULL, NULL);
+                                       len, cmd, 0, NULL, NULL);
 }
 
 static int get_remote_service_record(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid)
@@ -705,7 +777,7 @@ static const void *get_profile_interface(const char *profile_id)
                return NULL;
 
        if (!strcmp(profile_id, BT_PROFILE_SOCKETS_ID))
-               return bt_get_sock_interface();
+               return bt_get_socket_interface();
 
        if (!strcmp(profile_id, BT_PROFILE_HIDHOST_ID))
                return bt_get_hidhost_interface();
@@ -716,6 +788,18 @@ static const void *get_profile_interface(const char *profile_id)
        if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID))
                return bt_get_a2dp_interface();
 
+       if (!strcmp(profile_id, BT_PROFILE_AV_RC_ID))
+               return bt_get_avrcp_interface();
+
+       if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_ID))
+               return bt_get_handsfree_interface();
+
+       if (!strcmp(profile_id, BT_PROFILE_GATT_ID))
+               return bt_get_gatt_interface();
+
+       if (!strcmp(profile_id, BT_PROFILE_HEALTH_ID))
+               return bt_get_health_interface();
+
        return NULL;
 }
 
@@ -734,22 +818,62 @@ static int dut_mode_configure(uint8_t enable)
                                        sizeof(cmd), &cmd, 0, NULL, NULL);
 }
 
-static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t len)
+static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t buf_len)
 {
-       uint8_t cmd_buf[sizeof(struct hal_cmd_dut_mode_send) + len];
+       char cmd_buf[IPC_MTU];
        struct hal_cmd_dut_mode_send *cmd = (void *) cmd_buf;
+       size_t len;
 
-       DBG("opcode %u len %u", opcode, len);
+       DBG("opcode %u len %u", opcode, buf_len);
 
        if (!interface_ready())
                return BT_STATUS_NOT_READY;
 
        cmd->opcode = opcode;
-       cmd->len = len;
+       cmd->len = buf_len;
        memcpy(cmd->data, buf, cmd->len);
 
+       len = sizeof(*cmd) + cmd->len;
+
        return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND,
-                                       sizeof(cmd_buf), cmd, 0, NULL, NULL);
+                                               len, cmd, 0, NULL, NULL);
+}
+
+static int le_test_mode(uint16_t opcode, uint8_t *buf, uint8_t buf_len)
+{
+       char cmd_buf[IPC_MTU];
+       struct hal_cmd_le_test_mode *cmd = (void *) cmd_buf;
+       size_t len;
+
+       DBG("opcode %u len %u", opcode, buf_len);
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->opcode = opcode;
+       cmd->len = buf_len;
+       memcpy(cmd->data, buf, cmd->len);
+
+       len = sizeof(*cmd) + cmd->len;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE,
+                                               len, cmd, 0, NULL, NULL);
+}
+
+static int config_hci_snoop_log(uint8_t enable)
+{
+       const char *property;
+
+       DBG("enable %u", enable);
+
+       property = enable ? "bluetooth.start" : "bluetooth.stop";
+
+       if (property_set(property, "snoop") < 0) {
+               error("Failed to set %s=snoop", property);
+               return BT_STATUS_FAIL;
+       }
+
+       return BT_STATUS_SUCCESS;
 }
 
 static const bt_interface_t bluetooth_if = {
@@ -775,7 +899,9 @@ static const bt_interface_t bluetooth_if = {
        .ssp_reply = ssp_reply,
        .get_profile_interface = get_profile_interface,
        .dut_mode_configure = dut_mode_configure,
-       .dut_mode_send = dut_mode_send
+       .dut_mode_send = dut_mode_send,
+       .le_test_mode = le_test_mode,
+       .config_hci_snoop_log = config_hci_snoop_log,
 };
 
 static const bt_interface_t *get_bluetooth_interface(void)
@@ -791,6 +917,8 @@ static int close_bluetooth(struct hw_device_t *device)
 
        cleanup();
 
+       free(device);
+
        return 0;
 }
 
diff --git a/android/hal-gatt.c b/android/hal-gatt.c
new file mode 100644 (file)
index 0000000..93dc066
--- /dev/null
@@ -0,0 +1,1354 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+#include "hal-utils.h"
+
+static const btgatt_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+       return cbs != NULL;
+}
+
+static void gatt_id_from_hal(btgatt_gatt_id_t *to,
+                                               struct hal_gatt_gatt_id *from)
+{
+       memcpy(&to->uuid, from->uuid, sizeof(to->uuid));
+       to->inst_id = from->inst_id;
+}
+
+static void gatt_id_to_hal(struct hal_gatt_gatt_id *to, btgatt_gatt_id_t *from)
+{
+       memcpy(to->uuid, &from->uuid, sizeof(from->uuid));
+       to->inst_id = from->inst_id;
+}
+
+static void srvc_id_from_hal(btgatt_srvc_id_t *to,
+                                               struct hal_gatt_srvc_id *from)
+{
+       memcpy(&to->id.uuid, from->uuid, sizeof(to->id.uuid));
+       to->id.inst_id = from->inst_id;
+       to->is_primary = from->is_primary;
+}
+
+static void srvc_id_to_hal(struct hal_gatt_srvc_id *to, btgatt_srvc_id_t *from)
+{
+       memcpy(to->uuid, &from->id.uuid, sizeof(from->id.uuid));
+       to->inst_id = from->id.inst_id;
+       to->is_primary = from->is_primary;
+}
+
+/* Client Event Handlers */
+
+static void handle_register_client(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_register_client *ev = buf;
+
+       if (cbs->client->register_client_cb)
+               cbs->client->register_client_cb(ev->status, ev->client_if,
+                                               (bt_uuid_t *) ev->app_uuid);
+}
+
+static void handle_scan_result(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_scan_result *ev = buf;
+       uint8_t ad[62];
+
+       if (len != sizeof(*ev) + ev->len ) {
+               error("gatt: invalid scan result event, aborting");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Java assumes that passed data has 62 bytes */
+       memset(ad, 0, sizeof(ad));
+       memcpy(ad, ev->adv_data, ev->len > sizeof(ad) ? sizeof(ad) : ev->len);
+
+       if (cbs->client->scan_result_cb)
+               cbs->client->scan_result_cb((bt_bdaddr_t *) ev->bda, ev->rssi,
+                                                                       ad);
+}
+
+static void handle_connect(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_connect *ev = buf;
+
+       if (cbs->client->open_cb)
+               cbs->client->open_cb(ev->conn_id, ev->status, ev->client_if,
+                                               (bt_bdaddr_t *) ev->bda);
+}
+
+static void handle_disconnect(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_disconnect *ev = buf;
+
+       if (cbs->client->close_cb)
+               cbs->client->close_cb(ev->conn_id, ev->status, ev->client_if,
+                                               (bt_bdaddr_t *) ev->bda);
+}
+
+static void handle_search_complete(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_search_complete *ev = buf;
+
+       if (cbs->client->search_complete_cb)
+               cbs->client->search_complete_cb(ev->conn_id, ev->status);
+}
+
+static void handle_search_result(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_search_result *ev = buf;
+       btgatt_srvc_id_t srvc_id;
+
+       srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+
+       if (cbs->client->search_result_cb)
+               cbs->client->search_result_cb(ev->conn_id, &srvc_id);
+}
+
+static void handle_get_characteristic(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_get_characteristic *ev = buf;
+       btgatt_gatt_id_t char_id;
+       btgatt_srvc_id_t srvc_id;
+
+       srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+       gatt_id_from_hal(&char_id, &ev->char_id);
+
+       if (cbs->client->get_characteristic_cb)
+               cbs->client->get_characteristic_cb(ev->conn_id, ev->status,
+                                                       &srvc_id, &char_id,
+                                                       ev->char_prop);
+}
+
+static void handle_get_descriptor(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_get_descriptor *ev = buf;
+       btgatt_gatt_id_t descr_id;
+       btgatt_gatt_id_t char_id;
+       btgatt_srvc_id_t srvc_id;
+
+       srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+       gatt_id_from_hal(&char_id, &ev->char_id);
+       gatt_id_from_hal(&descr_id, &ev->descr_id);
+
+       if (cbs->client->get_descriptor_cb)
+               cbs->client->get_descriptor_cb(ev->conn_id, ev->status,
+                                               &srvc_id, &char_id, &descr_id);
+}
+
+static void handle_get_included_service(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_get_inc_service *ev = buf;
+       btgatt_srvc_id_t srvc_id;
+       btgatt_srvc_id_t incl_srvc_id;
+
+       srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+       srvc_id_from_hal(&incl_srvc_id, &ev->incl_srvc_id);
+
+       if (cbs->client->get_included_service_cb)
+               cbs->client->get_included_service_cb(ev->conn_id, ev->status,
+                                                               &srvc_id,
+                                                               &incl_srvc_id);
+}
+
+static void handle_register_for_notification(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_reg_for_notif *ev = buf;
+       btgatt_gatt_id_t char_id;
+       btgatt_srvc_id_t srvc_id;
+
+       srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+       gatt_id_from_hal(&char_id, &ev->char_id);
+
+       if (cbs->client->register_for_notification_cb)
+               cbs->client->register_for_notification_cb(ev->conn_id,
+                                                               ev->registered,
+                                                               ev->status,
+                                                               &srvc_id,
+                                                               &char_id);
+}
+
+static void handle_notify(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_notify *ev = buf;
+       btgatt_notify_params_t params;
+
+       if (len != sizeof(*ev) + ev->len ) {
+               error("gatt: invalid notify event, aborting");
+               exit(EXIT_FAILURE);
+       }
+
+       memset(&params, 0, sizeof(params));
+       memcpy(params.value, ev->value, ev->len);
+       memcpy(&params.bda, ev->bda, sizeof(params.bda));
+
+       srvc_id_from_hal(&params.srvc_id, &ev->srvc_id);
+       gatt_id_from_hal(&params.char_id, &ev->char_id);
+
+       params.len = ev->len;
+       params.is_notify = ev->is_notify;
+
+       if (cbs->client->notify_cb)
+               cbs->client->notify_cb(ev->conn_id, &params);
+}
+
+static void handle_read_characteristic(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_read_characteristic *ev = buf;
+       btgatt_read_params_t params;
+
+       if (len != sizeof(*ev) + ev->data.len ) {
+               error("gatt: invalid read characteristic event, aborting");
+               exit(EXIT_FAILURE);
+       }
+
+       memset(&params, 0, sizeof(params));
+
+       srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+       gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+       gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+       memcpy(&params.value.value, ev->data.value, ev->data.len);
+
+       params.value_type = ev->data.value_type;
+       params.value.len = ev->data.len;
+       params.status = ev->data.status;
+
+       if (cbs->client->read_characteristic_cb)
+               cbs->client->read_characteristic_cb(ev->conn_id, ev->status,
+                                                               &params);
+}
+
+static void handle_write_characteristic(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_write_characteristic *ev = buf;
+       btgatt_write_params_t params;
+
+       memset(&params, 0, sizeof(params));
+
+       srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+       gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+       gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+       params.status = ev->data.status;
+
+       if (cbs->client->write_characteristic_cb)
+               cbs->client->write_characteristic_cb(ev->conn_id, ev->status,
+                                                               &params);
+}
+
+static void handle_read_descriptor(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_read_descriptor *ev = buf;
+       btgatt_read_params_t params;
+
+       if (len != sizeof(*ev) + ev->data.len ) {
+               error("gatt: invalid read descriptor event, aborting");
+               exit(EXIT_FAILURE);
+       }
+
+       memset(&params, 0, sizeof(params));
+
+       srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+       gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+       gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+       memcpy(&params.value.value, ev->data.value, ev->data.len);
+
+       params.value_type = ev->data.value_type;
+       params.value.len = ev->data.len;
+       params.status = ev->data.status;
+
+       if (cbs->client->read_descriptor_cb)
+               cbs->client->read_descriptor_cb(ev->conn_id, ev->status,
+                                                               &params);
+}
+
+static void handle_write_descriptor(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_write_descriptor *ev = buf;
+       btgatt_write_params_t params;
+
+       memset(&params, 0, sizeof(params));
+
+       srvc_id_from_hal(&params.srvc_id, &ev->data.srvc_id);
+       gatt_id_from_hal(&params.char_id, &ev->data.char_id);
+       gatt_id_from_hal(&params.descr_id, &ev->data.descr_id);
+
+       params.status = ev->data.status;
+
+       if (cbs->client->write_descriptor_cb)
+               cbs->client->write_descriptor_cb(ev->conn_id, ev->status,
+                                                               &params);
+}
+
+static void handle_execute_write(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_exec_write *ev = buf;
+
+       if (cbs->client->execute_write_cb)
+               cbs->client->execute_write_cb(ev->conn_id, ev->status);
+}
+
+static void handle_read_remote_rssi(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_read_remote_rssi *ev = buf;
+
+       if (cbs->client->read_remote_rssi_cb)
+               cbs->client->read_remote_rssi_cb(ev->client_if,
+                                               (bt_bdaddr_t *) ev->address,
+                                               ev->rssi, ev->status);
+}
+
+static void handle_listen(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_client_listen *ev = buf;
+
+       if (cbs->client->listen_cb)
+               cbs->client->listen_cb(ev->status, ev->server_if);
+}
+
+/* Server Event Handlers */
+
+static void handle_register_server(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_register *ev = buf;
+
+       if (cbs->server->register_server_cb)
+               cbs->server->register_server_cb(ev->status, ev->server_if,
+                                               (bt_uuid_t *) &ev->uuid);
+}
+
+static void handle_connection(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_connection *ev = buf;
+
+       if (cbs->server->connection_cb)
+               cbs->server->connection_cb(ev->conn_id, ev->server_if,
+                                               ev->connected,
+                                               (bt_bdaddr_t *) &ev->bdaddr);
+}
+
+static void handle_service_added(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_service_added *ev = buf;
+       btgatt_srvc_id_t srvc_id;
+
+       srvc_id_from_hal(&srvc_id, &ev->srvc_id);
+
+       if (cbs->server->service_added_cb)
+               cbs->server->service_added_cb(ev->status, ev->server_if,
+                                               &srvc_id, ev->srvc_handle);
+}
+
+static void handle_included_service_added(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_inc_srvc_added *ev = buf;
+
+       if (cbs->server->included_service_added_cb)
+               cbs->server->included_service_added_cb(ev->status,
+                                                       ev->server_if,
+                                                       ev->srvc_handle,
+                                                       ev->incl_srvc_handle);
+}
+
+static void handle_characteristic_added(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_characteristic_added *ev = buf;
+
+       if (cbs->server->characteristic_added_cb)
+               cbs->server->characteristic_added_cb(ev->status, ev->server_if,
+                                                       (bt_uuid_t *) &ev->uuid,
+                                                       ev->srvc_handle,
+                                                       ev->char_handle);
+}
+
+static void handle_descriptor_added(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_descriptor_added *ev = buf;
+
+       if (cbs->server->descriptor_added_cb)
+               cbs->server->descriptor_added_cb(ev->status, ev->server_if,
+                                                       (bt_uuid_t *) &ev->uuid,
+                                                       ev->srvc_handle,
+                                                       ev->descr_handle);
+}
+
+static void handle_service_started(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_service_started *ev = buf;
+
+       if (cbs->server->service_started_cb)
+               cbs->server->service_started_cb(ev->status, ev->server_if,
+                                                       ev->srvc_handle);
+}
+
+static void handle_service_stopped(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_service_stopped *ev = buf;
+
+       if (cbs->server->service_stopped_cb)
+               cbs->server->service_stopped_cb(ev->status, ev->server_if,
+                                                       ev->srvc_handle);
+}
+
+static void handle_service_deleted(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_service_deleted *ev = buf;
+
+       if (cbs->server->service_deleted_cb)
+               cbs->server->service_deleted_cb(ev->status, ev->server_if,
+                                                       ev->srvc_handle);
+}
+
+static void handle_request_read(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_request_read *ev = buf;
+
+       if (cbs->server->request_read_cb)
+               cbs->server->request_read_cb(ev->conn_id, ev->trans_id,
+                                               (bt_bdaddr_t *) &ev->bdaddr,
+                                               ev->attr_handle, ev->offset,
+                                               ev->is_long);
+}
+
+static void handle_request_write(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_request_write *ev = buf;
+
+       if (len != sizeof(*ev) + ev->length ) {
+               error("gatt: invalid request write event, aborting");
+               exit(EXIT_FAILURE);
+       }
+
+       if (cbs->server->request_write_cb)
+               cbs->server->request_write_cb(ev->conn_id, ev->trans_id,
+                                               (bt_bdaddr_t *) ev->bdaddr,
+                                               ev->attr_handle, ev->offset,
+                                               ev->length, ev->need_rsp,
+                                               ev->is_prep, ev->value);
+}
+
+static void handle_request_exec_write(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_request_exec_write *ev = buf;
+
+       if (cbs->server->request_exec_write_cb)
+               cbs->server->request_exec_write_cb(ev->conn_id, ev->trans_id,
+                                               (bt_bdaddr_t *) ev->bdaddr,
+                                               ev->exec_write);
+}
+
+static void handle_response_confirmation(void *buf, uint16_t len)
+{
+       struct hal_ev_gatt_server_rsp_confirmation *ev = buf;
+
+       if (cbs->server->response_confirmation_cb)
+               cbs->server->response_confirmation_cb(ev->status, ev->handle);
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+       /* HAL_EV_GATT_CLIENT_REGISTER_CLIENT */
+       { handle_register_client, false,
+               sizeof(struct hal_ev_gatt_client_register_client) },
+       /* HAL_EV_GATT_CLIENT_SCAN_RESULT */
+       { handle_scan_result, true,
+               sizeof(struct hal_ev_gatt_client_scan_result) },
+       /* HAL_EV_GATT_CLIENT_CONNECT */
+       { handle_connect, false, sizeof(struct hal_ev_gatt_client_connect) },
+       /* HAL_EV_GATT_CLIENT_DISCONNECT */
+       { handle_disconnect, false,
+               sizeof(struct hal_ev_gatt_client_disconnect) },
+       /* HAL_EV_GATT_CLIENT_SEARCH_COMPLETE */
+       { handle_search_complete, false,
+               sizeof(struct hal_ev_gatt_client_search_complete) },
+       /* HAL_EV_GATT_CLIENT_SEARCH_RESULT */
+       { handle_search_result, false,
+               sizeof(struct hal_ev_gatt_client_search_result) },
+       /* HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC */
+       { handle_get_characteristic, false,
+               sizeof(struct hal_ev_gatt_client_get_characteristic) },
+       /* HAL_EV_GATT_CLIENT_GET_DESCRIPTOR */
+       { handle_get_descriptor, false,
+               sizeof(struct hal_ev_gatt_client_get_descriptor) },
+       /* HAL_EV_GATT_CLIENT_GET_INC_SERVICE */
+       { handle_get_included_service, false,
+               sizeof(struct hal_ev_gatt_client_get_inc_service) },
+       /* HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF */
+       { handle_register_for_notification, false,
+               sizeof(struct hal_ev_gatt_client_reg_for_notif) },
+       /* HAL_EV_GATT_CLIENT_NOTIFY */
+       { handle_notify, true, sizeof(struct hal_ev_gatt_client_notify) },
+       /* HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC */
+       { handle_read_characteristic, true,
+               sizeof(struct hal_ev_gatt_client_read_characteristic) },
+       /* HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC */
+       { handle_write_characteristic, false,
+               sizeof(struct hal_ev_gatt_client_write_characteristic) },
+       /* HAL_EV_GATT_CLIENT_READ_DESCRIPTOR */
+       { handle_read_descriptor, true,
+               sizeof(struct hal_ev_gatt_client_read_descriptor) },
+       /* HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR */
+       { handle_write_descriptor, false,
+               sizeof(struct hal_ev_gatt_client_write_descriptor) },
+       /* HAL_EV_GATT_CLIENT_EXEC_WRITE */
+       { handle_execute_write, false,
+               sizeof(struct hal_ev_gatt_client_exec_write) },
+       /* HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI */
+       { handle_read_remote_rssi, false,
+               sizeof(struct hal_ev_gatt_client_read_remote_rssi) },
+       /* HAL_EV_GATT_CLIENT_LISTEN */
+       { handle_listen, false, sizeof(struct hal_ev_gatt_client_listen) },
+       /* HAL_EV_GATT_SERVER_REGISTER */
+       { handle_register_server, false,
+               sizeof(struct hal_ev_gatt_server_register) },
+       /* HAL_EV_GATT_SERVER_CONNECTION */
+       { handle_connection, false,
+               sizeof(struct hal_ev_gatt_server_connection) },
+       /* HAL_EV_GATT_SERVER_SERVICE_ADDED */
+       { handle_service_added, false,
+               sizeof(struct hal_ev_gatt_server_service_added) },
+       /* HAL_EV_GATT_SERVER_INC_SRVC_ADDED */
+       { handle_included_service_added, false,
+               sizeof(struct hal_ev_gatt_server_inc_srvc_added) },
+       /* HAL_EV_GATT_SERVER_CHAR_ADDED */
+       { handle_characteristic_added, false,
+               sizeof(struct hal_ev_gatt_server_characteristic_added) },
+       /* HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED */
+       { handle_descriptor_added, false,
+               sizeof(struct hal_ev_gatt_server_descriptor_added) },
+       /* HAL_EV_GATT_SERVER_SERVICE_STARTED */
+       { handle_service_started, false,
+               sizeof(struct hal_ev_gatt_server_service_started) },
+       /* HAL_EV_GATT_SERVER_SERVICE_STOPPED */
+       { handle_service_stopped, false,
+               sizeof(struct hal_ev_gatt_server_service_stopped) },
+       /* HAL_EV_GATT_SERVER_SERVICE_DELETED */
+       { handle_service_deleted, false,
+               sizeof(struct hal_ev_gatt_server_service_deleted) },
+       /* HAL_EV_GATT_SERVER_REQUEST_READ */
+       { handle_request_read, false,
+               sizeof(struct hal_ev_gatt_server_request_read) },
+       /* HAL_EV_GATT_SERVER_REQUEST_WRITE */
+       { handle_request_write, true,
+               sizeof(struct hal_ev_gatt_server_request_write) },
+       /* HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE */
+       { handle_request_exec_write, false,
+               sizeof(struct hal_ev_gatt_server_request_exec_write) },
+       /* HAL_EV_GATT_SERVER_RSP_CONFIRMATION */
+       { handle_response_confirmation, false,
+               sizeof(struct hal_ev_gatt_server_rsp_confirmation) },
+};
+
+/* Client API */
+
+static bt_status_t register_client(bt_uuid_t *uuid)
+{
+       struct hal_cmd_gatt_client_register cmd;
+
+       memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t unregister_client(int client_if)
+{
+       struct hal_cmd_gatt_client_unregister cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.client_if = client_if;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UNREGISTER,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t scan(int client_if, bool start)
+{
+       struct hal_cmd_gatt_client_scan cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.client_if = client_if;
+       cmd.start = start;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr,
+                                                               bool is_direct)
+{
+       struct hal_cmd_gatt_client_connect cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.client_if = client_if;
+       cmd.is_direct = is_direct;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t disconnect(int client_if, const bt_bdaddr_t *bd_addr,
+                                                               int conn_id)
+{
+       struct hal_cmd_gatt_client_disconnect cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.client_if = client_if;
+       cmd.conn_id = conn_id;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISCONNECT,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t listen(int client_if, bool start)
+{
+       struct hal_cmd_gatt_client_listen cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.client_if = client_if;
+       cmd.start = start;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t refresh(int client_if, const bt_bdaddr_t *bd_addr)
+{
+       struct hal_cmd_gatt_client_refresh cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.client_if = client_if;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t search_service(int conn_id, bt_uuid_t *filter_uuid)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_gatt_client_search_service *cmd = (void *) buf;
+       size_t len = sizeof(*cmd);
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       memset(cmd, 0, sizeof(*cmd));
+
+       cmd->conn_id = conn_id;
+
+       if (filter_uuid) {
+               memcpy(cmd->filter_uuid, filter_uuid, sizeof(*filter_uuid));
+               len += sizeof(*filter_uuid);
+               cmd->filtered = 1;
+       }
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_SEARCH_SERVICE,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t get_included_service(int conn_id, btgatt_srvc_id_t *srvc_id,
+                                       btgatt_srvc_id_t *start_incl_srvc_id)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_gatt_client_get_included_service *cmd = (void *) buf;
+       size_t len = sizeof(*cmd);
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->conn_id = conn_id;
+
+       srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+       cmd->continuation = 0;
+
+       if (start_incl_srvc_id) {
+               srvc_id_to_hal(&cmd->incl_srvc_id[0], start_incl_srvc_id);
+               len += sizeof(cmd->incl_srvc_id[0]);
+               cmd->continuation = 1;
+       }
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t get_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+                                               btgatt_gatt_id_t *start_char_id)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_gatt_client_get_characteristic *cmd = (void *) buf;
+       size_t len = sizeof(*cmd);
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->conn_id = conn_id;
+
+       srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+       cmd->continuation = 0;
+
+       if (start_char_id) {
+               gatt_id_to_hal(&cmd->char_id[0], start_char_id);
+               len += sizeof(cmd->char_id[0]);
+               cmd->continuation = 1;
+       }
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t get_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+                                       btgatt_gatt_id_t *char_id,
+                                       btgatt_gatt_id_t *start_descr_id)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_gatt_client_get_descriptor *cmd = (void *) buf;
+       size_t len = sizeof(*cmd);
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->conn_id = conn_id;
+
+       srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+       gatt_id_to_hal(&cmd->char_id, char_id);
+       cmd->continuation = 0;
+
+       if (start_descr_id) {
+               gatt_id_to_hal(&cmd->descr_id[0], start_descr_id);
+               len += sizeof(cmd->descr_id[0]);
+               cmd->continuation = 1;
+       }
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_GET_DESCRIPTOR,
+                                       len, cmd, 0 , NULL, NULL);
+}
+
+static bt_status_t read_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+                                       btgatt_gatt_id_t *char_id,
+                                       int auth_req)
+{
+       struct hal_cmd_gatt_client_read_characteristic cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.conn_id = conn_id;
+       cmd.auth_req = auth_req;
+
+       srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+       gatt_id_to_hal(&cmd.char_id, char_id);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t write_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id,
+                                       btgatt_gatt_id_t *char_id,
+                                       int write_type, int len, int auth_req,
+                                       char *p_value)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_gatt_client_write_characteristic *cmd = (void *) buf;
+       size_t cmd_len = sizeof(*cmd) + len;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->conn_id = conn_id;
+       cmd->write_type = write_type;
+       cmd->len = len;
+       cmd->auth_req = auth_req;
+
+       srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+       gatt_id_to_hal(&cmd->char_id, char_id);
+
+       memcpy(cmd->value, p_value, len);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC,
+                                       cmd_len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t read_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+                                               btgatt_gatt_id_t *char_id,
+                                               btgatt_gatt_id_t *descr_id,
+                                               int auth_req)
+{
+       struct hal_cmd_gatt_client_read_descriptor cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.conn_id = conn_id;
+       cmd.auth_req = auth_req;
+
+       srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+       gatt_id_to_hal(&cmd.char_id, char_id);
+       gatt_id_to_hal(&cmd.descr_id, descr_id);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_READ_DESCRIPTOR,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t write_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id,
+                                       btgatt_gatt_id_t *char_id,
+                                       btgatt_gatt_id_t *descr_id,
+                                       int write_type, int len, int auth_req,
+                                       char *p_value)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_gatt_client_write_descriptor *cmd = (void *) buf;
+       size_t cmd_len = sizeof(*cmd) + len;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->conn_id = conn_id;
+       cmd->write_type = write_type;
+       cmd->len = len;
+       cmd->auth_req = auth_req;
+
+       srvc_id_to_hal(&cmd->srvc_id, srvc_id);
+       gatt_id_to_hal(&cmd->char_id, char_id);
+       gatt_id_to_hal(&cmd->descr_id, descr_id);
+
+       memcpy(cmd->value, p_value, len);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR,
+                                       cmd_len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t execute_write(int conn_id, int execute)
+{
+       struct hal_cmd_gatt_client_execute_write cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.conn_id = conn_id;
+       cmd.execute = execute;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_EXECUTE_WRITE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t register_for_notification(int client_if,
+                                               const bt_bdaddr_t *bd_addr,
+                                               btgatt_srvc_id_t *srvc_id,
+                                               btgatt_gatt_id_t *char_id)
+{
+       struct hal_cmd_gatt_client_register_for_notification cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.client_if = client_if;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+       srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+       gatt_id_to_hal(&cmd.char_id, char_id);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION,
+                               sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t deregister_for_notification(int client_if,
+                                               const bt_bdaddr_t *bd_addr,
+                                               btgatt_srvc_id_t *srvc_id,
+                                               btgatt_gatt_id_t *char_id)
+{
+       struct hal_cmd_gatt_client_deregister_for_notification cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.client_if = client_if;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+       srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+       gatt_id_to_hal(&cmd.char_id, char_id);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION,
+                               sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t read_remote_rssi(int client_if, const bt_bdaddr_t *bd_addr)
+{
+       struct hal_cmd_gatt_client_read_remote_rssi cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.client_if = client_if;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static int get_device_type(const bt_bdaddr_t *bd_addr)
+{
+       struct hal_cmd_gatt_client_get_device_type cmd;
+       uint8_t dev_type;
+       size_t resp_len = sizeof(dev_type);
+       bt_status_t status;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+       status = hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                               HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE,
+                               sizeof(cmd), &cmd, &resp_len, &dev_type, NULL);
+
+       if (status != BT_STATUS_SUCCESS || resp_len != sizeof(dev_type))
+               return 0;
+
+       return dev_type;
+}
+
+static bt_status_t set_adv_data(int server_if, bool set_scan_rsp,
+                               bool include_name, bool include_txpower,
+                               int min_interval, int max_interval,
+                               int appearance, uint16_t manufacturer_len,
+                               char *manufacturer_data)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_gatt_client_set_adv_data *cmd = (void *) buf;
+       size_t cmd_len = sizeof(*cmd) + manufacturer_len;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->server_if = server_if;
+       cmd->set_scan_rsp = set_scan_rsp;
+       cmd->include_name = include_name;
+       cmd->include_txpower = include_txpower;
+       cmd->min_interval = min_interval;
+       cmd->max_interval = max_interval;
+       cmd->appearance = appearance;
+       cmd->manufacturer_len = manufacturer_len;
+
+       memcpy(cmd->manufacturer_data, manufacturer_data, manufacturer_len);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA,
+                                               cmd_len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t test_command(int command, btgatt_test_params_t *params)
+{
+       struct hal_cmd_gatt_client_test_command cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.command = command;
+
+       memcpy(cmd.bda1, params->bda1, sizeof(*params->bda1));
+       memcpy(cmd.uuid1, params->uuid1, sizeof(*params->uuid1));
+
+       cmd.u1 = params->u1;
+       cmd.u2 = params->u2;
+       cmd.u3 = params->u3;
+       cmd.u4 = params->u4;
+       cmd.u5 = params->u5;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_TEST_COMMAND,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+/* Server API */
+
+static bt_status_t register_server(bt_uuid_t *uuid)
+{
+       struct hal_cmd_gatt_server_register cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t unregister_server(int server_if)
+{
+       struct hal_cmd_gatt_server_unregister cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.server_if = server_if;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_UNREGISTER,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr,
+                                                               bool is_direct)
+{
+       struct hal_cmd_gatt_server_connect cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.server_if = server_if;
+       cmd.is_direct = is_direct;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t server_disconnect(int server_if, const bt_bdaddr_t *bd_addr,
+                                                               int conn_id)
+{
+       struct hal_cmd_gatt_server_disconnect cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.server_if = server_if;
+       cmd.conn_id = conn_id;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_DISCONNECT,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t add_service(int server_if, btgatt_srvc_id_t *srvc_id,
+                                                               int num_handles)
+{
+       struct hal_cmd_gatt_server_add_service cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.server_if = server_if;
+       cmd.num_handles = num_handles;
+
+       srvc_id_to_hal(&cmd.srvc_id, srvc_id);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_SERVICE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t add_included_service(int server_if, int service_handle,
+                                               int included_handle)
+{
+       struct hal_cmd_gatt_server_add_inc_service cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.server_if = server_if;
+       cmd.service_handle = service_handle;
+       cmd.included_handle = included_handle;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_SERVER_ADD_INC_SERVICE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t add_characteristic(int server_if, int service_handle,
+                                               bt_uuid_t *uuid, int properties,
+                                               int permissions)
+{
+       struct hal_cmd_gatt_server_add_characteristic cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.server_if = server_if;
+       cmd.service_handle = service_handle;
+       cmd.properties = properties;
+       cmd.permissions = permissions;
+
+       memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t add_descriptor(int server_if, int service_handle,
+                                       bt_uuid_t *uuid, int permissions)
+{
+       struct hal_cmd_gatt_server_add_descriptor cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.server_if = server_if;
+       cmd.service_handle = service_handle;
+       cmd.permissions = permissions;
+
+       memcpy(cmd.uuid, uuid, sizeof(*uuid));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_SERVER_ADD_DESCRIPTOR,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t start_service(int server_if, int service_handle,
+                                                               int transport)
+{
+       struct hal_cmd_gatt_server_start_service cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.server_if = server_if;
+       cmd.service_handle = service_handle;
+       cmd.transport = transport;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_SERVER_START_SERVICE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t stop_service(int server_if, int service_handle)
+{
+       struct hal_cmd_gatt_server_stop_service cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.server_if = server_if;
+       cmd.service_handle = service_handle;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_STOP_SERVICE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t delete_service(int server_if, int service_handle)
+{
+       struct hal_cmd_gatt_server_delete_service cmd;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.server_if = server_if;
+       cmd.service_handle = service_handle;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_SERVER_DELETE_SERVICE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t send_indication(int server_if, int attribute_handle,
+                                       int conn_id, int len, int confirm,
+                                       char *p_value)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_gatt_server_send_indication *cmd = (void *) buf;
+       size_t cmd_len = sizeof(*cmd) + len;
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->server_if = server_if;
+       cmd->attribute_handle = attribute_handle;
+       cmd->conn_id = conn_id;
+       cmd->len = len;
+       cmd->confirm = confirm;
+
+       memcpy(cmd->value, p_value, len);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_SERVER_SEND_INDICATION,
+                                       cmd_len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t send_response(int conn_id, int trans_id, int status,
+                                               btgatt_response_t *response)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_gatt_server_send_response *cmd = (void *) buf;
+       size_t cmd_len = sizeof(*cmd) + sizeof(*response);
+
+       memset(buf, 0 , IPC_MTU);
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->conn_id = conn_id;
+       cmd->trans_id = trans_id;
+       cmd->status = status;
+       cmd->handle = response->attr_value.handle;
+       cmd->offset = response->attr_value.offset;
+       cmd->auth_req = response->attr_value.auth_req;
+       cmd->len = response->attr_value.len;
+
+       memcpy(cmd->data, response->attr_value.value, cmd->len);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_GATT,
+                                       HAL_OP_GATT_SERVER_SEND_RESPONSE,
+                                       cmd_len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t init(const btgatt_callbacks_t *callbacks)
+{
+       struct hal_cmd_register_module cmd;
+       int ret;
+
+       DBG("");
+
+       if (interface_ready())
+               return BT_STATUS_DONE;
+
+       cbs = callbacks;
+
+       hal_ipc_register(HAL_SERVICE_ID_GATT, ev_handlers,
+                               sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+       cmd.service_id = HAL_SERVICE_ID_GATT;
+       cmd.mode = HAL_MODE_DEFAULT;
+
+       ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       if (ret != BT_STATUS_SUCCESS) {
+               cbs = NULL;
+               hal_ipc_unregister(HAL_SERVICE_ID_GATT);
+       }
+
+       return ret;
+}
+
+static void cleanup(void)
+{
+       struct hal_cmd_unregister_module cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return;
+
+       cbs = NULL;
+
+       cmd.service_id = HAL_SERVICE_ID_GATT;
+
+       hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       hal_ipc_unregister(HAL_SERVICE_ID_GATT);
+}
+
+static btgatt_client_interface_t client_iface = {
+       .register_client = register_client,
+       .unregister_client = unregister_client,
+       .scan = scan,
+       .connect = connect,
+       .disconnect = disconnect,
+       .listen = listen,
+       .refresh = refresh,
+       .search_service = search_service,
+       .get_included_service = get_included_service,
+       .get_characteristic = get_characteristic,
+       .get_descriptor = get_descriptor,
+       .read_characteristic = read_characteristic,
+       .write_characteristic = write_characteristic,
+       .read_descriptor = read_descriptor,
+       .write_descriptor = write_descriptor,
+       .execute_write = execute_write,
+       .register_for_notification = register_for_notification,
+       .deregister_for_notification = deregister_for_notification,
+       .read_remote_rssi = read_remote_rssi,
+       .get_device_type = get_device_type,
+       .set_adv_data = set_adv_data,
+       .test_command = test_command,
+};
+
+static btgatt_server_interface_t server_iface = {
+       .register_server = register_server,
+       .unregister_server = unregister_server,
+       .connect = server_connect,
+       .disconnect = server_disconnect,
+       .add_service = add_service,
+       .add_included_service = add_included_service,
+       .add_characteristic = add_characteristic,
+       .add_descriptor = add_descriptor,
+       .start_service = start_service,
+       .stop_service = stop_service,
+       .delete_service = delete_service,
+       .send_indication = send_indication,
+       .send_response = send_response,
+};
+
+static btgatt_interface_t iface = {
+       .size = sizeof(iface),
+       .init = init,
+       .cleanup = cleanup,
+       .client = &client_iface,
+       .server = &server_iface,
+};
+
+btgatt_interface_t *bt_get_gatt_interface(void)
+{
+       return &iface;
+}
diff --git a/android/hal-handsfree.c b/android/hal-handsfree.c
new file mode 100644 (file)
index 0000000..d992506
--- /dev/null
@@ -0,0 +1,594 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/properties.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+#define MODE_PROPERTY_NAME "persist.sys.bluetooth.handsfree"
+
+static const bthf_callbacks_t *cbs = NULL;
+
+static bool interface_ready(void)
+{
+       return cbs != NULL;
+}
+
+static void handle_conn_state(void *buf, uint16_t len)
+{
+       struct hal_ev_handsfree_conn_state *ev = buf;
+
+       if (cbs->connection_state_cb)
+               cbs->connection_state_cb(ev->state,
+                                               (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_audio_state(void *buf, uint16_t len)
+{
+       struct hal_ev_handsfree_audio_state *ev = buf;
+
+       if (cbs->audio_state_cb)
+               cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr));
+}
+
+static void handle_vr_state(void *buf, uint16_t len)
+{
+       struct hal_ev_handsfree_vr_state *ev = buf;
+
+       if (cbs->vr_cmd_cb)
+               cbs->vr_cmd_cb(ev->state);
+}
+
+static void handle_answer(void *buf, uint16_t len)
+{
+       if (cbs->answer_call_cmd_cb)
+               cbs->answer_call_cmd_cb();
+}
+
+static void handle_hangup(void *buf, uint16_t len)
+{
+       if (cbs->hangup_call_cmd_cb)
+               cbs->hangup_call_cmd_cb();
+}
+
+static void handle_volume(void *buf, uint16_t len)
+{
+       struct hal_ev_handsfree_volume *ev = buf;
+
+       if (cbs->volume_cmd_cb)
+               cbs->volume_cmd_cb(ev->type, ev->volume);
+}
+
+static void handle_dial(void *buf, uint16_t len)
+{
+       struct hal_ev_handsfree_dial *ev = buf;
+       uint16_t num_len = ev->number_len;
+
+       if (len != sizeof(*ev) + num_len ||
+                       (num_len != 0 && ev->number[num_len - 1] != '\0')) {
+               error("invalid dial event, aborting");
+               exit(EXIT_FAILURE);
+       }
+
+       if (!cbs->dial_call_cmd_cb)
+               return;
+
+       if (ev->number_len)
+               cbs->dial_call_cmd_cb((char *) ev->number);
+       else
+               cbs->dial_call_cmd_cb(NULL);
+}
+
+static void handle_dtmf(void *buf, uint16_t len)
+{
+       struct hal_ev_handsfree_dtmf *ev = buf;
+
+       if (cbs->dtmf_cmd_cb)
+               cbs->dtmf_cmd_cb(ev->tone);
+}
+
+static void handle_nrec(void *buf, uint16_t len)
+{
+       struct hal_ev_handsfree_nrec *ev = buf;
+
+       if (cbs->nrec_cmd_cb)
+               cbs->nrec_cmd_cb(ev->nrec);
+}
+
+static void handle_chld(void *buf, uint16_t len)
+{
+       struct hal_ev_handsfree_chld *ev = buf;
+
+       if (cbs->chld_cmd_cb)
+               cbs->chld_cmd_cb(ev->chld);
+}
+
+static void handle_cnum(void *buf, uint16_t len)
+{
+       if (cbs->cnum_cmd_cb)
+               cbs->cnum_cmd_cb();
+}
+
+static void handle_cind(void *buf, uint16_t len)
+{
+       if (cbs->cind_cmd_cb)
+               cbs->cind_cmd_cb();
+}
+
+static void handle_cops(void *buf, uint16_t len)
+{
+       if (cbs->cops_cmd_cb)
+               cbs->cops_cmd_cb();
+}
+
+static void handle_clcc(void *buf, uint16_t len)
+{
+       if (cbs->clcc_cmd_cb)
+               cbs->clcc_cmd_cb();
+}
+
+static void handle_unknown_at(void *buf, uint16_t len)
+{
+       struct hal_ev_handsfree_unknown_at *ev = buf;
+
+       if (len != sizeof(*ev) + ev->len ||
+                       (ev->len != 0 && ev->buf[ev->len - 1] != '\0')) {
+               error("invalid unknown command event, aborting");
+               exit(EXIT_FAILURE);
+       }
+
+       if (cbs->unknown_at_cmd_cb)
+               cbs->unknown_at_cmd_cb((char *) ev->buf);
+}
+
+static void handle_hsp_key_press(void *buf, uint16_t len)
+{
+       if (cbs->key_pressed_cmd_cb)
+               cbs->key_pressed_cmd_cb();
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+       /* HAL_EV_HANDSFREE_CONN_STATE */
+       { handle_conn_state, false,
+                               sizeof(struct hal_ev_handsfree_conn_state) },
+       /* HAL_EV_HANDSFREE_AUDIO_STATE */
+       { handle_audio_state, false,
+                               sizeof(struct hal_ev_handsfree_audio_state) },
+       /* HAL_EV_HANDSFREE_VR */
+       { handle_vr_state, false, sizeof(struct hal_ev_handsfree_vr_state) },
+       /*HAL_EV_HANDSFREE_ANSWER */
+       { handle_answer, false, 0 },
+       /*HAL_EV_HANDSFREE_HANGUP */
+       { handle_hangup, false, 0 },
+       /* HAL_EV_HANDSFREE_VOLUME */
+       { handle_volume, false, sizeof(struct hal_ev_handsfree_volume) },
+       /* HAL_EV_HANDSFREE_DIAL */
+       { handle_dial, true, sizeof(struct hal_ev_handsfree_dial) },
+       /* HAL_EV_HANDSFREE_DTMF */
+       { handle_dtmf, false, sizeof(struct hal_ev_handsfree_dtmf) },
+       /* HAL_EV_HANDSFREE_NREC */
+       { handle_nrec, false, sizeof(struct hal_ev_handsfree_nrec) },
+       /* HAL_EV_HANDSFREE_CHLD */
+       { handle_chld, false, sizeof(struct hal_ev_handsfree_chld) },
+       /* HAL_EV_HANDSFREE_CNUM */
+       { handle_cnum, false, 0 },
+       /* HAL_EV_HANDSFREE_CIND */
+       { handle_cind, false, 0 },
+       /* HAL_EV_HANDSFREE_COPS */
+       { handle_cops, false, 0 },
+       /* HAL_EV_HANDSFREE_CLCC */
+       { handle_clcc, false, 0 },
+       /* HAL_EV_HANDSFREE_UNKNOWN_AT */
+       { handle_unknown_at, true, sizeof(struct hal_ev_handsfree_unknown_at) },
+       /* HAL_EV_HANDSFREE_HSP_KEY_PRESS */
+       { handle_hsp_key_press, false, 0 },
+};
+
+static uint8_t get_mode(void)
+{
+       char value[PROPERTY_VALUE_MAX];
+
+       if (property_get(MODE_PROPERTY_NAME, value, "") > 0 &&
+                                       (!strcasecmp(value, "hfp")))
+               return HAL_MODE_HANDSFREE_HFP;
+
+       if (property_get(MODE_PROPERTY_NAME, value, "") > 0 &&
+                                       (!strcasecmp(value, "hfp_wbs")))
+               return HAL_MODE_HANDSFREE_HFP_WBS;
+
+       return HAL_MODE_HANDSFREE_HSP_ONLY;
+}
+
+static bt_status_t init(bthf_callbacks_t *callbacks)
+{
+       struct hal_cmd_register_module cmd;
+       int ret;
+
+       DBG("");
+
+       if (interface_ready())
+               return BT_STATUS_DONE;
+
+       cbs = callbacks;
+
+       hal_ipc_register(HAL_SERVICE_ID_HANDSFREE, ev_handlers,
+                               sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+       cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
+       cmd.mode = get_mode();
+
+       ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       if (ret != BT_STATUS_SUCCESS) {
+               cbs = NULL;
+               hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE);
+       }
+
+       return ret;
+}
+
+static bt_status_t handsfree_connect(bt_bdaddr_t *bd_addr)
+{
+       struct hal_cmd_handsfree_connect cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (!bd_addr)
+               return BT_STATUS_PARM_INVALID;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONNECT,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
+{
+       struct hal_cmd_handsfree_disconnect cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (!bd_addr)
+               return BT_STATUS_PARM_INVALID;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                               HAL_OP_HANDSFREE_DISCONNECT, sizeof(cmd), &cmd,
+                               0, NULL, NULL);
+}
+
+static bt_status_t connect_audio(bt_bdaddr_t *bd_addr)
+{
+       struct hal_cmd_handsfree_connect_audio cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (!bd_addr)
+               return BT_STATUS_PARM_INVALID;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                               HAL_OP_HANDSFREE_CONNECT_AUDIO, sizeof(cmd),
+                               &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr)
+{
+       struct hal_cmd_handsfree_disconnect_audio cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (!bd_addr)
+               return BT_STATUS_PARM_INVALID;
+
+       memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                               HAL_OP_HANDSFREE_DISCONNECT_AUDIO, sizeof(cmd),
+                               &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t start_voice_recognition(void)
+{
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_START_VR,
+                                                       0, NULL, 0, NULL, NULL);
+}
+
+static bt_status_t stop_voice_recognition(void)
+{
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_STOP_VR,
+                                                       0, NULL, 0, NULL, NULL);
+}
+
+static bt_status_t volume_control(bthf_volume_type_t type, int volume)
+{
+       struct hal_cmd_handsfree_volume_control cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.type = type;
+       cmd.volume = volume;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                               HAL_OP_HANDSFREE_VOLUME_CONTROL, sizeof(cmd),
+                               &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t device_status_notification(bthf_network_state_t state,
+                                               bthf_service_type_t type,
+                                               int signal, int battery)
+{
+       struct hal_cmd_handsfree_device_status_notif cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.state = state;
+       cmd.type = type;
+       cmd.signal = signal;
+       cmd.battery = battery;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t cops_response(const char *cops)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_handsfree_cops_response *cmd = (void *) buf;
+       size_t len;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (!cops)
+               return BT_STATUS_PARM_INVALID;
+
+       cmd->len = strlen(cops) + 1;
+       memcpy(cmd->buf, cops, cmd->len);
+
+       len = sizeof(*cmd) + cmd->len;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                                               HAL_OP_HANDSFREE_COPS_RESPONSE,
+                                               len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t cind_response(int svc, int num_active, int num_held,
+                                       bthf_call_state_t state, int signal,
+                                       int roam, int batt_chg)
+{
+       struct hal_cmd_handsfree_cind_response cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.svc = svc;
+       cmd.num_active = num_active;
+       cmd.num_held = num_held;
+       cmd.state = state;
+       cmd.signal = signal;
+       cmd.roam = roam;
+       cmd.batt_chg = batt_chg;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_OP_HANDSFREE_CIND_RESPONSE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t formatted_at_response(const char *rsp)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_handsfree_formatted_at_response *cmd = (void *) buf;
+       size_t len;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (!rsp)
+               return BT_STATUS_PARM_INVALID;
+
+       cmd->len = strlen(rsp) + 1;
+       memcpy(cmd->buf, rsp, cmd->len);
+
+       len = sizeof(*cmd) + cmd->len;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t at_response(bthf_at_response_t response, int error)
+{
+       struct hal_cmd_handsfree_at_response cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.response = response;
+       cmd.error = error;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_OP_HANDSFREE_AT_RESPONSE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t clcc_response(int index, bthf_call_direction_t dir,
+                                       bthf_call_state_t state,
+                                       bthf_call_mode_t mode,
+                                       bthf_call_mpty_type_t mpty,
+                                       const char *number,
+                                       bthf_call_addrtype_t type)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_handsfree_clcc_response *cmd = (void *) buf;
+       size_t len;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->index = index;
+       cmd->dir = dir;
+       cmd->state = state;
+       cmd->mode = mode;
+       cmd->mpty = mpty;
+       cmd->type = type;
+
+       if (number) {
+               cmd->number_len = strlen(number) + 1;
+               memcpy(cmd->number, number, cmd->number_len);
+       } else {
+               cmd->number_len = 0;
+       }
+
+       len = sizeof(*cmd) + cmd->number_len;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                                               HAL_OP_HANDSFREE_CLCC_RESPONSE,
+                                               len, cmd, 0, NULL, NULL);
+}
+
+static bt_status_t phone_state_change(int num_active, int num_held,
+                                       bthf_call_state_t state,
+                                       const char *number,
+                                       bthf_call_addrtype_t type)
+{
+       char buf[IPC_MTU];
+       struct hal_cmd_handsfree_phone_state_change *cmd = (void *) buf;
+       size_t len;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd->num_active = num_active;
+       cmd->num_held = num_held;
+       cmd->state = state;
+       cmd->type = type;
+
+       if (number) {
+               cmd->number_len = strlen(number) + 1;
+               memcpy(cmd->number, number, cmd->number_len);
+       } else {
+               cmd->number_len = 0;
+       }
+
+       len = sizeof(*cmd) + cmd->number_len;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_OP_HANDSFREE_PHONE_STATE_CHANGE,
+                                       len, cmd, 0, NULL, NULL);
+}
+
+static void cleanup(void)
+{
+       struct hal_cmd_unregister_module cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return;
+
+       cbs = NULL;
+
+       cmd.service_id = HAL_SERVICE_ID_HANDSFREE;
+
+       hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE);
+}
+
+static bthf_interface_t iface = {
+       .size = sizeof(iface),
+       .init = init,
+       .connect = handsfree_connect,
+       .disconnect = disconnect,
+       .connect_audio = connect_audio,
+       .disconnect_audio = disconnect_audio,
+       .start_voice_recognition = start_voice_recognition,
+       .stop_voice_recognition = stop_voice_recognition,
+       .volume_control = volume_control,
+       .device_status_notification = device_status_notification,
+       .cops_response = cops_response,
+       .cind_response = cind_response,
+       .formatted_at_response = formatted_at_response,
+       .at_response = at_response,
+       .clcc_response = clcc_response,
+       .phone_state_change = phone_state_change,
+       .cleanup = cleanup
+};
+
+bthf_interface_t *bt_get_handsfree_interface(void)
+{
+       return &iface;
+}
diff --git a/android/hal-health.c b/android/hal-health.c
new file mode 100644 (file)
index 0000000..427d4c9
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "hal-log.h"
+#include "hal.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "hal-ipc.h"
+
+static const bthl_callbacks_t *cbacks = NULL;
+
+static bool interface_ready(void)
+{
+       return cbacks != NULL;
+}
+
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
+static const struct hal_ipc_handler ev_handlers[] = {
+};
+
+static bt_status_t register_application(bthl_reg_param_t *reg, int *app_id)
+{
+       uint8_t buf[IPC_MTU];
+       struct hal_cmd_health_reg_app *cmd = (void *) buf;
+       struct hal_cmd_health_mdep *mdep = (void *) buf;
+       struct hal_rsp_health_reg_app rsp;
+       size_t rsp_len = sizeof(rsp);
+       bt_status_t status;
+       uint16_t off, len;
+       int i;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (!reg || !app_id || !reg->application_name)
+               return BT_STATUS_PARM_INVALID;
+
+       memset(buf, 0, IPC_MTU);
+
+       cmd->num_of_mdep = reg->number_of_mdeps;
+
+       off = 0;
+       cmd->app_name_off = off;
+       len = strlen(reg->application_name) + 1;
+       memcpy(cmd->data, reg->application_name, len);
+       off += len;
+
+       if (reg->provider_name) {
+               len = strlen(reg->provider_name) + 1;
+               cmd->provider_name_off = off;
+               memcpy(cmd->data + off, reg->provider_name, len);
+               off += len;
+       }
+
+       if (reg->srv_name) {
+               len = strlen(reg->srv_name) + 1;
+               cmd->service_name_off = off;
+               memcpy(cmd->data + off, reg->srv_name, len);
+               off += len;
+       }
+
+       if (reg->srv_desp) {
+               len = strlen(reg->srv_desp) + 1;
+               cmd->service_descr_off = off;
+               memcpy(cmd->data + off, reg->srv_desp, len);
+               off += len;
+       }
+
+       cmd->len = off;
+       status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+                                               sizeof(*cmd) + cmd->len, &cmd,
+                                                       &rsp_len, &rsp, NULL);
+
+       if (status != BT_STATUS_SUCCESS)
+               return status;
+
+       for (i = 0; i < reg->number_of_mdeps; i++) {
+               memset(buf, 0, IPC_MTU);
+               mdep->role = reg->mdep_cfg[i].mdep_role;
+               mdep->data_type = reg->mdep_cfg[i].data_type;
+               mdep->channel_type = reg->mdep_cfg[i].channel_type;
+
+               if (reg->mdep_cfg[i].mdep_description) {
+                       mdep->descr_len =
+                               strlen(reg->mdep_cfg[i].mdep_description) + 1;
+                       memcpy(mdep->descr, reg->mdep_cfg[i].mdep_description,
+                                                       mdep->descr_len);
+               }
+
+               status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+                                               sizeof(*mdep) + mdep->descr_len,
+                                               buf, 0, NULL, NULL);
+
+               if (status != BT_STATUS_SUCCESS)
+                       return status;
+
+       }
+
+       *app_id = rsp.app_id;
+
+       return status;
+}
+
+static bt_status_t unregister_application(int app_id)
+{
+       struct hal_cmd_health_unreg_app cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.app_id = app_id;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t connect_channel(int app_id, bt_bdaddr_t *bd_addr,
+                                       int mdep_cfg_index, int *channel_id)
+{
+       struct hal_cmd_health_connect_channel cmd;
+       struct hal_rsp_health_connect_channel rsp;
+       size_t len = sizeof(rsp);
+       bt_status_t status;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       if (!bd_addr || !channel_id)
+               return BT_STATUS_PARM_INVALID;
+
+       cmd.app_id = app_id;
+       cmd.mdep_index = mdep_cfg_index;
+       memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+       status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH,
+                                       HAL_OP_HEALTH_CONNECT_CHANNEL,
+                                       sizeof(cmd), &cmd, &len, &rsp, NULL);
+
+       if (status == BT_STATUS_SUCCESS)
+               *channel_id = rsp.channel_id;
+
+       return status;
+}
+
+static bt_status_t destroy_channel(int channel_id)
+{
+       struct hal_cmd_health_destroy_channel cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return BT_STATUS_NOT_READY;
+
+       cmd.channel_id = channel_id;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_DESTROY_CHANNEL,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+}
+
+static bt_status_t init(bthl_callbacks_t *callbacks)
+{
+       struct hal_cmd_register_module cmd;
+       int ret;
+
+       DBG("");
+
+       if (interface_ready())
+               return BT_STATUS_DONE;
+
+       /* store reference to user callbacks */
+       cbacks = callbacks;
+
+       hal_ipc_register(HAL_SERVICE_ID_HEALTH, ev_handlers,
+                               sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
+       cmd.service_id = HAL_SERVICE_ID_HEALTH;
+       cmd.mode = HAL_MODE_DEFAULT;
+
+       ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       if (ret != BT_STATUS_SUCCESS) {
+               cbacks = NULL;
+               hal_ipc_unregister(HAL_SERVICE_ID_HEALTH);
+       }
+
+       return ret;
+}
+
+static void cleanup(void)
+{
+       struct hal_cmd_unregister_module cmd;
+
+       DBG("");
+
+       if (!interface_ready())
+               return;
+
+       cbacks = NULL;
+
+       cmd.service_id = HAL_SERVICE_ID_HEALTH;
+
+       hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       hal_ipc_unregister(HAL_SERVICE_ID_HEALTH);
+}
+
+static bthl_interface_t health_if = {
+       .size = sizeof(health_if),
+       .init = init,
+       .register_application = register_application,
+       .unregister_application = unregister_application,
+       .connect_channel = connect_channel,
+       .destroy_channel = destroy_channel,
+       .cleanup = cleanup
+};
+
+bthl_interface_t *bt_get_health_interface(void)
+{
+       return &health_if;
+}
index 6a6b682..5787b5e 100644 (file)
@@ -23,6 +23,7 @@
 #include "hal-log.h"
 #include "hal.h"
 #include "hal-msg.h"
+#include "ipc-common.h"
 #include "hal-ipc.h"
 
 static const bthh_callbacks_t *cbacks;
@@ -69,6 +70,15 @@ static void handle_proto_mode(void *buf, uint16_t len)
                                                        ev->status, ev->mode);
 }
 
+static void handle_idle_time(void *buf, uint16_t len)
+{
+       struct hal_ev_hidhost_idle_time *ev = buf;
+
+       if (cbacks->idle_time_cb)
+               cbacks->idle_time_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
+                                                               ev->idle_rate);
+}
+
 static void handle_get_report(void *buf, uint16_t len)
 {
        struct hal_ev_hidhost_get_report *ev = buf;
@@ -92,8 +102,10 @@ static void handle_virtual_unplug(void *buf, uint16_t len)
                                                                ev->status);
 }
 
-/* handlers will be called from notification thread context,
- * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
 static const struct hal_ipc_handler ev_handlers[] = {
        {       /* HAL_EV_HIDHOST_CONN_STATE */
                .handler = handle_conn_state,
@@ -110,6 +122,11 @@ static const struct hal_ipc_handler ev_handlers[] = {
                .var_len = false,
                .data_len = sizeof(struct hal_ev_hidhost_proto_mode),
        },
+       {       /* HAL_EV_HIDHOST_IDLE_TIME */
+               .handler = handle_idle_time,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_hidhost_idle_time),
+       },
        {       /* HAL_EV_HIDHOST_GET_REPORT */
                .handler = handle_get_report,
                .var_len = true,
@@ -218,16 +235,8 @@ static bt_status_t get_protocol(bt_bdaddr_t *bd_addr,
 
        memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
 
-       switch (protocol_mode) {
-       case BTHH_REPORT_MODE:
-               cmd.mode = HAL_HIDHOST_REPORT_PROTOCOL;
-               break;
-       case BTHH_BOOT_MODE:
-               cmd.mode = HAL_HIDHOST_BOOT_PROTOCOL;
-               break;
-       default:
-               return BT_STATUS_PARM_INVALID;
-       }
+       /* type match IPC type */
+       cmd.mode = protocol_mode;
 
        return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
                                HAL_OP_HIDHOST_GET_PROTOCOL,
@@ -249,16 +258,8 @@ static bt_status_t set_protocol(bt_bdaddr_t *bd_addr,
 
        memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
 
-       switch (protocol_mode) {
-       case BTHH_REPORT_MODE:
-               cmd.mode = HAL_HIDHOST_REPORT_PROTOCOL;
-               break;
-       case BTHH_BOOT_MODE:
-               cmd.mode = HAL_HIDHOST_BOOT_PROTOCOL;
-               break;
-       default:
-               return BT_STATUS_PARM_INVALID;
-       }
+       /* type match IPC type */
+       cmd.mode = protocol_mode;
 
        return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST,
                                HAL_OP_HIDHOST_SET_PROTOCOL,
@@ -284,19 +285,8 @@ static bt_status_t get_report(bt_bdaddr_t *bd_addr,
        cmd.id = report_id;
        cmd.buf_size = buffer_size;
 
-       switch (report_type) {
-       case BTHH_INPUT_REPORT:
-               cmd.type = HAL_HIDHOST_INPUT_REPORT;
-               break;
-       case BTHH_OUTPUT_REPORT:
-               cmd.type = HAL_HIDHOST_OUTPUT_REPORT;
-               break;
-       case BTHH_FEATURE_REPORT:
-               cmd.type = HAL_HIDHOST_FEATURE_REPORT;
-               break;
-       default:
-               return BT_STATUS_PARM_INVALID;
-       }
+       /* type match IPC type */
+       cmd.type = report_type;
 
        return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT,
                        sizeof(cmd), &cmd, 0, NULL, NULL);
@@ -306,7 +296,7 @@ static bt_status_t set_report(bt_bdaddr_t *bd_addr,
                                                bthh_report_type_t report_type,
                                                char *report)
 {
-       uint8_t buf[BLUEZ_HAL_MTU];
+       uint8_t buf[IPC_MTU];
        struct hal_cmd_hidhost_set_report *cmd = (void *) buf;
 
        DBG("");
@@ -321,19 +311,8 @@ static bt_status_t set_report(bt_bdaddr_t *bd_addr,
        cmd->len = strlen(report);
        memcpy(cmd->data, report, cmd->len);
 
-       switch (report_type) {
-       case BTHH_INPUT_REPORT:
-               cmd->type = HAL_HIDHOST_INPUT_REPORT;
-               break;
-       case BTHH_OUTPUT_REPORT:
-               cmd->type = HAL_HIDHOST_OUTPUT_REPORT;
-               break;
-       case BTHH_FEATURE_REPORT:
-               cmd->type = HAL_HIDHOST_FEATURE_REPORT;
-               break;
-       default:
-               return BT_STATUS_PARM_INVALID;
-       }
+       /* type match IPC type */
+       cmd->type = report_type;
 
        return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT,
                                sizeof(*cmd) + cmd->len, buf, 0, NULL, NULL);
@@ -341,7 +320,7 @@ static bt_status_t set_report(bt_bdaddr_t *bd_addr,
 
 static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data)
 {
-       uint8_t buf[BLUEZ_HAL_MTU];
+       uint8_t buf[IPC_MTU];
        struct hal_cmd_hidhost_send_data *cmd = (void *) buf;
 
        DBG("");
@@ -377,6 +356,7 @@ static bt_status_t init(bthh_callbacks_t *callbacks)
                                sizeof(ev_handlers)/sizeof(ev_handlers[0]));
 
        cmd.service_id = HAL_SERVICE_ID_HIDHOST;
+       cmd.mode = HAL_MODE_DEFAULT;
 
        ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
                                        sizeof(cmd), &cmd, 0, NULL, NULL);
index 57f4c13..43e2709 100644 (file)
@@ -124,12 +124,16 @@ Core Service (ID 0)
        Opcode 0x01 - Register module command/response
 
                Command parameters: Service id (1 octet)
+                                   Mode (1 octet)
                Response parameters: <none>
 
                In case a command is sent for an undeclared service ID, it will
                be rejected. Also there will be no notifications for undeclared
                service ID.
 
+               Valid Mode values: 0x00 = Default Mode
+                                  0xXX = as defined by service
+
                In case of an error, the error response will be returned.
 
        Opcode 0x02 - Unregister module command/response
@@ -145,6 +149,10 @@ Bluetooth Core HAL (ID 1)
 
 Android HAL name: "bluetooth" (BT_HARDWARE_MODULE_ID)
 
+       Service modes: 0x00 = Enable BR/EDR/LE if supported (default)
+                      0x01 = Enable BR/EDR only
+                      0x02 = Enable LE only
+
 Commands and responses:
 
        Opcode 0x00 - Error response
@@ -317,38 +325,41 @@ Notifications:
 
        Opcode 0x81 - Adapter State Changed notification
 
-               Notifications parameters: State (1 octect)
+               Notifications parameters: State (1 octet)
 
                Valid state values: 0x00 = Off
                                    0x01 = On
 
        Opcode 0x82 - Adapter Properties Changed notification
 
-               Notification parameters: Status (1 octect)
+               Notification parameters: Status (1 octet)
                                         Num properties (1 octet)
-                                        Type[i] (1 octect)
-                                        Length[i] (2 octets)
-                                        Value[i] (variable)
+                                        Type # (1 octet)
+                                        Length # (2 octets)
+                                        Value # (variable)
+                                        ...
 
        Opcode 0x83 - Remote Device Properties notification
 
-               Notification parameters: Status (1 octect)
+               Notification parameters: Status (1 octet)
                                         Remote address (6 octets)
                                         Num properties (1 octet)
-                                        Type[i] (1 octect)
-                                        Length[i] (2 octets)
-                                        Value[i] (variable)
+                                        Type # (1 octet)
+                                        Length # (2 octets)
+                                        Value # (variable)
+                                        ...
 
        Opcode 0x84 - Device Found notification
 
                Notification parameters: Num properties (1 octet)
-                                        Type[i] (1 octect)
-                                        Length[i] (2 octets)
-                                        Value[i] (variable)
+                                        Type # (1 octet)
+                                        Length # (2 octets)
+                                        Value # (variable)
+                                        ...
 
        Opcode 0x85 - Discovery State Changed notification
 
-               Notifications parameters: State (1 octect)
+               Notifications parameters: State (1 octet)
 
        Opcode 0x86 - PIN Request notification
 
@@ -366,7 +377,7 @@ Notifications:
 
        Opcode 0x88 - Bond State Changed notification
 
-               Notification parameters: Status (1 octect)
+               Notification parameters: Status (1 octet)
                                         Remote address (6 octets)
                                         Bond state (1 octet)
 
@@ -376,19 +387,19 @@ Notifications:
 
        Opcode 0x89 - ACL State Changed notification
 
-               Notification parameters: Status (1 octect)
+               Notification parameters: Status (1 octet)
                                         Remote address (6 octets)
                                         ACL state (1 octet)
 
        Opcode 0x8a - DUT Mode Receive notification
 
-               Notification parameters: Opcode (2 octects)
+               Notification parameters: Opcode (2 octets)
                                         Length  (1 octet)
                                         Data (variable)
 
        Opcode 0x8b - LE Test Mode notification
 
-               Notification parameters: Status (1 octect)
+               Notification parameters: Status (1 octet)
                                         Num packets (2 octets)
 
 
@@ -614,14 +625,20 @@ Notifications:
                                      0x01 = Boot
                                      0xff = Unsupported
 
-       Opcode 0x84 - Get Report notification
+       Opcode 0x84 - Idle Time notification
+
+               Notification parameters: Remote address (6 octets)
+                                        Status (1 octet)
+                                        Idle time (2 octets)
+
+       Opcode 0x85 - Get Report notification
 
                Notification parameters: Remote address (6 octets)
                                         Status (1 octet)
                                         Report length (2 octets)
                                         Report data (variable)
 
-       Opcode 0x85 - Virtual Unplug notification
+       Opcode 0x86 - Virtual Unplug notification
 
                Notification parameters: Remote address (6 octets)
                                         Status (1 octet)
@@ -709,7 +726,7 @@ Notifications:
 
        Opcode 0x81 - Control State notification
 
-               Notification parameters: Control state (1 octect)
+               Notification parameters: Control state (1 octet)
                                         Status (1 octet)
                                         Local role (1 octet)
                                         Interface name (17 octet)
@@ -723,7 +740,7 @@ Notifications:
 
        Opcode 0x82 - Connection State notification
 
-               Notification parameters: Connection state (1 octect)
+               Notification parameters: Connection state (1 octet)
                                         Status (1 octet)
                                         Remote address (6 octets)
                                         Local role (1 octet)
@@ -743,6 +760,12 @@ Bluetooth Handsfree HAL (ID 5)
 
 Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID)
 
+       Service modes: 0x00 = Headset Profile only mode (default)
+                      0x01 = Handsfree Profile (narrowband speech)
+                      0x02 = Handsfree Profile (narrowband and wideband speech)
+
+Commands and responses:
+
        Opcode 0x00 - Error response
 
                Response parameters: Status (1 octet)
@@ -881,7 +904,7 @@ Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID)
                                    Call mode (1 octet)
                                    Call multiparty type (1 octet)
                                    Call number type (1 octet)
-                                   Call number (variable)
+                                   Call number (string)
                Response parameters: <none>
 
                Valid call directions: 0x00 = Outgoing
@@ -913,7 +936,7 @@ Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID)
                                    Number of held calls (1 octet)
                                    Call setup state (1 octet)
                                    Call number type (1 octet)
-                                   Call number (variable)
+                                   Call number (string)
                Response parameters: <none>
 
                Valid call setup states: 0x00 = Active
@@ -933,7 +956,7 @@ Notifications:
 
        Opcode 0x81 - Connection State notification
 
-               Notification parameters: Connection state (1 octect)
+               Notification parameters: Connection state (1 octet)
                                         Remote address (6 octets)
 
                Valid connection states: 0x00 = Disconnected
@@ -944,7 +967,7 @@ Notifications:
 
        Opcode 0x82 - Audio State notification
 
-               Notification parameters: Audio state (1 octect)
+               Notification parameters: Audio state (1 octet)
                                         Remote address (6 octets)
 
                Valid audio states: 0x00 = Disconnected
@@ -970,6 +993,7 @@ Notifications:
        Opcode 0x86 - Volume Command notification
 
                Notification parameters: Volume type (1 octet)
+                                        Volume (1 octet)
 
                Valid volume types: 0x00 = Speaker
                                    0x01 = Microphone
@@ -1063,7 +1087,7 @@ Notifications:
 
        Opcode 0x81 - Connection State notification
 
-               Notification parameters: Connection state (1 octect)
+               Notification parameters: Connection state (1 octet)
                                         Remote address (6 octets)
 
                Valid connection states: 0x00 = Disconnected
@@ -1073,7 +1097,7 @@ Notifications:
 
        Opcode 0x82 - Audio State notification
 
-               Notification parameters: Audio state (1 octect)
+               Notification parameters: Audio state (1 octet)
                                         Remote address (6 octets)
 
                Valid connection states: 0x00 = Remote suspend
@@ -1086,6 +1110,8 @@ Bluetooth Health HAL (ID 7)
 
 Android HAL name: "health" (BT_PROFILE_HEALTH_ID)
 
+Commands and responses:
+
        Opcode 0x00 - Error response
 
                Response parameters: Status (1 octet)
@@ -1103,27 +1129,37 @@ Android HAL name: "health" (BT_PROFILE_HEALTH_ID)
 
        Opcode 0x01 - Register Application command/response
 
-               Command parameters: Application name (string)
-                                   Provider name (string)
-                                   Service name (string)
-                                   Service description (string)
-                                   Number of MDEP (1 octet)
-                                   MDEP Role[i] (1 octet)
-                                   Data type[i] (1 octet)
-                                   Channel type[i] (1 octet)
-                                   MDEP description (string)
+               Command parameters: Number of MDEP (1 octet)
+                                   Application name offset (2 octets)
+                                   Provider name offset (2 octets)
+                                   Service name offset (2 octets)
+                                   Service description offset (2 octets)
+                                   Data length (2 octets)
+                                   Data (data length)
                Response parameters: Application ID (2 octets)
 
+               Strings are null terminated.
                In case of an error, the error response will be returned.
 
-       Opcode 0x02 - Unregister Application command/response
+       Opcode 0x02 - Register Application MDEP data command/response
+
+               Command parameters: MDEP Role (1 octet)
+                                   Data type (1 octet)
+                                   Channel type (1 octet)
+                                   MDEP description length (2 octets)
+                                   MDEP description (MDEP desciption length)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x03 - Unregister Application command/response
 
                Command parameters: Application ID (2 octets)
                Response parameters: <none>
 
                In case of an error, the error response will be returned.
 
-       Opcode 0x03 - Connect Channel command/response
+       Opcode 0x04 - Connect Channel command/response
 
                Command parameters: Application ID (2 octets)
                                    Remote address (6 octets)
@@ -1132,7 +1168,7 @@ Android HAL name: "health" (BT_PROFILE_HEALTH_ID)
 
                In case of an error, the error response will be returned.
 
-       Opcode 0x04 - Destroy Channel command/response
+       Opcode 0x05 - Destroy Channel command/response
 
                Command parameters: Channel ID (2 octets)
                Response parameters: <none>
@@ -1143,7 +1179,7 @@ Notifications:
 
        Opcode 0x81 - Application Registration State notification
 
-               Notification parameters: Application ID (2 octects)
+               Notification parameters: Application ID (2 octets)
                                         Application state (1 octet)
 
                Valid application states: 0x00 = Registration success
@@ -1153,7 +1189,7 @@ Notifications:
 
        Opcode 0x82 - Channel State notification
 
-               Notification parameters: Application ID (2 octects)
+               Notification parameters: Application ID (2 octets)
                                         Remote address (6 octets)
                                         MDEP index (1 octet)
                                         Channel ID (2 octets)
@@ -1172,20 +1208,229 @@ Bluetooth Remote Control HAL (ID 8)
 
 Android HAL name: "avrcp" (BT_PROFILE_AV_RC_ID)
 
+Commands and responses:
+
        Opcode 0x00 - Error response
-       Opcode 0x01 - Get Play Status command/response
-       Opcode 0x02 - List Player Application Attributes command/response
-       Opcode 0x03 - List Player Application Values command/response
-       Opcode 0x04 - Get Player Application Values command/response
-       Opcode 0x05 - Get Player Application Attributes Text command/response
-       Opcode 0x06 - Get Player Application Values Text command/response
-       Opcode 0x07 - Get Element Attributes Text command/response
-       Opcode 0x08 - Set Player Attributes Value command/response
-       Opcode 0x09 - Register Notification command/response
 
-       Opcode 0x81 - Get Play Status notification
-       Opcode 0x82 - List Player Application Attributes notification
-       ...
+               Response parameters: Status (1 octet)
+
+               Valid status values: 0x01 = Fail
+                                    0x02 = Not ready
+                                    0x03 = No memory
+                                    0x04 = Busy
+                                    0x05 = Done (already completed)
+                                    0x06 = Unsupported
+                                    0x07 = Parameter invalid
+                                    0x08 = Unhandled
+                                    0x09 = Authentication failure
+                                    0x0a = Remote device down
+
+       Opcode 0x01 - Get Play Status Response command/response
+
+               Command parameters: Status (1 octet)
+                                   Duration (4 octets)
+                                   Position (4 octets)
+
+               In case of an error, the error response will be returned.
+
+               Valid status values: 0x00 = Stopped
+                                    0x01 = Playing
+                                    0x02 = Paused
+                                    0x03 = Fwd seek
+                                    0x04 = Rev seek
+                                    0xff = Error
+
+       Opcode 0x02 - List Player Attributes Response command/response
+
+               Command parameters: Number of attributes (1 octet)
+                                   Attribute # (1 octet)
+                                   ...
+
+               In case of an error, the error response will be returned.
+
+               Valid attributes: 0x01 = Equalizer
+                                 0x02 = Repead
+                                 0x03 = Shuffle
+                                 0x04 = Scan
+
+       Opcode 0x03 - List Player Values Response command/response
+
+               Command parameters: Number of values (1 octet)
+                                   Value # (1 octet)
+                                   ...
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x04 - Get Player Values Response command/response
+
+               Command parameters: Number of attributes (1 octet)
+                                   Attribute # (1 octet)
+                                   Value # (1 octet)
+                                   ...
+
+               In case of an error, the error response will be returned.
+
+               Valid attributes: Same as in List Player Attributes
+
+       Opcode 0x05 - Get Player Attributes Text Response command/response
+
+               Command parameters: Number of attributes (1 octet)
+                                   Attribute # (1 octet)
+                                   Attribute # text length (1 octet)
+                                   Attribute # text (variable)
+                                   ...
+
+               In case of an error, the error response will be returned.
+
+               Valid attributes: Same as in List Player Attributes
+
+       Opcode 0x06 - Get Player Values Text Response command/response
+
+               Command parameters: Number of values (1 octet)
+                                   Value # (1 octet)
+                                   Value # text length (1 octet)
+                                   Value # text (variable)
+                                   ...
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x07 - Get Element Attributes Text Response command/response
+
+               Command parameters: Number of elements (1 octet)
+                                   Element # (1 octet)
+                                   Element # text length (1 octet)
+                                   Element # text (variable)
+                                   ...
+
+               In case of an error, the error response will be returned.
+
+               Valid elements: 0x01 = Title
+                               0x02 = Artist
+                               0x03 = Album
+                               0x04 = Track Number
+                               0x05 = Number of Tracks
+                               0x06 = Genre
+                               0x06 = Duration
+
+       Opcode 0x08 - Set Player Attributes Value Response command/response
+
+               Command parameters: Status (1 octet)
+
+               In case of an error, the error response will be returned.
+
+               Valid status values: Same as in Get Play Status Response
+
+       Opcode 0x09 - Register Notification Response command/response
+
+               Command parameters: Event (1 octet)
+                                   Type (1 octet)
+                                   Data length (1 octet)
+                                   Data (variable)
+
+               In case of an error, the error response will be returned.
+
+               Valid event values: 0x01 = Status Changed
+                                   0x02 = Track Changed
+                                   0x03 = Track Reached End
+                                   0x04 = Track Reached Start
+                                   0x05 = Position Changed
+                                   0x08 = Setting Changed
+
+               Valid type values : 0x00 = Interim
+                                   0x01 = Changed
+
+       Opcode 0x0a - Set Volume command/response
+
+               Command parameters: Value (1 octet)
+
+               In case of an error, the error response will be returned.
+
+Notifications:
+
+       Opcode 0x81 - Remote Features notification
+
+               Notification parameters: Remote address (6 octets)
+                                        Features (1 octet)
+
+               Valid features values : 0x00 = None
+                                       0x01 = Metadata
+                                       0x02 = Absolute Volume
+                                       0x03 = Browse
+
+       Opcode 0x82 - Get Play Status notification
+
+               Notification parameters: <none>
+
+       Opcode 0x83 - List Player Attributes notification
+
+               Notification parameters: <none>
+
+       Opcode 0x84 - List Player Values notification
+
+               Notification parameters: Attribute (1 octet)
+
+               Valid attribute values: Same as in List Player Attributes
+
+       Opcode 0x85 - Get Player Values notification
+
+               Notification parameters: Number of attributes (1 octet)
+                                        Attribute # (1 octet)
+                                        ...
+
+               Valid attribute values: Same as in List Player Attributes
+
+       Opcode 0x86 - Get Player Attributes Text notification
+
+               Notification parameters: Number of attributes (1 octet)
+                                        Attribute # (1 octet)
+                                        ...
+
+               Valid attribute values: Same as in List Player Attributes
+
+       Opcode 0x87 - Get Player Values Text notification
+
+               Notification parameters: Attribute (1 octet)
+                                        Number of values (1 octet)
+                                        Value # (1 octet)
+                                        ...
+
+               Valid attribute values: Same as in List Player Attributes
+
+       Opcode 0x88 - Set Player Values notification
+
+               Notification parameters: Number of attributes (1 octet)
+                                        Attribute # (1 octet)
+                                        Value # (1 octet)
+                                        ...
+
+               Valid attribute values: Same as in List Player Attributes
+
+       Opcode 0x89 - Get Element Attributes notification
+
+               Notification parameters: Number of attributes (1 octet)
+                                        Attribute # (1 octet)
+                                        ...
+
+               Valid attribute values: Same as in Get Element Attribute
+
+       Opcode 0x8a - Register Notification notification
+
+               Notification parameters: Event (1 octet)
+                                        Parameter (4 octets)
+
+               Valid event values: Same as in Register Notification
+
+       Opcode 0x8b - Volume Changed notification
+
+               Notification parameters: Volume (1 octet)
+                                        Type (1 octet)
+
+               Valid type values: Same as in Register Notification
+
+       Opcode 0x8c - Passthrough Command notification
+
+               Notification parameters: ID (1 octet)
+                                        State (1 octet)
 
 
 Bluetooth GATT HAL (ID 9)
@@ -1193,68 +1438,642 @@ Bluetooth GATT HAL (ID 9)
 
 Android HAL name: "gatt" (BT_PROFILE_GATT_ID)
 
+Structures:
+
+       GATT Service ID: UUID (16 octets)
+                        Instance ID (1 octet)
+                        Is Primary (1 octet)
+
+       GATT Included Service ID: UUID (16 octets)
+                                 Instance ID (1 octet)
+                                 Is Primary (1 octet)
+
+       GATT Characteristic ID: UUID (16 octets)
+                               Instance ID (1 octet)
+
+       GATT Descriptor ID: UUID (16 octets)
+                           Instance ID (1 octet)
+
+Commands and responses:
+
        Opcode 0x00 - Error response
+
+               Response parameters: Status (1 octet)
+
+               Valid status values: 0x01 = Fail
+                                    0x02 = Not ready
+                                    0x03 = No memory
+                                    0x04 = Busy
+                                    0x05 = Done (already completed)
+                                    0x06 = Unsupported
+                                    0x07 = Parameter invalid
+                                    0x08 = Unhandled
+                                    0x09 = Authentication failure
+                                    0x0a = Remote device down
+
        Opcode 0x01 - Register Client command/response
+
+               Command parameters: Service UUID (16 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
        Opcode 0x02 - Unregister Client command/response
+
+               Command parameters: Client Interface (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
        Opcode 0x03 - Scan command/response
+
+               Command parameters: Client Interface (4 octets)
+                                   Start (1 octet)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
        Opcode 0x04 - Connect Device command/response
+
+               Command parameters: Client Interface (4 octets)
+                                   Remote address (6 octets)
+                                   Is Direct (1 octet)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
        Opcode 0x05 - Disconnect Device command/response
-       Opcode 0x06 - Refresh command/response
-       Opcode 0x07 - Search Service command/response
-       Opcode 0x08 - Get Included Service command/response
-       Opcode 0x09 - Get Characteristic command/response
-       Opcode 0x0a - Get Descriptor command/response
-       Opcode 0x0b - Read Characteristic command/response
-       Opcode 0x0c - Write Characteristic command/response
-       Opcode 0x0d - Read Descriptor command/response
-       Opcode 0x0e - Write Descriptor command/response
-       Opcode 0x0f - Execute Write command/response
-       Opcode 0x10 - Register For Notification command/response
-       Opcode 0x11 - Deregister For Notification command/response
-       Opcode 0x12 - Read Remote RSSI command/response
-       Opcode 0x13 - Get Device Type command/response
-       Opcode 0x14 - Test Command command/response
-       Opcode 0x15 - Register Server command/response
-       Opcode 0x16 - Unregister Server command/response
-       Opcode 0x17 - Connect Peripheral command/response
-       Opcode 0x18 - Disconnect Peripheral command/response
-       Opcode 0x19 - Add Service command/response
-       Opcode 0x1a - Add Included Service command/response
-       Opcode 0x1b - Add Characteristic command/response
-       Opcode 0x1c - Add Descriptor command/response
-       Opcode 0x1d - Start Service command/response
-       Opcode 0x1e - Stop Service command/response
-       Opcode 0x1f - Delete Service command/response
-       Opcode 0x20 - Send Indication command/response
-       Opcode 0x21 - Send Response command/response
+
+               Command parameters: Client Interface (4 octets)
+                                   Remote address (6 octets)
+                                   Connection ID (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x06 - Listen command/response
+
+               Command parameters: Client Interface (4 octets)
+                                   Start (1 octet)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x07 - Refresh command/response
+
+               Command parameters: Client Interface (4 octets)
+                                   Remote address (6 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x08 - Search Service command/response
+
+               Command parameters: Connection ID (4 octets)
+                                   Filtered (1 octet)
+                                   Filter UUID (16 octets)
+               Response parameters: <none>
+
+               Filter UUID shall only be present when Filtered is non-zero.
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x09 - Get Included Service command/response
+
+               Command parameters: Connection ID (4 octets)
+                                   GATT Service ID (18 octets)
+                                   Continuation (1 octet)
+                                   GATT Included Service ID (18 octets)
+                                   ...
+               Response parameters: <none>
+
+               GATT Included Service ID shall only be present when Continuation is non-zero.
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x0a - Get Characteristic command/response
+
+               Command parameters: Connection ID (4 octets)
+                                   GATT Service ID (18 octets)
+                                   Continuation (1 octet)
+                                   GATT Characteristic ID (17 octets)
+                                   ...
+               Response parameters: <none>
+
+               GATT Characteristic ID shall only be present when Continuation is non-zero.
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x0b - Get Descriptor command/response
+
+               Command parameters: Connection ID (4 octets)
+                                   GATT Service ID (18 octets)
+                                   GATT Characteristic ID (17 octets)
+                                   Continuation (1 octet)
+                                   GATT Descriptor ID (17 octets)
+                                   ...
+               Response parameters: <none>
+
+               GATT Descriptor ID shall only be present when Continuation is non-zero.
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x0c - Read Characteristic command/response
+
+               Command parameters: Connection ID (4 octets)
+                                   GATT Service ID (18 octets)
+                                   GATT Characteristic ID (17 octets)
+                                   Authorization (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x0d - Write Characteristic command/response
+
+               Command parameters: Connection ID (4 octets)
+                                   GATT Service ID (18 octets)
+                                   GATT Characteristic ID (17 octets)
+                                   Write Type (4 octets)
+                                   Length (4 octets)
+                                   Authorization Req. (4 octets)
+                                   Value (variable)
+               Response parameters: <none>
+
+               Valid Write Type: 0x01 = No response
+                                 0x02 = Default
+                                 0x03 = Prepare
+                                 0x04 = Signed
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x0e - Read Descriptor command/response
+
+               Command parameters: Connection ID (4 octets)
+                                   GATT Service ID (18 octets)
+                                   GATT Characteristic ID (17 octets)
+                                   GATT Descriptor ID (17 octets)
+                                   Authorization Req. (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x0f - Write Descriptor command/response
+
+               Command parameters: Connection ID (4 octets)
+                                   GATT Service ID (18 octets)
+                                   GATT Characteristic ID (17 octets)
+                                   GATT Descriptor ID (17 octets)
+                                   Write Type (4 octets)
+                                   Length (4 octets)
+                                   Authorization Req. (4 octets)
+                                   Value (variable)
+               Response parameters: <none>
+
+               Valid Write Type: 0x01 = No response
+                                 0x02 = Default
+                                 0x03 = Prepare
+                                 0x04 = Signed
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x10 - Execute Write command/response
+
+               Command parameters: Connection ID (4 octets)
+                                   Execute (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x11 - Register For Notification command/response
+
+               Command parameters: Client Interface (4 octets)
+                                   Remote address (6 octets)
+                                   GATT Service ID (18 octets)
+                                   GATT Characteristic ID (17 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x12 - Deregister For Notification command/response
+
+               Command parameters: Client Interface (4 octets)
+                                   Remote address (6 octets)
+                                   GATT Service ID (18 octets)
+                                   GATT Characteristic ID (17 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x13 - Read Remote RSSI command/response
+
+               Command parameters: Client Interface (4 octets)
+                                   Remote address (6 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x14 - Get Device Type command/response
+
+               Command parameters: Remote address (6 octets)
+               Response parameters: Device Type
+
+               Valid Device Type: 0x01 = BREDR
+                                  0x02 = BLE
+                                  0x03 = DUAL
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x15 - Set Advertising data command/response
+
+               Command parameters: Server Interface (4 octets)
+                                   Set Scan Resp. (1 octet)
+                                   Include Name (1 octet)
+                                   Include TX Power (1 octet)
+                                   Min. Interval (4 octets)
+                                   Max. Interval (4 octets)
+                                   Appearance (4 octets)
+                                   Manufacturer Len. (2 octets)
+                                   Manufacturer Data (variable)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x16 - Test Command command/response
+
+               Command parameters: Command (4 octets)
+                                   Address (6 octets)
+                                   UUID (16 octets)
+                                   U1 (2 octets)
+                                   U2 (2 octets)
+                                   U3 (2 octets)
+                                   U4 (2 octets)
+                                   U5 (2 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x17 - Register Server command/response
+
+               Command parameters: UUID (16 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x18 - Unregister Server command/response
+
+               Command parameters: Server (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x19 - Connect Peripheral command/response
+
+               Command parameters: Server (4 octets)
+                                   Remote address (6 octes)
+                                   Is Direct (1 octet)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x1a - Disconnect Peripheral command/response
+
+               Command parameters: Server (4 octets)
+                                   Remote address (6 octes)
+                                   Connection ID (1 octet)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x1b - Add Service command/response
+
+               Command parameters: Server (4 octets)
+                                   GATT Service ID (18 octets)
+                                   Number of Handles (4 octet)
+               Response parameters: <none>
+
+               Valid GATT Service ID: UUID (16 octets)
+                                      Instance ID (1 octet)
+                                      Is Primary (1 octet)
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x1c - Add Included Service command/response
+
+               Command parameters: Server (4 octets)
+                                   Service handle (4 octets)
+                                   Included handle (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x1d - Add Characteristic command/response
+
+               Command parameters: Server (4 octets)
+                                   Service handle (4 octets)
+                                   UUID (16 octets)
+                                   Properties (4 octets)
+                                   Permissions (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x1e - Add Descriptor command/response
+
+               Command parameters: Server (4 octets)
+                                   Service handle (4 octets)
+                                   UUID (16 octets)
+                                   Permissions (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x1f - Start Service command/response
+
+               Command parameters: Server (4 octets)
+                                   Service handle (4 octets)
+                                   Transport (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x20 - Stop Service command/response
+
+               Command parameters: Server (4 octets)
+                                   Service handle (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x21 - Delete Service command/response
+
+               Command parameters: Server (4 octets)
+                                   Service handle (4 octets)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x22 - Send Indication command/response
+
+               Command parameters: Server (4 octets)
+                                   Attribute handle (4 octets)
+                                   Connection ID (4 octets)
+                                   Length (4 octets)
+                                   Confirmation (4 octets)
+                                   Value (variable)
+               Response parameters: <none>
+
+               In case of an error, the error response will be returned.
+
+       Opcode 0x23 - Send Response command/response
+
+               Command parameters: Connection ID (4 octets)
+                                   Transaction ID (4 octets)
+                                   Handle (2 octets)
+                                   Offset (2 octets)
+                                   Auth Request (1 octect)
+                                   Status (4 octets)
+                                   GATT Response (4 octets)
+               Response parameters: <none>
+
+               Valid GATT Response: GATT Value (607 octets)
+                                    Handle (2 octets)
+
+               Valid GATT Value: Value (600 octets)
+                                 Handle (2 octets)
+                                 Offset (2 octets)
+                                 Length (2 octets)
+                                 Authentication Request (1 octet)
+
+               In case of an error, the error response will be returned.
+
+Notifications:
 
        Opcode 0x81 - Register Client notification
+
+               Notification parameters: Status (4 octets)
+                                        Client Interface (4 octets)
+                                        UUID (16 octets)
+
        Opcode 0x82 - Scan Result notification
+
+               Notification parameters: Address (6 octets)
+                                        RSSI (4 octets)
+                                        Length (2 octets)
+                                        Data (variable)
+
        Opcode 0x83 - Connect Device notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+                                        Client Interface (4 octets)
+                                        Address (6 octets)
+
        Opcode 0x84 - Disconnect Device notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+                                        Client Interface (4 octets)
+                                        Address (6 octets)
+
        Opcode 0x85 - Search Complete notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+
        Opcode 0x86 - Search Result notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        GATT Service ID (18 octets)
+
        Opcode 0x87 - Get Characteristic notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+                                        GATT Service ID (18 octets)
+                                        GATT Characteristic ID (17 octets)
+                                        Char Prop. (4 octets)
+
        Opcode 0x88 - Get Descriptor notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+                                        GATT Service ID (18 octets)
+                                        GATT Characteristic ID (17 octets)
+                                        GATT Descriptor ID (17 octets)
+
        Opcode 0x89 - Get Included Service notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+                                        GATT Service ID (18 octets)
+                                        GATT Included Service ID (18 octets)
+
        Opcode 0x8a - Register For Notification notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Registered (4 octets)
+                                        Status (4 octets)
+                                        GATT Service ID (18 octets)
+                                        GATT Characteristic ID (17 octets)
+
        Opcode 0x8b - Notify notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Address (6 octets)
+                                        GATT Service ID (18 octets)
+                                        GATT Characteristic ID (17 octets)
+                                        Is Notify (1 octet)
+                                        Length (2 octets)
+                                        Value (variable)
+
        Opcode 0x8c - Read Characteristic notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+                                        GATT Read Parameters (variable)
+
+               Valid GATT Read Parameters: GATT Service ID (18 octets)
+                                           GATT Characteristic ID (17 octets)
+                                           GATT Descriptor ID (17 octets)
+                                           Value Type (4 octets)
+                                           Status (1 octet)
+                                           Length (2 octets)
+                                           Value (variable)
+
        Opcode 0x8d - Write Characteristic notification
-       Opcode 0x8e - Execute Write notification
-       Opcode 0x8f - Read Descriptor notification
-       Opcode 0x90 - Write Descriptor notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+                                        GATT Write Parameters (53 octets)
+
+               Valid GATT Write Parameters: GATT Service ID (18 octets)
+                                            GATT Characteristic ID (17 octets)
+                                            GATT Description ID (17 octets)
+                                            Status (1 octet)
+
+       Opcode 0x8e - Read Descriptor notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+                                        GATT Read Parameters (variable)
+
+               Valid GATT Read Parameters: As described in Read Characteristic
+
+       Opcode 0x8f - Write Descriptor notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+                                        GATT Write Parameters (53 octets)
+
+               Valid GATT Write Parameters: As described in Write Characteristic
+
+       Opcode 0x90 - Execute Write notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Status (4 octets)
+
        Opcode 0x91 - Read Remote RSSI notification
-       Opcode 0x92 - Register Server notification
-       Opcode 0x93 - Connection notification
-       Opcode 0x94 - Service Added notification
-       Opcode 0x95 - Included Service Added notification
-       Opcode 0x96 - Characteristic Added notification
-       Opcode 0x97 - Descriptor Added notification
-       Opcode 0x98 - Service Started notification
-       Opcode 0x99 - Service Stopped notification
-       Opcode 0x9a - Service Deleted notification
-       Opcode 0x9b - Request Read notification
-       Opcode 0x9c - Request Write notification
-       Opcode 0x9d - Request Execute Write notification
-       Opcode 0x9e - Response Confirmation notification
+
+               Notification parameters: Client (4 octets)
+                                        Address (6 octets)
+                                        RSSI (4 octets)
+                                        Status (4 octets)
+
+       Opcode 0x92 - Listen notification
+
+               Notification parameters: Status (4 octets)
+                                        Server Interface (4 octets)
+
+       Opcode 0x93 - Register Server notification
+
+               Notification parameters: Status (4 octets)
+                                        Server (4 octets)
+                                        UUID (16 octets)
+
+       Opcode 0x94 - Connection notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Server (4 octets)
+                                        Connected (4 octets)
+                                        Address (6 octets)
+
+       Opcode 0x95 - Service Added notification
+
+               Notification parameters: Status (4 octets)
+                                        Server (4 octets)
+                                        GATT Service ID (18 octets)
+                                        Service Handle (4 octets)
+
+       Opcode 0x96 - Included Service Added notification
+
+               Notification patemeters: Status (4 octets)
+                                        Server (4 octets)
+                                        Service Handle (4 octets)
+                                        Included Service Handle (4 octets)
+
+       Opcode 0x97 - Characteristic Added notification
+
+               Notification parameters: Status (4 octets)
+                                        Server (4 octets)
+                                        UUID (16 octets)
+                                        Service Handle (4 octets)
+                                        Characteristic Handle (4 octets)
+
+       Opcode 0x98 - Descriptor Added notification
+
+               Notification parameters: Status (4 octets)
+                                        Server (4 octets)
+                                        UUID (6 octets)
+                                        Service Handle (4 octets)
+                                        Descriptor Handle (4 octets)
+
+       Opcode 0x99 - Service Started notification
+
+               Notification parameters: Status (4 octets)
+                                        Server (4 octets)
+                                        Service Handle (4 octets)
+
+       Opcode 0x9a - Service Stopped notification
+
+               Notification parameters: Status (4 octets)
+                                        Server (4 octets)
+                                        Service Handle (4 octets)
+
+       Opcode 0x9b - Service Deleted notification
+
+               Notification parameters: Status (4 octets)
+                                        Server (4 octets)
+                                        Service Handle (4 octets)
+
+       Opcode 0x9c - Request Read notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Trans ID (4 octets)
+                                        Address (6 octets)
+                                        Attribute Handle (4 octets)
+                                        Offset (4 octets)
+                                        Is Long (1 octet)
+
+       Opcode 0x9d - Request Write notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Trans ID (4 octets)
+                                        Address (6 octets)
+                                        Attribute Handle (4 octets)
+                                        Offset (4 octets)
+                                        Length (4 octets)
+                                        Need Response (4 octets)
+                                        Is Prepare (1 octet)
+                                        Value (variable)
+
+       Opcode 0x9e - Request Execute Write notification
+
+               Notification parameters: Connection ID (4 octets)
+                                        Trans ID (4 octets)
+                                        Address (6 octets)
+                                        Execute Write (4 octets)
+
+       Opcode 0x9f - Response Confirmation notification
+
+               Notification parameters: Status (4 octets)
+                                        Handle (4 octets)
index b19704a..8f5babe 100644 (file)
@@ -20,7 +20,6 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <stdbool.h>
-#include <errno.h>
 #include <poll.h>
 #include <unistd.h>
 #include <stdint.h>
 #include "hal.h"
 #include "hal-msg.h"
 #include "hal-log.h"
+#include "ipc-common.h"
 #include "hal-ipc.h"
 
-#define CONNECT_TIMEOUT (5 * 1000)
-#define SERVICE_NAME "bluetoothd"
+#define CONNECT_TIMEOUT (10 * 1000)
 
 static int cmd_sk = -1;
 static int notif_sk = -1;
@@ -65,7 +64,7 @@ void hal_ipc_unregister(uint8_t service)
 
 static void handle_msg(void *buf, ssize_t len)
 {
-       struct hal_hdr *msg = buf;
+       struct ipc_hdr *msg = buf;
        const struct hal_ipc_handler *handler;
        uint8_t opcode;
 
@@ -100,8 +99,10 @@ static void handle_msg(void *buf, ssize_t len)
                exit(EXIT_FAILURE);
        }
 
-       /* opcode is used as table offset and must be adjusted as events start
-        * with HAL_MINIMUM_EVENT offset */
+       /*
+        * opcode is used as table offset and must be adjusted as events start
+        * with HAL_MINIMUM_EVENT offset
+        */
        opcode = msg->opcode - HAL_MINIMUM_EVENT;
 
        /* if opcode is valid */
@@ -131,7 +132,7 @@ static void *notification_handler(void *data)
        struct iovec iv;
        struct cmsghdr *cmsg;
        char cmsgbuf[CMSG_SPACE(sizeof(int))];
-       char buf[BLUEZ_HAL_MTU];
+       char buf[IPC_MTU];
        ssize_t ret;
        int fd;
 
@@ -160,8 +161,12 @@ static void *notification_handler(void *data)
 
                /* socket was shutdown */
                if (ret == 0) {
-                       if (cmd_sk == -1)
+                       pthread_mutex_lock(&cmd_sk_mutex);
+                       if (cmd_sk == -1) {
+                               pthread_mutex_unlock(&cmd_sk_mutex);
                                break;
+                       }
+                       pthread_mutex_unlock(&cmd_sk_mutex);
 
                        error("Notification socket closed, aborting");
                        exit(EXIT_FAILURE);
@@ -259,8 +264,8 @@ bool hal_ipc_init(void)
        }
 
        /* Start Android Bluetooth daemon service */
-       if (property_set("ctl.start", SERVICE_NAME) < 0) {
-               error("Failed to start service %s", SERVICE_NAME);
+       if (property_set("bluetooth.start", "daemon") < 0) {
+               error("Failed to set bluetooth.start=daemon");
                close(sk);
                return false;
        }
@@ -284,10 +289,10 @@ bool hal_ipc_init(void)
        close(sk);
 
        err = pthread_create(&notif_th, NULL, notification_handler, NULL);
-       if (err < 0) {
+       if (err) {
                notif_th = 0;
-               error("Failed to start notification thread: %d (%s)", -err,
-                                                       strerror(-err));
+               error("Failed to start notification thread: %d (%s)", err,
+                                                       strerror(err));
                close(cmd_sk);
                cmd_sk = -1;
                close(notif_sk);
@@ -300,8 +305,10 @@ bool hal_ipc_init(void)
 
 void hal_ipc_cleanup(void)
 {
+       pthread_mutex_lock(&cmd_sk_mutex);
        close(cmd_sk);
        cmd_sk = -1;
+       pthread_mutex_unlock(&cmd_sk_mutex);
 
        shutdown(notif_sk, SHUT_RD);
 
@@ -315,9 +322,9 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
        ssize_t ret;
        struct msghdr msg;
        struct iovec iv[2];
-       struct hal_hdr cmd;
+       struct ipc_hdr cmd;
        char cmsgbuf[CMSG_SPACE(sizeof(int))];
-       struct hal_status s;
+       struct ipc_status s;
        size_t s_len = sizeof(s);
 
        if (cmd_sk < 0) {
@@ -396,7 +403,7 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
        }
 
        if (cmd.service_id != service_id) {
-               error("Invalid service id (%u vs %u), aborting",
+               error("Invalid service id (0x%x vs 0x%x), aborting",
                                                cmd.service_id, service_id);
                exit(EXIT_FAILURE);
        }
@@ -407,13 +414,13 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
        }
 
        if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) {
-               error("Invalid opcode received (%u vs %u), aborting",
+               error("Invalid opcode received (0x%x vs 0x%x), aborting",
                                                cmd.opcode, opcode);
                exit(EXIT_FAILURE);
        }
 
        if (cmd.opcode == HAL_OP_STATUS) {
-               struct hal_status *s = rsp;
+               struct ipc_status *s = rsp;
 
                if (sizeof(*s) != cmd.len) {
                        error("Invalid status length, aborting");
index 9bd024d..63ff61b 100644 (file)
@@ -25,7 +25,7 @@
 #define LOG_WARN " W"
 #define LOG_ERROR " E"
 #define LOG_DEBUG " D"
-#define ALOG(pri, tag, fmt, arg...) printf(tag pri": " fmt"\n", ##arg)
+#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg)
 #endif
 
 #define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg)
index 80c4a25..09bd9a0 100644 (file)
  *
  */
 
-#define BLUEZ_HAL_MTU 1024
-
 static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket";
 
-struct hal_hdr {
-       uint8_t  service_id;
-       uint8_t  opcode;
-       uint16_t len;
-       uint8_t  payload[0];
-} __attribute__((packed));
-
 #define HAL_MINIMUM_EVENT              0x81
 
 #define HAL_SERVICE_ID_CORE            0
 #define HAL_SERVICE_ID_BLUETOOTH       1
-#define HAL_SERVICE_ID_SOCK            2
+#define HAL_SERVICE_ID_SOCKET          2
 #define HAL_SERVICE_ID_HIDHOST         3
 #define HAL_SERVICE_ID_PAN             4
 #define HAL_SERVICE_ID_HANDSFREE       5
@@ -49,7 +40,7 @@ struct hal_hdr {
 
 /* Core Service */
 
-#define HAL_STATUS_SUCCESS             0x00
+#define HAL_STATUS_SUCCESS             IPC_STATUS_SUCCESS
 #define HAL_STATUS_FAILED              0x01
 #define HAL_STATUS_NOT_READY           0x02
 #define HAL_STATUS_NOMEM               0x03
@@ -61,14 +52,16 @@ struct hal_hdr {
 #define HAL_STATUS_AUTH_FAILURE                0x09
 #define HAL_STATUS_REMOTE_DEVICE_DOWN  0x0a
 
-#define HAL_OP_STATUS                  0x00
-struct hal_status {
-       uint8_t code;
-} __attribute__((packed));
+#define HAL_OP_STATUS                  IPC_OP_STATUS
+
+#define HAL_MODE_DEFAULT               0x00
+#define HAL_MODE_BREDR                 0x01
+#define HAL_MODE_LE                    0x02
 
 #define HAL_OP_REGISTER_MODULE         0x01
 struct hal_cmd_register_module {
        uint8_t service_id;
+       uint8_t mode;
 } __attribute__((packed));
 
 #define HAL_OP_UNREGISTER_MODULE       0x02
@@ -107,15 +100,32 @@ struct hal_cmd_get_adapter_prop {
 #define HAL_PROP_DEVICE_CLASS                  0x04
 #define HAL_PROP_DEVICE_TYPE                   0x05
 #define HAL_PROP_DEVICE_SERVICE_REC            0x06
+struct hal_prop_device_service_rec {
+       uint8_t uuid[16];
+       uint16_t channel;
+       uint8_t name_len;
+       uint8_t name[];
+} __attribute__((packed));
+
 #define HAL_PROP_DEVICE_FRIENDLY_NAME          0x0a
 #define HAL_PROP_DEVICE_RSSI                   0x0b
 #define HAL_PROP_DEVICE_VERSION_INFO           0x0c
+struct hal_prop_device_info {
+       uint8_t version;
+       uint16_t sub_version;
+       uint16_t manufacturer;
+} __attribute__((packed));
+
 #define HAL_PROP_DEVICE_TIMESTAMP              0xFF
 
 #define HAL_ADAPTER_SCAN_MODE_NONE             0x00
 #define HAL_ADAPTER_SCAN_MODE_CONN             0x01
 #define HAL_ADAPTER_SCAN_MODE_CONN_DISC        0x02
 
+#define HAL_TYPE_BREDR                         0x01
+#define HAL_TYPE_LE                            0x02
+#define HAL_TYPE_DUAL                          0x03
+
 #define HAL_OP_SET_ADAPTER_PROP                0x05
 struct hal_cmd_set_adapter_prop {
        uint8_t  type;
@@ -214,22 +224,29 @@ struct hal_cmd_le_test_mode {
 
 /* Bluetooth Socket HAL api */
 
-#define HAL_OP_SOCK_LISTEN             0x01
-struct hal_cmd_sock_listen {
-       uint8_t  type;
-       uint8_t  name[256];
-       uint8_t  uuid[16];
-       uint16_t channel;
-       uint8_t  flags;
+#define HAL_SOCK_RFCOMM                0x01
+#define HAL_SOCK_SCO           0x02
+#define HAL_SOCK_L2CAP         0x03
+
+#define HAL_SOCK_FLAG_ENCRYPT  0x01
+#define HAL_SOCK_FLAG_AUTH     0x02
+
+#define HAL_OP_SOCKET_LISTEN           0x01
+struct hal_cmd_socket_listen {
+       uint8_t type;
+       uint8_t name[256];
+       uint8_t uuid[16];
+       int32_t channel;
+       uint8_t flags;
 } __attribute__((packed));
 
-#define HAL_OP_SOCK_CONNECT            0x02
-struct hal_cmd_sock_connect {
-       uint8_t  bdaddr[6];
-       uint8_t  type;
-       uint8_t  uuid[16];
-       uint16_t channel;
-       uint8_t  flags;
+#define HAL_OP_SOCKET_CONNECT          0x02
+struct hal_cmd_socket_connect {
+       uint8_t bdaddr[6];
+       uint8_t type;
+       uint8_t uuid[16];
+       int32_t channel;
+       uint8_t flags;
 } __attribute__((packed));
 
 /* Bluetooth HID Host HAL API */
@@ -368,8 +385,477 @@ struct hal_cmd_pan_disconnect {
        uint8_t bdaddr[6];
 } __attribute__((packed));
 
-/* Notifications and confirmations */
+#define HAL_HEALTH_MDEP_ROLE_SOURCE    0x00
+#define HAL_HEALTH_MDEP_ROLE_SINK      0x01
+
+#define HAL_HEALTH_CHANNEL_TYPE_RELIABLE       0x00
+#define HAL_HEALTH_CHANNEL_TYPE_STREAMING      0x01
+#define HAL_HEALTH_CHANNEL_TYPE_ANY            0x02
+
+#define HAL_OP_HEALTH_REG_APP          0x01
+struct hal_cmd_health_reg_app {
+       uint8_t  num_of_mdep;
+       uint16_t app_name_off;
+       uint16_t provider_name_off;
+       uint16_t service_name_off;
+       uint16_t service_descr_off;
+       uint16_t len;
+       uint8_t  data[0];
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_MDEP             0x02
+struct hal_cmd_health_mdep {
+       uint8_t  role;
+       uint8_t  data_type;
+       uint8_t  channel_type;
+       uint16_t descr_len;
+       uint8_t  descr[0];
+} __attribute__((packed));
+
+struct hal_rsp_health_reg_app {
+       uint16_t app_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_UNREG_APP                0x03
+struct hal_cmd_health_unreg_app {
+       uint16_t app_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_CONNECT_CHANNEL  0x04
+struct hal_cmd_health_connect_channel {
+       uint16_t app_id;
+       uint8_t  bdaddr[6];
+       uint8_t  mdep_index;
+} __attribute__((packed));
+
+struct hal_rsp_health_connect_channel {
+       uint16_t  channel_id;
+} __attribute__((packed));
+
+#define HAL_OP_HEALTH_DESTROY_CHANNEL  0x05
+struct hal_cmd_health_destroy_channel {
+       uint16_t channel_id;
+} __attribute__((packed));
+
+/* Handsfree HAL API */
+
+#define HAL_MODE_HANDSFREE_HSP_ONLY            HAL_MODE_DEFAULT
+#define HAL_MODE_HANDSFREE_HFP                 0x01
+#define HAL_MODE_HANDSFREE_HFP_WBS             0x02
+
+#define HAL_OP_HANDSFREE_CONNECT               0x01
+struct hal_cmd_handsfree_connect {
+       uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_DISCONNECT            0x02
+struct hal_cmd_handsfree_disconnect {
+       uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_CONNECT_AUDIO         0x03
+struct hal_cmd_handsfree_connect_audio {
+       uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_DISCONNECT_AUDIO      0x04
+struct hal_cmd_handsfree_disconnect_audio {
+       uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_START_VR              0x05
+
+#define HAL_OP_HANDSFREE_STOP_VR               0x06
+
+#define HAL_HANDSFREE_VOLUME_TYPE_SPEAKER      0x00
+#define HAL_HANDSFREE_VOLUME_TYPE_MIC          0x01
+
+#define HAL_OP_HANDSFREE_VOLUME_CONTROL                0x07
+struct hal_cmd_handsfree_volume_control {
+       uint8_t type;
+       uint8_t volume;
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_NETWORK_STATE_NOT_AVAILABLE      0x00
+#define HAL_HANDSFREE_NETWORK_STATE_AVAILABLE          0x01
+
+#define HAL_HANDSFREE_SERVICE_TYPE_HOME                0x00
+#define HAL_HANDSFREE_SERVICE_TYPE_ROAMING     0x01
+
+#define HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF   0x08
+struct hal_cmd_handsfree_device_status_notif {
+       uint8_t state;
+       uint8_t type;
+       uint8_t signal;
+       uint8_t battery;
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_COPS_RESPONSE         0x09
+struct hal_cmd_handsfree_cops_response {
+       uint16_t len;
+       uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CALL_STATE_ACTIVE                0x00
+#define HAL_HANDSFREE_CALL_STATE_HELD          0x01
+#define HAL_HANDSFREE_CALL_STATE_DIALING       0x02
+#define HAL_HANDSFREE_CALL_STATE_ALERTING      0x03
+#define HAL_HANDSFREE_CALL_STATE_INCOMING      0x04
+#define HAL_HANDSFREE_CALL_STATE_WAITING       0x05
+#define HAL_HANDSFREE_CALL_STATE_IDLE          0x06
+
+#define HAL_OP_HANDSFREE_CIND_RESPONSE         0x0A
+struct hal_cmd_handsfree_cind_response {
+       uint8_t svc;
+       uint8_t num_active;
+       uint8_t num_held;
+       uint8_t state;
+       uint8_t signal;
+       uint8_t roam;
+       uint8_t batt_chg;
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE 0x0B
+struct hal_cmd_handsfree_formatted_at_response {
+       uint16_t len;
+       uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_AT_RESPONSE_ERROR                0x00
+#define HAL_HANDSFREE_AT_RESPONSE_OK           0x01
+
+#define HAL_OP_HANDSFREE_AT_RESPONSE           0x0C
+struct hal_cmd_handsfree_at_response {
+       uint8_t response;
+       uint8_t error;
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CALL_DIRECTION_OUTGOING  0x00
+#define HAL_HANDSFREE_CALL_DIRECTION_INCOMING  0x01
+
+#define HAL_HANDSFREE_CALL_TYPE_VOICE          0x00
+#define HAL_HANDSFREE_CALL_TYPE_DATA           0x01
+#define HAL_HANDSFREE_CALL_TYPE_FAX            0x02
+
+#define HAL_HANDSFREE_CALL_MPTY_TYPE_SINGLE    0x00
+#define HAL_HANDSFREE_CALL_MPTY_TYPE_MULTI     0x01
+
+#define HAL_HANDSFREE_CALL_ADDRTYPE_UNKNOWN    0x81
+#define HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL      0x91
+
+#define HAL_OP_HANDSFREE_CLCC_RESPONSE         0x0D
+struct hal_cmd_handsfree_clcc_response {
+       uint8_t index;
+       uint8_t dir;
+       uint8_t state;
+       uint8_t mode;
+       uint8_t mpty;
+       uint8_t type;
+       uint16_t number_len;
+       uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_OP_HANDSFREE_PHONE_STATE_CHANGE    0x0E
+struct hal_cmd_handsfree_phone_state_change {
+       uint8_t num_active;
+       uint8_t num_held;
+       uint8_t state;
+       uint8_t type;
+       uint16_t number_len;
+       uint8_t number[0];
+} __attribute__((packed));
+
+/* GATT HAL API */
+
+#define HAL_OP_GATT_CLIENT_REGISTER            0x01
+struct hal_cmd_gatt_client_register {
+       uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_UNREGISTER          0x02
+struct hal_cmd_gatt_client_unregister {
+       int32_t client_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SCAN                        0x03
+struct hal_cmd_gatt_client_scan {
+       int32_t client_if;
+       uint8_t start;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_CONNECT             0x04
+struct hal_cmd_gatt_client_connect {
+       int32_t client_if;
+       uint8_t bdaddr[6];
+       uint8_t is_direct;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DISCONNECT          0x05
+struct hal_cmd_gatt_client_disconnect {
+       int32_t client_if;
+       uint8_t bdaddr[6];
+       int32_t conn_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_LISTEN              0x06
+struct hal_cmd_gatt_client_listen {
+       int32_t client_if;
+       uint8_t start;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_REFRESH             0x07
+struct hal_cmd_gatt_client_refresh {
+       int32_t client_if;
+       uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SEARCH_SERVICE      0x08
+struct hal_cmd_gatt_client_search_service {
+       int32_t conn_id;
+       uint8_t filtered;
+       uint8_t filter_uuid[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE        0x09
+struct hal_gatt_srvc_id {
+       uint8_t uuid[16];
+       uint8_t inst_id;
+       uint8_t is_primary;
+} __attribute__((packed));
+
+struct hal_cmd_gatt_client_get_included_service {
+       int32_t conn_id;
+       struct hal_gatt_srvc_id srvc_id;
+       uint8_t continuation;
+       struct hal_gatt_srvc_id incl_srvc_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC  0x0a
+struct hal_gatt_gatt_id {
+       uint8_t uuid[16];
+       uint8_t inst_id;
+} __attribute__((packed));
+
+struct hal_cmd_gatt_client_get_characteristic {
+       int32_t conn_id;
+       struct hal_gatt_srvc_id srvc_id;
+       uint8_t continuation;
+       struct hal_gatt_gatt_id char_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_DESCRIPTOR      0x0b
+struct hal_cmd_gatt_client_get_descriptor {
+       int32_t conn_id;
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+       uint8_t continuation;
+       struct hal_gatt_gatt_id descr_id[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC 0x0c
+struct hal_cmd_gatt_client_read_characteristic {
+       int32_t conn_id;
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+       int32_t auth_req;
+} __attribute__((packed));
+
+#define GATT_WRITE_TYPE_NO_RESPONSE    0x01
+#define GATT_WRITE_TYPE_DEFAULT                0x02
+#define GATT_WRITE_TYPE_PREPARE                0x03
+#define GATT_WRITE_TYPE_SIGNED         0x04
+
+#define HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC        0x0d
+struct hal_cmd_gatt_client_write_characteristic {
+       int32_t conn_id;
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+       int32_t write_type;
+       int32_t len;
+       int32_t auth_req;
+       uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_DESCRIPTOR     0x0e
+struct hal_cmd_gatt_client_read_descriptor {
+       int32_t conn_id;
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+       struct hal_gatt_gatt_id descr_id;
+       int32_t auth_req;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR    0x0f
+struct hal_cmd_gatt_client_write_descriptor {
+       int32_t conn_id;
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+       struct hal_gatt_gatt_id descr_id;
+       int32_t write_type;
+       int32_t len;
+       int32_t auth_req;
+       uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_EXECUTE_WRITE       0x10
+struct hal_cmd_gatt_client_execute_write {
+       int32_t conn_id;
+       int32_t execute;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION   0x11
+struct hal_cmd_gatt_client_register_for_notification {
+       int32_t client_if;
+       uint8_t bdaddr[6];
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION 0x12
+struct hal_cmd_gatt_client_deregister_for_notification {
+       int32_t client_if;
+       uint8_t bdaddr[6];
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI    0x13
+struct hal_cmd_gatt_client_read_remote_rssi {
+       int32_t client_if;
+       uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE     0x14
+struct hal_cmd_gatt_client_get_device_type {
+       uint8_t bdaddr[6];
+} __attribute__((packed));
+
+struct hal_rsp_gatt_client_get_device_type {
+       uint8_t type;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_SET_ADV_DATA                0x015
+struct hal_cmd_gatt_client_set_adv_data {
+       int32_t  server_if;
+       uint8_t  set_scan_rsp;
+       uint8_t  include_name;
+       uint8_t  include_txpower;
+       int32_t  min_interval;
+       int32_t  max_interval;
+       int32_t  appearance;
+       uint16_t manufacturer_len;
+       uint8_t  manufacturer_data[0];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_CLIENT_TEST_COMMAND                0x16
+struct hal_cmd_gatt_client_test_command {
+       int32_t command;
+       uint8_t  bda1[6];
+       uint8_t  uuid1[16];
+       uint16_t u1;
+       uint16_t u2;
+       uint16_t u3;
+       uint16_t u4;
+       uint16_t u5;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_REGISTER            0x17
+struct hal_cmd_gatt_server_register {
+       uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_UNREGISTER          0x18
+struct hal_cmd_gatt_server_unregister {
+       int32_t server_if;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_CONNECT             0x19
+struct hal_cmd_gatt_server_connect {
+       int32_t server_if;
+       uint8_t bdaddr[6];
+       uint8_t is_direct;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_DISCONNECT          0x1a
+struct hal_cmd_gatt_server_disconnect {
+       int32_t server_if;
+       uint8_t bdaddr[6];
+       int32_t conn_id;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_SERVICE         0x1b
+struct hal_cmd_gatt_server_add_service {
+       int32_t server_if;
+       struct hal_gatt_srvc_id srvc_id;
+       int32_t num_handles;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_INC_SERVICE     0x1c
+struct hal_cmd_gatt_server_add_inc_service {
+       int32_t server_if;
+       int32_t service_handle;
+       int32_t included_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC  0x1d
+struct hal_cmd_gatt_server_add_characteristic {
+       int32_t server_if;
+       int32_t service_handle;
+       uint8_t uuid[16];
+       int32_t properties;
+       int32_t permissions;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_ADD_DESCRIPTOR      0x1e
+struct hal_cmd_gatt_server_add_descriptor {
+       int32_t server_if;
+       int32_t service_handle;
+       uint8_t uuid[16];
+       int32_t permissions;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_START_SERVICE       0x1f
+struct hal_cmd_gatt_server_start_service {
+       int32_t server_if;
+       int32_t service_handle;
+       int32_t transport;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_STOP_SERVICE                0x20
+struct hal_cmd_gatt_server_stop_service {
+       int32_t server_if;
+       int32_t service_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_DELETE_SERVICE      0x21
+struct hal_cmd_gatt_server_delete_service {
+       int32_t server_if;
+       int32_t service_handle;
+} __attribute__((packed));
+
+#define HAL_OP_GATT_SERVER_SEND_INDICATION     0x22
+struct hal_cmd_gatt_server_send_indication {
+       int32_t server_if;
+       int32_t attribute_handle;
+       int32_t conn_id;
+       int32_t len;
+       int32_t confirm;
+       uint8_t value[0];
+} __attribute__((packed));
 
+#define HAL_OP_GATT_SERVER_SEND_RESPONSE       0x23
+struct hal_cmd_gatt_server_send_response {
+       int32_t conn_id;
+       int32_t trans_id;
+       uint16_t handle;
+       uint16_t offset;
+       uint8_t auth_req;
+       int32_t status;
+       uint16_t len;
+       uint8_t data[0];
+} __attribute__((packed));
+
+/* Notifications and confirmations */
 
 #define HAL_POWER_OFF                  0x00
 #define HAL_POWER_ON                   0x01
@@ -501,7 +987,14 @@ struct hal_ev_hidhost_proto_mode {
        uint8_t mode;
 } __attribute__((packed));
 
-#define HAL_EV_HIDHOST_GET_REPORT              0x84
+#define HAL_EV_HIDHOST_IDLE_TIME               0x84
+struct hal_ev_hidhost_idle_time {
+       uint8_t bdaddr[6];
+       uint8_t status;
+       uint32_t idle_rate;
+} __attribute__((packed));
+
+#define HAL_EV_HIDHOST_GET_REPORT              0x85
 struct hal_ev_hidhost_get_report {
        uint8_t  bdaddr[6];
        uint8_t  status;
@@ -509,7 +1002,7 @@ struct hal_ev_hidhost_get_report {
        uint8_t  data[0];
 } __attribute__((packed));
 
-#define HAL_EV_HIDHOST_VIRTUAL_UNPLUG          0x85
+#define HAL_EV_HIDHOST_VIRTUAL_UNPLUG          0x86
 struct hal_ev_hidhost_virtual_unplug {
        uint8_t  bdaddr[6];
        uint8_t  status;
@@ -532,6 +1025,32 @@ struct hal_ev_pan_conn_state {
        uint8_t  remote_role;
 } __attribute__((packed));
 
+#define HAL_HEALTH_APP_REG_SUCCESS             0x00
+#define HAL_HEALTH_APP_REG_FAILED              0x01
+#define HAL_HEALTH_APP_DEREG_SUCCESS           0x02
+#define HAL_HEALTH_APP_DEREG_FAILED            0x03
+
+#define HAL_HEALTH_CHANNEL_CONNECTING          0x00
+#define HAL_HEALTH_CHANNEL_CONNECTED           0x01
+#define HAL_HEALTH_CHANNEL_DISCONNECTING       0x02
+#define HAL_HEALTH_CHANNEL_DISCONNECTED                0x03
+#define HAL_HEALTH_CHANNEL_DESTROYED           0x04
+
+#define HAL_EV_HEALTH_APP_REG_STATE            0x81
+struct hal_ev_health_app_reg_state {
+       uint16_t id;
+       uint8_t  state;
+} __attribute__((packed));
+
+#define HAL_EV_HEALTH_CHANNEL_STATE            0x82
+struct hal_ev_health_channel_state {
+       uint16_t app_id;
+       uint8_t  bdaddr[6];
+       uint8_t  mdep_index;
+       uint16_t channel_id;
+       uint8_t  channel_state;
+} __attribute__((packed));
+
 #define HAL_A2DP_STATE_DISCONNECTED            0x00
 #define HAL_A2DP_STATE_CONNECTING              0x01
 #define HAL_A2DP_STATE_CONNECTED               0x02
@@ -543,8 +1062,531 @@ struct hal_ev_a2dp_conn_state {
        uint8_t bdaddr[6];
 } __attribute__((packed));
 
+#define HAL_AUDIO_SUSPEND                      0x00
+#define HAL_AUDIO_STOPPED                      0x01
+#define HAL_AUDIO_STARTED                      0x02
+
 #define HAL_EV_A2DP_AUDIO_STATE                        0x82
 struct hal_ev_a2dp_audio_state {
        uint8_t state;
        uint8_t bdaddr[6];
 } __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED       0x00
+#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTING         0x01
+#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTED          0x02
+#define HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED      0x03
+#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING      0x04
+
+#define HAL_EV_HANDSFREE_CONN_STATE            0x81
+struct hal_ev_handsfree_conn_state {
+       uint8_t state;
+       uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED      0x00
+#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING                0x01
+#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED         0x02
+#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING     0x03
+
+#define HAL_EV_HANDSFREE_AUDIO_STATE           0x82
+struct hal_ev_handsfree_audio_state {
+       uint8_t state;
+       uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_VR_STOPPED       0x00
+#define HAL_HANDSFREE_VR_STARTED       0x01
+
+#define HAL_EV_HANDSFREE_VR            0x83
+struct hal_ev_handsfree_vr_state {
+       uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_ANSWER                0x84
+
+#define HAL_EV_HANDSFREE_HANGUP                0x85
+
+#define HAL_EV_HANDSFREE_VOLUME                0x86
+struct hal_ev_handsfree_volume {
+       uint8_t type;
+       uint8_t volume;
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_DIAL          0x87
+struct hal_ev_handsfree_dial {
+       uint16_t number_len;
+       uint8_t number[0];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_DTMF          0x88
+struct hal_ev_handsfree_dtmf {
+       uint8_t tone;
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_NREC_STOP                0x00
+#define HAL_HANDSFREE_NREC_START       0x01
+
+#define HAL_EV_HANDSFREE_NREC          0x89
+struct hal_ev_handsfree_nrec {
+       uint8_t nrec;
+} __attribute__((packed));
+
+#define HAL_HANDSFREE_CHLD_TYPE_RELEASEHELD                    0x00
+#define HAL_HANDSFREE_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD       0x01
+#define HAL_HANDSFREE_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD          0x02
+#define HAL_HANDSFREE_CHLD_TYPE_ADDHELDTOCONF                  0x03
+
+#define HAL_EV_HANDSFREE_CHLD          0x8A
+struct hal_ev_handsfree_chld {
+       uint8_t chld;
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_CNUM          0x8B
+
+#define HAL_EV_HANDSFREE_CIND          0x8C
+
+#define HAL_EV_HANDSFREE_COPS          0x8D
+
+#define HAL_EV_HANDSFREE_CLCC          0x8E
+
+#define HAL_EV_HANDSFREE_UNKNOWN_AT    0x8F
+struct hal_ev_handsfree_unknown_at {
+       uint16_t len;
+       uint8_t buf[0];
+} __attribute__((packed));
+
+#define HAL_EV_HANDSFREE_HSP_KEY_PRESS 0x90
+
+/* AVRCP HAL API */
+
+#define HAL_AVRCP_PLAY_STATUS_STOPPED  0x00
+#define HAL_AVRCP_PLAY_STATUS_PLAYING  0x01
+#define HAL_AVRCP_PLAY_STATUS_PAUSED   0x02
+#define HAL_AVRCP_PLAY_STATUS_FWD_SEEK 0x03
+#define HAL_AVRCP_PLAY_STATUS_REV_SEEK 0x04
+#define HAL_AVRCP_PLAY_STATUS_ERROR    0xff
+
+#define HAL_OP_AVRCP_GET_PLAY_STATUS   0x01
+struct hal_cmd_avrcp_get_play_status {
+       uint8_t status;
+       uint32_t duration;
+       uint32_t position;
+} __attribute__((packed));
+
+#define HAL_AVRCP_PLAYER_ATTR_EQUALIZER        0x01
+#define HAL_AVRCP_PLAYER_ATTR_REPEAT   0x02
+#define HAL_AVRCP_PLAYER_ATTR_SHUFFLE  0x03
+#define HAL_AVRCP_PLAYER_ATTR_SCAN     0x04
+
+#define HAL_OP_AVRCP_LIST_PLAYER_ATTRS 0x02
+struct hal_cmd_avrcp_list_player_attrs {
+       uint8_t number;
+       uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_LIST_PLAYER_VALUES        0x03
+struct hal_cmd_avrcp_list_player_values {
+       uint8_t number;
+       uint8_t values[0];
+} __attribute__((packed));
+
+struct hal_avrcp_player_attr_value {
+       uint8_t attr;
+       uint8_t value;
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_ATTRS  0x04
+struct hal_cmd_avrcp_get_player_attrs {
+       uint8_t number;
+       struct hal_avrcp_player_attr_value attrs[0];
+} __attribute__((packed));
+
+struct hal_avrcp_player_setting_text {
+       uint8_t id;
+       uint8_t len;
+       uint8_t text[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT     0x05
+struct hal_cmd_avrcp_get_player_attrs_text {
+       uint8_t number;
+       struct hal_avrcp_player_setting_text attrs[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT    0x06
+struct hal_cmd_avrcp_get_player_values_text {
+       uint8_t number;
+       struct hal_avrcp_player_setting_text values[0];
+} __attribute__((packed));
+
+#define HAL_AVRCP_MEDIA_ATTR_TITLE             0x01
+#define HAL_AVRCP_MEDIA_ATTR_ARTIST            0x02
+#define HAL_AVRCP_MEDIA_ATTR_ALBUM             0x03
+#define HAL_AVRCP_MEDIA_ATTR_TRACK_NUM         0x04
+#define HAL_AVRCP_MEDIA_ATTR_NUM_TRACKS                0x05
+#define HAL_AVRCP_MEDIA_ATTR_GENRE             0x06
+#define HAL_AVRCP_MEDIA_ATTR_DURATION          0x07
+
+#define HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT    0x07
+struct hal_cmd_avrcp_get_element_attrs_text {
+       uint8_t number;
+       struct hal_avrcp_player_setting_text values[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE    0x08
+struct hal_cmd_avrcp_set_player_attrs_value {
+       uint8_t status;
+} __attribute__((packed));
+
+#define HAL_AVRCP_EVENT_STATUS_CHANGED         0x01
+#define HAL_AVRCP_EVENT_TRACK_CHANGED          0x02
+#define HAL_AVRCP_EVENT_TRACK_REACHED_END      0x03
+#define HAL_AVRCP_EVENT_TRACK_REACHED_START    0x04
+#define HAL_AVRCP_EVENT_POSITION_CHANGED       0x05
+#define HAL_AVRCP_EVENT_SETTING_CHANGED                0x08
+
+#define HAL_AVRCP_EVENT_TYPE_INTERIM           0x00
+#define HAL_AVRCP_EVENT_TYPE_CHANGED           0x01
+
+#define HAL_OP_AVRCP_REGISTER_NOTIFICATION     0x09
+struct hal_cmd_avrcp_register_notification {
+       uint8_t event;
+       uint8_t type;
+       uint8_t len;
+       uint8_t data[0];
+} __attribute__((packed));
+
+#define HAL_OP_AVRCP_SET_VOLUME                        0x0a
+struct hal_cmd_avrcp_set_volume {
+       uint8_t value;
+} __attribute__((packed));
+
+#define HAL_AVRCP_FEATURE_NONE                 0x00
+#define HAL_AVRCP_FEATURE_METADATA             0x01
+#define HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME      0x02
+#define HAL_AVRCP_FEATURE_BROWSE               0x04
+
+#define HAL_EV_AVRCP_REMOTE_FEATURES           0x81
+struct hal_ev_avrcp_remote_features {
+       uint8_t bdaddr[6];
+       uint8_t features;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAY_STATUS           0x82
+#define HAL_EV_AVRCP_LIST_PLAYER_ATTRS         0x83
+
+#define HAL_EV_AVRCP_LIST_PLAYER_VALUES                0x84
+struct hal_ev_avrcp_list_player_values {
+       uint8_t attr;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_VALUES         0x85
+struct hal_ev_avrcp_get_player_values {
+       uint8_t number;
+       uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT     0x86
+struct hal_ev_avrcp_get_player_attrs_text {
+       uint8_t number;
+       uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT    0x87
+struct hal_ev_avrcp_get_player_values_text {
+       uint8_t attr;
+       uint8_t number;
+       uint8_t values[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_SET_PLAYER_VALUES         0x88
+struct hal_ev_avrcp_set_player_values {
+       uint8_t number;
+       struct hal_avrcp_player_attr_value attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_GET_ELEMENT_ATTRS         0x89
+struct hal_ev_avrcp_get_element_attrs {
+       uint8_t number;
+       uint8_t attrs[0];
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_REGISTER_NOTIFICATION     0x8a
+struct hal_ev_avrcp_register_notification {
+       uint8_t event;
+       uint32_t param;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_VOLUME_CHANGED            0x8b
+struct hal_ev_avrcp_volume_changed {
+       uint8_t volume;
+       uint8_t type;
+} __attribute__((packed));
+
+#define HAL_EV_AVRCP_PASSTHROUGH_CMD           0x8c
+struct hal_ev_avrcp_passthrough_cmd {
+       uint8_t id;
+       uint8_t state;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_REGISTER_CLIENT     0x81
+struct hal_ev_gatt_client_register_client {
+       int32_t status;
+       int32_t client_if;
+       uint8_t app_uuid[16];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SCAN_RESULT 0x82
+struct hal_ev_gatt_client_scan_result {
+       uint8_t  bda[6];
+       int32_t  rssi;
+       uint16_t len;
+       uint8_t  adv_data[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_CONNECT     0x83
+struct hal_ev_gatt_client_connect {
+       int32_t conn_id;
+       int32_t status;
+       int32_t client_if;
+       uint8_t bda[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_DISCONNECT  0x84
+struct hal_ev_gatt_client_disconnect {
+       int32_t conn_id;
+       int32_t status;
+       int32_t client_if;
+       uint8_t bda[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SEARCH_COMPLETE     0x85
+struct hal_ev_gatt_client_search_complete {
+       int32_t conn_id;
+       int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_SEARCH_RESULT       0x86
+struct hal_ev_gatt_client_search_result {
+       int32_t conn_id;
+       struct hal_gatt_srvc_id srvc_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC  0x87
+struct hal_ev_gatt_client_get_characteristic {
+       int32_t conn_id;
+       int32_t status;
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+       int32_t char_prop;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_DESCRIPTOR      0x88
+struct hal_ev_gatt_client_get_descriptor {
+       int32_t conn_id;
+       int32_t status;
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+       struct hal_gatt_gatt_id descr_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_GET_INC_SERVICE     0X89
+struct hal_ev_gatt_client_get_inc_service {
+       int32_t conn_id;
+       int32_t status;
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_srvc_id incl_srvc_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF  0x8a
+struct hal_ev_gatt_client_reg_for_notif {
+       int32_t conn_id;
+       int32_t registered;
+       int32_t status;
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_NOTIFY              0x8b
+struct hal_ev_gatt_client_notify {
+       int32_t conn_id;
+       uint8_t bda[6];
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+       uint8_t  is_notify;
+       uint16_t len;
+       uint8_t  value[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC 0x8c
+struct hal_gatt_read_params {
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+       struct hal_gatt_gatt_id descr_id;
+       uint8_t  status;
+       uint16_t value_type;
+       uint16_t len;
+       uint8_t  value[0];
+} __attribute__((packed));
+
+struct hal_ev_gatt_client_read_characteristic {
+       int32_t conn_id;
+       int32_t status;
+       struct hal_gatt_read_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC        0x8d
+struct hal_gatt_write_params {
+       struct hal_gatt_srvc_id srvc_id;
+       struct hal_gatt_gatt_id char_id;
+       struct hal_gatt_gatt_id descr_id;
+       uint8_t status;
+} __attribute__((packed));
+
+struct hal_ev_gatt_client_write_characteristic {
+       int32_t conn_id;
+       int32_t status;
+       struct hal_gatt_write_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_DESCRIPTOR     0x8e
+struct hal_ev_gatt_client_read_descriptor {
+       int32_t conn_id;
+       int32_t status;
+       struct hal_gatt_read_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR    0x8f
+struct hal_ev_gatt_client_write_descriptor {
+       int32_t conn_id;
+       int32_t status;
+       struct hal_gatt_write_params data;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_EXEC_WRITE          0x90
+struct hal_ev_gatt_client_exec_write {
+       int32_t conn_id;
+       int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI    0x91
+struct hal_ev_gatt_client_read_remote_rssi {
+       int32_t client_if;
+       uint8_t address[6];
+       int32_t rssi;
+       int32_t status;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_CLIENT_LISTEN              0x92
+struct hal_ev_gatt_client_listen {
+       int32_t status;
+       int32_t server_if;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REGISTER            0x93
+struct hal_ev_gatt_server_register {
+       int32_t status;
+       int32_t server_if;
+       uint8_t uuid[16];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CONNECTION          0x94
+struct hal_ev_gatt_server_connection {
+       int32_t conn_id;
+       int32_t server_if;
+       int32_t connected;
+       uint8_t bdaddr[6];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_ADDED       0x95
+struct hal_ev_gatt_server_service_added {
+       int32_t status;
+       int32_t server_if;
+       struct hal_gatt_srvc_id srvc_id;
+       int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_INC_SRVC_ADDED      0x96
+struct hal_ev_gatt_server_inc_srvc_added {
+       int32_t status;
+       int32_t server_if;
+       int32_t srvc_handle;
+       int32_t incl_srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_CHAR_ADDED          0x97
+struct hal_ev_gatt_server_characteristic_added {
+       int32_t status;
+       int32_t server_if;
+       uint8_t uuid[16];
+       int32_t srvc_handle;
+       int32_t char_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED    0x98
+struct hal_ev_gatt_server_descriptor_added {
+       int32_t status;
+       int32_t server_if;
+       uint8_t uuid[16];
+       int32_t srvc_handle;
+       int32_t descr_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_STARTED     0x99
+struct hal_ev_gatt_server_service_started {
+       int32_t status;
+       int32_t server_if;
+       int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_STOPPED     0x9a
+struct hal_ev_gatt_server_service_stopped {
+       int32_t status;
+       int32_t server_if;
+       int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_SERVICE_DELETED     0x9b
+struct hal_ev_gatt_server_service_deleted {
+       int32_t status;
+       int32_t server_if;
+       int32_t srvc_handle;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_READ                0x9c
+struct hal_ev_gatt_server_request_read {
+       int32_t conn_id;
+       int32_t trans_id;
+       uint8_t bdaddr[6];
+       int32_t attr_handle;
+       int32_t offset;
+       uint8_t is_long;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_WRITE       0x9d
+struct hal_ev_gatt_server_request_write {
+       int32_t conn_id;
+       int32_t trans_id;
+       uint8_t bdaddr[6];
+       int32_t attr_handle;
+       int32_t offset;
+       int32_t length;
+       uint8_t need_rsp;
+       uint8_t is_prep;
+       uint8_t value[0];
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE  0x9e
+struct hal_ev_gatt_server_request_exec_write {
+       int32_t conn_id;
+       int32_t trans_id;
+       uint8_t bdaddr[6];
+       int32_t exec_write;
+} __attribute__((packed));
+
+#define HAL_EV_GATT_SERVER_RSP_CONFIRMATION    0x9f
+struct hal_ev_gatt_server_rsp_confirmation {
+       int32_t status;
+       int32_t handle;
+} __attribute__((packed));
index 8c0f8d8..a9ac4ff 100644 (file)
@@ -45,13 +45,25 @@ static void handle_ctrl_state(void *buf, uint16_t len)
 {
        struct hal_ev_pan_ctrl_state *ev = buf;
 
+       /*
+        * FIXME: Callback declared in bt_pan.h is 'typedef void
+        * (*btpan_control_state_callback)(btpan_control_state_t state,
+        * bt_status_t error, int local_role, const char* ifname);
+        * But PanService.Java defined it wrong way.
+        * private void onControlStateChanged(int local_role, int state,
+        * int error, String ifname).
+        * First and third parameters are misplaced, so sending data according
+        * to PanService.Java, fix this if issue fixed in PanService.Java.
+        */
        if (cbs->control_state_cb)
-               cbs->control_state_cb(ev->state, ev->status,
-                                       ev->local_role, (char *)ev->name);
+               cbs->control_state_cb(ev->local_role, ev->state, ev->status,
+                                                       (char *)ev->name);
 }
 
-/* handlers will be called from notification thread context,
- * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */
+/*
+ * handlers will be called from notification thread context,
+ * index in table equals to 'opcode - HAL_MINIMUM_EVENT'
+ */
 static const struct hal_ipc_handler ev_handlers[] = {
        {       /* HAL_EV_PAN_CTRL_STATE */
                .handler = handle_ctrl_state,
@@ -148,6 +160,7 @@ static bt_status_t pan_init(const btpan_callbacks_t *callbacks)
                                sizeof(ev_handlers)/sizeof(ev_handlers[0]));
 
        cmd.service_id = HAL_SERVICE_ID_PAN;
+       cmd.mode = HAL_MODE_DEFAULT;
 
        ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
                                        sizeof(cmd), &cmd, 0, NULL, NULL);
@@ -160,9 +173,9 @@ static bt_status_t pan_init(const btpan_callbacks_t *callbacks)
        return ret;
 }
 
-static void pan_cleanup()
+static void pan_cleanup(void)
 {
-       struct hal_cmd_register_module cmd;
+       struct hal_cmd_unregister_module cmd;
 
        DBG("");
 
diff --git a/android/hal-sco.c b/android/hal-sco.c
new file mode 100644 (file)
index 0000000..ea9857e
--- /dev/null
@@ -0,0 +1,913 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <hardware/audio.h>
+#include <hardware/hardware.h>
+#include <audio_utils/resampler.h>
+
+#include "../src/shared/util.h"
+#include "sco-msg.h"
+#include "ipc-common.h"
+#include "hal-log.h"
+
+#define AUDIO_STREAM_DEFAULT_RATE      44100
+#define AUDIO_STREAM_SCO_RATE          8000
+#define AUDIO_STREAM_DEFAULT_FORMAT    AUDIO_FORMAT_PCM_16_BIT
+
+#define OUT_BUFFER_SIZE                        2560
+#define OUT_STREAM_FRAMES              2560
+
+#define SOCKET_POLL_TIMEOUT_MS         500
+
+static int listen_sk = -1;
+static int ipc_sk = -1;
+
+static pthread_t ipc_th = 0;
+static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct sco_audio_config {
+       uint32_t rate;
+       uint32_t channels;
+       uint32_t frame_num;
+       uint16_t mtu;
+       audio_format_t format;
+};
+
+struct sco_stream_out {
+       struct audio_stream_out stream;
+
+       struct sco_audio_config cfg;
+       int fd;
+
+       uint8_t *downmix_buf;
+
+       struct resampler_itfe *resampler;
+       int16_t *resample_buf;
+       uint32_t resample_frame_num;
+};
+
+struct sco_dev {
+       struct audio_hw_device dev;
+       struct sco_stream_out *out;
+};
+
+/*
+ * return the minimum frame numbers from resampling between BT stack's rate
+ * and audio flinger's. For output stream, 'output' shall be true, otherwise
+ * false for input streams at audio flinger side.
+ */
+static size_t get_resample_frame_num(uint32_t sco_rate, uint32_t rate,
+                                               size_t frame_num, bool output)
+{
+       size_t resample_frames_num = frame_num * sco_rate / rate + output;
+
+       DBG("resampler: sco_rate %d frame_num %zd rate %d resample frames %zd",
+                               sco_rate, frame_num, rate, resample_frames_num);
+
+       return resample_frames_num;
+}
+
+/* SCO IPC functions */
+
+static int sco_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len,
+                       void *param, size_t *rsp_len, void *rsp, int *fd)
+{
+       ssize_t ret;
+       struct msghdr msg;
+       struct iovec iv[2];
+       struct ipc_hdr cmd;
+       char cmsgbuf[CMSG_SPACE(sizeof(int))];
+       struct ipc_status s;
+       size_t s_len = sizeof(s);
+
+       pthread_mutex_lock(&sk_mutex);
+
+       if (ipc_sk < 0) {
+               error("sco: Invalid cmd socket passed to sco_ipc_cmd");
+               goto failed;
+       }
+
+       if (!rsp || !rsp_len) {
+               memset(&s, 0, s_len);
+               rsp_len = &s_len;
+               rsp = &s;
+       }
+
+       memset(&msg, 0, sizeof(msg));
+       memset(&cmd, 0, sizeof(cmd));
+
+       cmd.service_id = service_id;
+       cmd.opcode = opcode;
+       cmd.len = len;
+
+       iv[0].iov_base = &cmd;
+       iv[0].iov_len = sizeof(cmd);
+
+       iv[1].iov_base = param;
+       iv[1].iov_len = len;
+
+       msg.msg_iov = iv;
+       msg.msg_iovlen = 2;
+
+       ret = sendmsg(ipc_sk, &msg, 0);
+       if (ret < 0) {
+               error("sco: Sending command failed:%s", strerror(errno));
+               goto failed;
+       }
+
+       /* socket was shutdown */
+       if (ret == 0) {
+               error("sco: Command socket closed");
+               goto failed;
+       }
+
+       memset(&msg, 0, sizeof(msg));
+       memset(&cmd, 0, sizeof(cmd));
+
+       iv[0].iov_base = &cmd;
+       iv[0].iov_len = sizeof(cmd);
+
+       iv[1].iov_base = rsp;
+       iv[1].iov_len = *rsp_len;
+
+       msg.msg_iov = iv;
+       msg.msg_iovlen = 2;
+
+       if (fd) {
+               memset(cmsgbuf, 0, sizeof(cmsgbuf));
+               msg.msg_control = cmsgbuf;
+               msg.msg_controllen = sizeof(cmsgbuf);
+       }
+
+       ret = recvmsg(ipc_sk, &msg, 0);
+       if (ret < 0) {
+               error("sco: Receiving command response failed:%s",
+                                                       strerror(errno));
+               goto failed;
+       }
+
+       if (ret < (ssize_t) sizeof(cmd)) {
+               error("sco: Too small response received(%zd bytes)", ret);
+               goto failed;
+       }
+
+       if (cmd.service_id != service_id) {
+               error("sco: Invalid service id (%u vs %u)", cmd.service_id,
+                                                               service_id);
+               goto failed;
+       }
+
+       if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) {
+               error("sco: Malformed response received(%zd bytes)", ret);
+               goto failed;
+       }
+
+       if (cmd.opcode != opcode && cmd.opcode != SCO_OP_STATUS) {
+               error("sco: Invalid opcode received (%u vs %u)",
+                                               cmd.opcode, opcode);
+               goto failed;
+       }
+
+       if (cmd.opcode == SCO_OP_STATUS) {
+               struct ipc_status *s = rsp;
+
+               if (sizeof(*s) != cmd.len) {
+                       error("sco: Invalid status length");
+                       goto failed;
+               }
+
+               if (s->code == SCO_STATUS_SUCCESS) {
+                       error("sco: Invalid success status response");
+                       goto failed;
+               }
+
+               pthread_mutex_unlock(&sk_mutex);
+
+               return s->code;
+       }
+
+       pthread_mutex_unlock(&sk_mutex);
+
+       /* Receive auxiliary data in msg */
+       if (fd) {
+               struct cmsghdr *cmsg;
+
+               *fd = -1;
+
+               for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+                                       cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+                       if (cmsg->cmsg_level == SOL_SOCKET
+                                       && cmsg->cmsg_type == SCM_RIGHTS) {
+                               memcpy(fd, CMSG_DATA(cmsg), sizeof(int));
+                               break;
+                       }
+               }
+
+               if (*fd < 0)
+                       goto failed;
+       }
+
+       if (rsp_len)
+               *rsp_len = cmd.len;
+
+       return SCO_STATUS_SUCCESS;
+
+failed:
+       /* Some serious issue happen on IPC - recover */
+       shutdown(ipc_sk, SHUT_RDWR);
+       pthread_mutex_unlock(&sk_mutex);
+
+       return SCO_STATUS_FAILED;
+}
+
+static int ipc_connect_sco(int *fd, uint16_t *mtu)
+{
+       struct sco_rsp_connect rsp;
+       size_t rsp_len = sizeof(rsp);
+       int ret;
+
+       DBG("");
+
+       ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_CONNECT, 0, NULL, &rsp_len,
+                                                               &rsp, fd);
+
+       *mtu = rsp.mtu;
+
+       return ret;
+}
+
+/* Audio stream functions */
+
+static void downmix_to_mono(struct sco_stream_out *out, const uint8_t *buffer,
+                                                       size_t frame_num)
+{
+       const int16_t *input = (const void *) buffer;
+       int16_t *output = (void *) out->downmix_buf;
+       size_t i;
+
+       for (i = 0; i < frame_num; i++) {
+               int16_t l = le16_to_cpu(get_unaligned(&input[i * 2]));
+               int16_t r = le16_to_cpu(get_unaligned(&input[i * 2 + 1]));
+
+               put_unaligned(cpu_to_le16((l + r) >> 1), &output[i]);
+       }
+}
+
+static bool write_data(struct sco_stream_out *out, const uint8_t *buffer,
+                                                               size_t bytes)
+{
+       struct pollfd pfd;
+       size_t len, written = 0;
+       int ret;
+       uint16_t mtu = /* out->cfg.mtu */ 48;
+       uint8_t read_buf[mtu];
+       bool do_write = false;
+
+       pfd.fd = out->fd;
+       pfd.events = POLLOUT | POLLIN | POLLHUP | POLLNVAL;
+
+       while (bytes > written) {
+
+               /* poll for sending */
+               if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) {
+                       DBG("timeout fd %d", out->fd);
+                       return false;
+               }
+
+               if (pfd.revents & (POLLHUP | POLLNVAL)) {
+                       error("error fd %d, events 0x%x", out->fd, pfd.revents);
+                       return false;
+               }
+
+               /* FIXME synchronize by time instead of read() */
+               if (pfd.revents & POLLIN) {
+                       ret = read(out->fd, read_buf, mtu);
+                       if (ret < 0) {
+                               error("Error reading fd %d (%s)", out->fd,
+                                                       strerror(errno));
+                               return false;
+                       }
+
+                       do_write = true;
+               }
+
+               if (!do_write)
+                       continue;
+
+               len = bytes - written > mtu ? mtu : bytes - written;
+
+               ret = write(out->fd, buffer + written, len);
+               if (ret > 0) {
+                       written += ret;
+                       do_write = false;
+                       continue;
+               }
+
+               if (errno == EAGAIN) {
+                       ret = errno;
+                       warn("write failed (%d)", ret);
+                       continue;
+               }
+
+               if (errno != EINTR) {
+                       ret = errno;
+                       error("write failed (%d) fd %d bytes %zd", ret, out->fd,
+                                                                       bytes);
+                       return false;
+               }
+       }
+
+       DBG("written %zd bytes", bytes);
+
+       return true;
+}
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+                                                               size_t bytes)
+{
+       struct sco_stream_out *out = (struct sco_stream_out *) stream;
+       size_t frame_num = bytes / audio_stream_frame_size(&out->stream.common);
+       size_t output_frame_num = frame_num;
+       void *send_buf = out->downmix_buf;
+       size_t total;
+
+       DBG("write to fd %d bytes %zu", out->fd, bytes);
+
+       if (!out->downmix_buf) {
+               error("sco: downmix buffer not initialized");
+               return -1;
+       }
+
+       downmix_to_mono(out, buffer, frame_num);
+
+       if (out->resampler) {
+               int ret;
+
+               /* limit resampler's output within what resample buf can hold */
+               output_frame_num = out->resample_frame_num;
+
+               ret = out->resampler->resample_from_input(out->resampler,
+                                                       send_buf,
+                                                       &frame_num,
+                                                       out->resample_buf,
+                                                       &output_frame_num);
+               if (ret) {
+                       error("Failed to resample frames: %zd input %zd (%s)",
+                               frame_num, output_frame_num, strerror(ret));
+                       return -1;
+               }
+
+               send_buf = out->resample_buf;
+
+               DBG("Resampled: frame_num %zd, output_frame_num %zd",
+                                               frame_num, output_frame_num);
+       }
+
+       total = output_frame_num * sizeof(int16_t) * 1;
+
+       DBG("total %zd", total);
+
+       if (!write_data(out, send_buf, total))
+               return -1;
+
+       return bytes;
+}
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+       struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+       DBG("rate %u", out->cfg.rate);
+
+       return out->cfg.rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+       DBG("rate %u", rate);
+
+       return 0;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+       struct sco_stream_out *out = (struct sco_stream_out *) stream;
+       size_t size = audio_stream_frame_size(&out->stream.common) *
+                                                       out->cfg.frame_num;
+
+       DBG("buf size %zd", size);
+
+       return size;
+}
+
+static uint32_t out_get_channels(const struct audio_stream *stream)
+{
+       struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+       DBG("channels num: %u", popcount(out->cfg.channels));
+
+       return out->cfg.channels;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+       struct sco_stream_out *out = (struct sco_stream_out *) stream;
+
+       DBG("format: %u", out->cfg.format);
+
+       return out->cfg.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+       DBG("");
+
+       return -ENOSYS;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+       DBG("");
+
+       return 0;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+       DBG("");
+
+       return -ENOSYS;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+       DBG("%s", kvpairs);
+
+       return 0;
+}
+
+static char *out_get_parameters(const struct audio_stream *stream,
+                                                       const char *keys)
+{
+       DBG("");
+
+       return strdup("");
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+       DBG("");
+
+       return 0;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+                                                               float right)
+{
+       DBG("");
+
+       return -ENOSYS;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+                                                       uint32_t *dsp_frames)
+{
+       DBG("");
+
+       return -ENOSYS;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream,
+                                                       effect_handle_t effect)
+{
+       DBG("");
+
+       return -ENOSYS;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream,
+                                                       effect_handle_t effect)
+{
+       DBG("");
+
+       return -ENOSYS;
+}
+
+static int sco_open_output_stream(struct audio_hw_device *dev,
+                                       audio_io_handle_t handle,
+                                       audio_devices_t devices,
+                                       audio_output_flags_t flags,
+                                       struct audio_config *config,
+                                       struct audio_stream_out **stream_out)
+{
+       struct sco_dev *adev = (struct sco_dev *) dev;
+       struct sco_stream_out *out;
+       int fd = -1;
+       int chan_num, ret;
+       size_t resample_size;
+       uint16_t mtu;
+
+       DBG("");
+
+       if (ipc_connect_sco(&fd, &mtu) != SCO_STATUS_SUCCESS) {
+               error("sco: cannot get fd");
+               return -EIO;
+       }
+
+       DBG("got sco fd %d mtu %u", fd, mtu);
+
+       out = calloc(1, sizeof(struct sco_stream_out));
+       if (!out)
+               return -ENOMEM;
+
+       out->stream.common.get_sample_rate = out_get_sample_rate;
+       out->stream.common.set_sample_rate = out_set_sample_rate;
+       out->stream.common.get_buffer_size = out_get_buffer_size;
+       out->stream.common.get_channels = out_get_channels;
+       out->stream.common.get_format = out_get_format;
+       out->stream.common.set_format = out_set_format;
+       out->stream.common.standby = out_standby;
+       out->stream.common.dump = out_dump;
+       out->stream.common.set_parameters = out_set_parameters;
+       out->stream.common.get_parameters = out_get_parameters;
+       out->stream.common.add_audio_effect = out_add_audio_effect;
+       out->stream.common.remove_audio_effect = out_remove_audio_effect;
+       out->stream.get_latency = out_get_latency;
+       out->stream.set_volume = out_set_volume;
+       out->stream.write = out_write;
+       out->stream.get_render_position = out_get_render_position;
+
+       /* Configuration for Android */
+       out->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+       out->cfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+       out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
+       out->cfg.frame_num = OUT_STREAM_FRAMES;
+       out->cfg.mtu = mtu;
+
+       out->downmix_buf = malloc(out_get_buffer_size(&out->stream.common));
+       if (!out->downmix_buf) {
+               free(out);
+               return -ENOMEM;
+       }
+
+       DBG("size %zd", out_get_buffer_size(&out->stream.common));
+
+       /* Channel numbers for resampler */
+       chan_num = 1;
+
+       ret = create_resampler(out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num,
+                                               RESAMPLER_QUALITY_DEFAULT, NULL,
+                                               &out->resampler);
+       if (ret) {
+               error("Failed to create resampler (%s)", strerror(ret));
+               goto failed;
+       }
+
+       DBG("Created resampler: input rate [%d] output rate [%d] channels [%d]",
+                               out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num);
+
+       out->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE,
+                                                       out->cfg.rate,
+                                                       out->cfg.frame_num, 1);
+
+       if (!out->resample_frame_num) {
+               error("frame num is too small to resample, discard it");
+               goto failed;
+       }
+
+       resample_size = sizeof(int16_t) * chan_num * out->resample_frame_num;
+
+       out->resample_buf = malloc(resample_size);
+       if (!out->resample_buf) {
+               error("failed to allocate resample buffer for %u frames",
+                                               out->resample_frame_num);
+               goto failed;
+       }
+
+       DBG("resampler: frame num %u buf size %zd bytes",
+                                       out->resample_frame_num, resample_size);
+
+       *stream_out = &out->stream;
+       adev->out = out;
+       out->fd = fd;
+
+       return 0;
+failed:
+       free(out->downmix_buf);
+       free(out);
+       stream_out = NULL;
+       adev->out = NULL;
+
+       return ret;
+}
+
+static void sco_close_output_stream(struct audio_hw_device *dev,
+                                       struct audio_stream_out *stream_out)
+{
+       struct sco_dev *sco_dev = (struct sco_dev *) dev;
+       struct sco_stream_out *out = (struct sco_stream_out *) stream_out;
+
+       DBG("dev %p stream %p fd %d", dev, stream_out, sco_dev->out->fd);
+
+       if (sco_dev->out && sco_dev->out->fd) {
+               close(sco_dev->out->fd);
+               sco_dev->out->fd = -1;
+       }
+
+       free(out->downmix_buf);
+       free(out);
+       sco_dev->out = NULL;
+}
+
+static int sco_set_parameters(struct audio_hw_device *dev,
+                                                       const char *kvpairs)
+{
+       DBG("%s", kvpairs);
+
+       return 0;
+}
+
+static char *sco_get_parameters(const struct audio_hw_device *dev,
+                                                       const char *keys)
+{
+       DBG("");
+
+       return strdup("");
+}
+
+static int sco_init_check(const struct audio_hw_device *dev)
+{
+       DBG("");
+
+       return 0;
+}
+
+static int sco_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+       DBG("%f", volume);
+
+       return 0;
+}
+
+static int sco_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+       DBG("%f", volume);
+
+       return 0;
+}
+
+static int sco_set_mode(struct audio_hw_device *dev, int mode)
+{
+       DBG("");
+
+       return -ENOSYS;
+}
+
+static int sco_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+       DBG("");
+
+       return -ENOSYS;
+}
+
+static int sco_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+       DBG("");
+
+       return -ENOSYS;
+}
+
+static size_t sco_get_input_buffer_size(const struct audio_hw_device *dev,
+                                       const struct audio_config *config)
+{
+       DBG("");
+
+       return -ENOSYS;
+}
+
+static int sco_open_input_stream(struct audio_hw_device *dev,
+                                       audio_io_handle_t handle,
+                                       audio_devices_t devices,
+                                       struct audio_config *config,
+                                       struct audio_stream_in **stream_in)
+{
+       DBG("");
+
+       return 0;
+}
+
+static void sco_close_input_stream(struct audio_hw_device *dev,
+                                       struct audio_stream_in *stream_in)
+{
+       DBG("");
+
+       free(stream_in);
+}
+
+static int sco_dump(const audio_hw_device_t *device, int fd)
+{
+       DBG("");
+
+       return 0;
+}
+
+static int sco_close(hw_device_t *device)
+{
+       DBG("");
+
+       free(device);
+
+       return 0;
+}
+
+static void *ipc_handler(void *data)
+{
+       bool done = false;
+       struct pollfd pfd;
+       int sk;
+
+       DBG("");
+
+       while (!done) {
+               DBG("Waiting for connection ...");
+
+               sk = accept(listen_sk, NULL, NULL);
+               if (sk < 0) {
+                       int err = errno;
+
+                       if (err == EINTR)
+                               continue;
+
+                       if (err != ECONNABORTED && err != EINVAL)
+                               error("sco: Failed to accept socket: %d (%s)",
+                                                       err, strerror(err));
+
+                       break;
+               }
+
+               pthread_mutex_lock(&sk_mutex);
+               ipc_sk = sk;
+               pthread_mutex_unlock(&sk_mutex);
+
+               DBG("SCO IPC: Connected");
+
+               memset(&pfd, 0, sizeof(pfd));
+               pfd.fd = ipc_sk;
+               pfd.events = POLLHUP | POLLERR | POLLNVAL;
+
+               /* Check if socket is still alive. Empty while loop.*/
+               while (poll(&pfd, 1, -1) < 0 && errno == EINTR);
+
+               if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) {
+                       info("SCO HAL: Socket closed");
+
+                       pthread_mutex_lock(&sk_mutex);
+                       close(ipc_sk);
+                       ipc_sk = -1;
+                       pthread_mutex_unlock(&sk_mutex);
+               }
+       }
+
+       info("Closing SCO IPC thread");
+       return NULL;
+}
+
+static int sco_ipc_init(void)
+{
+       struct sockaddr_un addr;
+       int err;
+       int sk;
+
+       DBG("");
+
+       sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+       if (sk < 0) {
+               err = -errno;
+               error("sco: Failed to create socket: %d (%s)", -err,
+                                                               strerror(-err));
+               return err;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+
+       memcpy(addr.sun_path, BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH));
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               err = -errno;
+               error("sco: Failed to bind socket: %d (%s)", -err,
+                                                               strerror(-err));
+               goto failed;
+       }
+
+       if (listen(sk, 1) < 0) {
+               err = -errno;
+               error("sco: Failed to listen on the socket: %d (%s)", -err,
+                                                               strerror(-err));
+               goto failed;
+       }
+
+       listen_sk = sk;
+
+       err = pthread_create(&ipc_th, NULL, ipc_handler, NULL);
+       if (err) {
+               err = -err;
+               ipc_th = 0;
+               error("sco: Failed to start IPC thread: %d (%s)",
+                                                       -err, strerror(-err));
+               goto failed;
+       }
+
+       return 0;
+
+failed:
+       close(sk);
+       return err;
+}
+
+static int sco_open(const hw_module_t *module, const char *name,
+                                                       hw_device_t **device)
+{
+       struct sco_dev *dev;
+       int err;
+
+       DBG("");
+
+       if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+               error("SCO: interface %s not matching [%s]", name,
+                                               AUDIO_HARDWARE_INTERFACE);
+               return -EINVAL;
+       }
+
+       err = sco_ipc_init();
+       if (err < 0)
+               return err;
+
+       dev = calloc(1, sizeof(struct sco_dev));
+       if (!dev)
+               return -ENOMEM;
+
+       dev->dev.common.tag = HARDWARE_DEVICE_TAG;
+       dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT;
+       dev->dev.common.module = (struct hw_module_t *) module;
+       dev->dev.common.close = sco_close;
+
+       dev->dev.init_check = sco_init_check;
+       dev->dev.set_voice_volume = sco_set_voice_volume;
+       dev->dev.set_master_volume = sco_set_master_volume;
+       dev->dev.set_mode = sco_set_mode;
+       dev->dev.set_mic_mute = sco_set_mic_mute;
+       dev->dev.get_mic_mute = sco_get_mic_mute;
+       dev->dev.set_parameters = sco_set_parameters;
+       dev->dev.get_parameters = sco_get_parameters;
+       dev->dev.get_input_buffer_size = sco_get_input_buffer_size;
+       dev->dev.open_output_stream = sco_open_output_stream;
+       dev->dev.close_output_stream = sco_close_output_stream;
+       dev->dev.open_input_stream = sco_open_input_stream;
+       dev->dev.close_input_stream = sco_close_input_stream;
+       dev->dev.dump = sco_dump;
+
+       *device = &dev->dev.common;
+
+       return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+       .open = sco_open,
+};
+
+struct audio_module HAL_MODULE_INFO_SYM = {
+       .common = {
+               .tag = HARDWARE_MODULE_TAG,
+               .version_major = 1,
+               .version_minor = 0,
+               .id = AUDIO_HARDWARE_MODULE_ID,
+               .name = "SCO Audio HW HAL",
+               .author = "Intel Corporation",
+               .methods = &hal_module_methods,
+       },
+};
similarity index 55%
rename from android/hal-sock.c
rename to android/hal-socket.c
index f45be30..cfd50d1 100644 (file)
 #include "hal-utils.h"
 #include "hal.h"
 
-static bt_status_t sock_listen_rfcomm(const char *service_name,
+static bt_status_t socket_listen(btsock_type_t type, const char *service_name,
                                        const uint8_t *uuid, int chan,
                                        int *sock, int flags)
 {
-       struct hal_cmd_sock_listen cmd;
+       struct hal_cmd_socket_listen cmd;
 
-       DBG("");
+       if (!sock)
+               return BT_STATUS_PARM_INVALID;
+
+       DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x",
+               btuuid2str(uuid), chan, sock, type, service_name, flags);
 
        memset(&cmd, 0, sizeof(cmd));
 
+       /* type match IPC type */
+       cmd.type = type;
        cmd.flags = flags;
-       cmd.type = BTSOCK_RFCOMM;
        cmd.channel = chan;
 
        if (uuid)
@@ -46,77 +51,46 @@ static bt_status_t sock_listen_rfcomm(const char *service_name,
        if (service_name)
                memcpy(cmd.name, service_name, strlen(service_name));
 
-       return hal_ipc_cmd(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN,
+       return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN,
                                sizeof(cmd), &cmd, NULL, NULL, sock);
 }
 
-static bt_status_t sock_listen(btsock_type_t type, const char *service_name,
+static bt_status_t socket_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type,
                                        const uint8_t *uuid, int chan,
                                        int *sock, int flags)
 {
-       if ((!uuid && chan <= 0) || !sock) {
-               error("Invalid params: uuid %s, chan %d, sock %p",
-                                               btuuid2str(uuid), chan, sock);
-               return BT_STATUS_PARM_INVALID;
-       }
-
-       DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x",
-               btuuid2str(uuid), chan, sock, type, service_name, flags);
-
-       switch (type) {
-       case BTSOCK_RFCOMM:
-               return sock_listen_rfcomm(service_name, uuid, chan, sock,
-                                                                       flags);
-       default:
-               error("%s: Socket type %d not supported", __func__, type);
-               break;
-       }
+       struct hal_cmd_socket_connect cmd;
 
-       return BT_STATUS_UNSUPPORTED;
-}
-
-static bt_status_t sock_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type,
-                                       const uint8_t *uuid, int chan,
-                                       int *sock, int flags)
-{
-       struct hal_cmd_sock_connect cmd;
-
-       if ((!uuid && chan <= 0) || !bdaddr || !sock) {
-               error("Invalid params: bd_addr %s, uuid %s, chan %d, sock %p",
-                       bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock);
+       if (!sock)
                return BT_STATUS_PARM_INVALID;
-       }
 
        DBG("bdaddr %s uuid %s chan %d sock %p type %d flags 0x%02x",
                bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock, type, flags);
 
-       if (type != BTSOCK_RFCOMM) {
-               error("Socket type %u not supported", type);
-               return BT_STATUS_UNSUPPORTED;
-       }
-
        memset(&cmd, 0, sizeof(cmd));
 
-       cmd.flags = flags;
+       /* type match IPC type */
        cmd.type = type;
+       cmd.flags = flags;
        cmd.channel = chan;
 
        if (uuid)
                memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
 
-       memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr));
+       if (bdaddr)
+               memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr));
 
-       return hal_ipc_cmd(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT,
+       return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT,
                                        sizeof(cmd), &cmd, NULL, NULL, sock);
 }
 
-static btsock_interface_t sock_if = {
-       sizeof(sock_if),
-       sock_listen,
-       sock_connect
+static btsock_interface_t socket_if = {
+       sizeof(socket_if),
+       socket_listen,
+       socket_connect
 };
 
-btsock_interface_t *bt_get_sock_interface(void)
+btsock_interface_t *bt_get_socket_interface(void)
 {
-       return &sock_if;
+       return &socket_if;
 }
index e3c0c60..ceefefc 100644 (file)
@@ -125,9 +125,7 @@ INTMAP(bt_property_type_t, -1, "(unknown)")
        DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT),
        DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME),
        DELEMENT(BT_PROPERTY_REMOTE_RSSI),
-#if PLATFORM_SDK_VERSION > 17
        DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO),
-#endif
        DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP),
 ENDMAP
 
index 75de7e9..8a1d015 100644 (file)
@@ -34,7 +34,7 @@ void str2bt_uuid_t(const char *str, bt_uuid_t *uuid);
 const char *btproperty2str(const bt_property_t *property);
 const char *bdaddr2str(const bt_bdaddr_t *bd_addr);
 
-/**
+/*
  * Begin mapping section
  *
  * There are some mappings between integer values (enums) and strings
index b475411..6998e9a 100644 (file)
 #include <hardware/bt_hh.h>
 #include <hardware/bt_pan.h>
 #include <hardware/bt_av.h>
+#include <hardware/bt_rc.h>
+#include <hardware/bt_hf.h>
+#include <hardware/bt_gatt.h>
+#include <hardware/bt_gatt_client.h>
+#include <hardware/bt_gatt_server.h>
+#include <hardware/bt_hl.h>
 
-btsock_interface_t *bt_get_sock_interface(void);
+btsock_interface_t *bt_get_socket_interface(void);
 bthh_interface_t *bt_get_hidhost_interface(void);
 btpan_interface_t *bt_get_pan_interface(void);
 btav_interface_t *bt_get_a2dp_interface(void);
+btrc_interface_t *bt_get_avrcp_interface(void);
+bthf_interface_t *bt_get_handsfree_interface(void);
+btgatt_interface_t *bt_get_gatt_interface(void);
+bthl_interface_t *bt_get_health_interface(void);
 
 void bt_thread_associate(void);
 void bt_thread_disassociate(void);
diff --git a/android/handsfree.c b/android/handsfree.c
new file mode 100644 (file)
index 0000000..7ca0ba8
--- /dev/null
@@ -0,0 +1,2681 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/uuid-helper.h"
+#include "src/shared/hfp.h"
+#include "btio/btio.h"
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "handsfree.h"
+#include "bluetooth.h"
+#include "src/log.h"
+#include "utils.h"
+#include "sco-msg.h"
+
+#define HSP_AG_CHANNEL 12
+#define HFP_AG_CHANNEL 13
+
+#define HFP_AG_FEAT_3WAY       0x00000001
+#define HFP_AG_FEAT_ECNR       0x00000002
+#define HFP_AG_FEAT_VR         0x00000004
+#define HFP_AG_FEAT_INBAND     0x00000008
+#define HFP_AG_FEAT_VTAG       0x00000010
+#define HFP_AG_FEAT_REJ_CALL   0x00000020
+#define HFP_AG_FEAT_ECS                0x00000040
+#define HFP_AG_FEAT_ECC                0x00000080
+#define HFP_AG_FEAT_EXT_ERR    0x00000100
+#define HFP_AG_FEAT_CODEC      0x00000200
+
+#define HFP_HF_FEAT_ECNR       0x00000001
+#define HFP_HF_FEAT_3WAY       0x00000002
+#define HFP_HF_FEAT_CLI                0x00000004
+#define HFP_HF_FEAT_VR         0x00000008
+#define HFP_HF_FEAT_RVC                0x00000010
+#define HFP_HF_FEAT_ECS                0x00000020
+#define HFP_HF_FEAT_ECC                0x00000040
+#define HFP_HF_FEAT_CODEC      0x00000080
+
+#define HFP_AG_FEATURES (HFP_AG_FEAT_3WAY | HFP_AG_FEAT_ECNR |\
+                               HFP_AG_FEAT_VR | HFP_AG_FEAT_REJ_CALL |\
+                               HFP_AG_FEAT_ECS | HFP_AG_FEAT_EXT_ERR)
+
+#define HFP_AG_CHLD "0,1,2,3"
+
+/* offsets in indicators table, should be incremented when sending CIEV */
+#define IND_SERVICE    0
+#define IND_CALL       1
+#define IND_CALLSETUP  2
+#define IND_CALLHELD   3
+#define IND_SIGNAL     4
+#define IND_ROAM       5
+#define IND_BATTCHG    6
+#define IND_COUNT      (IND_BATTCHG + 1)
+
+#define RING_TIMEOUT 2
+
+#define CVSD_OFFSET 0
+#define MSBC_OFFSET 1
+#define CODECS_COUNT (MSBC_OFFSET + 1)
+
+#define CODEC_ID_CVSD 0x01
+#define CODEC_ID_MSBC 0x02
+
+struct indicator {
+       const char *name;
+       int min;
+       int max;
+       int val;
+       bool always_active;
+       bool active;
+};
+
+struct hfp_codec {
+       uint8_t type;
+       bool local_supported;
+       bool remote_supported;
+};
+
+static const struct indicator inds_defaults[] = {
+               { "service",   0, 1, 0, false, true },
+               { "call",      0, 1, 0, true, true },
+               { "callsetup", 0, 3, 0, true, true },
+               { "callheld",  0, 2, 0, true, true },
+               { "signal",    0, 5, 0, false, true },
+               { "roam",      0, 1, 0, false, true },
+               { "battchg",   0, 5, 0, false, true },
+};
+
+static const struct hfp_codec codecs_defaults[] = {
+       { CODEC_ID_CVSD, true, false},
+       { CODEC_ID_MSBC, false, false},
+};
+
+static struct {
+       bdaddr_t bdaddr;
+       uint8_t state;
+       uint8_t audio_state;
+       uint32_t features;
+
+       bool clip_enabled;
+       bool cmee_enabled;
+       bool ccwa_enabled;
+       bool indicators_enabled;
+       struct indicator inds[IND_COUNT];
+       int num_active;
+       int num_held;
+       int setup_state;
+       bool call_hanging_up;
+
+       uint8_t negotiated_codec;
+       uint8_t proposed_codec;
+       struct hfp_codec codecs[CODECS_COUNT];
+
+       guint ring;
+       bool hsp;
+
+       struct hfp_gw *gw;
+
+       GIOChannel *sco;
+       guint sco_watch;
+} device;
+
+static uint32_t hfp_ag_features = 0;
+
+static bdaddr_t adapter_addr;
+
+static struct ipc *hal_ipc = NULL;
+static struct ipc *sco_ipc = NULL;
+
+static uint32_t hfp_record_id = 0;
+static GIOChannel *hfp_server = NULL;
+
+static uint32_t hsp_record_id = 0;
+static GIOChannel *hsp_server = NULL;
+
+static GIOChannel *sco_server = NULL;
+
+static void device_set_state(uint8_t state)
+{
+       struct hal_ev_handsfree_conn_state ev;
+       char address[18];
+
+       if (device.state == state)
+               return;
+
+       device.state = state;
+
+       ba2str(&device.bdaddr, address);
+       DBG("device %s state %u", address, state);
+
+       bdaddr2android(&device.bdaddr, ev.bdaddr);
+       ev.state = state;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_EV_HANDSFREE_CONN_STATE, sizeof(ev), &ev);
+}
+
+static void device_set_audio_state(uint8_t state)
+{
+       struct hal_ev_handsfree_audio_state ev;
+       char address[18];
+
+       if (device.audio_state == state)
+               return;
+
+       device.audio_state = state;
+
+       ba2str(&device.bdaddr, address);
+       DBG("device %s audio state %u", address, state);
+
+       bdaddr2android(&device.bdaddr, ev.bdaddr);
+       ev.state = state;
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_EV_HANDSFREE_AUDIO_STATE, sizeof(ev), &ev);
+}
+
+static void init_codecs(void)
+{
+       memcpy(device.codecs, codecs_defaults, sizeof(device.codecs));
+
+       if (hfp_ag_features & HFP_AG_FEAT_CODEC)
+               device.codecs[MSBC_OFFSET].local_supported = true;
+}
+
+static void device_init(const bdaddr_t *bdaddr)
+{
+       bacpy(&device.bdaddr, bdaddr);
+
+       device.setup_state = HAL_HANDSFREE_CALL_STATE_IDLE;
+
+       memcpy(device.inds, inds_defaults, sizeof(device.inds));
+
+       init_codecs();
+
+       device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTING);
+}
+
+static void device_cleanup(void)
+{
+       if (device.gw) {
+               hfp_gw_unref(device.gw);
+               device.gw = NULL;
+       }
+
+       device_set_state(HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED);
+
+       if (device.sco_watch) {
+               g_source_remove(device.sco_watch);
+               device.sco_watch = 0;
+       }
+
+       if (device.sco) {
+               g_io_channel_shutdown(device.sco, TRUE, NULL);
+               g_io_channel_unref(device.sco);
+               device.sco = NULL;
+       }
+
+       if (device.ring) {
+               g_source_remove(device.ring);
+               device.ring = 0;
+       }
+
+       device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+
+       memset(&device, 0, sizeof(device));
+}
+
+static void disconnect_watch(void *user_data)
+{
+       DBG("");
+
+       device_cleanup();
+}
+
+static void at_cmd_unknown(const char *command, void *user_data)
+{
+       uint8_t buf[IPC_MTU];
+       struct hal_ev_handsfree_unknown_at *ev = (void *) buf;
+
+       if (device.state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) {
+               hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+               hfp_gw_disconnect(device.gw);
+               return;
+       }
+
+       /* copy while string including terminating NULL */
+       ev->len = strlen(command) + 1;
+       memcpy(ev->buf, command, ev->len);
+
+       if (ev->len > IPC_MTU - sizeof(*ev)) {
+               hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+               return;
+       }
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                       HAL_EV_HANDSFREE_UNKNOWN_AT, sizeof(*ev) + ev->len, ev);
+}
+
+static void at_cmd_vgm(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                               void *user_data)
+{
+       struct hal_ev_handsfree_volume ev;
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &val) || val > 15)
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               ev.type = HAL_HANDSFREE_VOLUME_TYPE_MIC;
+               ev.volume = val;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev);
+
+               /* Framework is not replying with result for AT+VGM */
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_vgs(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                               void *user_data)
+{
+       struct hal_ev_handsfree_volume ev;
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &val) || val > 15)
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               ev.type = HAL_HANDSFREE_VOLUME_TYPE_SPEAKER;
+               ev.volume = val;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev);
+
+               /* Framework is not replying with result for AT+VGS */
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cops(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       unsigned int val;
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &val) || val != 3)
+                       break;
+
+               if (!hfp_gw_result_get_number(result, &val) || val != 0)
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                               HAL_EV_HANDSFREE_COPS, 0, NULL);
+               return;
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bia(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       unsigned int val, i, def;
+       bool tmp[IND_COUNT];
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               for (i = 0; i < IND_COUNT; i++)
+                       tmp[i] = device.inds[i].active;
+
+               i = 0;
+
+               do {
+                       def = (i < IND_COUNT) ? device.inds[i].active : 0;
+
+                       if (!hfp_gw_result_get_number_default(result, &val, def))
+                               goto failed;
+
+                       if (val > 1)
+                               goto failed;
+
+                       if (i < IND_COUNT) {
+                               tmp[i] = val || device.inds[i].always_active;
+                               i++;
+                       }
+               } while (hfp_gw_result_has_next(result));
+
+               for (i = 0; i < IND_COUNT; i++)
+                       device.inds[i].active = tmp[i];
+
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+failed:
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_a(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_COMMAND:
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_EV_HANDSFREE_ANSWER, 0, NULL);
+
+               /* Framework is not replying with result for ATA */
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_SET:
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_d(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       char buf[IPC_MTU];
+       struct hal_ev_handsfree_dial *ev = (void *) buf;
+       int cnt;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_unquoted_string(result,
+                                               (char *) ev->number, 255))
+                       break;
+
+               ev->number_len = strlen((char *) ev->number);
+
+               if (ev->number[ev->number_len - 1] != ';')
+                       break;
+
+               if (ev->number[0] == '>')
+                       cnt = strspn((char *) ev->number + 1, "0123456789") + 1;
+               else
+                       cnt = strspn((char *) ev->number, "0123456789ABC*#+");
+
+               if (cnt != ev->number_len - 1)
+                       break;
+
+               ev->number_len++;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_EV_HANDSFREE_DIAL,
+                                       sizeof(*ev) + ev->number_len, ev);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_ccwa(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &val) || val > 1)
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               device.ccwa_enabled = val;
+
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_chup(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_COMMAND:
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_EV_HANDSFREE_HANGUP, 0, NULL);
+
+               /* Framework is not replying with result for AT+CHUP */
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_SET:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_clcc(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_COMMAND:
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_EV_HANDSFREE_CLCC, 0, NULL);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_SET:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cmee(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &val) || val > 1)
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               device.cmee_enabled = val;
+
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_clip(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &val) || val > 1)
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               device.clip_enabled = val;
+
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_vts(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       struct hal_ev_handsfree_dtmf ev;
+       char str[2];
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_unquoted_string(result, str, 2))
+                       break;
+
+               if (!((str[0] >= '0' && str[0] <= '9') ||
+                               (str[0] >= 'A' && str[0] <= 'D') ||
+                               str[0] == '*' || str[0] == '#'))
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               ev.tone = str[0];
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_EV_HANDSFREE_DTMF, sizeof(ev), &ev);
+
+               /* Framework is not replying with result for AT+VTS */
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cnum(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_COMMAND:
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                               HAL_EV_HANDSFREE_CNUM, 0, NULL);
+               return;
+       case HFP_GW_CMD_TYPE_SET:
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_binp(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       DBG("");
+
+       /* TODO */
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bldn(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       struct hal_ev_handsfree_dial ev;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_COMMAND:
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               ev.number_len = 0;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_EV_HANDSFREE_DIAL, sizeof(ev), &ev);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_SET:
+               break;
+       }
+}
+
+static void at_cmd_bvra(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       struct hal_ev_handsfree_vr_state ev;
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &val) || val > 1)
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               if (val)
+                       ev.state = HAL_HANDSFREE_VR_STARTED;
+               else
+                       ev.state = HAL_HANDSFREE_VR_STOPPED;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_EV_HANDSFREE_VR, sizeof(ev), &ev);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_nrec(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       struct hal_ev_handsfree_nrec ev;
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               /*
+                * Android HAL defines start and stop parameter for NREC
+                * callback, but spec allows HF to only disable AG's NREC
+                * feature for SLC duration. Follow spec here.
+                */
+               if (!hfp_gw_result_get_number(result, &val) || val != 0)
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               ev.nrec = HAL_HANDSFREE_NREC_STOP;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_EV_HANDSFREE_NREC, sizeof(ev), &ev);
+
+               /* Framework is not replying with result for AT+NREC */
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bsir(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       DBG("");
+
+       /* TODO */
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_btrh(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       DBG("");
+
+       /* TODO */
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static gboolean sco_watch_cb(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       g_io_channel_shutdown(device.sco, TRUE, NULL);
+       g_io_channel_unref(device.sco);
+       device.sco = NULL;
+
+       DBG("");
+
+       device.sco_watch = 0;
+
+       device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+
+       return FALSE;
+}
+
+static void select_codec(uint8_t codec_type)
+{
+       uint8_t type = CODEC_ID_CVSD;
+       int i;
+
+       if (codec_type > 0) {
+               type = codec_type;
+               goto done;
+       }
+
+       for (i = CODECS_COUNT - 1; i >= CVSD_OFFSET; i--) {
+               if (!device.codecs[i].local_supported)
+                       continue;
+
+               if (!device.codecs[i].remote_supported)
+                       continue;
+
+               type = device.codecs[i].type;
+               break;
+       }
+
+done:
+       device.proposed_codec = type;
+
+       hfp_gw_send_info(device.gw, "+BCS: %u", type);
+}
+
+static void connect_sco_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+       if (err) {
+               uint8_t status;
+
+               error("handsfree: audio connect failed (%s)", err->message);
+
+               status = HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED;
+               device_set_audio_state(status);
+
+               if (!(device.features & HFP_HF_FEAT_CODEC))
+                       return;
+
+               /* If other failed, try connecting with CVSD */
+               if (device.negotiated_codec != CODEC_ID_CVSD) {
+                       info("handsfree: trying fallback with CVSD");
+                       select_codec(CODEC_ID_CVSD);
+               }
+
+               return;
+       }
+
+       g_io_channel_set_close_on_unref(chan, TRUE);
+
+       device.sco = g_io_channel_ref(chan);
+       device.sco_watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                                       sco_watch_cb, NULL);
+
+       device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED);
+}
+
+static bool connect_sco(void)
+{
+       GIOChannel *io;
+       GError *gerr = NULL;
+       uint16_t voice_settings;
+
+       if (device.sco)
+               return false;
+
+       if (!(device.features & HFP_HF_FEAT_CODEC))
+               voice_settings = 0;
+       else if (device.negotiated_codec != CODEC_ID_CVSD)
+               voice_settings = BT_VOICE_TRANSPARENT;
+       else
+               voice_settings = BT_VOICE_CVSD_16BIT;
+
+       io = bt_io_connect(connect_sco_cb, NULL, NULL, &gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                               BT_IO_OPT_DEST_BDADDR, &device.bdaddr,
+                               BT_IO_OPT_VOICE, voice_settings,
+                               BT_IO_OPT_INVALID);
+
+       if (!io) {
+               error("handsfree: unable to connect audio: %s", gerr->message);
+               g_error_free(gerr);
+               return false;
+       }
+
+       g_io_channel_unref(io);
+
+       device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING);
+
+       return true;
+}
+
+static void at_cmd_bcc(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_COMMAND:
+               if (!(device.features & HFP_HF_FEAT_CODEC))
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+               /* we haven't negotiated codec, start selection */
+               if (!device.negotiated_codec) {
+                       select_codec(0);
+                       return;
+               }
+               /*
+                * we try connect to negotiated codec. If it fails, and it isn't
+                * CVSD codec, try connect CVSD
+                */
+               if (!connect_sco() && device.negotiated_codec != CODEC_ID_CVSD)
+                       select_codec(CODEC_ID_CVSD);
+
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_SET:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_bcs(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &val))
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               /* Remote replied with other codec. Reply with error */
+               if (device.proposed_codec != val) {
+                       device.proposed_codec = 0;
+                       break;
+               }
+
+               device.proposed_codec = 0;
+               device.negotiated_codec = val;
+
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+               /* Connect sco with negotiated parameters */
+               connect_sco();
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_ckpd(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &val) || val != 200)
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_EV_HANDSFREE_HSP_KEY_PRESS, 0, NULL);
+
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void register_post_slc_at(void)
+{
+       if (device.hsp) {
+               hfp_gw_register(device.gw, at_cmd_ckpd, "+CKPD", NULL, NULL);
+               hfp_gw_register(device.gw, at_cmd_vgs, "+VGS", NULL, NULL);
+               hfp_gw_register(device.gw, at_cmd_vgm, "+VGM", NULL, NULL);
+               return;
+       }
+
+       hfp_gw_register(device.gw, at_cmd_a, "A", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_d, "D", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_ccwa, "+CCWA", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_chup, "+CHUP", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_clcc, "+CLCC", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_cops, "+COPS", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_cmee, "+CMEE", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_clip, "+CLIP", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_vts, "+VTS", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_cnum, "+CNUM", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_bia, "+BIA", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_binp, "+BINP", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_bldn, "+BLDN", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_bvra, "+BVRA", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_nrec, "+NREC", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_vgs, "+VGS", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_vgm, "+VGM", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_bsir, "+BSIR", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_btrh, "+BTRH", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_bcc, "+BCC", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_bcs, "+BCS", NULL, NULL);
+}
+
+static void at_cmd_cmer(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       unsigned int val;
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               /* mode must be =3 */
+               if (!hfp_gw_result_get_number(result, &val) || val != 3)
+                       break;
+
+               /* keyp is don't care */
+               if (!hfp_gw_result_get_number(result, &val))
+                       break;
+
+               /* disp is don't care */
+               if (!hfp_gw_result_get_number(result, &val))
+                       break;
+
+               /* ind must be 0 or 1 */
+               if (!hfp_gw_result_get_number(result, &val) || val > 1)
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               device.indicators_enabled = val;
+
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+               if (device.features & HFP_HF_FEAT_3WAY)
+                       return;
+
+               register_post_slc_at();
+               device_set_state(HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+               return;
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_cind(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       char *buf, *ptr;
+       int len;
+       unsigned int i;
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_TEST:
+
+               /*
+                * If device supports Codec Negotiation, AT+BAC should be
+                * received first
+                */
+               if ((device.features & HFP_HF_FEAT_CODEC) &&
+                               !device.codecs[CVSD_OFFSET].remote_supported)
+                       break;
+
+               len = strlen("+CIND:") + 1;
+
+               for (i = 0; i < IND_COUNT; i++) {
+                       len += strlen("(\"\",(X,X)),");
+                       len += strlen(device.inds[i].name);
+               }
+
+               buf = g_malloc(len);
+
+               ptr = buf + sprintf(buf, "+CIND:");
+
+               for (i = 0; i < IND_COUNT; i++) {
+                       ptr += sprintf(ptr, "(\"%s\",(%d%c%d)),",
+                                       device.inds[i].name,
+                                       device.inds[i].min,
+                                       device.inds[i].max == 1 ? ',' : '-',
+                                       device.inds[i].max);
+               }
+
+               ptr--;
+               *ptr = '\0';
+
+               hfp_gw_send_info(device.gw, "%s", buf);
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+               g_free(buf);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                               HAL_EV_HANDSFREE_CIND, 0, NULL);
+               return;
+       case HFP_GW_CMD_TYPE_SET:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_brsf(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       unsigned int feat;
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &feat))
+                       break;
+
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               /* TODO verify features */
+               device.features = feat;
+
+               hfp_gw_send_info(device.gw, "+BRSF: %u", hfp_ag_features);
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void at_cmd_chld(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       struct hal_ev_handsfree_chld ev;
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!hfp_gw_result_get_number(result, &val) || val > 3)
+                       break;
+
+               /* No ECC support */
+               if (hfp_gw_result_has_next(result))
+                       break;
+
+               /* value match HAL type */
+               ev.chld = val;
+
+               ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_EV_HANDSFREE_CHLD, sizeof(ev), &ev);
+               return;
+       case HFP_GW_CMD_TYPE_TEST:
+               hfp_gw_send_info(device.gw, "+CHLD: (%s)", HFP_AG_CHLD);
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+               register_post_slc_at();
+               device_set_state(HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+               return;
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static struct hfp_codec *find_codec_by_type(uint8_t type)
+{
+       int i;
+
+       for (i = 0; i < CODECS_COUNT; i++)
+               if (type == device.codecs[i].type)
+                       return &device.codecs[i];
+
+       return NULL;
+}
+
+static void at_cmd_bac(struct hfp_gw_result *result, enum hfp_gw_cmd_type type,
+                                                       void *user_data)
+{
+       unsigned int val;
+
+       DBG("");
+
+       switch (type) {
+       case HFP_GW_CMD_TYPE_SET:
+               if (!(device.features & HFP_HF_FEAT_CODEC))
+                       goto failed;
+
+               /* set codecs to defaults */
+               init_codecs();
+               device.negotiated_codec = 0;
+
+               /*
+                * At least CVSD mandatory codec must exist
+                * HFP V1.6 4.34.1
+                */
+               if (!hfp_gw_result_get_number(result, &val) ||
+                                                       val != CODEC_ID_CVSD)
+                       goto failed;
+
+               device.codecs[CVSD_OFFSET].remote_supported = true;
+
+               if (hfp_gw_result_get_number(result, &val)) {
+                       if (val != CODEC_ID_MSBC)
+                               goto failed;
+
+                       device.codecs[MSBC_OFFSET].remote_supported = true;
+               }
+
+               while (hfp_gw_result_has_next(result)) {
+                       struct hfp_codec *codec;
+
+                       if (!hfp_gw_result_get_number(result, &val))
+                               goto failed;
+
+                       codec = find_codec_by_type(val);
+                       if (!codec)
+                               continue;
+
+                       codec->remote_supported = true;
+               }
+
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+               if (device.proposed_codec)
+                       select_codec(0);
+               return;
+       case HFP_GW_CMD_TYPE_TEST:
+       case HFP_GW_CMD_TYPE_READ:
+       case HFP_GW_CMD_TYPE_COMMAND:
+               break;
+       }
+
+failed:
+       hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+}
+
+static void register_slc_at(void)
+{
+       hfp_gw_register(device.gw, at_cmd_brsf, "+BRSF", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_cind, "+CIND", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_cmer, "+CMER", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_chld, "+CHLD", NULL, NULL);
+       hfp_gw_register(device.gw, at_cmd_bac, "+BAC", NULL, NULL);
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+       DBG("");
+
+       if (err) {
+               error("handsfree: connect failed (%s)", err->message);
+               goto failed;
+       }
+
+       device.gw = hfp_gw_new(g_io_channel_unix_get_fd(chan));
+       if (!device.gw)
+               goto failed;
+
+       g_io_channel_set_close_on_unref(chan, FALSE);
+
+       hfp_gw_set_close_on_unref(device.gw, true);
+       hfp_gw_set_command_handler(device.gw, at_cmd_unknown, NULL, NULL);
+       hfp_gw_set_disconnect_handler(device.gw, disconnect_watch, NULL, NULL);
+
+       if (device.hsp) {
+               register_post_slc_at();
+               device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTED);
+               device_set_state(HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED);
+               return;
+       }
+
+       register_slc_at();
+       device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTED);
+       return;
+
+failed:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+       device_cleanup();
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+       char address[18];
+       bdaddr_t bdaddr;
+       GError *err = NULL;
+
+       bt_io_get(chan, &err,
+                       BT_IO_OPT_DEST, address,
+                       BT_IO_OPT_DEST_BDADDR, &bdaddr,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("handsfree: confirm failed (%s)", err->message);
+               g_error_free(err);
+               goto drop;
+       }
+
+       DBG("incoming connect from %s", address);
+
+       if (device.state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+               info("handsfree: refusing connection from %s", address);
+               goto drop;
+       }
+
+       device_init(&bdaddr);
+
+       if (!bt_io_accept(chan, connect_cb, NULL, NULL, NULL)) {
+               error("handsfree: failed to accept connection");
+               device_cleanup();
+               goto drop;
+       }
+
+       device.hsp = GPOINTER_TO_INT(data);
+       return;
+
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+       sdp_list_t *protos, *classes;
+       GError *gerr = NULL;
+       GIOChannel *io;
+       uuid_t uuid;
+       int channel;
+
+       DBG("");
+
+       if (err < 0) {
+               error("handsfree: unable to get SDP record: %s",
+                                                               strerror(-err));
+               goto fail;
+       }
+
+       if (!recs || !recs->data) {
+               info("handsfree: no HSP SDP records found");
+               goto fail;
+       }
+
+       if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) {
+               error("handsfree: unable to get service classes from record");
+               goto fail;
+       }
+
+       if (sdp_get_access_protos(recs->data, &protos) < 0) {
+               error("handsfree: unable to get access protocols from record");
+               sdp_list_free(classes, free);
+               goto fail;
+       }
+
+       /* TODO read remote version? */
+       /* TODO read volume control support */
+
+       memcpy(&uuid, classes->data, sizeof(uuid));
+       sdp_list_free(classes, free);
+
+       if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
+                       uuid.value.uuid16 != HEADSET_SVCLASS_ID) {
+               sdp_list_free(protos, NULL);
+               error("handsfree: invalid service record or not HSP");
+               goto fail;
+       }
+
+       channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+       sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+       sdp_list_free(protos, NULL);
+       if (channel <= 0) {
+               error("handsfree: unable to get RFCOMM channel from record");
+               goto fail;
+       }
+
+       io = bt_io_connect(connect_cb, NULL, NULL, &gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                               BT_IO_OPT_DEST_BDADDR, &device.bdaddr,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_CHANNEL, channel,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("handsfree: unable to connect: %s", gerr->message);
+               g_error_free(gerr);
+               goto fail;
+       }
+
+       device.hsp = true;
+
+       g_io_channel_unref(io);
+       return;
+
+fail:
+       device_cleanup();
+}
+
+static int sdp_search_hsp(void)
+{
+       uuid_t uuid;
+
+       sdp_uuid16_create(&uuid, HEADSET_SVCLASS_ID);
+
+       return bt_search_service(&adapter_addr, &device.bdaddr, &uuid,
+                                       sdp_hsp_search_cb, NULL, NULL, 0);
+}
+
+static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+       sdp_list_t *protos, *classes;
+       GError *gerr = NULL;
+       GIOChannel *io;
+       uuid_t uuid;
+       int channel;
+
+       DBG("");
+
+       if (err < 0) {
+               error("handsfree: unable to get SDP record: %s",
+                                                               strerror(-err));
+               goto fail;
+       }
+
+       if (!recs || !recs->data) {
+               info("handsfree: no HFP SDP records found, trying HSP");
+
+               if (sdp_search_hsp() < 0) {
+                       error("handsfree: HSP SDP search failed");
+                       goto fail;
+               }
+
+               return;
+       }
+
+       if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) {
+               error("handsfree: unable to get service classes from record");
+               goto fail;
+       }
+
+       if (sdp_get_access_protos(recs->data, &protos) < 0) {
+               error("handsfree: unable to get access protocols from record");
+               sdp_list_free(classes, free);
+               goto fail;
+       }
+
+       /* TODO read remote version? */
+
+       memcpy(&uuid, classes->data, sizeof(uuid));
+       sdp_list_free(classes, free);
+
+       if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
+                       uuid.value.uuid16 != HANDSFREE_SVCLASS_ID) {
+               sdp_list_free(protos, NULL);
+               error("handsfree: invalid service record or not HFP");
+               goto fail;
+       }
+
+       channel = sdp_get_proto_port(protos, RFCOMM_UUID);
+       sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
+       sdp_list_free(protos, NULL);
+       if (channel <= 0) {
+               error("handsfree: unable to get RFCOMM channel from record");
+               goto fail;
+       }
+
+       io = bt_io_connect(connect_cb, NULL, NULL, &gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                               BT_IO_OPT_DEST_BDADDR, &device.bdaddr,
+                               BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                               BT_IO_OPT_CHANNEL, channel,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("handsfree: unable to connect: %s", gerr->message);
+               g_error_free(gerr);
+               goto fail;
+       }
+
+       g_io_channel_unref(io);
+       return;
+
+fail:
+       device_cleanup();
+}
+
+static int sdp_search_hfp(void)
+{
+       uuid_t uuid;
+
+       sdp_uuid16_create(&uuid, HANDSFREE_SVCLASS_ID);
+
+       return bt_search_service(&adapter_addr, &device.bdaddr, &uuid,
+                                       sdp_hfp_search_cb, NULL, NULL, 0);
+}
+
+static void handle_connect(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_connect *cmd = buf;
+       char addr[18];
+       uint8_t status;
+       bdaddr_t bdaddr;
+       int ret;
+
+       DBG("");
+
+       if (device.state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       android2bdaddr(&cmd->bdaddr, &bdaddr);
+
+       ba2str(&bdaddr, addr);
+       DBG("connecting to %s", addr);
+
+       device_init(&bdaddr);
+
+       /* prefer HFP over HSP */
+       ret = hfp_server ? sdp_search_hfp() : sdp_search_hsp();
+       if (ret < 0) {
+               error("handsfree: SDP search failed");
+               device_cleanup();
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_OP_HANDSFREE_CONNECT, status);
+}
+
+static void handle_disconnect(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_disconnect *cmd = buf;
+       bdaddr_t bdaddr;
+       uint8_t status;
+
+       DBG("");
+
+       android2bdaddr(cmd->bdaddr, &bdaddr);
+
+       if (device.state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED ||
+                       bacmp(&device.bdaddr, &bdaddr)) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+
+       }
+
+       if (device.state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING) {
+               status = HAL_STATUS_SUCCESS;
+               goto failed;
+       }
+
+       if (device.state == HAL_EV_HANDSFREE_CONN_STATE_CONNECTING) {
+               device_cleanup();
+       } else {
+               device_set_state(HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING);
+               hfp_gw_disconnect(device.gw);
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_OP_HANDSFREE_DISCONNECT, status);
+}
+
+static bool disconnect_sco(void)
+{
+       if (!device.sco)
+               return false;
+
+       device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING);
+
+       if (device.sco_watch) {
+               g_source_remove(device.sco_watch);
+               device.sco_watch = 0;
+       }
+
+       g_io_channel_shutdown(device.sco, TRUE, NULL);
+       g_io_channel_unref(device.sco);
+       device.sco = NULL;
+
+       device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
+       return true;
+}
+
+static bool connect_audio(void)
+{
+       if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED)
+               return false;
+
+       /* we haven't negotiated codec, start selection */
+       if ((device.features & HFP_HF_FEAT_CODEC) && !device.negotiated_codec) {
+               select_codec(0);
+               return true;
+       }
+
+       return connect_sco();
+}
+
+static void handle_connect_audio(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_connect_audio *cmd = buf;
+       bdaddr_t bdaddr;
+       uint8_t status;
+
+       DBG("");
+
+       android2bdaddr(cmd->bdaddr, &bdaddr);
+
+       if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED ||
+                       bacmp(&device.bdaddr, &bdaddr)) {
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       status = connect_audio() ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_OP_HANDSFREE_CONNECT_AUDIO, status);
+}
+
+static void handle_disconnect_audio(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_disconnect_audio *cmd = buf;
+       bdaddr_t bdaddr;
+       uint8_t status;
+
+       DBG("");
+
+       android2bdaddr(cmd->bdaddr, &bdaddr);
+
+       if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED ||
+                       bacmp(&device.bdaddr, &bdaddr)) {
+               status = HAL_STATUS_FAILED;
+               goto done;
+       }
+
+       status = disconnect_sco() ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED;
+
+done:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_OP_HANDSFREE_DISCONNECT_AUDIO, status);
+}
+
+static void handle_start_vr(const void *buf, uint16_t len)
+{
+       uint8_t status;
+
+       DBG("");
+
+       if (device.features & HFP_HF_FEAT_VR) {
+               hfp_gw_send_info(device.gw, "+BVRA: 1");
+               status = HAL_STATUS_SUCCESS;
+       } else {
+               status = HAL_STATUS_FAILED;
+       }
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_OP_HANDSFREE_START_VR, status);
+}
+
+static void handle_stop_vr(const void *buf, uint16_t len)
+{
+       uint8_t status;
+
+       DBG("");
+
+       if (device.features & HFP_HF_FEAT_VR) {
+               hfp_gw_send_info(device.gw, "+BVRA: 0");
+               status = HAL_STATUS_SUCCESS;
+       } else {
+               status = HAL_STATUS_FAILED;
+       }
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_OP_HANDSFREE_STOP_VR, status);
+}
+
+static void handle_volume_control(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_volume_control *cmd = buf;
+       uint8_t status, volume;
+
+       DBG("type=%u volume=%u", cmd->type, cmd->volume);
+
+       volume = cmd->volume > 15 ? 15 : cmd->volume;
+
+       switch (cmd->type) {
+       case HAL_HANDSFREE_VOLUME_TYPE_MIC:
+               hfp_gw_send_info(device.gw, "+VGM: %u", volume);
+
+               status = HAL_STATUS_SUCCESS;
+               break;
+       case HAL_HANDSFREE_VOLUME_TYPE_SPEAKER:
+               hfp_gw_send_info(device.gw, "+VGS: %u", volume);
+
+               status = HAL_STATUS_SUCCESS;
+               break;
+       default:
+               status = HAL_STATUS_FAILED;
+               break;
+       }
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_OP_HANDSFREE_VOLUME_CONTROL, status);
+}
+
+static void update_indicator(int ind, uint8_t val)
+{
+       DBG("ind=%u new=%u old=%u", ind, val, device.inds[ind].val);
+
+       if (device.inds[ind].val == val)
+               return;
+
+       device.inds[ind].val = val;
+
+       if (!device.indicators_enabled)
+               return;
+
+       if (!device.inds[ind].active)
+               return;
+
+       /* indicator numbers in CIEV start from 1 */
+       hfp_gw_send_info(device.gw, "+CIEV: %u,%u", ind + 1, val);
+}
+
+static void handle_device_status_notif(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_device_status_notif *cmd = buf;
+
+       DBG("");
+
+       update_indicator(IND_SERVICE, cmd->state);
+       update_indicator(IND_ROAM, cmd->type);
+       update_indicator(IND_SIGNAL, cmd->signal);
+       update_indicator(IND_BATTCHG, cmd->battery);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF,
+                                       HAL_STATUS_SUCCESS);
+}
+
+static void handle_cops(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_cops_response *cmd = buf;
+
+       if (len != sizeof(*cmd) + cmd->len ||
+                       (cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) {
+               error("Invalid cops response command, terminating");
+               raise(SIGTERM);
+               return;
+       }
+
+       DBG("");
+
+       hfp_gw_send_info(device.gw, "+COPS: 0,0,\"%.16s\"",
+                                       cmd->len ? (char *) cmd->buf : "");
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                       HAL_OP_HANDSFREE_COPS_RESPONSE, HAL_STATUS_SUCCESS);
+}
+
+static unsigned int get_callsetup(uint8_t state)
+{
+       switch (state) {
+       case HAL_HANDSFREE_CALL_STATE_INCOMING:
+               return 1;
+       case HAL_HANDSFREE_CALL_STATE_DIALING:
+               return 2;
+       case HAL_HANDSFREE_CALL_STATE_ALERTING:
+               return 3;
+       default:
+               return 0;
+       }
+}
+
+static void handle_cind(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_cind_response *cmd = buf;
+
+       DBG("");
+
+       /* HAL doesn't provide indicators values so need to convert here */
+       device.inds[IND_SERVICE].val = cmd->svc;
+       device.inds[IND_CALL].val = !!(cmd->num_active + cmd->num_held);
+       device.inds[IND_CALLSETUP].val = get_callsetup(cmd->state);
+       device.inds[IND_CALLHELD].val = cmd->num_held ?
+                                               (cmd->num_active ? 1 : 2) : 0;
+       device.inds[IND_SIGNAL].val = cmd->signal;
+       device.inds[IND_ROAM].val = cmd->roam;
+       device.inds[IND_BATTCHG].val = cmd->batt_chg;
+
+       /* Order must match indicators_defaults table */
+       hfp_gw_send_info(device.gw, "+CIND: %u,%u,%u,%u,%u,%u,%u",
+                                               device.inds[IND_SERVICE].val,
+                                               device.inds[IND_CALL].val,
+                                               device.inds[IND_CALLSETUP].val,
+                                               device.inds[IND_CALLHELD].val,
+                                               device.inds[IND_SIGNAL].val,
+                                               device.inds[IND_ROAM].val,
+                                               device.inds[IND_BATTCHG].val);
+
+       hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                       HAL_OP_HANDSFREE_CIND_RESPONSE, HAL_STATUS_SUCCESS);
+}
+
+static void handle_formatted_at_resp(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_formatted_at_response *cmd = buf;
+
+       DBG("");
+
+       if (len != sizeof(*cmd) + cmd->len ||
+                       (cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) {
+               error("Invalid formatted AT response command, terminating");
+               raise(SIGTERM);
+               return;
+       }
+
+       DBG("");
+
+       hfp_gw_send_info(device.gw, "%s", cmd->len ? (char *) cmd->buf : "");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                                       HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE,
+                                       HAL_STATUS_SUCCESS);
+}
+
+static void handle_at_resp(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_at_response *cmd = buf;
+
+       DBG("");
+
+       if (cmd->response == HAL_HANDSFREE_AT_RESPONSE_OK)
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+       else if (device.cmee_enabled)
+               hfp_gw_send_error(device.gw, cmd->error);
+       else
+               hfp_gw_send_result(device.gw, HFP_RESULT_ERROR);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                       HAL_OP_HANDSFREE_AT_RESPONSE, HAL_STATUS_SUCCESS);
+}
+
+static void handle_clcc_resp(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_clcc_response *cmd = buf;
+       uint8_t status;
+       char *number;
+
+       if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 &&
+                               cmd->number[cmd->number_len - 1] != '\0')) {
+               error("Invalid CLCC response command, terminating");
+               raise(SIGTERM);
+               return;
+       }
+
+       DBG("");
+
+       if (!cmd->index) {
+               hfp_gw_send_result(device.gw, HFP_RESULT_OK);
+
+               status = HAL_STATUS_SUCCESS;
+               goto done;
+       }
+
+       number = cmd->number_len ? (char *) cmd->number : "";
+
+       switch (cmd->state) {
+       case HAL_HANDSFREE_CALL_STATE_INCOMING:
+       case HAL_HANDSFREE_CALL_STATE_WAITING:
+       case HAL_HANDSFREE_CALL_STATE_ACTIVE:
+       case HAL_HANDSFREE_CALL_STATE_HELD:
+       case HAL_HANDSFREE_CALL_STATE_DIALING:
+       case HAL_HANDSFREE_CALL_STATE_ALERTING:
+               if (cmd->type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL &&
+                                                       number[0] != '+')
+                       hfp_gw_send_info(device.gw,
+                                       "+CLCC: %u,%u,%u,%u,%u,\"+%s\",%u",
+                                       cmd->index, cmd->dir, cmd->state,
+                                       cmd->mode, cmd->mpty, number,
+                                       cmd->type);
+               else
+                       hfp_gw_send_info(device.gw,
+                                       "+CLCC: %u,%u,%u,%u,%u,\"%s\",%u",
+                                       cmd->index, cmd->dir, cmd->state,
+                                       cmd->mode, cmd->mpty, number,
+                                       cmd->type);
+
+               status = HAL_STATUS_SUCCESS;
+               break;
+       case HAL_HANDSFREE_CALL_STATE_IDLE:
+       default:
+               status = HAL_STATUS_FAILED;
+               break;
+       }
+
+done:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_OP_HANDSFREE_CLCC_RESPONSE, status);
+}
+
+static gboolean ring_cb(gpointer user_data)
+{
+       char *clip = user_data;
+
+       hfp_gw_send_info(device.gw, "RING");
+
+       if (device.clip_enabled && clip)
+               hfp_gw_send_info(device.gw, "%s", clip);
+
+       return TRUE;
+}
+
+static void phone_state_dialing(int num_active, int num_held)
+{
+       update_indicator(IND_CALLSETUP, 2);
+
+       if (num_active == 0 && num_held > 0)
+               update_indicator(IND_CALLHELD, 2);
+
+       if (device.num_active == 0 && device.num_held == 0)
+               connect_audio();
+}
+
+static void phone_state_alerting(int num_active, int num_held)
+{
+       update_indicator(IND_CALLSETUP, 3);
+}
+
+static void phone_state_waiting(int num_active, int num_held, uint8_t type,
+                                       const uint8_t *number, int number_len)
+{
+       char *num;
+
+       if (!device.ccwa_enabled)
+               return;
+
+       num = number_len ? (char *) number : "";
+
+       if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+')
+               hfp_gw_send_info(device.gw, "+CCWA: \"+%s\",%u", num, type);
+       else
+               hfp_gw_send_info(device.gw, "+CCWA: \"%s\",%u", num, type);
+
+       update_indicator(IND_CALLSETUP, 1);
+}
+
+static void phone_state_incoming(int num_active, int num_held, uint8_t type,
+                                       const uint8_t *number, int number_len)
+{
+       char *clip, *num;
+
+       if (device.setup_state == HAL_HANDSFREE_CALL_STATE_INCOMING) {
+               if (device.num_active != num_active ||
+                                               device.num_held != num_held) {
+                       /*
+                        * calls changed while waiting call ie. due to
+                        * termination of active call
+                        */
+                       update_indicator(IND_CALLHELD,
+                                       num_held ? (num_active ? 1 : 2) : 0);
+                       update_indicator(IND_CALL, !!(num_active + num_held));
+               }
+
+               return;
+       }
+
+       if (device.call_hanging_up)
+               return;
+
+       if (num_active > 0 || num_held > 0) {
+               phone_state_waiting(num_active, num_held, type, number,
+                                                               number_len);
+               return;
+       }
+
+       update_indicator(IND_CALLSETUP, 1);
+
+       num = number_len ? (char *) number : "";
+
+       if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+')
+               clip = g_strdup_printf("+CLIP: \"+%s\",%u", num, type);
+       else
+               clip = g_strdup_printf("+CLIP: \"%s\",%u", num, type);
+
+       /* send first RING */
+       ring_cb(clip);
+
+       device.ring = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
+                                                       RING_TIMEOUT, ring_cb,
+                                                       clip, g_free);
+
+       if (!device.ring)
+               g_free(clip);
+}
+
+static void phone_state_idle(int num_active, int num_held)
+{
+       if (device.ring) {
+               g_source_remove(device.ring);
+               device.ring = 0;
+       }
+
+       switch (device.setup_state) {
+       case HAL_HANDSFREE_CALL_STATE_INCOMING:
+               if (num_active > device.num_active) {
+                       update_indicator(IND_CALL, 1);
+
+                       if (device.num_active == 0 && device.num_held == 0)
+                               connect_audio();
+               }
+
+               if (num_held > device.num_held)
+                       update_indicator(IND_CALLHELD, 1);
+
+               update_indicator(IND_CALLSETUP, 0);
+
+               if (num_active == device.num_active &&
+                                               num_held == device.num_held)
+                       device.call_hanging_up = true;
+
+               break;
+       case HAL_HANDSFREE_CALL_STATE_DIALING:
+       case HAL_HANDSFREE_CALL_STATE_ALERTING:
+               if (num_active > device.num_active)
+                       update_indicator(IND_CALL, 1);
+
+               update_indicator(IND_CALLHELD,
+                                       num_held ? (num_active ? 1 : 2) : 0);
+
+               update_indicator(IND_CALLSETUP, 0);
+               break;
+       case HAL_HANDSFREE_CALL_STATE_IDLE:
+
+               if (device.call_hanging_up) {
+                       device.call_hanging_up = false;
+                       return;
+               }
+
+               /* check if calls swapped */
+               if (num_held != 0 && num_active != 0 &&
+                               device.num_active == num_held &&
+                               device.num_held == num_active) {
+                       /* TODO better way for forcing indicator */
+                       device.inds[IND_CALLHELD].val = 0;
+               } else if ((num_active > 0 || num_held > 0) &&
+                                               device.num_active == 0 &&
+                                               device.num_held == 0) {
+                       /*
+                        * If number of active or held calls change but there
+                        * was no call setup change this means that there were
+                        * calls present when headset was connected.
+                        */
+                       connect_audio();
+               } else if (num_active == 0 && num_held == 0) {
+                       disconnect_sco();
+               }
+
+               update_indicator(IND_CALLHELD,
+                                       num_held ? (num_active ? 1 : 2) : 0);
+               update_indicator(IND_CALL, !!(num_active + num_held));
+               update_indicator(IND_CALLSETUP, 0);
+
+               /* If call was terminated due to carrier lost send NO CARRIER */
+               if (num_active == 0 && num_held == 0 &&
+                               device.inds[IND_SERVICE].val == 0 &&
+                               (device.num_active > 0 || device.num_held > 0))
+                       hfp_gw_send_info(device.gw, "NO CARRIER");
+
+               break;
+       default:
+               DBG("unhandled state %u", device.setup_state);
+               break;
+       }
+}
+
+static void handle_phone_state_change(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_handsfree_phone_state_change *cmd = buf;
+       uint8_t status;
+
+       if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 &&
+                               cmd->number[cmd->number_len - 1] != '\0')) {
+               error("Invalid phone state change command, terminating");
+               raise(SIGTERM);
+               return;
+       }
+
+       DBG("active=%u hold=%u state=%u", cmd->num_active, cmd->num_held,
+                                                               cmd->state);
+
+       switch (cmd->state) {
+       case HAL_HANDSFREE_CALL_STATE_DIALING:
+               phone_state_dialing(cmd->num_active, cmd->num_held);
+               break;
+       case HAL_HANDSFREE_CALL_STATE_ALERTING:
+               phone_state_alerting(cmd->num_active, cmd->num_held);
+               break;
+       case HAL_HANDSFREE_CALL_STATE_INCOMING:
+               phone_state_incoming(cmd->num_active, cmd->num_held, cmd->type,
+                                               cmd->number, cmd->number_len);
+               break;
+       case HAL_HANDSFREE_CALL_STATE_IDLE:
+               phone_state_idle(cmd->num_active, cmd->num_held);
+               break;
+       default:
+               DBG("unhandled new state %u (current state %u)", cmd->state,
+                                                       device.setup_state);
+
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       device.num_active = cmd->num_active;
+       device.num_held = cmd->num_held;
+       device.setup_state = cmd->state;
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
+                               HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+       /* HAL_OP_HANDSFREE_CONNECT */
+       { handle_connect, false,
+               sizeof(struct hal_cmd_handsfree_connect) },
+       /* HAL_OP_HANDSFREE_DISCONNECT */
+       { handle_disconnect, false,
+               sizeof(struct hal_cmd_handsfree_disconnect) },
+       /* HAL_OP_HANDSFREE_CONNECT_AUDIO */
+       { handle_connect_audio, false,
+               sizeof(struct hal_cmd_handsfree_connect_audio) },
+       /* HAL_OP_HANDSFREE_DISCONNECT_AUDIO */
+       { handle_disconnect_audio, false,
+               sizeof(struct hal_cmd_handsfree_disconnect_audio) },
+       /* define HAL_OP_HANDSFREE_START_VR */
+       { handle_start_vr, false, 0 },
+       /* define HAL_OP_HANDSFREE_STOP_VR */
+       { handle_stop_vr, false, 0 },
+       /* HAL_OP_HANDSFREE_VOLUME_CONTROL */
+       { handle_volume_control, false,
+               sizeof(struct hal_cmd_handsfree_volume_control) },
+       /* HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF */
+       { handle_device_status_notif, false,
+               sizeof(struct hal_cmd_handsfree_device_status_notif) },
+       /* HAL_OP_HANDSFREE_COPS_RESPONSE */
+       { handle_cops, true,
+               sizeof(struct hal_cmd_handsfree_cops_response) },
+       /* HAL_OP_HANDSFREE_CIND_RESPONSE */
+       { handle_cind, false,
+               sizeof(struct hal_cmd_handsfree_cind_response) },
+       /* HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE */
+       { handle_formatted_at_resp, true,
+               sizeof(struct hal_cmd_handsfree_formatted_at_response) },
+       /* HAL_OP_HANDSFREE_AT_RESPONSE */
+       { handle_at_resp, false,
+               sizeof(struct hal_cmd_handsfree_at_response) },
+       /* HAL_OP_HANDSFREE_CLCC_RESPONSE */
+       { handle_clcc_resp, true,
+               sizeof(struct hal_cmd_handsfree_clcc_response) },
+       /* HAL_OP_HANDSFREE_PHONE_STATE_CHANGE */
+       { handle_phone_state_change, true,
+               sizeof(struct hal_cmd_handsfree_phone_state_change) },
+};
+
+static sdp_record_t *headset_ag_record(void)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+       uuid_t l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t *record;
+       sdp_data_t *channel;
+       uint8_t netid = 0x01;
+       sdp_data_t *network;
+       uint8_t ch = HSP_AG_CHANNEL;
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       network = sdp_data_alloc(SDP_UINT8, &netid);
+       if (!network) {
+               sdp_record_free(record);
+               return NULL;
+       }
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(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, "Voice Gateway", 0, 0);
+
+       sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(pfseq, NULL);
+       sdp_list_free(aproto, NULL);
+       sdp_list_free(root, NULL);
+       sdp_list_free(svclass_id, NULL);
+
+       return record;
+}
+
+static void confirm_sco_cb(GIOChannel *chan, gpointer user_data)
+{
+       char address[18];
+       bdaddr_t bdaddr;
+       GError *err = NULL;
+
+       if (device.sco)
+               goto drop;
+
+       bt_io_get(chan, &err,
+                       BT_IO_OPT_DEST, address,
+                       BT_IO_OPT_DEST_BDADDR, &bdaddr,
+                       BT_IO_OPT_INVALID);
+       if (err) {
+               error("handsfree: audio confirm failed (%s)", err->message);
+               g_error_free(err);
+               goto drop;
+       }
+
+       DBG("incoming SCO connection from %s", address);
+
+       if (device.state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED ||
+                       bacmp(&device.bdaddr, &bdaddr)) {
+               error("handsfree: audio connection from %s rejected", address);
+               goto drop;
+       }
+
+       if (!bt_io_accept(chan, connect_sco_cb, NULL, NULL, NULL)) {
+               error("handsfree: failed to accept audio connection");
+               goto drop;
+       }
+
+       device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING);
+       return;
+
+drop:
+       g_io_channel_shutdown(chan, TRUE, NULL);
+}
+
+static bool enable_hsp_ag(void)
+{
+       sdp_record_t *rec;
+       GError *err = NULL;
+
+       DBG("");
+
+       hsp_server =  bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(true),
+                                       NULL, &err,
+                                       BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                                       BT_IO_OPT_CHANNEL, HSP_AG_CHANNEL,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                                       BT_IO_OPT_INVALID);
+       if (!hsp_server) {
+               error("Failed to listen on Headset rfcomm: %s", err->message);
+               g_error_free(err);
+               return false;
+       }
+
+       rec = headset_ag_record();
+       if (!rec) {
+               error("Failed to allocate Headset record");
+               goto failed;
+       }
+
+       if (bt_adapter_add_record(rec, 0) < 0) {
+               error("Failed to register Headset record");
+               sdp_record_free(rec);
+               goto failed;
+       }
+
+       hsp_record_id = rec->handle;
+       return true;
+
+failed:
+       g_io_channel_shutdown(hsp_server, TRUE, NULL);
+       g_io_channel_unref(hsp_server);
+       hsp_server = NULL;
+
+       return false;
+}
+
+static void cleanup_hsp_ag(void)
+{
+       if (hsp_server) {
+               g_io_channel_shutdown(hsp_server, TRUE, NULL);
+               g_io_channel_unref(hsp_server);
+               hsp_server = NULL;
+       }
+
+       if (hsp_record_id > 0) {
+               bt_adapter_remove_record(hsp_record_id);
+               hsp_record_id = 0;
+       }
+}
+
+static sdp_record_t *hfp_ag_record(void)
+{
+       sdp_list_t *svclass_id, *pfseq, *apseq, *root;
+       uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
+       uuid_t l2cap_uuid, rfcomm_uuid;
+       sdp_profile_desc_t profile;
+       sdp_list_t *aproto, *proto[2];
+       sdp_record_t *record;
+       sdp_data_t *channel, *features;
+       uint8_t netid = 0x01;
+       uint16_t sdpfeat;
+       sdp_data_t *network;
+       uint8_t ch = HFP_AG_CHANNEL;
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       network = sdp_data_alloc(SDP_UINT8, &netid);
+       if (!network) {
+               sdp_record_free(record);
+               return NULL;
+       }
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(record, root);
+
+       sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
+       svclass_id = sdp_list_append(NULL, &svclass_uuid);
+       sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
+       svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
+       sdp_set_service_classes(record, svclass_id);
+
+       sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
+       profile.version = 0x0106;
+       pfseq = sdp_list_append(NULL, &profile);
+       sdp_set_profile_descs(record, pfseq);
+
+       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       apseq = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
+       proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
+       channel = sdp_data_alloc(SDP_UINT8, &ch);
+       proto[1] = sdp_list_append(proto[1], channel);
+       apseq = sdp_list_append(apseq, proto[1]);
+
+       /* Codec Negotiation bit in SDP feature is different then in BRSF */
+       sdpfeat = hfp_ag_features & 0x0000003F;
+       if (hfp_ag_features & HFP_AG_FEAT_CODEC)
+               sdpfeat |= 0x00000020;
+       else
+               sdpfeat &= ~0x00000020;
+
+       features = sdp_data_alloc(SDP_UINT16, &sdpfeat);
+       sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_set_info_attr(record, "Hands-Free Audio Gateway", NULL, NULL);
+
+       sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);
+
+       sdp_data_free(channel);
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(pfseq, NULL);
+       sdp_list_free(aproto, NULL);
+       sdp_list_free(root, NULL);
+       sdp_list_free(svclass_id, NULL);
+
+       return record;
+}
+
+static bool enable_hfp_ag(void)
+{
+       sdp_record_t *rec;
+       GError *err = NULL;
+
+       DBG("");
+
+       if (hfp_server)
+               return false;
+
+       hfp_server =  bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(false),
+                                       NULL, &err,
+                                       BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                                       BT_IO_OPT_CHANNEL, HFP_AG_CHANNEL,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                                       BT_IO_OPT_INVALID);
+       if (!hfp_server) {
+               error("Failed to listen on Handsfree rfcomm: %s", err->message);
+               g_error_free(err);
+               return false;
+       }
+
+       rec = hfp_ag_record();
+       if (!rec) {
+               error("Failed to allocate Handsfree record");
+               goto failed;
+       }
+
+       if (bt_adapter_add_record(rec, 0) < 0) {
+               error("Failed to register Handsfree record");
+               sdp_record_free(rec);
+               goto failed;
+       }
+
+       hfp_record_id = rec->handle;
+       return true;
+
+failed:
+       g_io_channel_shutdown(hfp_server, TRUE, NULL);
+       g_io_channel_unref(hfp_server);
+       hfp_server = NULL;
+
+       return false;
+}
+
+static void cleanup_hfp_ag(void)
+{
+       if (hfp_server) {
+               g_io_channel_shutdown(hfp_server, TRUE, NULL);
+               g_io_channel_unref(hfp_server);
+               hfp_server = NULL;
+       }
+
+       if (hfp_record_id > 0) {
+               bt_adapter_remove_record(hfp_record_id);
+               hfp_record_id = 0;
+       }
+}
+
+static bool enable_sco_server(void)
+{
+       GError *err = NULL;
+
+       sco_server = bt_io_listen(NULL, confirm_sco_cb, NULL, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                               BT_IO_OPT_INVALID);
+       if (!sco_server) {
+               error("handsfree: Failed to listen on SCO: %s", err->message);
+               g_error_free(err);
+               cleanup_hsp_ag();
+               cleanup_hfp_ag();
+               return false;
+       }
+
+       return true;
+}
+
+static void disable_sco_server(void)
+{
+       if (sco_server) {
+               g_io_channel_shutdown(sco_server, TRUE, NULL);
+               g_io_channel_unref(sco_server);
+               sco_server = NULL;
+       }
+}
+
+static void bt_sco_connect(const void *buf, uint16_t len)
+{
+       int fd;
+       GError *err;
+       struct sco_rsp_connect rsp;
+
+       DBG("");
+
+       if (!device.sco)
+               goto failed;
+
+       err = NULL;
+       if (!bt_io_get(device.sco, &err, BT_IO_OPT_MTU, &rsp.mtu,
+                                                       BT_IO_OPT_INVALID)) {
+               error("Unable to get MTU: %s\n", err->message);
+               g_clear_error(&err);
+               goto failed;
+       }
+
+       fd = g_io_channel_unix_get_fd(device.sco);
+
+       DBG("fd %d mtu %u", fd, rsp.mtu);
+
+       ipc_send_rsp_full(sco_ipc, SCO_SERVICE_ID, SCO_OP_CONNECT,
+                                                       sizeof(rsp), &rsp, fd);
+
+       return;
+
+failed:
+       ipc_send_rsp(sco_ipc, SCO_SERVICE_ID, SCO_OP_STATUS, SCO_STATUS_FAILED);
+}
+
+static const struct ipc_handler sco_handlers[] = {
+       /* SCO_OP_CONNECT */
+       { bt_sco_connect, false, 0 }
+};
+
+static void bt_sco_unregister(void)
+{
+       DBG("");
+
+       ipc_cleanup(sco_ipc);
+       sco_ipc = NULL;
+}
+
+static bool bt_sco_register(ipc_disconnect_cb disconnect)
+{
+       DBG("");
+
+       sco_ipc = ipc_init(BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH),
+                               SCO_SERVICE_ID, false, disconnect, NULL);
+       if (!sco_ipc)
+               return false;
+
+       ipc_register(sco_ipc, SCO_SERVICE_ID, sco_handlers,
+                                               G_N_ELEMENTS(sco_handlers));
+
+       return true;
+}
+
+bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+       DBG("mode 0x%x", mode);
+
+       bacpy(&adapter_addr, addr);
+
+       if (!enable_hsp_ag())
+               return false;
+
+       if (!enable_sco_server()) {
+               cleanup_hsp_ag();
+               return false;
+       }
+
+       if (mode == HAL_MODE_HANDSFREE_HSP_ONLY)
+               goto done;
+
+       hfp_ag_features = HFP_AG_FEATURES;
+
+       if (mode == HAL_MODE_HANDSFREE_HFP_WBS)
+               hfp_ag_features |= HFP_AG_FEAT_CODEC;
+
+       if (enable_hfp_ag())
+               goto done;
+
+       cleanup_hsp_ag();
+       disable_sco_server();
+       hfp_ag_features = 0;
+       return false;
+
+done:
+       hal_ipc = ipc;
+       ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers,
+                                               G_N_ELEMENTS(cmd_handlers));
+
+       bt_sco_register(NULL);
+
+       return true;
+}
+
+void bt_handsfree_unregister(void)
+{
+       DBG("");
+
+       bt_sco_unregister();
+       ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE);
+       hal_ipc = NULL;
+
+       cleanup_hfp_ag();
+       cleanup_hsp_ag();
+       disable_sco_server();
+
+       hfp_ag_features = 0;
+}
diff --git a/android/handsfree.h b/android/handsfree.h
new file mode 100644 (file)
index 0000000..e5eff47
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_handsfree_unregister(void);
diff --git a/android/hardware/audio.h b/android/hardware/audio.h
new file mode 100644 (file)
index 0000000..61d92db
--- /dev/null
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_HAL_INTERFACE_H
+#define ANDROID_AUDIO_HAL_INTERFACE_H
+
+#include <stdint.h>
+#include <strings.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio_effect.h>
+
+__BEGIN_DECLS
+
+/**
+ * The id of this module
+ */
+#define AUDIO_HARDWARE_MODULE_ID "audio"
+
+/**
+ * Name of the audio devices to open
+ */
+#define AUDIO_HARDWARE_INTERFACE "audio_hw_if"
+
+
+/* Use version 0.1 to be compatible with first generation of audio hw module with version_major
+ * hardcoded to 1. No audio module API change.
+ */
+#define AUDIO_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1)
+#define AUDIO_MODULE_API_VERSION_CURRENT AUDIO_MODULE_API_VERSION_0_1
+
+/* First generation of audio devices had version hardcoded to 0. all devices with versions < 1.0
+ * will be considered of first generation API.
+ */
+#define AUDIO_DEVICE_API_VERSION_0_0 HARDWARE_DEVICE_API_VERSION(0, 0)
+#define AUDIO_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0)
+#define AUDIO_DEVICE_API_VERSION_2_0 HARDWARE_DEVICE_API_VERSION(2, 0)
+#define AUDIO_DEVICE_API_VERSION_CURRENT AUDIO_DEVICE_API_VERSION_2_0
+
+/**
+ * List of known audio HAL modules. This is the base name of the audio HAL
+ * library composed of the "audio." prefix, one of the base names below and
+ * a suffix specific to the device.
+ * e.g: audio.primary.goldfish.so or audio.a2dp.default.so
+ */
+
+#define AUDIO_HARDWARE_MODULE_ID_PRIMARY "primary"
+#define AUDIO_HARDWARE_MODULE_ID_A2DP "a2dp"
+#define AUDIO_HARDWARE_MODULE_ID_USB "usb"
+#define AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX "r_submix"
+#define AUDIO_HARDWARE_MODULE_ID_CODEC_OFFLOAD "codec_offload"
+
+/**************************************/
+
+/**
+ *  standard audio parameters that the HAL may need to handle
+ */
+
+/**
+ *  audio device parameters
+ */
+
+/* BT SCO Noise Reduction + Echo Cancellation parameters */
+#define AUDIO_PARAMETER_KEY_BT_NREC "bt_headset_nrec"
+#define AUDIO_PARAMETER_VALUE_ON "on"
+#define AUDIO_PARAMETER_VALUE_OFF "off"
+
+/* TTY mode selection */
+#define AUDIO_PARAMETER_KEY_TTY_MODE "tty_mode"
+#define AUDIO_PARAMETER_VALUE_TTY_OFF "tty_off"
+#define AUDIO_PARAMETER_VALUE_TTY_VCO "tty_vco"
+#define AUDIO_PARAMETER_VALUE_TTY_HCO "tty_hco"
+#define AUDIO_PARAMETER_VALUE_TTY_FULL "tty_full"
+
+/* A2DP sink address set by framework */
+#define AUDIO_PARAMETER_A2DP_SINK_ADDRESS "a2dp_sink_address"
+
+/* Screen state */
+#define AUDIO_PARAMETER_KEY_SCREEN_STATE "screen_state"
+
+/**
+ *  audio stream parameters
+ */
+
+#define AUDIO_PARAMETER_STREAM_ROUTING "routing"            // audio_devices_t
+#define AUDIO_PARAMETER_STREAM_FORMAT "format"              // audio_format_t
+#define AUDIO_PARAMETER_STREAM_CHANNELS "channels"          // audio_channel_mask_t
+#define AUDIO_PARAMETER_STREAM_FRAME_COUNT "frame_count"    // size_t
+#define AUDIO_PARAMETER_STREAM_INPUT_SOURCE "input_source"  // audio_source_t
+#define AUDIO_PARAMETER_STREAM_SAMPLING_RATE "sampling_rate" // uint32_t
+
+/* Query supported formats. The response is a '|' separated list of strings from
+ * audio_format_t enum e.g: "sup_formats=AUDIO_FORMAT_PCM_16_BIT" */
+#define AUDIO_PARAMETER_STREAM_SUP_FORMATS "sup_formats"
+/* Query supported channel masks. The response is a '|' separated list of strings from
+ * audio_channel_mask_t enum e.g: "sup_channels=AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_MONO" */
+#define AUDIO_PARAMETER_STREAM_SUP_CHANNELS "sup_channels"
+/* Query supported sampling rates. The response is a '|' separated list of integer values e.g:
+ * "sup_sampling_rates=44100|48000" */
+#define AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES "sup_sampling_rates"
+
+/**
+ * audio codec parameters
+ */
+
+#define AUDIO_OFFLOAD_CODEC_PARAMS "music_offload_codec_param"
+#define AUDIO_OFFLOAD_CODEC_BIT_PER_SAMPLE "music_offload_bit_per_sample"
+#define AUDIO_OFFLOAD_CODEC_BIT_RATE "music_offload_bit_rate"
+#define AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE "music_offload_avg_bit_rate"
+#define AUDIO_OFFLOAD_CODEC_ID "music_offload_codec_id"
+#define AUDIO_OFFLOAD_CODEC_BLOCK_ALIGN "music_offload_block_align"
+#define AUDIO_OFFLOAD_CODEC_SAMPLE_RATE "music_offload_sample_rate"
+#define AUDIO_OFFLOAD_CODEC_ENCODE_OPTION "music_offload_encode_option"
+#define AUDIO_OFFLOAD_CODEC_NUM_CHANNEL  "music_offload_num_channels"
+#define AUDIO_OFFLOAD_CODEC_DOWN_SAMPLING  "music_offload_down_sampling"
+#define AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES  "delay_samples"
+#define AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES  "padding_samples"
+
+/**************************************/
+
+/* common audio stream configuration parameters
+ * You should memset() the entire structure to zero before use to
+ * ensure forward compatibility
+ */
+struct audio_config {
+    uint32_t sample_rate;
+    audio_channel_mask_t channel_mask;
+    audio_format_t  format;
+    audio_offload_info_t offload_info;
+};
+typedef struct audio_config audio_config_t;
+
+/* common audio stream parameters and operations */
+struct audio_stream {
+
+    /**
+     * Return the sampling rate in Hz - eg. 44100.
+     */
+    uint32_t (*get_sample_rate)(const struct audio_stream *stream);
+
+    /* currently unused - use set_parameters with key
+     *    AUDIO_PARAMETER_STREAM_SAMPLING_RATE
+     */
+    int (*set_sample_rate)(struct audio_stream *stream, uint32_t rate);
+
+    /**
+     * Return size of input/output buffer in bytes for this stream - eg. 4800.
+     * It should be a multiple of the frame size.  See also get_input_buffer_size.
+     */
+    size_t (*get_buffer_size)(const struct audio_stream *stream);
+
+    /**
+     * Return the channel mask -
+     *  e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO
+     */
+    audio_channel_mask_t (*get_channels)(const struct audio_stream *stream);
+
+    /**
+     * Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT
+     */
+    audio_format_t (*get_format)(const struct audio_stream *stream);
+
+    /* currently unused - use set_parameters with key
+     *     AUDIO_PARAMETER_STREAM_FORMAT
+     */
+    int (*set_format)(struct audio_stream *stream, audio_format_t format);
+
+    /**
+     * Put the audio hardware input/output into standby mode.
+     * Driver should exit from standby mode at the next I/O operation.
+     * Returns 0 on success and <0 on failure.
+     */
+    int (*standby)(struct audio_stream *stream);
+
+    /** dump the state of the audio input/output device */
+    int (*dump)(const struct audio_stream *stream, int fd);
+
+    /** Return the set of device(s) which this stream is connected to */
+    audio_devices_t (*get_device)(const struct audio_stream *stream);
+
+    /**
+     * Currently unused - set_device() corresponds to set_parameters() with key
+     * AUDIO_PARAMETER_STREAM_ROUTING for both input and output.
+     * AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by
+     * input streams only.
+     */
+    int (*set_device)(struct audio_stream *stream, audio_devices_t device);
+
+    /**
+     * set/get audio stream parameters. The function accepts a list of
+     * parameter key value pairs in the form: key1=value1;key2=value2;...
+     *
+     * Some keys are reserved for standard parameters (See AudioParameter class)
+     *
+     * If the implementation does not accept a parameter change while
+     * the output is active but the parameter is acceptable otherwise, it must
+     * return -ENOSYS.
+     *
+     * The audio flinger will put the stream in standby and then change the
+     * parameter value.
+     */
+    int (*set_parameters)(struct audio_stream *stream, const char *kv_pairs);
+
+    /*
+     * Returns a pointer to a heap allocated string. The caller is responsible
+     * for freeing the memory for it using free().
+     */
+    char * (*get_parameters)(const struct audio_stream *stream,
+                             const char *keys);
+    int (*add_audio_effect)(const struct audio_stream *stream,
+                             effect_handle_t effect);
+    int (*remove_audio_effect)(const struct audio_stream *stream,
+                             effect_handle_t effect);
+};
+typedef struct audio_stream audio_stream_t;
+
+/* type of asynchronous write callback events. Mutually exclusive */
+typedef enum {
+    STREAM_CBK_EVENT_WRITE_READY, /* non blocking write completed */
+    STREAM_CBK_EVENT_DRAIN_READY  /* drain completed */
+} stream_callback_event_t;
+
+typedef int (*stream_callback_t)(stream_callback_event_t event, void *param, void *cookie);
+
+/* type of drain requested to audio_stream_out->drain(). Mutually exclusive */
+typedef enum {
+    AUDIO_DRAIN_ALL,            /* drain() returns when all data has been played */
+    AUDIO_DRAIN_EARLY_NOTIFY    /* drain() returns a short time before all data
+                                   from the current track has been played to
+                                   give time for gapless track switch */
+} audio_drain_type_t;
+
+/**
+ * audio_stream_out is the abstraction interface for the audio output hardware.
+ *
+ * It provides information about various properties of the audio output
+ * hardware driver.
+ */
+
+struct audio_stream_out {
+    struct audio_stream common;
+
+    /**
+     * Return the audio hardware driver estimated latency in milliseconds.
+     */
+    uint32_t (*get_latency)(const struct audio_stream_out *stream);
+
+    /**
+     * Use this method in situations where audio mixing is done in the
+     * hardware. This method serves as a direct interface with hardware,
+     * allowing you to directly set the volume as apposed to via the framework.
+     * This method might produce multiple PCM outputs or hardware accelerated
+     * codecs, such as MP3 or AAC.
+     */
+    int (*set_volume)(struct audio_stream_out *stream, float left, float right);
+
+    /**
+     * Write audio buffer to driver. Returns number of bytes written, or a
+     * negative status_t. If at least one frame was written successfully prior to the error,
+     * it is suggested that the driver return that successful (short) byte count
+     * and then return an error in the subsequent call.
+     *
+     * If set_callback() has previously been called to enable non-blocking mode
+     * the write() is not allowed to block. It must write only the number of
+     * bytes that currently fit in the driver/hardware buffer and then return
+     * this byte count. If this is less than the requested write size the
+     * callback function must be called when more space is available in the
+     * driver/hardware buffer.
+     */
+    ssize_t (*write)(struct audio_stream_out *stream, const void* buffer,
+                     size_t bytes);
+
+    /* return the number of audio frames written by the audio dsp to DAC since
+     * the output has exited standby
+     */
+    int (*get_render_position)(const struct audio_stream_out *stream,
+                               uint32_t *dsp_frames);
+
+    /**
+     * get the local time at which the next write to the audio driver will be presented.
+     * The units are microseconds, where the epoch is decided by the local audio HAL.
+     */
+    int (*get_next_write_timestamp)(const struct audio_stream_out *stream,
+                                    int64_t *timestamp);
+
+    /**
+     * set the callback function for notifying completion of non-blocking
+     * write and drain.
+     * Calling this function implies that all future write() and drain()
+     * must be non-blocking and use the callback to signal completion.
+     */
+    int (*set_callback)(struct audio_stream_out *stream,
+            stream_callback_t callback, void *cookie);
+
+    /**
+     * Notifies to the audio driver to stop playback however the queued buffers are
+     * retained by the hardware. Useful for implementing pause/resume. Empty implementation
+     * if not supported however should be implemented for hardware with non-trivial
+     * latency. In the pause state audio hardware could still be using power. User may
+     * consider calling suspend after a timeout.
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+    int (*pause)(struct audio_stream_out* stream);
+
+    /**
+     * Notifies to the audio driver to resume playback following a pause.
+     * Returns error if called without matching pause.
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+    int (*resume)(struct audio_stream_out* stream);
+
+    /**
+     * Requests notification when data buffered by the driver/hardware has
+     * been played. If set_callback() has previously been called to enable
+     * non-blocking mode, the drain() must not block, instead it should return
+     * quickly and completion of the drain is notified through the callback.
+     * If set_callback() has not been called, the drain() must block until
+     * completion.
+     * If type==AUDIO_DRAIN_ALL, the drain completes when all previously written
+     * data has been played.
+     * If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all
+     * data for the current track has played to allow time for the framework
+     * to perform a gapless track switch.
+     *
+     * Drain must return immediately on stop() and flush() call
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+    int (*drain)(struct audio_stream_out* stream, audio_drain_type_t type );
+
+    /**
+     * Notifies to the audio driver to flush the queued data. Stream must already
+     * be paused before calling flush().
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     */
+   int (*flush)(struct audio_stream_out* stream);
+
+    /**
+     * Return a recent count of the number of audio frames presented to an external observer.
+     * This excludes frames which have been written but are still in the pipeline.
+     * The count is not reset to zero when output enters standby.
+     * Also returns the value of CLOCK_MONOTONIC as of this presentation count.
+     * The returned count is expected to be 'recent',
+     * but does not need to be the most recent possible value.
+     * However, the associated time should correspond to whatever count is returned.
+     * Example:  assume that N+M frames have been presented, where M is a 'small' number.
+     * Then it is permissible to return N instead of N+M,
+     * and the timestamp should correspond to N rather than N+M.
+     * The terms 'recent' and 'small' are not defined.
+     * They reflect the quality of the implementation.
+     *
+     * 3.0 and higher only.
+     */
+    int (*get_presentation_position)(const struct audio_stream_out *stream,
+                               uint64_t *frames, struct timespec *timestamp);
+
+};
+typedef struct audio_stream_out audio_stream_out_t;
+
+struct audio_stream_in {
+    struct audio_stream common;
+
+    /** set the input gain for the audio driver. This method is for
+     *  for future use */
+    int (*set_gain)(struct audio_stream_in *stream, float gain);
+
+    /** Read audio buffer in from audio driver. Returns number of bytes read, or a
+     *  negative status_t. If at least one frame was read prior to the error,
+     *  read should return that byte count and then return an error in the subsequent call.
+     */
+    ssize_t (*read)(struct audio_stream_in *stream, void* buffer,
+                    size_t bytes);
+
+    /**
+     * Return the amount of input frames lost in the audio driver since the
+     * last call of this function.
+     * Audio driver is expected to reset the value to 0 and restart counting
+     * upon returning the current value by this function call.
+     * Such loss typically occurs when the user space process is blocked
+     * longer than the capacity of audio driver buffers.
+     *
+     * Unit: the number of input audio frames
+     */
+    uint32_t (*get_input_frames_lost)(struct audio_stream_in *stream);
+};
+typedef struct audio_stream_in audio_stream_in_t;
+
+/**
+ * return the frame size (number of bytes per sample).
+ */
+static inline size_t audio_stream_frame_size(const struct audio_stream *s)
+{
+    size_t chan_samp_sz;
+    audio_format_t format = s->get_format(s);
+
+    if (audio_is_linear_pcm(format)) {
+        chan_samp_sz = audio_bytes_per_sample(format);
+        return popcount(s->get_channels(s)) * chan_samp_sz;
+    }
+
+    return sizeof(int8_t);
+}
+
+
+/**********************************************************************/
+
+/**
+ * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
+ * and the fields of this data structure must begin with hw_module_t
+ * followed by module specific information.
+ */
+struct audio_module {
+    struct hw_module_t common;
+};
+
+struct audio_hw_device {
+    struct hw_device_t common;
+
+    /**
+     * used by audio flinger to enumerate what devices are supported by
+     * each audio_hw_device implementation.
+     *
+     * Return value is a bitmask of 1 or more values of audio_devices_t
+     *
+     * NOTE: audio HAL implementations starting with
+     * AUDIO_DEVICE_API_VERSION_2_0 do not implement this function.
+     * All supported devices should be listed in audio_policy.conf
+     * file and the audio policy manager must choose the appropriate
+     * audio module based on information in this file.
+     */
+    uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);
+
+    /**
+     * check to see if the audio hardware interface has been initialized.
+     * returns 0 on success, -ENODEV on failure.
+     */
+    int (*init_check)(const struct audio_hw_device *dev);
+
+    /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */
+    int (*set_voice_volume)(struct audio_hw_device *dev, float volume);
+
+    /**
+     * set the audio volume for all audio activities other than voice call.
+     * Range between 0.0 and 1.0. If any value other than 0 is returned,
+     * the software mixer will emulate this capability.
+     */
+    int (*set_master_volume)(struct audio_hw_device *dev, float volume);
+
+    /**
+     * Get the current master volume value for the HAL, if the HAL supports
+     * master volume control.  AudioFlinger will query this value from the
+     * primary audio HAL when the service starts and use the value for setting
+     * the initial master volume across all HALs.  HALs which do not support
+     * this method may leave it set to NULL.
+     */
+    int (*get_master_volume)(struct audio_hw_device *dev, float *volume);
+
+    /**
+     * set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode
+     * is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is
+     * playing, and AUDIO_MODE_IN_CALL when a call is in progress.
+     */
+    int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode);
+
+    /* mic mute */
+    int (*set_mic_mute)(struct audio_hw_device *dev, bool state);
+    int (*get_mic_mute)(const struct audio_hw_device *dev, bool *state);
+
+    /* set/get global audio parameters */
+    int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs);
+
+    /*
+     * Returns a pointer to a heap allocated string. The caller is responsible
+     * for freeing the memory for it using free().
+     */
+    char * (*get_parameters)(const struct audio_hw_device *dev,
+                             const char *keys);
+
+    /* Returns audio input buffer size according to parameters passed or
+     * 0 if one of the parameters is not supported.
+     * See also get_buffer_size which is for a particular stream.
+     */
+    size_t (*get_input_buffer_size)(const struct audio_hw_device *dev,
+                                    const struct audio_config *config);
+
+    /** This method creates and opens the audio hardware output stream */
+    int (*open_output_stream)(struct audio_hw_device *dev,
+                              audio_io_handle_t handle,
+                              audio_devices_t devices,
+                              audio_output_flags_t flags,
+                              struct audio_config *config,
+                              struct audio_stream_out **stream_out);
+
+    void (*close_output_stream)(struct audio_hw_device *dev,
+                                struct audio_stream_out* stream_out);
+
+    /** This method creates and opens the audio hardware input stream */
+    int (*open_input_stream)(struct audio_hw_device *dev,
+                             audio_io_handle_t handle,
+                             audio_devices_t devices,
+                             struct audio_config *config,
+                             struct audio_stream_in **stream_in);
+
+    void (*close_input_stream)(struct audio_hw_device *dev,
+                               struct audio_stream_in *stream_in);
+
+    /** This method dumps the state of the audio hardware */
+    int (*dump)(const struct audio_hw_device *dev, int fd);
+
+    /**
+     * set the audio mute status for all audio activities.  If any value other
+     * than 0 is returned, the software mixer will emulate this capability.
+     */
+    int (*set_master_mute)(struct audio_hw_device *dev, bool mute);
+
+    /**
+     * Get the current master mute status for the HAL, if the HAL supports
+     * master mute control.  AudioFlinger will query this value from the primary
+     * audio HAL when the service starts and use the value for setting the
+     * initial master mute across all HALs.  HALs which do not support this
+     * method may leave it set to NULL.
+     */
+    int (*get_master_mute)(struct audio_hw_device *dev, bool *mute);
+};
+typedef struct audio_hw_device audio_hw_device_t;
+
+/** convenience API for opening and closing a supported device */
+
+static inline int audio_hw_device_open(const struct hw_module_t* module,
+                                       struct audio_hw_device** device)
+{
+    return module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
+                                 (struct hw_device_t**)device);
+}
+
+static inline int audio_hw_device_close(struct audio_hw_device* device)
+{
+    return device->common.close(&device->common);
+}
+
+
+__END_DECLS
+
+#endif  // ANDROID_AUDIO_INTERFACE_H
diff --git a/android/hardware/audio_effect.h b/android/hardware/audio_effect.h
new file mode 100644 (file)
index 0000000..857a300
--- /dev/null
@@ -0,0 +1,1009 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_EFFECT_H
+#define ANDROID_AUDIO_EFFECT_H
+
+#include <errno.h>
+#include <stdint.h>
+#include <strings.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <system/audio.h>
+
+__BEGIN_DECLS
+
+
+/////////////////////////////////////////////////
+//      Common Definitions
+/////////////////////////////////////////////////
+
+//
+//--- Effect descriptor structure effect_descriptor_t
+//
+
+// Unique effect ID (can be generated from the following site:
+//  http://www.itu.int/ITU-T/asn1/uuid.html)
+// This format is used for both "type" and "uuid" fields of the effect descriptor structure.
+// - When used for effect type and the engine is implementing and effect corresponding to a standard
+// OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface.
+// - When used as uuid, it should be a unique UUID for this particular implementation.
+typedef struct effect_uuid_s {
+    uint32_t timeLow;
+    uint16_t timeMid;
+    uint16_t timeHiAndVersion;
+    uint16_t clockSeq;
+    uint8_t node[6];
+} effect_uuid_t;
+
+// Maximum length of character strings in structures defines by this API.
+#define EFFECT_STRING_LEN_MAX 64
+
+// NULL UUID definition (matches SL_IID_NULL_)
+#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \
+                                  { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } }
+static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER;
+static const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_;
+static const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210";
+
+
+// The effect descriptor contains necessary information to facilitate the enumeration of the effect
+// engines present in a library.
+typedef struct effect_descriptor_s {
+    effect_uuid_t type;     // UUID of to the OpenSL ES interface implemented by this effect
+    effect_uuid_t uuid;     // UUID for this particular implementation
+    uint32_t apiVersion;    // Version of the effect control API implemented
+    uint32_t flags;         // effect engine capabilities/requirements flags (see below)
+    uint16_t cpuLoad;       // CPU load indication (see below)
+    uint16_t memoryUsage;   // Data Memory usage (see below)
+    char    name[EFFECT_STRING_LEN_MAX];   // human readable effect name
+    char    implementor[EFFECT_STRING_LEN_MAX];    // human readable effect implementor name
+} effect_descriptor_t;
+
+// CPU load and memory usage indication: each effect implementation must provide an indication of
+// its CPU and memory usage for the audio effect framework to limit the number of effects
+// instantiated at a given time on a given platform.
+// The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS.
+// The memory usage is expressed in KB and includes only dynamically allocated memory
+
+// Definitions for flags field of effect descriptor.
+//  +---------------------------+-----------+-----------------------------------
+//  | description               | bits      | values
+//  +---------------------------+-----------+-----------------------------------
+//  | connection mode           | 0..2      | 0 insert: after track process
+//  |                           |           | 1 auxiliary: connect to track auxiliary
+//  |                           |           |  output and use send level
+//  |                           |           | 2 replace: replaces track process function;
+//  |                           |           |   must implement SRC, volume and mono to stereo.
+//  |                           |           | 3 pre processing: applied below audio HAL on input
+//  |                           |           | 4 post processing: applied below audio HAL on output
+//  |                           |           | 5 - 7 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | insertion preference      | 3..5      | 0 none
+//  |                           |           | 1 first of the chain
+//  |                           |           | 2 last of the chain
+//  |                           |           | 3 exclusive (only effect in the insert chain)
+//  |                           |           | 4..7 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Volume management         | 6..8      | 0 none
+//  |                           |           | 1 implements volume control
+//  |                           |           | 2 requires volume indication
+//  |                           |           | 4 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Device indication         | 9..11     | 0 none
+//  |                           |           | 1 requires device updates
+//  |                           |           | 2, 4 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Sample input mode         | 12..13    | 1 direct: process() function or EFFECT_CMD_SET_CONFIG
+//  |                           |           |   command must specify a buffer descriptor
+//  |                           |           | 2 provider: process() function uses the
+//  |                           |           |   bufferProvider indicated by the
+//  |                           |           |   EFFECT_CMD_SET_CONFIG command to request input.
+//  |                           |           |   buffers.
+//  |                           |           | 3 both: both input modes are supported
+//  +---------------------------+-----------+-----------------------------------
+//  | Sample output mode        | 14..15    | 1 direct: process() function or EFFECT_CMD_SET_CONFIG
+//  |                           |           |   command must specify a buffer descriptor
+//  |                           |           | 2 provider: process() function uses the
+//  |                           |           |   bufferProvider indicated by the
+//  |                           |           |   EFFECT_CMD_SET_CONFIG command to request output
+//  |                           |           |   buffers.
+//  |                           |           | 3 both: both output modes are supported
+//  +---------------------------+-----------+-----------------------------------
+//  | Hardware acceleration     | 16..17    | 0 No hardware acceleration
+//  |                           |           | 1 non tunneled hw acceleration: the process() function
+//  |                           |           |   reads the samples, send them to HW accelerated
+//  |                           |           |   effect processor, reads back the processed samples
+//  |                           |           |   and returns them to the output buffer.
+//  |                           |           | 2 tunneled hw acceleration: the process() function is
+//  |                           |           |   transparent. The effect interface is only used to
+//  |                           |           |   control the effect engine. This mode is relevant for
+//  |                           |           |   global effects actually applied by the audio
+//  |                           |           |   hardware on the output stream.
+//  +---------------------------+-----------+-----------------------------------
+//  | Audio Mode indication     | 18..19    | 0 none
+//  |                           |           | 1 requires audio mode updates
+//  |                           |           | 2..3 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Audio source indication   | 20..21    | 0 none
+//  |                           |           | 1 requires audio source updates
+//  |                           |           | 2..3 reserved
+//  +---------------------------+-----------+-----------------------------------
+//  | Effect offload supported  | 22        | 0 The effect cannot be offloaded to an audio DSP
+//  |                           |           | 1 The effect can be offloaded to an audio DSP
+//  +---------------------------+-----------+-----------------------------------
+
+// Insert mode
+#define EFFECT_FLAG_TYPE_SHIFT          0
+#define EFFECT_FLAG_TYPE_SIZE           3
+#define EFFECT_FLAG_TYPE_MASK           (((1 << EFFECT_FLAG_TYPE_SIZE) -1) \
+                                            << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_INSERT         (0 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_AUXILIARY      (1 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_REPLACE        (2 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_PRE_PROC       (3 << EFFECT_FLAG_TYPE_SHIFT)
+#define EFFECT_FLAG_TYPE_POST_PROC      (4 << EFFECT_FLAG_TYPE_SHIFT)
+
+// Insert preference
+#define EFFECT_FLAG_INSERT_SHIFT        (EFFECT_FLAG_TYPE_SHIFT + EFFECT_FLAG_TYPE_SIZE)
+#define EFFECT_FLAG_INSERT_SIZE         3
+#define EFFECT_FLAG_INSERT_MASK         (((1 << EFFECT_FLAG_INSERT_SIZE) -1) \
+                                            << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_ANY          (0 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_FIRST        (1 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_LAST         (2 << EFFECT_FLAG_INSERT_SHIFT)
+#define EFFECT_FLAG_INSERT_EXCLUSIVE    (3 << EFFECT_FLAG_INSERT_SHIFT)
+
+
+// Volume control
+#define EFFECT_FLAG_VOLUME_SHIFT        (EFFECT_FLAG_INSERT_SHIFT + EFFECT_FLAG_INSERT_SIZE)
+#define EFFECT_FLAG_VOLUME_SIZE         3
+#define EFFECT_FLAG_VOLUME_MASK         (((1 << EFFECT_FLAG_VOLUME_SIZE) -1) \
+                                            << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_CTRL         (1 << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_IND          (2 << EFFECT_FLAG_VOLUME_SHIFT)
+#define EFFECT_FLAG_VOLUME_NONE         (0 << EFFECT_FLAG_VOLUME_SHIFT)
+
+// Device indication
+#define EFFECT_FLAG_DEVICE_SHIFT        (EFFECT_FLAG_VOLUME_SHIFT + EFFECT_FLAG_VOLUME_SIZE)
+#define EFFECT_FLAG_DEVICE_SIZE         3
+#define EFFECT_FLAG_DEVICE_MASK         (((1 << EFFECT_FLAG_DEVICE_SIZE) -1) \
+                                            << EFFECT_FLAG_DEVICE_SHIFT)
+#define EFFECT_FLAG_DEVICE_IND          (1 << EFFECT_FLAG_DEVICE_SHIFT)
+#define EFFECT_FLAG_DEVICE_NONE         (0 << EFFECT_FLAG_DEVICE_SHIFT)
+
+// Sample input modes
+#define EFFECT_FLAG_INPUT_SHIFT         (EFFECT_FLAG_DEVICE_SHIFT + EFFECT_FLAG_DEVICE_SIZE)
+#define EFFECT_FLAG_INPUT_SIZE          2
+#define EFFECT_FLAG_INPUT_MASK          (((1 << EFFECT_FLAG_INPUT_SIZE) -1) \
+                                            << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_DIRECT        (1 << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_PROVIDER      (2 << EFFECT_FLAG_INPUT_SHIFT)
+#define EFFECT_FLAG_INPUT_BOTH          (3 << EFFECT_FLAG_INPUT_SHIFT)
+
+// Sample output modes
+#define EFFECT_FLAG_OUTPUT_SHIFT        (EFFECT_FLAG_INPUT_SHIFT + EFFECT_FLAG_INPUT_SIZE)
+#define EFFECT_FLAG_OUTPUT_SIZE         2
+#define EFFECT_FLAG_OUTPUT_MASK         (((1 << EFFECT_FLAG_OUTPUT_SIZE) -1) \
+                                            << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_DIRECT       (1 << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_PROVIDER     (2 << EFFECT_FLAG_OUTPUT_SHIFT)
+#define EFFECT_FLAG_OUTPUT_BOTH         (3 << EFFECT_FLAG_OUTPUT_SHIFT)
+
+// Hardware acceleration mode
+#define EFFECT_FLAG_HW_ACC_SHIFT        (EFFECT_FLAG_OUTPUT_SHIFT + EFFECT_FLAG_OUTPUT_SIZE)
+#define EFFECT_FLAG_HW_ACC_SIZE         2
+#define EFFECT_FLAG_HW_ACC_MASK         (((1 << EFFECT_FLAG_HW_ACC_SIZE) -1) \
+                                            << EFFECT_FLAG_HW_ACC_SHIFT)
+#define EFFECT_FLAG_HW_ACC_SIMPLE       (1 << EFFECT_FLAG_HW_ACC_SHIFT)
+#define EFFECT_FLAG_HW_ACC_TUNNEL       (2 << EFFECT_FLAG_HW_ACC_SHIFT)
+
+// Audio mode indication
+#define EFFECT_FLAG_AUDIO_MODE_SHIFT    (EFFECT_FLAG_HW_ACC_SHIFT + EFFECT_FLAG_HW_ACC_SIZE)
+#define EFFECT_FLAG_AUDIO_MODE_SIZE     2
+#define EFFECT_FLAG_AUDIO_MODE_MASK     (((1 << EFFECT_FLAG_AUDIO_MODE_SIZE) -1) \
+                                            << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+#define EFFECT_FLAG_AUDIO_MODE_IND      (1 << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+#define EFFECT_FLAG_AUDIO_MODE_NONE     (0 << EFFECT_FLAG_AUDIO_MODE_SHIFT)
+
+// Audio source indication
+#define EFFECT_FLAG_AUDIO_SOURCE_SHIFT  (EFFECT_FLAG_AUDIO_MODE_SHIFT + EFFECT_FLAG_AUDIO_MODE_SIZE)
+#define EFFECT_FLAG_AUDIO_SOURCE_SIZE   2
+#define EFFECT_FLAG_AUDIO_SOURCE_MASK   (((1 << EFFECT_FLAG_AUDIO_SOURCE_SIZE) -1) \
+                                          << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+#define EFFECT_FLAG_AUDIO_SOURCE_IND    (1 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+#define EFFECT_FLAG_AUDIO_SOURCE_NONE   (0 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT)
+
+// Effect offload indication
+#define EFFECT_FLAG_OFFLOAD_SHIFT       (EFFECT_FLAG_AUDIO_SOURCE_SHIFT + \
+                                                    EFFECT_FLAG_AUDIO_SOURCE_SIZE)
+#define EFFECT_FLAG_OFFLOAD_SIZE        1
+#define EFFECT_FLAG_OFFLOAD_MASK        (((1 << EFFECT_FLAG_OFFLOAD_SIZE) -1) \
+                                          << EFFECT_FLAG_OFFLOAD_SHIFT)
+#define EFFECT_FLAG_OFFLOAD_SUPPORTED   (1 << EFFECT_FLAG_OFFLOAD_SHIFT)
+
+#define EFFECT_MAKE_API_VERSION(M, m)  (((M)<<16) | ((m) & 0xFFFF))
+#define EFFECT_API_VERSION_MAJOR(v)    ((v)>>16)
+#define EFFECT_API_VERSION_MINOR(v)    ((m) & 0xFFFF)
+
+
+
+/////////////////////////////////////////////////
+//      Effect control interface
+/////////////////////////////////////////////////
+
+// Effect control interface version 2.0
+#define EFFECT_CONTROL_API_VERSION EFFECT_MAKE_API_VERSION(2,0)
+
+// Effect control interface structure: effect_interface_s
+// The effect control interface is exposed by each effect engine implementation. It consists of
+// a set of functions controlling the configuration, activation and process of the engine.
+// The functions are grouped in a structure of type effect_interface_s.
+//
+// Effect control interface handle: effect_handle_t
+// The effect_handle_t serves two purposes regarding the implementation of the effect engine:
+// - 1 it is the address of a pointer to an effect_interface_s structure where the functions
+// of the effect control API for a particular effect are located.
+// - 2 it is the address of the context of a particular effect instance.
+// A typical implementation in the effect library would define a structure as follows:
+// struct effect_module_s {
+//        const struct effect_interface_s *itfe;
+//        effect_config_t config;
+//        effect_context_t context;
+// }
+// The implementation of EffectCreate() function would then allocate a structure of this
+// type and return its address as effect_handle_t
+typedef struct effect_interface_s **effect_handle_t;
+
+
+// Forward definition of type audio_buffer_t
+typedef struct audio_buffer_s audio_buffer_t;
+
+
+
+
+
+
+// Effect control interface definition
+struct effect_interface_s {
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:       process
+    //
+    //    Description:    Effect process function. Takes input samples as specified
+    //          (count and location) in input buffer descriptor and output processed
+    //          samples as specified in output buffer descriptor. If the buffer descriptor
+    //          is not specified the function must use either the buffer or the
+    //          buffer provider function installed by the EFFECT_CMD_SET_CONFIG command.
+    //          The effect framework will call the process() function after the EFFECT_CMD_ENABLE
+    //          command is received and until the EFFECT_CMD_DISABLE is received. When the engine
+    //          receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully
+    //          and when done indicate that it is OK to stop calling the process() function by
+    //          returning the -ENODATA status.
+    //
+    //    NOTE: the process() function implementation should be "real-time safe" that is
+    //      it should not perform blocking calls: malloc/free, sleep, read/write/open/close,
+    //      pthread_cond_wait/pthread_mutex_lock...
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //          inBuffer:   buffer descriptor indicating where to read samples to process.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command.
+    //
+    //          outBuffer:   buffer descriptor indicating where to write processed samples.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command.
+    //
+    //    Output:
+    //        returned value:    0 successful operation
+    //                          -ENODATA the engine has finished the disable phase and the framework
+    //                                  can stop calling process()
+    //                          -EINVAL invalid interface handle or
+    //                                  invalid input/output buffer description
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*process)(effect_handle_t self,
+                       audio_buffer_t *inBuffer,
+                       audio_buffer_t *outBuffer);
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:       command
+    //
+    //    Description:    Send a command and receive a response to/from effect engine.
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //          cmdCode:    command code: the command can be a standardized command defined in
+    //              effect_command_e (see below) or a proprietary command.
+    //          cmdSize:    size of command in bytes
+    //          pCmdData:   pointer to command data
+    //          pReplyData: pointer to reply data
+    //
+    //    Input/Output:
+    //          replySize: maximum size of reply data as input
+    //                      actual size of reply data as output
+    //
+    //    Output:
+    //          returned value: 0       successful operation
+    //                          -EINVAL invalid interface handle or
+    //                                  invalid command/reply size or format according to command code
+    //              The return code should be restricted to indicate problems related to the this
+    //              API specification. Status related to the execution of a particular command should be
+    //              indicated as part of the reply field.
+    //
+    //          *pReplyData updated with command response
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*command)(effect_handle_t self,
+                       uint32_t cmdCode,
+                       uint32_t cmdSize,
+                       void *pCmdData,
+                       uint32_t *replySize,
+                       void *pReplyData);
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        get_descriptor
+    //
+    //    Description:    Returns the effect descriptor
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //
+    //    Input/Output:
+    //          pDescriptor:    address where to return the effect descriptor.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -EINVAL     invalid interface handle or invalid pDescriptor
+    //        *pDescriptor:     updated with the effect descriptor.
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*get_descriptor)(effect_handle_t self,
+                              effect_descriptor_t *pDescriptor);
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:       process_reverse
+    //
+    //    Description:    Process reverse stream function. This function is used to pass
+    //          a reference stream to the effect engine. If the engine does not need a reference
+    //          stream, this function pointer can be set to NULL.
+    //          This function would typically implemented by an Echo Canceler.
+    //
+    //    Input:
+    //          self:       handle to the effect interface this function
+    //              is called on.
+    //          inBuffer:   buffer descriptor indicating where to read samples to process.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command.
+    //
+    //          outBuffer:   buffer descriptor indicating where to write processed samples.
+    //              If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command.
+    //              If the buffer and buffer provider in the configuration received by
+    //              EFFECT_CMD_SET_CONFIG_REVERSE are also NULL, do not return modified reverse
+    //              stream data
+    //
+    //    Output:
+    //        returned value:    0 successful operation
+    //                          -ENODATA the engine has finished the disable phase and the framework
+    //                                  can stop calling process_reverse()
+    //                          -EINVAL invalid interface handle or
+    //                                  invalid input/output buffer description
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*process_reverse)(effect_handle_t self,
+                               audio_buffer_t *inBuffer,
+                               audio_buffer_t *outBuffer);
+};
+
+
+//
+//--- Standardized command codes for command() function
+//
+enum effect_command_e {
+   EFFECT_CMD_INIT,                 // initialize effect engine
+   EFFECT_CMD_SET_CONFIG,           // configure effect engine (see effect_config_t)
+   EFFECT_CMD_RESET,                // reset effect engine
+   EFFECT_CMD_ENABLE,               // enable effect process
+   EFFECT_CMD_DISABLE,              // disable effect process
+   EFFECT_CMD_SET_PARAM,            // set parameter immediately (see effect_param_t)
+   EFFECT_CMD_SET_PARAM_DEFERRED,   // set parameter deferred
+   EFFECT_CMD_SET_PARAM_COMMIT,     // commit previous set parameter deferred
+   EFFECT_CMD_GET_PARAM,            // get parameter
+   EFFECT_CMD_SET_DEVICE,           // set audio device (see audio.h, audio_devices_t)
+   EFFECT_CMD_SET_VOLUME,           // set volume
+   EFFECT_CMD_SET_AUDIO_MODE,       // set the audio mode (normal, ring, ...)
+   EFFECT_CMD_SET_CONFIG_REVERSE,   // configure effect engine reverse stream(see effect_config_t)
+   EFFECT_CMD_SET_INPUT_DEVICE,     // set capture device (see audio.h, audio_devices_t)
+   EFFECT_CMD_GET_CONFIG,           // read effect engine configuration
+   EFFECT_CMD_GET_CONFIG_REVERSE,   // read configure effect engine reverse stream configuration
+   EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,// get all supported configurations for a feature.
+   EFFECT_CMD_GET_FEATURE_CONFIG,   // get current feature configuration
+   EFFECT_CMD_SET_FEATURE_CONFIG,   // set current feature configuration
+   EFFECT_CMD_SET_AUDIO_SOURCE,     // set the audio source (see audio.h, audio_source_t)
+   EFFECT_CMD_OFFLOAD,              // set if effect thread is an offload one,
+                                    // send the ioHandle of the effect thread
+   EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code
+};
+
+//==================================================================================================
+// command: EFFECT_CMD_INIT
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Initialize effect engine: All configurations return to default
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply new audio parameters configurations for input and output buffers
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_RESET
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Reset the effect engine. Keep configuration but resets state and buffer content
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_ENABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Enable the process. Called by the framework before the first call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_DISABLE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Disable the process. Called by the framework after the last call to process()
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set a parameter and apply it immediately
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_DEFERRED
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_PARAM_COMMIT
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_GET_PARAM
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Get a parameter value
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_param_t) + size of param
+//  data: effect_param_t + param
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(effect_param_t) + size of param and value
+//  data: effect_param_t + param + value. See effect_param_t definition below for value offset
+//==================================================================================================
+// command: EFFECT_CMD_SET_DEVICE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the rendering device the audio output path is connected to. See audio.h, audio_devices_t
+//  for device values.
+//  The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
+//  command when the device changes
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_VOLUME
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set and get volume. Used by audio framework to delegate volume control to effect engine.
+//  The effect implementation must set EFFECT_FLAG_VOLUME_IND or EFFECT_FLAG_VOLUME_CTRL flag in
+//  its descriptor to receive this command before every call to process() function
+//  If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return
+//  the volume that should be applied before the effect is processed. The overall volume (the volume
+//  actually applied by the effect engine multiplied by the returned value) should match the value
+//  indicated in the command.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: n * sizeof(uint32_t)
+//  data: volume for each channel defined in effect_config_t for output buffer expressed in
+//      8.24 fixed point format
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: n * sizeof(uint32_t) / 0
+//  data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor:
+//              volume for each channel defined in effect_config_t for output buffer expressed in
+//              8.24 fixed point format
+//        - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor:
+//              N/A
+//  It is legal to receive a null pointer as pReplyData in which case the effect framework has
+//  delegated volume control to another effect
+//==================================================================================================
+// command: EFFECT_CMD_SET_AUDIO_MODE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its
+//  descriptor to receive this command when the audio mode changes.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: audio_mode_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_SET_CONFIG_REVERSE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Apply new audio parameters configurations for input and output buffers of reverse stream.
+//  An example of reverse stream is the echo reference supplied to an Acoustic Echo Canceler.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(int)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_INPUT_DEVICE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the capture device the audio input path is connected to. See audio.h, audio_devices_t
+//  for device values.
+//  The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this
+//  command when the device changes
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_GET_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Read audio parameters configurations for input and output buffers
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//==================================================================================================
+// command: EFFECT_CMD_GET_CONFIG_REVERSE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Read audio parameters configurations for input and output buffers of reverse stream
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 0
+//  data: N/A
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(effect_config_t)
+//  data: effect_config_t
+//==================================================================================================
+// command: EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Queries for supported configurations for a particular feature (e.g. get the supported
+// combinations of main and auxiliary channels for a noise suppressor).
+// The command parameter is the feature identifier (See effect_feature_e for a list of defined
+// features) followed by the maximum number of configuration descriptor to return.
+// The reply is composed of:
+//  - status (uint32_t):
+//          - 0 if feature is supported
+//          - -ENOSYS if the feature is not supported,
+//          - -ENOMEM if the feature is supported but the total number of supported configurations
+//          exceeds the maximum number indicated by the caller.
+//  - total number of supported configurations (uint32_t)
+//  - an array of configuration descriptors.
+// The actual number of descriptors returned must not exceed the maximum number indicated by
+// the caller.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: 2 x sizeof(uint32_t)
+//  data: effect_feature_e + maximum number of configurations to return
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 2 x sizeof(uint32_t) + n x sizeof (<config descriptor>)
+//  data: status + total number of configurations supported + array of n config descriptors
+//==================================================================================================
+// command: EFFECT_CMD_GET_FEATURE_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Retrieves current configuration for a given feature.
+// The reply status is:
+//      - 0 if feature is supported
+//      - -ENOSYS if the feature is not supported,
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: effect_feature_e
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(uint32_t) + sizeof (<config descriptor>)
+//  data: status + config descriptor
+//==================================================================================================
+// command: EFFECT_CMD_SET_FEATURE_CONFIG
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Sets current configuration for a given feature.
+// The reply status is:
+//      - 0 if feature is supported
+//      - -ENOSYS if the feature is not supported,
+//      - -EINVAL if the configuration is invalid
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t) + sizeof (<config descriptor>)
+//  data: effect_feature_e + config descriptor
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(uint32_t)
+//  data: status
+//==================================================================================================
+// command: EFFECT_CMD_SET_AUDIO_SOURCE
+//--------------------------------------------------------------------------------------------------
+// description:
+//  Set the audio source the capture path is configured for (Camcorder, voice recognition...).
+//  See audio.h, audio_source_t for values.
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: 0
+//  data: N/A
+//==================================================================================================
+// command: EFFECT_CMD_OFFLOAD
+//--------------------------------------------------------------------------------------------------
+// description:
+//  1.indicate if the playback thread the effect is attached to is offloaded or not
+//  2.update the io handle of the playback thread the effect is attached to
+//--------------------------------------------------------------------------------------------------
+// command format:
+//  size: sizeof(effect_offload_param_t)
+//  data: effect_offload_param_t
+//--------------------------------------------------------------------------------------------------
+// reply format:
+//  size: sizeof(uint32_t)
+//  data: uint32_t
+//--------------------------------------------------------------------------------------------------
+// command: EFFECT_CMD_FIRST_PROPRIETARY
+//--------------------------------------------------------------------------------------------------
+// description:
+//  All proprietary effect commands must use command codes above this value. The size and format of
+//  command and response fields is free in this case
+//==================================================================================================
+
+
+// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t
+// structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with
+// regard to the channel mask definition in audio.h, audio_channel_mask_t e.g :
+// Stereo: left, right
+// 5 point 1: front left, front right, front center, low frequency, back left, back right
+// The buffer size is expressed in frame count, a frame being composed of samples for all
+// channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by
+// definition
+struct audio_buffer_s {
+    size_t   frameCount;        // number of frames in buffer
+    union {
+        void*       raw;        // raw pointer to start of buffer
+        int32_t*    s32;        // pointer to signed 32 bit data at start of buffer
+        int16_t*    s16;        // pointer to signed 16 bit data at start of buffer
+        uint8_t*    u8;         // pointer to unsigned 8 bit data at start of buffer
+    };
+};
+
+// The buffer_provider_s structure contains functions that can be used
+// by the effect engine process() function to query and release input
+// or output audio buffer.
+// The getBuffer() function is called to retrieve a buffer where data
+// should read from or written to by process() function.
+// The releaseBuffer() function MUST be called when the buffer retrieved
+// with getBuffer() is not needed anymore.
+// The process function should use the buffer provider mechanism to retrieve
+// input or output buffer if the inBuffer or outBuffer passed as argument is NULL
+// and the buffer configuration (buffer_config_t) given by the EFFECT_CMD_SET_CONFIG
+// command did not specify an audio buffer.
+
+typedef int32_t (* buffer_function_t)(void *cookie, audio_buffer_t *buffer);
+
+typedef struct buffer_provider_s {
+    buffer_function_t getBuffer;       // retrieve next buffer
+    buffer_function_t releaseBuffer;   // release used buffer
+    void       *cookie;                // for use by client of buffer provider functions
+} buffer_provider_t;
+
+
+// The buffer_config_s structure specifies the input or output audio format
+// to be used by the effect engine. It is part of the effect_config_t
+// structure that defines both input and output buffer configurations and is
+// passed by the EFFECT_CMD_SET_CONFIG or EFFECT_CMD_SET_CONFIG_REVERSE command.
+typedef struct buffer_config_s {
+    audio_buffer_t  buffer;     // buffer for use by process() function if not passed explicitly
+    uint32_t   samplingRate;    // sampling rate
+    uint32_t   channels;        // channel mask (see audio_channel_mask_t in audio.h)
+    buffer_provider_t bufferProvider;   // buffer provider
+    uint8_t    format;          // Audio format  (see see audio_format_t in audio.h)
+    uint8_t    accessMode;      // read/write or accumulate in buffer (effect_buffer_access_e)
+    uint16_t   mask;            // indicates which of the above fields is valid
+} buffer_config_t;
+
+// Values for "accessMode" field of buffer_config_t:
+//   overwrite, read only, accumulate (read/modify/write)
+enum effect_buffer_access_e {
+    EFFECT_BUFFER_ACCESS_WRITE,
+    EFFECT_BUFFER_ACCESS_READ,
+    EFFECT_BUFFER_ACCESS_ACCUMULATE
+
+};
+
+// feature identifiers for EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS command
+enum effect_feature_e {
+    EFFECT_FEATURE_AUX_CHANNELS, // supports auxiliary channels (e.g. dual mic noise suppressor)
+    EFFECT_FEATURE_CNT
+};
+
+// EFFECT_FEATURE_AUX_CHANNELS feature configuration descriptor. Describe a combination
+// of main and auxiliary channels supported
+typedef struct channel_config_s {
+    audio_channel_mask_t main_channels; // channel mask for main channels
+    audio_channel_mask_t aux_channels;  // channel mask for auxiliary channels
+} channel_config_t;
+
+
+// Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field
+// in buffer_config_t must be taken into account when executing the EFFECT_CMD_SET_CONFIG command
+#define EFFECT_CONFIG_BUFFER    0x0001  // buffer field must be taken into account
+#define EFFECT_CONFIG_SMP_RATE  0x0002  // samplingRate field must be taken into account
+#define EFFECT_CONFIG_CHANNELS  0x0004  // channels field must be taken into account
+#define EFFECT_CONFIG_FORMAT    0x0008  // format field must be taken into account
+#define EFFECT_CONFIG_ACC_MODE  0x0010  // accessMode field must be taken into account
+#define EFFECT_CONFIG_PROVIDER  0x0020  // bufferProvider field must be taken into account
+#define EFFECT_CONFIG_ALL (EFFECT_CONFIG_BUFFER | EFFECT_CONFIG_SMP_RATE | \
+                           EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \
+                           EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER)
+
+
+// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_CONFIG
+// command to configure audio parameters and buffers for effect engine input and output.
+typedef struct effect_config_s {
+    buffer_config_t   inputCfg;
+    buffer_config_t   outputCfg;
+} effect_config_t;
+
+
+// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM
+// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command.
+// psize and vsize represent the actual size of parameter and value.
+//
+// NOTE: the start of value field inside the data field is always on a 32 bit boundary:
+//
+//  +-----------+
+//  | status    | sizeof(int)
+//  +-----------+
+//  | psize     | sizeof(int)
+//  +-----------+
+//  | vsize     | sizeof(int)
+//  +-----------+
+//  |           |   |           |
+//  ~ parameter ~   > psize     |
+//  |           |   |           >  ((psize - 1)/sizeof(int) + 1) * sizeof(int)
+//  +-----------+               |
+//  | padding   |               |
+//  +-----------+
+//  |           |   |
+//  ~ value     ~   > vsize
+//  |           |   |
+//  +-----------+
+
+typedef struct effect_param_s {
+    int32_t     status;     // Transaction status (unused for command, used for reply)
+    uint32_t    psize;      // Parameter size
+    uint32_t    vsize;      // Value size
+    char        data[];     // Start of Parameter + Value data
+} effect_param_t;
+
+// structure used by EFFECT_CMD_OFFLOAD command
+typedef struct effect_offload_param_s {
+    bool isOffload;         // true if the playback thread the effect is attached to is offloaded
+    int ioHandle;           // io handle of the playback thread the effect is attached to
+} effect_offload_param_t;
+
+
+/////////////////////////////////////////////////
+//      Effect library interface
+/////////////////////////////////////////////////
+
+// Effect library interface version 3.0
+// Note that EffectsFactory.c only checks the major version component, so changes to the minor
+// number can only be used for fully backwards compatible changes
+#define EFFECT_LIBRARY_API_VERSION EFFECT_MAKE_API_VERSION(3,0)
+
+#define AUDIO_EFFECT_LIBRARY_TAG ((('A') << 24) | (('E') << 16) | (('L') << 8) | ('T'))
+
+// Every effect library must have a data structure named AUDIO_EFFECT_LIBRARY_INFO_SYM
+// and the fields of this data structure must begin with audio_effect_library_t
+
+typedef struct audio_effect_library_s {
+    // tag must be initialized to AUDIO_EFFECT_LIBRARY_TAG
+    uint32_t tag;
+    // Version of the effect library API : 0xMMMMmmmm MMMM: Major, mmmm: minor
+    uint32_t version;
+    // Name of this library
+    const char *name;
+    // Author/owner/implementor of the library
+    const char *implementor;
+
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        create_effect
+    //
+    //    Description:    Creates an effect engine of the specified implementation uuid and
+    //          returns an effect control interface on this engine. The function will allocate the
+    //          resources for an instance of the requested effect engine and return
+    //          a handle on the effect control interface.
+    //
+    //    Input:
+    //          uuid:    pointer to the effect uuid.
+    //          sessionId:  audio session to which this effect instance will be attached. All effects
+    //              created with the same session ID are connected in series and process the same signal
+    //              stream. Knowing that two effects are part of the same effect chain can help the
+    //              library implement some kind of optimizations.
+    //          ioId:   identifies the output or input stream this effect is directed to at audio HAL.
+    //              For future use especially with tunneled HW accelerated effects
+    //
+    //    Input/Output:
+    //          pHandle:        address where to return the effect interface handle.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -ENODEV     library failed to initialize
+    //                          -EINVAL     invalid pEffectUuid or pHandle
+    //                          -ENOENT     no effect with this uuid found
+    //        *pHandle:         updated with the effect interface handle.
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*create_effect)(const effect_uuid_t *uuid,
+                             int32_t sessionId,
+                             int32_t ioId,
+                             effect_handle_t *pHandle);
+
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        release_effect
+    //
+    //    Description:    Releases the effect engine whose handle is given as argument.
+    //          All resources allocated to this particular instance of the effect are
+    //          released.
+    //
+    //    Input:
+    //          handle:         handle on the effect interface to be released.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -ENODEV     library failed to initialize
+    //                          -EINVAL     invalid interface handle
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*release_effect)(effect_handle_t handle);
+
+    ////////////////////////////////////////////////////////////////////////////////
+    //
+    //    Function:        get_descriptor
+    //
+    //    Description:    Returns the descriptor of the effect engine which implementation UUID is
+    //          given as argument.
+    //
+    //    Input/Output:
+    //          uuid:           pointer to the effect uuid.
+    //          pDescriptor:    address where to return the effect descriptor.
+    //
+    //    Output:
+    //        returned value:    0          successful operation.
+    //                          -ENODEV     library failed to initialize
+    //                          -EINVAL     invalid pDescriptor or uuid
+    //        *pDescriptor:     updated with the effect descriptor.
+    //
+    ////////////////////////////////////////////////////////////////////////////////
+    int32_t (*get_descriptor)(const effect_uuid_t *uuid,
+                              effect_descriptor_t *pDescriptor);
+} audio_effect_library_t;
+
+// Name of the hal_module_info
+#define AUDIO_EFFECT_LIBRARY_INFO_SYM         AELI
+
+// Name of the hal_module_info as a string
+#define AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR  "AELI"
+
+__END_DECLS
+
+#endif  // ANDROID_AUDIO_EFFECT_H
diff --git a/android/hardware/hardware.c b/android/hardware/hardware.c
new file mode 100644 (file)
index 0000000..4bd5eba
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/hardware.h>
+
+#include <dlfcn.h>
+#include <string.h>
+#include <pthread.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#define LOG_TAG "HAL"
+
+#define LOG_INFO " I"
+#define LOG_WARN " W"
+#define LOG_ERROR " E"
+#define LOG_DEBUG " D"
+#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg)
+
+#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg)
+#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg)
+#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg)
+
+/**
+ * Load the file defined by the variant and if successful
+ * return the dlopen handle and the hmi.
+ * @return 0 = success, !0 = failure.
+ */
+static int load(const char *id,
+        const char *path,
+        const struct hw_module_t **pHmi)
+{
+    int status;
+    void *handle;
+    struct hw_module_t *hmi;
+    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
+
+    /*
+     * load the symbols resolving undefined symbols before
+     * dlopen returns. Since RTLD_GLOBAL is not or'd in with
+     * RTLD_NOW the external symbols will not be global
+     */
+    handle = dlopen(path, RTLD_NOW);
+    if (handle == NULL) {
+        char const *err_str = dlerror();
+        error("load: module=%s\n%s", path, err_str?err_str:"unknown");
+        status = -EINVAL;
+        goto done;
+    }
+
+    /* Get the address of the struct hal_module_info. */
+    hmi = (struct hw_module_t *)dlsym(handle, sym);
+    if (hmi == NULL) {
+        error("load: couldn't find symbol %s", sym);
+        status = -EINVAL;
+        goto done;
+    }
+
+    /* Check that the id matches */
+    if (strcmp(id, hmi->id) != 0) {
+        error("load: id=%s != hmi->id=%s", id, hmi->id);
+        status = -EINVAL;
+        goto done;
+    }
+
+    hmi->dso = handle;
+
+    *pHmi = hmi;
+
+    info("loaded HAL id=%s path=%s hmi=%p handle=%p",
+                id, path, *pHmi, handle);
+
+    return 0;
+
+done:
+    hmi = NULL;
+    if (handle != NULL) {
+        dlclose(handle);
+        handle = NULL;
+    }
+
+    return status;
+}
+
+int hw_get_module_by_class(const char *class_id, const char *inst,
+                           const struct hw_module_t **module)
+{
+    char path[PATH_MAX];
+    char name[PATH_MAX];
+
+    if (inst)
+        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
+    else
+        snprintf(name, PATH_MAX, "%s", class_id);
+
+    /*
+     * Here we rely on the fact that calling dlopen multiple times on
+     * the same .so will simply increment a refcount (and not load
+     * a new copy of the library).
+     * We also assume that dlopen() is thread-safe.
+     */
+    snprintf(path, sizeof(path), "%s/%s.default.so", PLUGINDIR, name);
+
+    return load(class_id, path, module);
+}
+
+int hw_get_module(const char *id, const struct hw_module_t **module)
+{
+    return hw_get_module_by_class(id, NULL, module);
+}
diff --git a/android/health.c b/android/health.c
new file mode 100644 (file)
index 0000000..655d9f9
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/log.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+#include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+#include "health.h"
+
+static bdaddr_t adapter_addr;
+static struct ipc *hal_ipc = NULL;
+
+static void bt_health_register_app(const void *buf, uint16_t len)
+{
+       DBG("Not implemented");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP,
+                                                       HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_health_mdep_cfg_data(const void *buf, uint16_t len)
+{
+       DBG("Not implemented");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP,
+                                                       HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_health_unregister_app(const void *buf, uint16_t len)
+{
+       DBG("Not implemented");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP,
+                                                       HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_health_connect_channel(const void *buf, uint16_t len)
+{
+       DBG("Not implemented");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+                       HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_UNSUPPORTED);
+}
+
+static void bt_health_destroy_channel(const void *buf, uint16_t len)
+{
+       DBG("Not implemented");
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH,
+                       HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_UNSUPPORTED);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+       /* HAL_OP_HEALTH_REG_APP */
+       { bt_health_register_app, true,
+                               sizeof(struct hal_cmd_health_reg_app) },
+       /* HAL_OP_HEALTH_MDEP */
+       { bt_health_mdep_cfg_data, true,
+                               sizeof(struct hal_cmd_health_mdep) },
+       /* HAL_OP_HEALTH_UNREG_APP */
+       { bt_health_unregister_app, false,
+                               sizeof(struct hal_cmd_health_unreg_app) },
+       /* HAL_OP_HEALTH_CONNECT_CHANNEL */
+       { bt_health_connect_channel, false,
+                               sizeof(struct hal_cmd_health_connect_channel) },
+       /* HAL_OP_HEALTH_DESTROY_CHANNEL */
+       { bt_health_destroy_channel, false,
+                               sizeof(struct hal_cmd_health_destroy_channel) },
+};
+
+bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+       DBG("");
+
+       bacpy(&adapter_addr, addr);
+
+       hal_ipc = ipc;
+       ipc_register(hal_ipc, HAL_SERVICE_ID_HEALTH, cmd_handlers,
+                                               G_N_ELEMENTS(cmd_handlers));
+
+       return true;
+}
+
+void bt_health_unregister(void)
+{
+       DBG("");
+
+       ipc_unregister(hal_ipc, HAL_SERVICE_ID_HEALTH);
+       hal_ipc = NULL;
+}
diff --git a/android/health.h b/android/health.h
new file mode 100644 (file)
index 0000000..0b32fd3
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
+void bt_health_unregister(void);
index 8bfdfed..1758020 100644 (file)
@@ -2,21 +2,21 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
@@ -30,6 +30,7 @@
 #include <errno.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <ctype.h>
 
 #include <glib.h>
 
 #include "lib/bluetooth.h"
 #include "lib/sdp.h"
 #include "lib/sdp_lib.h"
-#include "lib/uuid.h"
 #include "src/shared/mgmt.h"
+#include "src/shared/util.h"
 #include "src/sdp-client.h"
-#include "src/glib-helper.h"
+#include "src/uuid-helper.h"
 #include "profiles/input/uhid_copy.h"
+#include "src/log.h"
 
-#include "log.h"
 #include "hal-msg.h"
+#include "ipc-common.h"
 #include "ipc.h"
 #include "hidhost.h"
 #include "utils.h"
@@ -82,6 +84,8 @@ static GIOChannel *ctrl_io = NULL;
 static GIOChannel *intr_io = NULL;
 static GSList *devices = NULL;
 
+static struct ipc *hal_ipc = NULL;
+
 struct hid_device {
        bdaddr_t        dst;
        uint8_t         state;
@@ -119,14 +123,16 @@ static void uhid_destroy(int fd)
        ev.type = UHID_DESTROY;
 
        if (write(fd, &ev, sizeof(ev)) < 0)
-               error("Failed to destroy uHID device: %s (%d)",
+               error("hidhost: Failed to destroy uHID device: %s (%d)",
                                                strerror(errno), errno);
 
        close(fd);
 }
 
-static void hid_device_free(struct hid_device *dev)
+static void hid_device_free(void *data)
 {
+       struct hid_device *dev = data;
+
        if (dev->ctrl_watch > 0)
                g_source_remove(dev->ctrl_watch);
 
@@ -148,37 +154,72 @@ static void hid_device_free(struct hid_device *dev)
                uhid_destroy(dev->uhid_fd);
 
        g_free(dev->rd_data);
+       g_free(dev);
+}
 
+static void hid_device_remove(struct hid_device *dev)
+{
        devices = g_slist_remove(devices, dev);
-       g_free(dev);
+       hid_device_free(dev);
 }
 
-static void handle_uhid_event(struct hid_device *dev, struct uhid_event *ev)
+static bool hex2buf(const uint8_t *hex, uint8_t *buf, int buf_size)
 {
-       int fd, i;
-       uint8_t *req = NULL;
-       uint8_t req_size = 0;
+       int i, j;
+       char c;
+       uint8_t b;
+
+       for (i = 0, j = 0; i < buf_size; i++, j++) {
+               c = toupper(hex[j]);
+
+               if (c >= '0' && c <= '9')
+                       b = c - '0';
+               else if (c >= 'A' && c <= 'F')
+                       b = 10 + c - 'A';
+               else
+                       return false;
+
+               j++;
+
+               c = toupper(hex[j]);
+
+               if (c >= '0' && c <= '9')
+                       b = b * 16 + c - '0';
+               else if (c >= 'A' && c <= 'F')
+                       b = b * 16 + 10 + c - 'A';
+               else
+                       return false;
+
+               buf[i] = b;
+       }
+
+       return true;
+}
 
-       if (!(dev->ctrl_io))
+static void handle_uhid_output(struct hid_device *dev,
+                                               struct uhid_output_req *output)
+{
+       int fd, req_size;
+       uint8_t *req;
+
+       if (!dev->ctrl_io)
                return;
 
-       req_size = 1 + (ev->u.output.size / 2);
-       req = g_try_malloc0(req_size);
+       req_size = 1 + output->size;
+       req = malloc0(req_size);
        if (!req)
                return;
 
-       req[0] = HID_MSG_SET_REPORT | ev->u.output.rtype;
-       for (i = 0; i < (req_size - 1); i++)
-               sscanf((char *) &(ev->u.output.data)[i * 2],
-                                                       "%hhx", &req[1 + i]);
+       req[0] = HID_MSG_SET_REPORT | output->rtype;
+       memcpy(req + 1, output->data, req_size - 1);
 
        fd = g_io_channel_unix_get_fd(dev->ctrl_io);
 
        if (write(fd, req, req_size) < 0)
-               error("error writing set_report: %s (%d)",
-                                               strerror(errno), errno);
+               error("hidhost: error writing set_report: %s (%d)",
+                                                       strerror(errno), errno);
 
-       g_free(req);
+       free(req);
 }
 
 static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond,
@@ -208,31 +249,38 @@ static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond,
        switch (ev.type) {
        case UHID_START:
        case UHID_STOP:
-               /* These are called to start and stop the underlying hardware.
+               /*
+                * These are called to start and stop the underlying hardware.
                 * We open the channels before creating the device so the
                 * hardware is always ready. No need to handle these.
                 * The kernel never destroys a device itself! Only an explicit
-                * UHID_DESTROY request can remove a device. */
-
+                * UHID_DESTROY request can remove a device.
+                */
                break;
        case UHID_OPEN:
        case UHID_CLOSE:
-               /* OPEN/CLOSE are sent whenever user-space opens any interface
+               /*
+                * OPEN/CLOSE are sent whenever user-space opens any interface
                 * provided by the kernel HID device. Whenever the open-count
                 * is non-zero we must be ready for I/O. As long as it is zero,
                 * we can decide to drop all I/O and put the device
-                * asleep This is optional, though. */
+                * asleep This is optional, though.
+                */
                break;
        case UHID_OUTPUT:
+               handle_uhid_output(dev, &ev.u.output);
+               break;
        case UHID_FEATURE:
-               handle_uhid_event(dev, &ev);
+               /* TODO */
                break;
        case UHID_OUTPUT_EV:
-               /* This is only sent by kernels prior to linux-3.11. It
+               /*
+                * This is only sent by kernels prior to linux-3.11. It
                 * requires us to parse HID-descriptors in user-space to
                 * properly handle it. This is redundant as the kernel
                 * does it already. That's why newer kernels assemble
-                * the output-reports and send it to us via UHID_OUTPUT. */
+                * the output-reports and send it to us via UHID_OUTPUT.
+                */
                DBG("UHID_OUTPUT_EV unsupported");
                break;
        default:
@@ -260,7 +308,8 @@ static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data)
        fd = g_io_channel_unix_get_fd(chan);
        bread = read(fd, buf, sizeof(buf));
        if (bread < 0) {
-               error("read: %s(%d)", strerror(errno), -errno);
+               error("hidhost: read from interrupt failed: %s(%d)",
+                                               strerror(errno), -errno);
                return TRUE;
        }
 
@@ -296,8 +345,8 @@ static void bt_hid_notify_state(struct hid_device *dev, uint8_t state)
        bdaddr2android(&dev->dst, ev.bdaddr);
        ev.state = state;
 
-       ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_CONN_STATE,
-                                                       sizeof(ev), &ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+                               HAL_EV_HIDHOST_CONN_STATE, sizeof(ev), &ev);
 }
 
 static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond,
@@ -314,9 +363,11 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond,
 error:
        bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
 
-       /* Checking for ctrl_watch avoids a double g_io_channel_shutdown since
+       /*
+        * 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 */
+        * this mainloop iteration
+        */
        if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->ctrl_watch)
                g_io_channel_shutdown(chan, TRUE, NULL);
 
@@ -324,7 +375,7 @@ error:
        if (dev->ctrl_io && !(cond & G_IO_NVAL))
                g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL);
 
-       hid_device_free(dev);
+       hid_device_remove(dev);
 
        return FALSE;
 }
@@ -355,8 +406,8 @@ static void bt_hid_notify_proto_mode(struct hid_device *dev, uint8_t *buf,
                ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL;
        }
 
-       ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_PROTO_MODE,
-                                                       sizeof(ev), &ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+                               HAL_EV_HIDHOST_PROTO_MODE, sizeof(ev), &ev);
 }
 
 static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf,
@@ -369,26 +420,30 @@ static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf,
        ba2str(&dev->dst, address);
        DBG("device %s", address);
 
-       ev_len = sizeof(*ev) + sizeof(struct hal_ev_hidhost_get_report) + 1;
+       ev_len = sizeof(*ev);
 
        if (!((buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) ||
                        (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_OUTPUT)) ||
-                       (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_FEATURE)))) {
+                       (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_FEATURE)))) {
                ev = g_malloc0(ev_len);
                ev->status = buf[0];
                bdaddr2android(&dev->dst, ev->bdaddr);
                goto send;
        }
 
-       /* Report porotocol mode reply contains id after hdr, in boot
-        * protocol mode id doesn't exist */
+       /*
+        * Report porotocol mode reply contains id after hdr, in boot
+        * protocol mode id doesn't exist
+        */
        ev_len += (dev->boot_dev) ? (len - 1) : (len - 2);
        ev = g_malloc0(ev_len);
        ev->status = HAL_HIDHOST_STATUS_OK;
        bdaddr2android(&dev->dst, ev->bdaddr);
 
-       /* Report porotocol mode reply contains id after hdr, in boot
-        * protocol mode id doesn't exist */
+       /*
+        * Report porotocol mode reply contains id after hdr, in boot
+        * protocol mode id doesn't exist
+        */
        if (dev->boot_dev) {
                ev->len = len - 1;
                memcpy(ev->data, buf + 1, ev->len);
@@ -398,8 +453,8 @@ static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf,
        }
 
 send:
-       ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_GET_REPORT,
-                                                               ev_len, ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+                               HAL_EV_HIDHOST_GET_REPORT, ev_len, ev);
        g_free(ev);
 }
 
@@ -423,9 +478,8 @@ static void bt_hid_notify_virtual_unplug(struct hid_device *dev,
                ev.status = HAL_HIDHOST_STATUS_OK;
        }
 
-       ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_VIRTUAL_UNPLUG,
-                                                       sizeof(ev), &ev);
-
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+                               HAL_EV_HIDHOST_VIRTUAL_UNPLUG, sizeof(ev), &ev);
 }
 
 static gboolean ctrl_io_watch_cb(GIOChannel *chan, gpointer data)
@@ -439,7 +493,8 @@ static gboolean ctrl_io_watch_cb(GIOChannel *chan, gpointer data)
        fd = g_io_channel_unix_get_fd(chan);
        bread = read(fd, buf, sizeof(buf));
        if (bread < 0) {
-               error("read: %s(%d)", strerror(errno), -errno);
+               error("hidhost: read from control failed: %s(%d)",
+                                               strerror(errno), -errno);
                return TRUE;
        }
 
@@ -476,16 +531,18 @@ static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond,
 error:
        bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
 
-       /* Checking for intr_watch avoids a double g_io_channel_shutdown since
+       /*
+        * 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 */
+        * this mainloop iteration
+        */
        if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->intr_watch)
                g_io_channel_shutdown(chan, TRUE, NULL);
 
        if (dev->intr_io && !(cond & G_IO_NVAL))
                g_io_channel_shutdown(dev->intr_io, TRUE, NULL);
 
-       hid_device_free(dev);
+       hid_device_remove(dev);
 
        return FALSE;
 }
@@ -508,8 +565,8 @@ static void bt_hid_set_info(struct hid_device *dev)
        memset(ev.descr, 0, sizeof(ev.descr));
        memcpy(ev.descr, dev->rd_data, ev.descr_len);
 
-       ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO, sizeof(ev),
-                                                                       &ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO,
+                                                       sizeof(ev), &ev);
 }
 
 static int uhid_create(struct hid_device *dev)
@@ -522,7 +579,8 @@ static int uhid_create(struct hid_device *dev)
        dev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
        if (dev->uhid_fd < 0) {
                err = -errno;
-               error("Failed to open uHID device: %s", strerror(errno));
+               error("hidhost: Failed to open uHID device: %s",
+                                                       strerror(errno));
                return err;
        }
 
@@ -539,7 +597,8 @@ static int uhid_create(struct hid_device *dev)
 
        if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0) {
                err = -errno;
-               error("Failed to create uHID device: %s", strerror(errno));
+               error("hidhost: Failed to create uHID device: %s",
+                                                       strerror(errno));
                close(dev->uhid_fd);
                dev->uhid_fd = -1;
                return err;
@@ -564,7 +623,8 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
        DBG("");
 
        if (conn_err) {
-               error("%s", conn_err->message);
+               error("hidhost: Failed to connect interrupt channel (%s)",
+                                                       conn_err->message);
                state = HAL_HIDHOST_STATE_FAILED;
                goto failed;
        }
@@ -584,7 +644,7 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
 
 failed:
        bt_hid_notify_state(dev, state);
-       hid_device_free(dev);
+       hid_device_remove(dev);
 }
 
 static void control_connect_cb(GIOChannel *chan, GError *conn_err,
@@ -597,7 +657,8 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err,
 
        if (conn_err) {
                bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
-               error("%s", conn_err->message);
+               error("hidhost: Failed to connect control channel (%s)",
+                                                       conn_err->message);
                goto failed;
        }
 
@@ -609,7 +670,8 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err,
                                        BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
                                        BT_IO_OPT_INVALID);
        if (!dev->intr_io) {
-               error("%s", err->message);
+               error("hidhost: Failed to connect interrupt channel (%s)",
+                                                               err->message);
                g_error_free(err);
                goto failed;
        }
@@ -621,7 +683,7 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err,
        return;
 
 failed:
-       hid_device_free(dev);
+       hid_device_remove(dev);
 }
 
 static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
@@ -633,12 +695,12 @@ static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
        DBG("");
 
        if (err < 0) {
-               error("Unable to get SDP record: %s", strerror(-err));
+               error("hidhost: Unable to get SDP record: %s", strerror(-err));
                goto fail;
        }
 
        if (!recs || !recs->data) {
-               error("No SDP records found");
+               error("hidhost: No SDP records found");
                goto fail;
        }
 
@@ -646,18 +708,6 @@ static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
                sdp_record_t *rec = list->data;
                sdp_data_t *data;
 
-               data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
-               if (data)
-                       dev->vendor = data->val.uint16;
-
-               data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
-               if (data)
-                       dev->product = data->val.uint16;
-
-               data = sdp_data_get(rec, SDP_ATTR_VERSION);
-               if (data)
-                       dev->version = data->val.uint16;
-
                data = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE);
                if (data)
                        dev->country = data->val.uint8;
@@ -708,7 +758,8 @@ static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
                                        BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
                                        BT_IO_OPT_INVALID);
        if (gerr) {
-               error("%s", gerr->message);
+               error("hidhost: Failed to connect control channel (%s)",
+                                                               gerr->message);
                g_error_free(gerr);
                goto fail;
        }
@@ -717,7 +768,57 @@ static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
 
 fail:
        bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
-       hid_device_free(dev);
+       hid_device_remove(dev);
+}
+
+static void hid_sdp_did_search_cb(sdp_list_t *recs, int err, gpointer data)
+{
+       struct hid_device *dev = data;
+       sdp_list_t *list;
+       uuid_t uuid;
+
+       DBG("");
+
+       if (err < 0) {
+               error("hidhost: Unable to get Device ID SDP record: %s",
+                                                               strerror(-err));
+               goto fail;
+       }
+
+       if (!recs || !recs->data) {
+               error("hidhost: No Device ID SDP records found");
+               goto fail;
+       }
+
+       for (list = recs; list; list = list->next) {
+               sdp_record_t *rec = list->data;
+               sdp_data_t *data;
+
+               data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
+               if (data)
+                       dev->vendor = data->val.uint16;
+
+               data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
+               if (data)
+                       dev->product = data->val.uint16;
+
+               data = sdp_data_get(rec, SDP_ATTR_VERSION);
+               if (data)
+                       dev->version = data->val.uint16;
+       }
+
+       sdp_uuid16_create(&uuid, HID_SVCLASS_ID);
+       if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+                               hid_sdp_search_cb, dev, NULL, 0) < 0) {
+               error("hidhost: Failed to search SDP details");
+               goto fail;
+       }
+
+       return;
+
+fail:
+       bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
+       hid_device_remove(dev);
 }
 
 static void bt_hid_connect(const void *buf, uint16_t len)
@@ -747,11 +848,11 @@ static void bt_hid_connect(const void *buf, uint16_t len)
        ba2str(&dev->dst, addr);
        DBG("connecting to %s", addr);
 
-       bt_string2uuid(&uuid, HID_UUID);
+       sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
        if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
-                                       hid_sdp_search_cb, dev, NULL) < 0) {
-               error("Failed to search sdp details");
-               hid_device_free(dev);
+                               hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
+               error("hidhost: Failed to search DeviceID SDP details");
+               hid_device_remove(dev);
                status = HAL_STATUS_FAILED;
                goto failed;
        }
@@ -762,7 +863,8 @@ static void bt_hid_connect(const void *buf, uint16_t len)
        status = HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT,
+                                                                       status);
 }
 
 static void bt_hid_disconnect(const void *buf, uint16_t len)
@@ -797,7 +899,8 @@ static void bt_hid_disconnect(const void *buf, uint16_t len)
        status = HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT,
+                                                                       status);
 }
 
 static void bt_hid_virtual_unplug(const void *buf, uint16_t len)
@@ -832,7 +935,7 @@ static void bt_hid_virtual_unplug(const void *buf, uint16_t len)
        fd = g_io_channel_unix_get_fd(dev->ctrl_io);
 
        if (write(fd, &hdr, sizeof(hdr)) < 0) {
-               error("error writing virtual unplug command: %s (%d)",
+               error("hidhost: Error writing virtual unplug command: %s (%d)",
                                                strerror(errno), errno);
                status = HAL_STATUS_FAILED;
                goto failed;
@@ -850,19 +953,29 @@ static void bt_hid_virtual_unplug(const void *buf, uint16_t len)
        status = HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
-                                                                       status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+                                       HAL_OP_HIDHOST_VIRTUAL_UNPLUG, status);
 }
 
 static void bt_hid_info(const void *buf, uint16_t len)
 {
-       /* Data from hal_cmd_hidhost_set_info is usefull only when we create
+       const struct hal_cmd_hidhost_set_info *cmd = buf;
+
+       if (len != sizeof(*cmd) + cmd->descr_len) {
+               error("Invalid hid set info size (%u bytes), terminating", len);
+               raise(SIGTERM);
+               return;
+       }
+
+       /*
+        * Data from hal_cmd_hidhost_set_info is usefull only when we create
         * UHID device. Once device is created all the transactions will be
         * done through the fd. There is no way to use this information
-        * once device is created with HID internals. */
+        * once device is created with HID internals.
+        */
        DBG("Not supported");
 
-       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
                                                        HAL_STATUS_UNSUPPORTED);
 }
 
@@ -878,6 +991,15 @@ static void bt_hid_get_protocol(const void *buf, uint16_t len)
 
        DBG("");
 
+       switch (cmd->mode) {
+       case HAL_HIDHOST_REPORT_PROTOCOL:
+       case HAL_HIDHOST_BOOT_PROTOCOL:
+               break;
+       default:
+               status = HAL_STATUS_INVALID;
+               goto failed;
+       }
+
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
@@ -888,16 +1010,11 @@ static void bt_hid_get_protocol(const void *buf, uint16_t len)
 
        dev = l->data;
 
-       if (dev->boot_dev) {
-               status = HAL_STATUS_UNSUPPORTED;
-               goto failed;
-       }
-
        hdr = HID_MSG_GET_PROTOCOL | cmd->mode;
        fd = g_io_channel_unix_get_fd(dev->ctrl_io);
 
        if (write(fd, &hdr, sizeof(hdr)) < 0) {
-               error("error writing device_get_protocol: %s (%d)",
+               error("hidhost: Error writing device_get_protocol: %s (%d)",
                                                strerror(errno), errno);
                status = HAL_STATUS_FAILED;
                goto failed;
@@ -908,8 +1025,8 @@ static void bt_hid_get_protocol(const void *buf, uint16_t len)
        status = HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_PROTOCOL,
-                                                                       status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+                                       HAL_OP_HIDHOST_GET_PROTOCOL, status);
 }
 
 static void bt_hid_set_protocol(const void *buf, uint16_t len)
@@ -924,6 +1041,15 @@ static void bt_hid_set_protocol(const void *buf, uint16_t len)
 
        DBG("");
 
+       switch (cmd->mode) {
+       case HAL_HIDHOST_REPORT_PROTOCOL:
+       case HAL_HIDHOST_BOOT_PROTOCOL:
+               break;
+       default:
+               status = HAL_STATUS_INVALID;
+               goto failed;
+       }
+
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
@@ -934,16 +1060,11 @@ static void bt_hid_set_protocol(const void *buf, uint16_t len)
 
        dev = l->data;
 
-       if (dev->boot_dev) {
-               status = HAL_STATUS_UNSUPPORTED;
-               goto failed;
-       }
-
        hdr = HID_MSG_SET_PROTOCOL | cmd->mode;
        fd = g_io_channel_unix_get_fd(dev->ctrl_io);
 
        if (write(fd, &hdr, sizeof(hdr)) < 0) {
-               error("error writing device_set_protocol: %s (%d)",
+               error("hidhost: error writing device_set_protocol: %s (%d)",
                                                strerror(errno), errno);
                status = HAL_STATUS_FAILED;
                goto failed;
@@ -954,8 +1075,8 @@ static void bt_hid_set_protocol(const void *buf, uint16_t len)
        status = HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_PROTOCOL,
-                                                                       status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST,
+                                       HAL_OP_HIDHOST_SET_PROTOCOL, status);
 }
 
 static void bt_hid_get_report(const void *buf, uint16_t len)
@@ -971,6 +1092,16 @@ static void bt_hid_get_report(const void *buf, uint16_t len)
 
        DBG("");
 
+       switch (cmd->type) {
+       case HAL_HIDHOST_INPUT_REPORT:
+       case HAL_HIDHOST_OUTPUT_REPORT:
+       case HAL_HIDHOST_FEATURE_REPORT:
+               break;
+       default:
+               status = HAL_STATUS_INVALID;
+               goto failed;
+       }
+
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
@@ -992,13 +1123,13 @@ static void bt_hid_get_report(const void *buf, uint16_t len)
 
        if (cmd->buf_size > 0) {
                req[0] = req[0] | HID_GET_REPORT_SIZE_FIELD;
-               bt_put_le16(cmd->buf_size, &req[2]);
+               put_le16(cmd->buf_size, &req[2]);
        }
 
        fd = g_io_channel_unix_get_fd(dev->ctrl_io);
 
        if (write(fd, req, req_size) < 0) {
-               error("error writing hid_get_report: %s (%d)",
+               error("hidhost: error writing hid_get_report: %s (%d)",
                                                strerror(errno), errno);
                g_free(req);
                status = HAL_STATUS_FAILED;
@@ -1011,7 +1142,8 @@ static void bt_hid_get_report(const void *buf, uint16_t len)
        status = HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT,
+                                                                       status);
 }
 
 static void bt_hid_set_report(const void *buf, uint16_t len)
@@ -1020,8 +1152,8 @@ static void bt_hid_set_report(const void *buf, uint16_t len)
        struct hid_device *dev;
        GSList *l;
        bdaddr_t dst;
-       int i, fd;
-       uint8_t *req;
+       int fd;
+       uint8_t *req = NULL;
        uint8_t req_size;
        uint8_t status;
 
@@ -1034,6 +1166,16 @@ static void bt_hid_set_report(const void *buf, uint16_t len)
                return;
        }
 
+       switch (cmd->type) {
+       case HAL_HIDHOST_INPUT_REPORT:
+       case HAL_HIDHOST_OUTPUT_REPORT:
+       case HAL_HIDHOST_FEATURE_REPORT:
+               break;
+       default:
+               status = HAL_STATUS_INVALID;
+               goto failed;
+       }
+
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
@@ -1057,28 +1199,33 @@ static void bt_hid_set_report(const void *buf, uint16_t len)
        }
 
        req[0] = HID_MSG_SET_REPORT | cmd->type;
-       /* Report data coming to HAL is in ascii format, HAL sends
-        * data in hex to daemon, so convert to binary. */
-       for (i = 0; i < (req_size - 1); i++)
-               sscanf((char *) &(cmd->data)[i * 2], "%hhx", &(req + 1)[i]);
+       /*
+        * Report data coming to HAL is in ascii format, HAL sends
+        * data in hex to daemon, so convert to binary.
+        */
+       if (!hex2buf(cmd->data, req + 1, req_size - 1)) {
+               status = HAL_STATUS_INVALID;
+               goto failed;
+       }
 
        fd = g_io_channel_unix_get_fd(dev->ctrl_io);
 
        if (write(fd, req, req_size) < 0) {
-               error("error writing hid_set_report: %s (%d)",
+               error("hidhost: error writing hid_set_report: %s (%d)",
                                                strerror(errno), errno);
-               g_free(req);
                status = HAL_STATUS_FAILED;
                goto failed;
        }
 
        dev->last_hid_msg = HID_MSG_SET_REPORT;
-       g_free(req);
 
        status = HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, status);
+       g_free(req);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT,
+                                                                       status);
 }
 
 static void bt_hid_send_data(const void *buf, uint16_t len)
@@ -1087,8 +1234,8 @@ static void bt_hid_send_data(const void *buf, uint16_t len)
        struct hid_device *dev;
        GSList *l;
        bdaddr_t dst;
-       int i, fd;
-       uint8_t *req;
+       int fd;
+       uint8_t *req = NULL;
        uint8_t req_size;
        uint8_t status;
 
@@ -1124,27 +1271,31 @@ static void bt_hid_send_data(const void *buf, uint16_t len)
        }
 
        req[0] = HID_MSG_DATA | HID_DATA_TYPE_OUTPUT;
-       /* Report data coming to HAL is in ascii format, HAL sends
-        * data in hex to daemon, so convert to binary. */
-       for (i = 0; i < (req_size - 1); i++)
-               sscanf((char *) &(cmd->data)[i * 2], "%hhx", &(req + 1)[i]);
+       /*
+        * Report data coming to HAL is in ascii format, HAL sends
+        * data in hex to daemon, so convert to binary.
+        */
+       if (!hex2buf(cmd->data, req + 1, req_size - 1)) {
+               status = HAL_STATUS_INVALID;
+               goto failed;
+       }
 
        fd = g_io_channel_unix_get_fd(dev->intr_io);
 
        if (write(fd, req, req_size) < 0) {
-               error("error writing data to HID device: %s (%d)",
+               error("hidhost: error writing data to HID device: %s (%d)",
                                                strerror(errno), errno);
-               g_free(req);
                status = HAL_STATUS_FAILED;
                goto failed;
        }
 
-       g_free(req);
-
        status = HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, status);
+       g_free(req);
+
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA,
+                                                                       status);
 }
 
 static const struct ipc_handler cmd_handlers[] = {
@@ -1174,7 +1325,7 @@ static const struct ipc_handler cmd_handlers[] = {
 static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 {
        struct hid_device *dev;
-       bdaddr_t src, dst;
+       bdaddr_t dst;
        char address[18];
        uint16_t psm;
        GError *gerr = NULL;
@@ -1182,17 +1333,17 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
        uuid_t uuid;
 
        if (err) {
-               error("%s", err->message);
+               error("hidhost: Connect failed (%s)", err->message);
                return;
        }
 
        bt_io_get(chan, &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);
+               error("hidhost: Failed to read remote address (%s)",
+                                                               gerr->message);
                g_io_channel_shutdown(chan, TRUE, NULL);
                g_error_free(gerr);
                return;
@@ -1212,11 +1363,11 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
                dev->ctrl_io = g_io_channel_ref(chan);
                dev->uhid_fd = -1;
 
-               bt_string2uuid(&uuid, HID_UUID);
-               if (bt_search_service(&src, &dev->dst, &uuid,
-                                       hid_sdp_search_cb, dev, NULL) < 0) {
-                       error("failed to search sdp details");
-                       hid_device_free(dev);
+               sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID);
+               if (bt_search_service(&adapter_addr, &dev->dst, &uuid,
+                               hid_sdp_did_search_cb, dev, NULL, 0) < 0) {
+                       error("hidhost: Failed to search DID SDP details");
+                       hid_device_remove(dev);
                        return;
                }
 
@@ -1243,7 +1394,7 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
        }
 }
 
-bool bt_hid_register(const bdaddr_t *addr)
+bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
 {
        GError *err = NULL;
 
@@ -1257,7 +1408,8 @@ bool bt_hid_register(const bdaddr_t *addr)
                                BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
                                BT_IO_OPT_INVALID);
        if (!ctrl_io) {
-               error("Failed to listen on ctrl channel: %s", err->message);
+               error("hidhost: Failed to listen on control channel: %s",
+                                                               err->message);
                g_error_free(err);
                return false;
        }
@@ -1268,7 +1420,8 @@ bool bt_hid_register(const bdaddr_t *addr)
                                BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
                                BT_IO_OPT_INVALID);
        if (!intr_io) {
-               error("Failed to listen on intr channel: %s", err->message);
+               error("hidhost: Failed to listen on interrupt channel: %s",
+                                                               err->message);
                g_error_free(err);
 
                g_io_channel_shutdown(ctrl_io, TRUE, NULL);
@@ -1278,25 +1431,19 @@ bool bt_hid_register(const bdaddr_t *addr)
                return false;
        }
 
-       ipc_register(HAL_SERVICE_ID_HIDHOST, cmd_handlers,
+       hal_ipc = ipc;
+
+       ipc_register(hal_ipc, HAL_SERVICE_ID_HIDHOST, cmd_handlers,
                                                G_N_ELEMENTS(cmd_handlers));
 
        return true;
 }
 
-static void free_hid_devices(gpointer data, gpointer user_data)
-{
-       struct hid_device *dev = data;
-
-       bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED);
-       hid_device_free(dev);
-}
-
 void bt_hid_unregister(void)
 {
        DBG("");
 
-       g_slist_foreach(devices, free_hid_devices, NULL);
+       g_slist_free_full(devices, hid_device_free);
        devices = NULL;
 
        if (ctrl_io) {
@@ -1311,5 +1458,6 @@ void bt_hid_unregister(void)
                intr_io = NULL;
        }
 
-       ipc_unregister(HAL_SERVICE_ID_HIDHOST);
+       ipc_unregister(hal_ipc, HAL_SERVICE_ID_HIDHOST);
+       hal_ipc = NULL;
 }
index ea14446..e6b87ed 100644 (file)
@@ -2,24 +2,24 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
-bool bt_hid_register(const bdaddr_t *addr);
+bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
 void bt_hid_unregister(void);
diff --git a/android/init.bluetooth.rc b/android/init.bluetooth.rc
new file mode 100644 (file)
index 0000000..af62121
--- /dev/null
@@ -0,0 +1,38 @@
+# required permissions
+on boot
+    chown bluetooth bluetooth /data/misc/bluetooth
+    chown bluetooth bluetooth /dev/uhid
+    chown system    bluetooth /dev/uinput
+
+# services
+on property:bluetooth.start=daemon
+    setprop bluetooth.start none
+    start bluetoothd
+
+on property:bluetooth.stop=daemon
+    setprop bluetooth.stop none
+    stop bluetoothd
+
+on property:bluetooth.start=snoop
+    setprop bluetooth.start none
+    start bluetoothd-snoop
+
+on property:bluetooth.stop=snoop
+    setprop bluetooth.stop none
+    stop bluetoothd-snoop
+
+service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd
+    class main
+    # init does not yet support setting capabilities so run as root,
+    # bluetoothd drop uid to bluetooth with the right linux capabilities
+    group bluetooth
+    disabled
+    oneshot
+
+service bluetoothd-snoop /system/bin/logwrapper /system/bin/bluetoothd-snoop
+    class main
+    # init does not yet support setting capabilities so run as root,
+    # bluetoothd-snoop drops unneeded linux capabilities
+    group nobody
+    disabled
+    oneshot
diff --git a/android/ipc-common.h b/android/ipc-common.h
new file mode 100644 (file)
index 0000000..27736e4
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#define IPC_MTU 1024
+
+#define IPC_STATUS_SUCCESS     0x00
+
+struct ipc_hdr {
+       uint8_t  service_id;
+       uint8_t  opcode;
+       uint16_t len;
+       uint8_t  payload[0];
+} __attribute__((packed));
+
+#define IPC_OP_STATUS          0x00
+struct ipc_status {
+       uint8_t code;
+} __attribute__((packed));
diff --git a/android/ipc-tester.c b/android/ipc-tester.c
new file mode 100644 (file)
index 0000000..63fd105
--- /dev/null
@@ -0,0 +1,1261 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/un.h>
+#include <libgen.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hciemu.h"
+
+#include "hal-msg.h"
+#include "ipc-common.h"
+
+#include <cutils/properties.h>
+
+#define WAIT_FOR_SIGNAL_TIME 2 /* in seconds */
+#define EMULATOR_SIGNAL "emulator_started"
+
+struct test_data {
+       struct mgmt *mgmt;
+       uint16_t mgmt_index;
+       struct hciemu *hciemu;
+       enum hciemu_type hciemu_type;
+       pid_t bluetoothd_pid;
+       bool setup_done;
+};
+
+struct ipc_data {
+       void *buffer;
+       size_t len;
+};
+
+struct generic_data {
+       struct ipc_data ipc_data;
+
+       unsigned int num_services;
+       int init_services[];
+};
+
+struct regmod_msg {
+       struct ipc_hdr header;
+       struct hal_cmd_register_module cmd;
+} __attribute__((packed));
+
+#define CONNECT_TIMEOUT (5 * 1000)
+#define SERVICE_NAME "bluetoothd"
+
+static char exec_dir[PATH_MAX + 1];
+
+static int cmd_sk = -1;
+static int notif_sk = -1;
+
+static void read_info_callback(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct mgmt_rp_read_info *rp = param;
+       char addr[18];
+       uint16_t manufacturer;
+       uint32_t supported_settings, current_settings;
+
+       tester_print("Read Info callback");
+       tester_print("  Status: 0x%02x", status);
+
+       if (status || !param) {
+               tester_pre_setup_failed();
+               return;
+       }
+
+       ba2str(&rp->bdaddr, addr);
+       manufacturer = btohs(rp->manufacturer);
+       supported_settings = btohl(rp->supported_settings);
+       current_settings = btohl(rp->current_settings);
+
+       tester_print("  Address: %s", addr);
+       tester_print("  Version: 0x%02x", rp->version);
+       tester_print("  Manufacturer: 0x%04x", manufacturer);
+       tester_print("  Supported settings: 0x%08x", supported_settings);
+       tester_print("  Current settings: 0x%08x", current_settings);
+       tester_print("  Class: 0x%02x%02x%02x",
+                       rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+       tester_print("  Name: %s", rp->name);
+       tester_print("  Short name: %s", rp->short_name);
+
+       if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+               tester_pre_setup_failed();
+               return;
+       }
+
+       tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+
+       tester_print("Index Added callback");
+       tester_print("  Index: 0x%04x", index);
+
+       data->mgmt_index = index;
+
+       mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+                                       read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+
+       tester_print("Index Removed callback");
+       tester_print("  Index: 0x%04x", index);
+
+       if (index != data->mgmt_index)
+               return;
+
+       mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+       mgmt_unref(data->mgmt);
+       data->mgmt = NULL;
+
+       tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+
+       tester_print("Read Index List callback");
+       tester_print("  Status: 0x%02x", status);
+
+       if (status || !param) {
+               tester_pre_setup_failed();
+               return;
+       }
+
+       mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+                                       index_added_callback, NULL, NULL);
+
+       mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+                                       index_removed_callback, NULL, NULL);
+
+       data->hciemu = hciemu_new(data->hciemu_type);
+       if (!data->hciemu) {
+               tester_warn("Failed to setup HCI emulation");
+               tester_pre_setup_failed();
+               return;
+       }
+
+       tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *data)
+{
+       struct test_data *test_data = tester_get_data();
+
+       if (!tester_use_debug())
+               fclose(stderr);
+
+       test_data->mgmt = mgmt_new_default();
+       if (!test_data->mgmt) {
+               tester_warn("Failed to setup management interface");
+               tester_pre_setup_failed();
+               return;
+       }
+
+       mgmt_send(test_data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0,
+                               NULL, read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *data)
+{
+       struct test_data *test_data = tester_get_data();
+
+       if (test_data->hciemu) {
+               hciemu_unref(test_data->hciemu);
+               test_data->hciemu = NULL;
+       }
+}
+
+static void bluetoothd_start(int hci_index)
+{
+       char prg_name[PATH_MAX + 1];
+       char index[8];
+       char *prg_argv[4];
+
+       snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
+       snprintf(index, sizeof(index), "%d", hci_index);
+
+       prg_argv[0] = prg_name;
+       prg_argv[1] = "-i";
+       prg_argv[2] = index;
+       prg_argv[3] = NULL;
+
+       if (!tester_use_debug())
+               fclose(stderr);
+
+       execve(prg_argv[0], prg_argv, NULL);
+}
+
+static void emulator(int pipe, int hci_index)
+{
+       static const char SYSTEM_SOCKET_PATH[] = "\0android_system";
+       char buf[1024];
+       struct sockaddr_un addr;
+       struct timeval tv;
+       int fd;
+       ssize_t len;
+
+       fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (fd < 0)
+               goto failed;
+
+       tv.tv_sec = WAIT_FOR_SIGNAL_TIME;
+       tv.tv_usec = 0;
+       setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH));
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Failed to bind system socket");
+               goto failed;
+       }
+
+       len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL));
+
+       if (len != sizeof(EMULATOR_SIGNAL))
+               goto failed;
+
+       memset(buf, 0, sizeof(buf));
+
+       len = read(fd, buf, sizeof(buf));
+       if (len <= 0 || strcmp(buf, "ctl.start=bluetoothd"))
+               goto failed;
+
+       close(pipe);
+       close(fd);
+       bluetoothd_start(hci_index);
+
+failed:
+       close(pipe);
+       close(fd);
+}
+
+static int accept_connection(int sk)
+{
+       int err;
+       struct pollfd pfd;
+       int new_sk;
+
+       memset(&pfd, 0 , sizeof(pfd));
+       pfd.fd = sk;
+       pfd.events = POLLIN;
+
+       err = poll(&pfd, 1, CONNECT_TIMEOUT);
+       if (err < 0) {
+               err = errno;
+               tester_warn("Failed to poll: %d (%s)", err, strerror(err));
+               return -errno;
+       }
+
+       if (err == 0) {
+               tester_warn("bluetoothd connect timeout");
+               return -errno;
+       }
+
+       new_sk = accept(sk, NULL, NULL);
+       if (new_sk < 0) {
+               err = errno;
+               tester_warn("Failed to accept socket: %d (%s)",
+                                                       err, strerror(err));
+               return -errno;
+       }
+
+       return new_sk;
+}
+
+static bool init_ipc(void)
+{
+       struct sockaddr_un addr;
+
+       int sk;
+       int err;
+
+       sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+       if (sk < 0) {
+               err = errno;
+               tester_warn("Failed to create socket: %d (%s)", err,
+                                                       strerror(err));
+               return false;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+
+       memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               err = errno;
+               tester_warn("Failed to bind socket: %d (%s)", err,
+                                                               strerror(err));
+               close(sk);
+               return false;
+       }
+
+       if (listen(sk, 2) < 0) {
+               err = errno;
+               tester_warn("Failed to listen on socket: %d (%s)", err,
+                                                               strerror(err));
+               close(sk);
+               return false;
+       }
+
+       /* Start Android Bluetooth daemon service */
+       if (property_set("ctl.start", SERVICE_NAME) < 0) {
+               tester_warn("Failed to start service %s", SERVICE_NAME);
+               close(sk);
+               return false;
+       }
+
+       cmd_sk = accept_connection(sk);
+       if (cmd_sk < 0) {
+               close(sk);
+               return false;
+       }
+
+       notif_sk = accept_connection(sk);
+       if (notif_sk < 0) {
+               close(sk);
+               close(cmd_sk);
+               cmd_sk = -1;
+               return false;
+       }
+
+       tester_print("bluetoothd connected");
+
+       close(sk);
+
+       return true;
+}
+
+static void cleanup_ipc(void)
+{
+       if (cmd_sk < 0)
+               return;
+
+       close(cmd_sk);
+       cmd_sk = -1;
+}
+
+static gboolean check_for_daemon(gpointer user_data)
+{
+       int status;
+       struct test_data *data = user_data;
+
+       if ((waitpid(data->bluetoothd_pid, &status, WNOHANG))
+                                                       != data->bluetoothd_pid)
+               return true;
+
+       if (data->setup_done) {
+               if (WIFEXITED(status) &&
+                               (WEXITSTATUS(status) == EXIT_SUCCESS)) {
+                       tester_test_passed();
+                       return false;
+               }
+               tester_test_failed();
+       } else {
+               tester_setup_failed();
+               test_post_teardown(data);
+       }
+
+       tester_warn("Unexpected Daemon shutdown with status %d", status);
+       return false;
+}
+
+static bool setup_module(int service_id)
+{
+       struct ipc_hdr response;
+       struct ipc_hdr expected_response;
+
+       struct regmod_msg btmodule_msg = {
+               .header = {
+                       .service_id = HAL_SERVICE_ID_CORE,
+                       .opcode = HAL_OP_REGISTER_MODULE,
+                       .len = sizeof(struct hal_cmd_register_module),
+                       },
+               .cmd = {
+                       .service_id = service_id,
+                       },
+       };
+
+       if (write(cmd_sk, &btmodule_msg, sizeof(btmodule_msg)) < 0)
+               goto fail;
+
+       if (read(cmd_sk, &response, sizeof(response)) < 0)
+               goto fail;
+
+       expected_response = btmodule_msg.header;
+       expected_response.len = 0;
+
+       if (memcmp(&response, &expected_response, sizeof(response)) == 0)
+               return true;
+
+fail:
+       tester_warn("Module registration failed.");
+       return false;
+}
+
+static void setup(const void *data)
+{
+       const struct generic_data *generic_data = data;
+       struct test_data *test_data = tester_get_data();
+       int signal_fd[2];
+       char buf[1024];
+       pid_t pid;
+       int len;
+       unsigned int i;
+
+       if (pipe(signal_fd))
+               goto failed;
+
+       pid = fork();
+
+       if (pid < 0) {
+               close(signal_fd[0]);
+               close(signal_fd[1]);
+               goto failed;
+       }
+
+       if (pid == 0) {
+               if (!tester_use_debug())
+                       fclose(stderr);
+
+               close(signal_fd[0]);
+               emulator(signal_fd[1], test_data->mgmt_index);
+               exit(0);
+       }
+
+       close(signal_fd[1]);
+       test_data->bluetoothd_pid = pid;
+
+       len = read(signal_fd[0], buf, sizeof(buf));
+       if (len <= 0 || (strcmp(buf, EMULATOR_SIGNAL))) {
+               close(signal_fd[0]);
+               goto failed;
+       }
+
+       g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, check_for_daemon, test_data,
+                                                                       NULL);
+
+       if (!init_ipc()) {
+               tester_warn("Cannot initialize IPC mechanism!");
+               goto failed;
+       }
+       tester_print("Will init %d services.", generic_data->num_services);
+
+       for (i = 0; i < generic_data->num_services; i++)
+               if (!setup_module(generic_data->init_services[i])) {
+                       cleanup_ipc();
+                       goto failed;
+               }
+
+       test_data->setup_done = true;
+
+       tester_setup_complete();
+       return;
+
+failed:
+       g_idle_remove_by_data(test_data);
+       tester_setup_failed();
+       test_post_teardown(data);
+}
+
+static void teardown(const void *data)
+{
+       struct test_data *test_data = tester_get_data();
+
+       g_idle_remove_by_data(test_data);
+       cleanup_ipc();
+
+       if (test_data->bluetoothd_pid)
+               waitpid(test_data->bluetoothd_pid, NULL, 0);
+
+       tester_teardown_complete();
+}
+
+static void ipc_send_tc(const void *data)
+{
+       const struct generic_data *generic_data = data;
+       const struct ipc_data *ipc_data = &generic_data->ipc_data;
+
+       if (ipc_data->len) {
+               if (write(cmd_sk, ipc_data->buffer, ipc_data->len) < 0)
+                       tester_test_failed();
+       }
+}
+
+#define service_data(args...) { args }
+
+#define gen_data(writelen, writebuf, servicelist...) \
+       {                                                               \
+               .ipc_data = {                                           \
+                       .buffer = writebuf,                             \
+                       .len = writelen,                                \
+               },                                                      \
+               .init_services = service_data(servicelist),             \
+               .num_services = sizeof((const int[])                    \
+                                       service_data(servicelist)) /    \
+                                       sizeof(int),                    \
+       }
+
+#define test_generic(name, test, setup, teardown, buffer, writelen, \
+                                                       services...) \
+       do {                                                            \
+               struct test_data *user;                                 \
+               static const struct generic_data data =                 \
+                               gen_data(writelen, buffer, services);   \
+               user = g_malloc0(sizeof(struct test_data));             \
+               if (!user)                                              \
+                       break;                                          \
+               user->hciemu_type = HCIEMU_TYPE_BREDRLE;                \
+               tester_add_full(name, &data, test_pre_setup, setup,     \
+                               test, teardown, test_post_teardown,     \
+                               3, user, g_free);                       \
+       } while (0)
+
+#define test_opcode_valid(_name, _service, _opcode, _len, _servicelist...) \
+       do {                                                            \
+               static struct ipc_hdr hdr = {                           \
+                       .service_id = _service,                         \
+                       .opcode = _opcode,                              \
+                       .len = _len,                                    \
+               };                                                      \
+                                                                       \
+               test_generic("Opcode out of range: "_name,              \
+                               ipc_send_tc, setup, teardown,           \
+                               &hdr,                                   \
+                               sizeof(hdr),                            \
+                               _servicelist);                          \
+       } while (0)
+
+struct vardata {
+       struct ipc_hdr hdr;
+       uint8_t buf[IPC_MTU];
+} __attribute__((packed));
+
+#define test_datasize_valid(_name, _service, _opcode, _hlen, _addatasize, \
+                                                       _servicelist...) \
+       do {                                                            \
+               static struct vardata vdata = {                         \
+                       .hdr.service_id = _service,                     \
+                       .hdr.opcode = _opcode,                          \
+                       .hdr.len = (_hlen) + (_addatasize),             \
+                       .buf = {},                                      \
+               };                                                      \
+               test_generic("Data size "_name,                         \
+                               ipc_send_tc, setup, teardown,           \
+                               &vdata,                                 \
+                               sizeof(vdata.hdr) + (_hlen) + (_addatasize),\
+                               _servicelist);                          \
+       } while (0)
+
+struct regmod_msg register_bt_msg = {
+       .header = {
+               .service_id = HAL_SERVICE_ID_CORE,
+               .opcode = HAL_OP_REGISTER_MODULE,
+               .len = sizeof(struct hal_cmd_register_module),
+               },
+       .cmd = {
+               .service_id = HAL_SERVICE_ID_BLUETOOTH,
+               },
+};
+
+struct regmod_msg register_bt_malformed_size_msg = {
+       .header = {
+               .service_id = HAL_SERVICE_ID_CORE,
+               .opcode = HAL_OP_REGISTER_MODULE,
+               /* wrong payload size declared */
+               .len = sizeof(struct hal_cmd_register_module) - 1,
+               },
+       .cmd = {
+               .service_id = HAL_SERVICE_ID_CORE,
+               },
+};
+
+struct malformed_data3_struct {
+       struct regmod_msg valid_msg;
+       int redundant_data;
+}  __attribute__((packed));
+
+static struct malformed_data3_struct malformed_data3_msg = {
+       /* valid register service message */
+       .valid_msg = {
+               .header = {
+                       .service_id = HAL_SERVICE_ID_CORE,
+                       .opcode = HAL_OP_REGISTER_MODULE,
+                       .len = sizeof(struct hal_cmd_register_module),
+                       },
+               .cmd = {
+                       .service_id = HAL_SERVICE_ID_CORE,
+                       },
+       },
+       /* plus redundant data */
+       . redundant_data = 666,
+};
+
+struct ipc_hdr enable_unknown_service_hdr = {
+       .service_id = HAL_SERVICE_ID_MAX + 1,
+       .opcode = HAL_OP_REGISTER_MODULE,
+       .len = 0,
+};
+
+struct ipc_hdr enable_bt_service_hdr = {
+       .service_id = HAL_SERVICE_ID_BLUETOOTH,
+       .opcode = HAL_OP_ENABLE,
+       .len = 0,
+};
+
+struct bt_set_adapter_prop_data {
+       struct ipc_hdr hdr;
+       struct hal_cmd_set_adapter_prop prop;
+
+       /* data placeholder for hal_cmd_set_adapter_prop.val[0] */
+       uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+                               sizeof(struct hal_cmd_set_adapter_prop)];
+} __attribute__((packed));
+
+#define set_name "new name"
+
+static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_overs = {
+       .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+       .hdr.opcode = HAL_OP_SET_ADAPTER_PROP,
+       .hdr.len = sizeof(struct hal_cmd_set_adapter_prop) +
+                                               sizeof(set_name),
+
+       .prop.type = HAL_PROP_ADAPTER_NAME,
+       /* declare wrong descriptor length */
+       .prop.len = sizeof(set_name) + 1,
+       /* init prop.val[0] */
+       .buf = set_name,
+};
+
+static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_unders = {
+       .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+       .hdr.opcode = HAL_OP_SET_ADAPTER_PROP,
+       .hdr.len = sizeof(struct hal_cmd_set_adapter_prop) +
+                                               sizeof(set_name),
+
+       .prop.type = HAL_PROP_ADAPTER_NAME,
+       /* declare wrong descriptor length */
+       .prop.len = sizeof(set_name) - 1,
+       /* init prop.val[0] */
+       .buf = set_name,
+};
+
+struct bt_set_remote_prop_data {
+       struct ipc_hdr hdr;
+       struct hal_cmd_set_remote_device_prop prop;
+
+       /* data placeholder for hal_cmd_set_remote_device_prop.val[0] */
+       uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+                               sizeof(struct hal_cmd_set_remote_device_prop)];
+} __attribute__((packed));
+
+static struct bt_set_remote_prop_data bt_set_remote_prop_data_overs = {
+       .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+       .hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP,
+       .hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) +
+                                               sizeof(set_name),
+
+       .prop.bdaddr = {},
+       .prop.type = HAL_PROP_DEVICE_NAME,
+       /* declare wrong descriptor length */
+       .prop.len = sizeof(set_name) + 1,
+       .buf = set_name,
+};
+
+static struct bt_set_remote_prop_data bt_set_remote_prop_data_unders = {
+       .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH,
+       .hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP,
+       .hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) +
+                                               sizeof(set_name),
+
+       .prop.bdaddr = {},
+       .prop.type = HAL_PROP_DEVICE_NAME,
+       /* declare wrong descriptor length */
+       .prop.len = sizeof(set_name) - 1,
+       .buf = set_name,
+};
+
+struct hidhost_set_info_data {
+       struct ipc_hdr hdr;
+       struct hal_cmd_hidhost_set_info info;
+
+       /* data placeholder for hal_cmd_hidhost_set_info.descr[0] field */
+       uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+                               sizeof(struct hal_cmd_hidhost_set_info)];
+} __attribute__((packed));
+
+#define set_info_data "some descriptor"
+
+static struct hidhost_set_info_data hidhost_set_info_data_overs = {
+       .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+       .hdr.opcode = HAL_OP_HIDHOST_SET_INFO,
+       .hdr.len = sizeof(struct hal_cmd_hidhost_set_info) +
+                                                       sizeof(set_info_data),
+
+       /* declare wrong descriptor length */
+       .info.descr_len = sizeof(set_info_data) + 1,
+       /* init .info.descr[0] */
+       .buf = set_info_data,
+};
+
+static struct hidhost_set_info_data hidhost_set_info_data_unders = {
+       .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+       .hdr.opcode = HAL_OP_HIDHOST_SET_INFO,
+       .hdr.len = sizeof(struct hal_cmd_hidhost_set_info) +
+                                                       sizeof(set_info_data),
+
+       /* declare wrong descriptor length */
+       .info.descr_len = sizeof(set_info_data) - 1,
+       /* init .info.descr[0] */
+       .buf = set_info_data,
+};
+
+struct hidhost_set_report_data {
+       struct ipc_hdr hdr;
+       struct hal_cmd_hidhost_set_report report;
+
+       /* data placeholder for hal_cmd_hidhost_set_report.data[0] field */
+       uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+                               sizeof(struct hal_cmd_hidhost_set_report)];
+} __attribute__((packed));
+
+#define set_rep_data "1234567890"
+
+static struct hidhost_set_report_data hidhost_set_report_data_overs = {
+       .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+       .hdr.opcode = HAL_OP_HIDHOST_SET_REPORT,
+       .hdr.len = sizeof(struct hal_cmd_hidhost_set_report) +
+                                                       sizeof(set_rep_data),
+
+       /* declare wrong descriptor length */
+       .report.len = sizeof(set_rep_data) + 1,
+       /* init report.data[0] */
+       .buf = set_rep_data,
+};
+
+static struct hidhost_set_report_data hidhost_set_report_data_unders = {
+       .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+       .hdr.opcode = HAL_OP_HIDHOST_SET_REPORT,
+       .hdr.len = sizeof(struct hal_cmd_hidhost_set_report) +
+                                                       sizeof(set_rep_data),
+
+       /* declare wrong descriptor length */
+       .report.len = sizeof(set_rep_data) - 1,
+       /* init report.data[0] */
+       .buf = set_rep_data,
+};
+
+struct hidhost_send_data_data {
+       struct ipc_hdr hdr;
+       struct hal_cmd_hidhost_send_data hiddata;
+
+       /* data placeholder for hal_cmd_hidhost_send_data.data[0] field */
+       uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) -
+                               sizeof(struct hal_cmd_hidhost_send_data)];
+} __attribute__((packed));
+
+#define send_data_data "1234567890"
+
+static struct hidhost_send_data_data hidhost_send_data_overs = {
+       .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+       .hdr.opcode = HAL_OP_HIDHOST_SEND_DATA,
+       .hdr.len = sizeof(struct hal_cmd_hidhost_send_data) +
+                                                       sizeof(send_data_data),
+
+       /* declare wrong descriptor length */
+       .hiddata.len = sizeof(send_data_data) + 1,
+       /* init .hiddata.data[0] */
+       .buf = send_data_data,
+};
+
+static struct hidhost_send_data_data hidhost_send_data_unders = {
+       .hdr.service_id = HAL_SERVICE_ID_HIDHOST,
+       .hdr.opcode = HAL_OP_HIDHOST_SEND_DATA,
+       .hdr.len = sizeof(struct hal_cmd_hidhost_send_data) +
+                                                       sizeof(send_data_data),
+
+       /* declare wrong descriptor length */
+       .hiddata.len = sizeof(send_data_data) - 1,
+       /* init .hiddata.data[0] */
+       .buf = send_data_data,
+};
+
+int main(int argc, char *argv[])
+{
+       snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0]));
+
+       tester_init(&argc, &argv);
+
+       /* check general IPC errors */
+       test_generic("Too small data",
+                               ipc_send_tc, setup, teardown,
+                               &register_bt_msg, 1);
+
+       test_generic("Malformed data (wrong payload declared)",
+                               ipc_send_tc, setup, teardown,
+                               &register_bt_malformed_size_msg,
+                               sizeof(register_bt_malformed_size_msg),
+                               HAL_SERVICE_ID_BLUETOOTH);
+
+       test_generic("Malformed data2 (undersized msg)",
+                               ipc_send_tc, setup, teardown,
+                               &register_bt_msg,
+                               sizeof(register_bt_msg) - 1,
+                               HAL_SERVICE_ID_BLUETOOTH);
+
+       test_generic("Malformed data3 (oversized msg)",
+                               ipc_send_tc, setup, teardown,
+                               &malformed_data3_msg,
+                               sizeof(malformed_data3_msg),
+                               HAL_SERVICE_ID_BLUETOOTH);
+
+       test_generic("Invalid service",
+                               ipc_send_tc, setup, teardown,
+                               &enable_unknown_service_hdr,
+                               sizeof(enable_unknown_service_hdr),
+                               HAL_SERVICE_ID_BLUETOOTH);
+
+       test_generic("Enable unregistered service",
+                               ipc_send_tc, setup, teardown,
+                               &enable_bt_service_hdr,
+                               sizeof(enable_bt_service_hdr));
+
+       /* check service handler's max opcode value */
+       test_opcode_valid("CORE", HAL_SERVICE_ID_CORE, 0x03, 0);
+
+       test_opcode_valid("BLUETOOTH", HAL_SERVICE_ID_BLUETOOTH, 0x15, 0,
+                       HAL_SERVICE_ID_BLUETOOTH);
+
+       test_opcode_valid("SOCK", HAL_SERVICE_ID_SOCKET, 0x03, 0,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+
+       test_opcode_valid("HIDHOST", HAL_SERVICE_ID_HIDHOST, 0x10, 0,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+
+       test_opcode_valid("PAN", HAL_SERVICE_ID_PAN, 0x05, 0,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+
+       test_opcode_valid("A2DP", HAL_SERVICE_ID_A2DP, 0x03, 0,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+
+       /* check for valid data size */
+       test_datasize_valid("CORE Register+", HAL_SERVICE_ID_CORE,
+                       HAL_OP_REGISTER_MODULE,
+                       sizeof(struct hal_cmd_register_module), 1);
+       test_datasize_valid("CORE Register-", HAL_SERVICE_ID_CORE,
+                       HAL_OP_REGISTER_MODULE,
+                       sizeof(struct hal_cmd_register_module), -1);
+       test_datasize_valid("CORE Unregister+", HAL_SERVICE_ID_CORE,
+                       HAL_OP_UNREGISTER_MODULE,
+                       sizeof(struct hal_cmd_unregister_module), 1);
+       test_datasize_valid("CORE Unregister-", HAL_SERVICE_ID_CORE,
+                       HAL_OP_UNREGISTER_MODULE,
+                       sizeof(struct hal_cmd_unregister_module), -1);
+
+       /* check for valid data size for BLUETOOTH */
+       test_datasize_valid("BT Enable+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_ENABLE,
+                       0, 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Disable+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_DISABLE,
+                       0, 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Adapter Props+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_ADAPTER_PROPS,
+                       0, 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_ADAPTER_PROP,
+                       sizeof(struct hal_cmd_get_adapter_prop), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_ADAPTER_PROP,
+                       sizeof(struct hal_cmd_get_adapter_prop), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Set Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_SET_ADAPTER_PROP,
+                       sizeof(struct hal_cmd_set_adapter_prop), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Set Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_SET_ADAPTER_PROP,
+                       sizeof(struct hal_cmd_set_adapter_prop), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_generic("Data size BT Set Adapter Prop Vardata+",
+                       ipc_send_tc, setup, teardown,
+                       &bt_set_adapter_prop_data_overs,
+                       (sizeof(struct ipc_hdr) +
+                               sizeof(struct hal_cmd_set_adapter_prop) +
+                               sizeof(set_name)),
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_generic("Data size BT Set Adapter Prop Vardata+",
+                       ipc_send_tc, setup, teardown,
+                       &bt_set_adapter_prop_data_unders,
+                       (sizeof(struct ipc_hdr) +
+                               sizeof(struct hal_cmd_set_adapter_prop) +
+                               sizeof(set_name)),
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Remote Props+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_REMOTE_DEVICE_PROPS,
+                       sizeof(struct hal_cmd_get_remote_device_props), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Remote Props-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_REMOTE_DEVICE_PROPS,
+                       sizeof(struct hal_cmd_get_remote_device_props), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Remote Prop+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_REMOTE_DEVICE_PROP,
+                       sizeof(struct hal_cmd_get_remote_device_prop), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Remote Prop-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_REMOTE_DEVICE_PROP,
+                       sizeof(struct hal_cmd_get_remote_device_prop), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Set Remote Prop+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_SET_REMOTE_DEVICE_PROP,
+                       sizeof(struct hal_cmd_set_remote_device_prop), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Set Remote Prop-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_SET_REMOTE_DEVICE_PROP,
+                       sizeof(struct hal_cmd_set_remote_device_prop), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_generic("Data size BT Set Remote Prop Vardata+",
+                       ipc_send_tc, setup, teardown,
+                       &bt_set_remote_prop_data_overs,
+                       (sizeof(struct ipc_hdr) +
+                               sizeof(struct hal_cmd_set_remote_device_prop) +
+                               sizeof(set_name)),
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_generic("Data size BT Set Remote Prop Vardata-",
+                       ipc_send_tc, setup, teardown,
+                       &bt_set_remote_prop_data_unders,
+                       (sizeof(struct ipc_hdr) +
+                               sizeof(struct hal_cmd_set_remote_device_prop) +
+                               sizeof(set_name)),
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Remote SV Rec+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_REMOTE_SERVICE_REC,
+                       sizeof(struct hal_cmd_get_remote_service_rec), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Remote SV Rec-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_REMOTE_SERVICE_REC,
+                       sizeof(struct hal_cmd_get_remote_service_rec), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Remote Services+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_REMOTE_SERVICES,
+                       sizeof(struct hal_cmd_get_remote_services), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Get Remote Services-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_GET_REMOTE_SERVICES,
+                       sizeof(struct hal_cmd_get_remote_services), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Start Discovery+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_START_DISCOVERY,
+                       0, 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Cancel Discovery+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_CANCEL_DISCOVERY,
+                       0, 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Create Bond+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_CREATE_BOND,
+                       sizeof(struct hal_cmd_create_bond), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Create Bond-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_CREATE_BOND,
+                       sizeof(struct hal_cmd_create_bond), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Remove Bond+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_REMOVE_BOND,
+                       sizeof(struct hal_cmd_remove_bond), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Remove Bond-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_REMOVE_BOND,
+                       sizeof(struct hal_cmd_remove_bond), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Cancel Bond+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_CANCEL_BOND,
+                       sizeof(struct hal_cmd_cancel_bond), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Cancel Bond-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_CANCEL_BOND,
+                       sizeof(struct hal_cmd_cancel_bond), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Pin Reply+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_PIN_REPLY,
+                       sizeof(struct hal_cmd_pin_reply), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT Pin Reply-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_PIN_REPLY,
+                       sizeof(struct hal_cmd_pin_reply), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT SSP Reply+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_SSP_REPLY,
+                       sizeof(struct hal_cmd_ssp_reply), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT SSP Reply-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_SSP_REPLY,
+                       sizeof(struct hal_cmd_ssp_reply), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT DUT Mode Conf+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_DUT_MODE_CONF,
+                       sizeof(struct hal_cmd_dut_mode_conf), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT DUT Mode Conf-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_DUT_MODE_CONF,
+                       sizeof(struct hal_cmd_dut_mode_conf), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT DUT Mode Send+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_DUT_MODE_SEND,
+                       sizeof(struct hal_cmd_dut_mode_send), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT DUT Mode Send-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_DUT_MODE_SEND,
+                       sizeof(struct hal_cmd_dut_mode_send), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT LE Test+", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_LE_TEST_MODE,
+                       sizeof(struct hal_cmd_le_test_mode), 1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+       test_datasize_valid("BT LE Test-", HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_OP_LE_TEST_MODE,
+                       sizeof(struct hal_cmd_le_test_mode), -1,
+                       HAL_SERVICE_ID_BLUETOOTH);
+
+       /* check for valid data size for SOCK */
+       test_datasize_valid("SOCKET Listen+", HAL_SERVICE_ID_SOCKET,
+                       HAL_OP_SOCKET_LISTEN,
+                       sizeof(struct hal_cmd_socket_listen), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+       test_datasize_valid("SOCKET Listen-", HAL_SERVICE_ID_SOCKET,
+                       HAL_OP_SOCKET_LISTEN,
+                       sizeof(struct hal_cmd_socket_listen), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+       test_datasize_valid("SOCKET Connect+", HAL_SERVICE_ID_SOCKET,
+                       HAL_OP_SOCKET_CONNECT,
+                       sizeof(struct hal_cmd_socket_connect), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+       test_datasize_valid("SOCKET Connect-", HAL_SERVICE_ID_SOCKET,
+                       HAL_OP_SOCKET_CONNECT,
+                       sizeof(struct hal_cmd_socket_connect), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET);
+
+       /* check for valid data size for HID Host */
+       test_datasize_valid("HIDHOST Connect+", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_CONNECT,
+                       sizeof(struct hal_cmd_hidhost_connect), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Connect-", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_CONNECT,
+                       sizeof(struct hal_cmd_hidhost_connect), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Disconnect+", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_DISCONNECT,
+                       sizeof(struct hal_cmd_hidhost_disconnect), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Disconnect-", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_DISCONNECT,
+                       sizeof(struct hal_cmd_hidhost_disconnect), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Virt. Unplug+", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+                       sizeof(struct hal_cmd_hidhost_virtual_unplug), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Virt. Unplug-", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+                       sizeof(struct hal_cmd_hidhost_virtual_unplug), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Set Info+", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_SET_INFO,
+                       sizeof(struct hal_cmd_hidhost_set_info), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Set Info-", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_SET_INFO,
+                       sizeof(struct hal_cmd_hidhost_set_info), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_generic("Data size HIDHOST Set Info Vardata+",
+                       ipc_send_tc, setup, teardown,
+                       &hidhost_set_info_data_overs,
+                       (sizeof(struct ipc_hdr) +
+                               sizeof(struct hal_cmd_hidhost_set_info) +
+                               sizeof(set_info_data)),
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_generic("Data size HIDHOST Set Info Vardata-",
+                       ipc_send_tc, setup, teardown,
+                       &hidhost_set_info_data_unders,
+                       (sizeof(struct ipc_hdr) +
+                               sizeof(struct hal_cmd_hidhost_set_info) +
+                               sizeof(set_info_data)),
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Get Protocol+", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_GET_PROTOCOL,
+                       sizeof(struct hal_cmd_hidhost_get_protocol), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Get Protocol-", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_GET_PROTOCOL,
+                       sizeof(struct hal_cmd_hidhost_get_protocol), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Set Protocol+", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_SET_PROTOCOL,
+                       sizeof(struct hal_cmd_hidhost_set_protocol), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Set Protocol-", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_SET_PROTOCOL,
+                       sizeof(struct hal_cmd_hidhost_set_protocol), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Get Report+", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_GET_REPORT,
+                       sizeof(struct hal_cmd_hidhost_get_report), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Get Report-", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_GET_REPORT,
+                       sizeof(struct hal_cmd_hidhost_get_report), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Set Report+", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_SET_REPORT,
+                       sizeof(struct hal_cmd_hidhost_set_report), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Set Report-", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_SET_REPORT,
+                       sizeof(struct hal_cmd_hidhost_set_report), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_generic("Data size HIDHOST Set Report Vardata+",
+                       ipc_send_tc, setup, teardown,
+                       &hidhost_set_report_data_overs,
+                       (sizeof(struct ipc_hdr) +
+                               sizeof(struct hal_cmd_hidhost_set_report) +
+                               sizeof(set_rep_data)),
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_generic("Data size HIDHOST Set Report Vardata-",
+                       ipc_send_tc, setup, teardown,
+                       &hidhost_set_report_data_unders,
+                       (sizeof(struct ipc_hdr) +
+                               sizeof(struct hal_cmd_hidhost_set_report) +
+                               sizeof(set_rep_data)),
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Send Data+", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_SEND_DATA,
+                       sizeof(struct hal_cmd_hidhost_send_data), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_datasize_valid("HIDHOST Send Data-", HAL_SERVICE_ID_HIDHOST,
+                       HAL_OP_HIDHOST_SEND_DATA,
+                       sizeof(struct hal_cmd_hidhost_send_data), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_generic("Data size HIDHOST Send Vardata+",
+                       ipc_send_tc, setup, teardown,
+                       &hidhost_send_data_overs,
+                       (sizeof(struct ipc_hdr) +
+                               sizeof(struct hal_cmd_hidhost_send_data) +
+                               sizeof(send_data_data)),
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+       test_generic("Data size HIDHOST Send Vardata-",
+                       ipc_send_tc, setup, teardown,
+                       &hidhost_send_data_unders,
+                       (sizeof(struct ipc_hdr) +
+                               sizeof(struct hal_cmd_hidhost_send_data) +
+                               sizeof(send_data_data)),
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST);
+
+       /* check for valid data size for PAN */
+       test_datasize_valid("PAN Enable+", HAL_SERVICE_ID_PAN,
+                       HAL_OP_PAN_ENABLE,
+                       sizeof(struct hal_cmd_pan_enable), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+       test_datasize_valid("PAN Enable-", HAL_SERVICE_ID_PAN,
+                       HAL_OP_PAN_ENABLE,
+                       sizeof(struct hal_cmd_pan_enable), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+       test_datasize_valid("PAN Get Role+", HAL_SERVICE_ID_PAN,
+                       HAL_OP_PAN_GET_ROLE,
+                       0, 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+       test_datasize_valid("PAN Connect+", HAL_SERVICE_ID_PAN,
+                       HAL_OP_PAN_CONNECT,
+                       sizeof(struct hal_cmd_pan_connect), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+       test_datasize_valid("PAN Connect-", HAL_SERVICE_ID_PAN,
+                       HAL_OP_PAN_CONNECT,
+                       sizeof(struct hal_cmd_pan_connect), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+       test_datasize_valid("PAN Disconnect+", HAL_SERVICE_ID_PAN,
+                       HAL_OP_PAN_DISCONNECT,
+                       sizeof(struct hal_cmd_pan_disconnect), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+       test_datasize_valid("PAN Disconnect-", HAL_SERVICE_ID_PAN,
+                       HAL_OP_PAN_DISCONNECT,
+                       sizeof(struct hal_cmd_pan_disconnect), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN);
+
+       /* check for valid data size for A2DP */
+       test_datasize_valid("A2DP Connect+", HAL_SERVICE_ID_A2DP,
+                       HAL_OP_A2DP_CONNECT,
+                       sizeof(struct hal_cmd_a2dp_connect), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+       test_datasize_valid("A2DP Connect-", HAL_SERVICE_ID_A2DP,
+                       HAL_OP_A2DP_CONNECT,
+                       sizeof(struct hal_cmd_a2dp_connect), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+       test_datasize_valid("A2DP Disconnect+", HAL_SERVICE_ID_A2DP,
+                       HAL_OP_A2DP_DISCONNECT,
+                       sizeof(struct hal_cmd_a2dp_disconnect), 1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+       test_datasize_valid("A2DP Disconnect-", HAL_SERVICE_ID_A2DP,
+                       HAL_OP_A2DP_DISCONNECT,
+                       sizeof(struct hal_cmd_a2dp_disconnect), -1,
+                       HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP);
+
+       return tester_run();
+}
index 6f940cd..8cd34ea 100644 (file)
@@ -2,21 +2,21 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 <signal.h>
 #include <stdbool.h>
 #include <sys/socket.h>
-#include <sys/socket.h>
 #include <sys/un.h>
 #include <unistd.h>
 #include <glib.h>
 
-#include "hal-msg.h"
+#include "ipc-common.h"
 #include "ipc.h"
-#include "log.h"
+#include "src/log.h"
 
-struct service_handler {
-       const struct ipc_handler *handler;
-       uint8_t size;
+struct ipc {
+       struct service_handler *services;
+       int service_max;
+
+       const char *path;
+       size_t size;
+
+       GIOChannel *cmd_io;
+       guint cmd_watch;
+
+       bool notifications;
+       GIOChannel *notif_io;
+       guint notif_watch;
+
+       ipc_disconnect_cb disconnect_cb;
+       void *disconnect_cb_data;
 };
 
-static struct service_handler services[HAL_SERVICE_ID_MAX + 1];
+static void ipc_disconnect(struct ipc *ipc, bool in_cleanup)
+{
+       if (ipc->cmd_watch) {
+               g_source_remove(ipc->cmd_watch);
+               ipc->cmd_watch = 0;
+       }
 
-static GIOChannel *cmd_io = NULL;
-static GIOChannel *notif_io = NULL;
+       if (ipc->cmd_io) {
+               g_io_channel_shutdown(ipc->cmd_io, TRUE, NULL);
+               g_io_channel_unref(ipc->cmd_io);
+               ipc->cmd_io = NULL;
+       }
+
+       if (ipc->notif_watch) {
+               g_source_remove(ipc->notif_watch);
+               ipc->notif_watch = 0;
+       }
 
-static void ipc_handle_msg(const void *buf, ssize_t len)
+       if (ipc->notif_io) {
+               g_io_channel_shutdown(ipc->notif_io, TRUE, NULL);
+               g_io_channel_unref(ipc->notif_io);
+               ipc->notif_io = NULL;
+       }
+
+       if (in_cleanup)
+               return;
+
+       if (ipc->disconnect_cb)
+               ipc->disconnect_cb(ipc->disconnect_cb_data);
+}
+
+static int ipc_handle_msg(struct service_handler *handlers, size_t max_index,
+                                               const void *buf, ssize_t len)
 {
-       const struct hal_hdr *msg = buf;
+       const struct ipc_hdr *msg = buf;
        const struct ipc_handler *handler;
 
        if (len < (ssize_t) sizeof(*msg)) {
-               error("IPC: message too small (%zd bytes), terminating", len);
-               raise(SIGTERM);
-               return;
+               DBG("message too small (%zd bytes)", len);
+               return -EBADMSG;
        }
 
        if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
-               error("IPC: message malformed (%zd bytes), terminating", len);
-               raise(SIGTERM);
-               return;
+               DBG("message malformed (%zd bytes)", len);
+               return -EBADMSG;
        }
 
        /* if service is valid */
-       if (msg->service_id > HAL_SERVICE_ID_MAX) {
-               error("IPC: unknown service (0x%x), terminating",
-                                                       msg->service_id);
-               raise(SIGTERM);
-               return;
+       if (msg->service_id > max_index) {
+               DBG("unknown service (0x%x)", msg->service_id);
+               return -EOPNOTSUPP;
        }
 
        /* if service is registered */
-       if (!services[msg->service_id].handler) {
-               error("IPC: unregistered service (0x%x), terminating",
-                                                       msg->service_id);
-               raise(SIGTERM);
-               return;
+       if (!handlers[msg->service_id].handler) {
+               DBG("service not registered (0x%x)", msg->service_id);
+               return -EOPNOTSUPP;
        }
 
        /* if opcode is valid */
-       if (msg->opcode == HAL_OP_STATUS ||
-                       msg->opcode > services[msg->service_id].size) {
-               error("IPC: invalid opcode 0x%x for service 0x%x, terminating",
-                                               msg->opcode, msg->service_id);
-               raise(SIGTERM);
-               return;
+       if (msg->opcode == IPC_OP_STATUS ||
+                       msg->opcode > handlers[msg->service_id].size) {
+               DBG("invalid opcode 0x%x for service 0x%x", msg->opcode,
+                                                       msg->service_id);
+               return -EOPNOTSUPP;
        }
 
        /* opcode is table offset + 1 */
-       handler = &services[msg->service_id].handler[msg->opcode - 1];
+       handler = &handlers[msg->service_id].handler[msg->opcode - 1];
 
        /* if payload size is valid */
        if ((handler->var_len && handler->data_len > msg->len) ||
                        (!handler->var_len && handler->data_len != msg->len)) {
-               error("IPC: size invalid opcode 0x%x service 0x%x, terminating",
-                                               msg->service_id, msg->opcode);
-               raise(SIGTERM);
-               return;
+               DBG("invalid size for opcode 0x%x service 0x%x",
+                                               msg->opcode, msg->service_id);
+               return -EMSGSIZE;
        }
 
        handler->handler(msg->payload, msg->len);
+
+       return 0;
 }
 
 static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
                                                        gpointer user_data)
 {
-       char buf[BLUEZ_HAL_MTU];
+       struct ipc *ipc = user_data;
+
+       char buf[IPC_MTU];
        ssize_t ret;
-       int fd;
+       int fd, err;
 
        if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               info("IPC: command socket closed, terminating");
+               info("IPC: command socket closed");
+
+               ipc->cmd_watch = 0;
                goto fail;
        }
 
@@ -124,29 +161,40 @@ static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
 
        ret = read(fd, buf, sizeof(buf));
        if (ret < 0) {
-               error("IPC: command read failed, terminating (%s)",
-                                                       strerror(errno));
+               error("IPC: command read failed (%s)", strerror(errno));
+               goto fail;
+       }
+
+       err = ipc_handle_msg(ipc->services, ipc->service_max, buf, ret);
+       if (err < 0) {
+               error("IPC: failed to handle message (%s)", strerror(-err));
                goto fail;
        }
 
-       ipc_handle_msg(buf, ret);
        return TRUE;
 
 fail:
-       raise(SIGTERM);
+       ipc_disconnect(ipc, false);
+
        return FALSE;
 }
 
 static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond,
                                                        gpointer user_data)
 {
-       info("IPC: notification socket closed, terminating");
-       raise(SIGTERM);
+       struct ipc *ipc = user_data;
+
+       info("IPC: notification socket closed");
+
+       ipc->notif_watch = 0;
+
+       ipc_disconnect(ipc, false);
 
        return FALSE;
 }
 
-static GIOChannel *connect_hal(GIOFunc connect_cb)
+static GIOChannel *ipc_connect(const char *path, size_t size,
+                                       GIOFunc connect_cb, void *user_data)
 {
        struct sockaddr_un addr;
        GIOCondition cond;
@@ -168,18 +216,13 @@ static GIOChannel *connect_hal(GIOFunc connect_cb)
        memset(&addr, 0, sizeof(addr));
        addr.sun_family = AF_UNIX;
 
-       memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+       memcpy(addr.sun_path, path, size);
 
-       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               error("IPC: failed to connect HAL socket: %d (%s)", errno,
-                                                       strerror(errno));
-               g_io_channel_unref(io);
-               return NULL;
-       }
+       connect(sk, (struct sockaddr *) &addr, sizeof(addr));
 
        cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
 
-       g_io_add_watch(io, cond, connect_cb, NULL);
+       g_io_add_watch(io, cond, connect_cb, user_data);
 
        return io;
 }
@@ -187,23 +230,27 @@ static GIOChannel *connect_hal(GIOFunc connect_cb)
 static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond,
                                                        gpointer user_data)
 {
+       struct ipc *ipc = user_data;
+
        DBG("");
 
        if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               error("IPC: notification socket connect failed, terminating");
-               raise(SIGTERM);
+               error("IPC: notification socket connect failed");
+
+               ipc_disconnect(ipc, false);
+
                return FALSE;
        }
 
        cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL;
 
-       g_io_add_watch(io, cond, notif_watch_cb, NULL);
+       ipc->notif_watch = g_io_add_watch(io, cond, notif_watch_cb, ipc);
 
        cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
 
-       g_io_add_watch(cmd_io, cond, cmd_watch_cb, NULL);
+       ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc);
 
-       info("IPC: successfully connected");
+       info("IPC: successfully connected (with notifications)");
 
        return FALSE;
 }
@@ -211,41 +258,70 @@ static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond,
 static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
                                                        gpointer user_data)
 {
+       struct ipc *ipc = user_data;
+
        DBG("");
 
        if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               error("IPC: command socket connect failed, terminating");
-               raise(SIGTERM);
+               error("IPC: command socket connect failed");
+               ipc_disconnect(ipc, false);
+
                return FALSE;
        }
 
-       notif_io = connect_hal(notif_connect_cb);
-       if (!notif_io)
-               raise(SIGTERM);
+       if (ipc->notifications) {
+               ipc->notif_io = ipc_connect(ipc->path, ipc->size,
+                                                       notif_connect_cb, ipc);
+               if (!ipc->notif_io)
+                       ipc_disconnect(ipc, false);
+
+               return FALSE;
+       }
+
+       cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+       ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc);
+
+       info("IPC: successfully connected (without notifications)");
 
        return FALSE;
 }
 
-void ipc_init(void)
+struct ipc *ipc_init(const char *path, size_t size, int max_service_id,
+                                       bool notifications,
+                                       ipc_disconnect_cb cb, void *cb_data)
 {
-       cmd_io = connect_hal(cmd_connect_cb);
-       if (!cmd_io)
-               raise(SIGTERM);
+       struct ipc *ipc;
+
+       ipc = g_new0(struct ipc, 1);
+
+       ipc->services = g_new0(struct service_handler, max_service_id + 1);
+       ipc->service_max = max_service_id;
+
+       ipc->path = path;
+       ipc->size = size;
+
+       ipc->notifications = notifications;
+
+       ipc->cmd_io = ipc_connect(path, size, cmd_connect_cb, ipc);
+       if (!ipc->cmd_io) {
+               g_free(ipc->services);
+               g_free(ipc);
+               return NULL;
+       }
+
+       ipc->disconnect_cb = cb;
+       ipc->disconnect_cb_data = cb_data;
+
+       return ipc;
 }
 
-void ipc_cleanup(void)
+void ipc_cleanup(struct ipc *ipc)
 {
-       if (cmd_io) {
-               g_io_channel_shutdown(cmd_io, TRUE, NULL);
-               g_io_channel_unref(cmd_io);
-               cmd_io = NULL;
-       }
+       ipc_disconnect(ipc, true);
 
-       if (notif_io) {
-               g_io_channel_shutdown(notif_io, TRUE, NULL);
-               g_io_channel_unref(notif_io);
-               notif_io = NULL;
-       }
+       g_free(ipc->services);
+       g_free(ipc);
 }
 
 static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
@@ -253,7 +329,7 @@ static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
 {
        struct msghdr msg;
        struct iovec iv[2];
-       struct hal_hdr m;
+       struct ipc_hdr m;
        char cmsgbuf[CMSG_SPACE(sizeof(int))];
        struct cmsghdr *cmsg;
 
@@ -288,54 +364,63 @@ static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
        }
 
        if (sendmsg(sk, &msg, 0) < 0) {
-               error("IPC send failed, terminating :%s", strerror(errno));
+               error("IPC send failed :%s", strerror(errno));
+
+               /* TODO disconnect IPC here when this function becomes static */
                raise(SIGTERM);
        }
 }
 
-void ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status)
+void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+                                                               uint8_t status)
 {
-       struct hal_status s;
+       struct ipc_status s;
        int sk;
 
-       sk = g_io_channel_unix_get_fd(cmd_io);
+       sk = g_io_channel_unix_get_fd(ipc->cmd_io);
 
-       if (status == HAL_STATUS_SUCCESS) {
+       if (status == IPC_STATUS_SUCCESS) {
                ipc_send(sk, service_id, opcode, 0, NULL, -1);
                return;
        }
 
        s.code = status;
 
-       ipc_send(sk, service_id, HAL_OP_STATUS, sizeof(s), &s, -1);
+       ipc_send(sk, service_id, IPC_OP_STATUS, sizeof(s), &s, -1);
 }
 
-void ipc_send_rsp_full(uint8_t service_id, uint8_t opcode, uint16_t len,
-                                                       void *param, int fd)
+void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+                                       uint16_t len, void *param, int fd)
 {
-       ipc_send(g_io_channel_unix_get_fd(cmd_io), service_id, opcode, len,
+       ipc_send(g_io_channel_unix_get_fd(ipc->cmd_io), service_id, opcode, len,
                                                                param, fd);
 }
 
-void ipc_send_notif(uint8_t service_id, uint8_t opcode,  uint16_t len,
-                                                               void *param)
+void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+                                               uint16_t len, void *param)
 {
-       if (!notif_io)
+       if (!ipc || !ipc->notif_io)
                return;
 
-       ipc_send(g_io_channel_unix_get_fd(notif_io), service_id, opcode, len,
-                                                               param, -1);
+       ipc_send(g_io_channel_unix_get_fd(ipc->notif_io), service_id, opcode,
+                                                               len, param, -1);
 }
 
-void ipc_register(uint8_t service, const struct ipc_handler *handlers,
-                                                               uint8_t size)
+void ipc_register(struct ipc *ipc, uint8_t service,
+                       const struct ipc_handler *handlers, uint8_t size)
 {
-       services[service].handler = handlers;
-       services[service].size = size;
+       if (service > ipc->service_max)
+               return;
+
+       ipc->services[service].handler = handlers;
+       ipc->services[service].size = size;
 }
 
-void ipc_unregister(uint8_t service)
+void ipc_unregister(struct ipc *ipc, uint8_t service)
 {
-       services[service].handler = NULL;
-       services[service].size = 0;
+       if (service > ipc->service_max)
+               return;
+
+       ipc->services[service].handler = NULL;
+       ipc->services[service].size = 0;
 }
index 6cd102b..cc4e92d 100644 (file)
@@ -2,21 +2,21 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
@@ -26,14 +26,27 @@ struct ipc_handler {
        bool var_len;
        size_t data_len;
 };
-void ipc_init(void);
-void ipc_cleanup(void);
 
-void ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status);
-void ipc_send_rsp_full(uint8_t service_id, uint8_t opcode, uint16_t len,
-                                                       void *param, int fd);
-void ipc_send_notif(uint8_t service_id, uint8_t opcode,  uint16_t len,
-                                                               void *param);
-void ipc_register(uint8_t service, const struct ipc_handler *handlers,
-                                                               uint8_t size);
-void ipc_unregister(uint8_t service);
+struct service_handler {
+       const struct ipc_handler *handler;
+       uint8_t size;
+};
+
+struct ipc;
+
+typedef void (*ipc_disconnect_cb) (void *data);
+
+struct ipc *ipc_init(const char *path, size_t size, int max_service_id,
+                                       bool notifications,
+                                       ipc_disconnect_cb cb, void *cb_data);
+void ipc_cleanup(struct ipc *ipc);
+
+void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+                                                               uint8_t status);
+void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+                                       uint16_t len, void *param, int fd);
+void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode,
+                                               uint16_t len, void *param);
+void ipc_register(struct ipc *ipc, uint8_t service,
+                       const struct ipc_handler *handlers, uint8_t size);
+void ipc_unregister(struct ipc *ipc, uint8_t service);
index 5210b4b..0a0c150 100644 (file)
@@ -2,21 +2,21 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 #include <unistd.h>
 
 #include <sys/signalfd.h>
+#if defined(ANDROID)
+#include <sys/capability.h>
+#include <linux/prctl.h>
+#endif
 
 #include <glib.h>
 
-#include "log.h"
+#include "src/log.h"
 #include "src/sdpd.h"
 
 #include "lib/bluetooth.h"
 
+#include "ipc-common.h"
+#include "ipc.h"
 #include "bluetooth.h"
 #include "socket.h"
 #include "hidhost.h"
 #include "hal-msg.h"
-#include "ipc.h"
 #include "a2dp.h"
 #include "pan.h"
-
-/* TODO: Consider to remove PLATFORM_SDKVERSION check if requirement
-*  for minimal Android platform version increases. */
-#if defined(ANDROID) && PLATFORM_SDK_VERSION >= 18
-#include <sys/capability.h>
-#endif
+#include "avrcp.h"
+#include "handsfree.h"
+#include "gatt.h"
+#include "health.h"
 
 #define STARTUP_GRACE_SECONDS 5
 #define SHUTDOWN_GRACE_SECONDS 10
@@ -67,6 +70,8 @@ static bdaddr_t adapter_bdaddr;
 
 static GMainLoop *event_loop;
 
+static struct ipc *hal_ipc = NULL;
+
 static bool services[HAL_SERVICE_ID_MAX + 1] = { false };
 
 static void service_register(const void *buf, uint16_t len)
@@ -81,29 +86,60 @@ static void service_register(const void *buf, uint16_t len)
 
        switch (m->service_id) {
        case HAL_SERVICE_ID_BLUETOOTH:
-               bt_bluetooth_register();
+               if (!bt_bluetooth_register(hal_ipc, m->mode)) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
 
                break;
-       case HAL_SERVICE_ID_SOCK:
-               bt_socket_register(&adapter_bdaddr);
+       case HAL_SERVICE_ID_SOCKET:
+               bt_socket_register(hal_ipc, &adapter_bdaddr, m->mode);
 
                break;
        case HAL_SERVICE_ID_HIDHOST:
-               if (!bt_hid_register(&adapter_bdaddr)) {
+               if (!bt_hid_register(hal_ipc, &adapter_bdaddr, m->mode)) {
                        status = HAL_STATUS_FAILED;
                        goto failed;
                }
 
                break;
        case HAL_SERVICE_ID_A2DP:
-               if (!bt_a2dp_register(&adapter_bdaddr)) {
+               if (!bt_a2dp_register(hal_ipc, &adapter_bdaddr, m->mode)) {
                        status = HAL_STATUS_FAILED;
                        goto failed;
                }
 
                break;
        case HAL_SERVICE_ID_PAN:
-               if (!bt_pan_register(&adapter_bdaddr)) {
+               if (!bt_pan_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
+
+               break;
+       case HAL_SERVICE_ID_AVRCP:
+               if (!bt_avrcp_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
+
+               break;
+       case HAL_SERVICE_ID_HANDSFREE:
+               if (!bt_handsfree_register(hal_ipc, &adapter_bdaddr, m->mode)) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
+
+               break;
+       case HAL_SERVICE_ID_GATT:
+               if (!bt_gatt_register(hal_ipc, &adapter_bdaddr)) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
+
+               break;
+       case HAL_SERVICE_ID_HEALTH:
+               if (!bt_health_register(hal_ipc, &adapter_bdaddr, m->mode)) {
                        status = HAL_STATUS_FAILED;
                        goto failed;
                }
@@ -122,7 +158,8 @@ static void service_register(const void *buf, uint16_t len)
        info("Service ID=%u registered", m->service_id);
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+                                                               status);
 }
 
 static void service_unregister(const void *buf, uint16_t len)
@@ -139,7 +176,7 @@ static void service_unregister(const void *buf, uint16_t len)
        case HAL_SERVICE_ID_BLUETOOTH:
                bt_bluetooth_unregister();
                break;
-       case HAL_SERVICE_ID_SOCK:
+       case HAL_SERVICE_ID_SOCKET:
                bt_socket_unregister();
                break;
        case HAL_SERVICE_ID_HIDHOST:
@@ -151,9 +188,23 @@ static void service_unregister(const void *buf, uint16_t len)
        case HAL_SERVICE_ID_PAN:
                bt_pan_unregister();
                break;
+       case HAL_SERVICE_ID_AVRCP:
+               bt_avrcp_unregister();
+               break;
+       case HAL_SERVICE_ID_HANDSFREE:
+               bt_handsfree_unregister();
+               break;
+       case HAL_SERVICE_ID_GATT:
+               bt_gatt_unregister();
+               break;
+       case HAL_SERVICE_ID_HEALTH:
+               bt_health_unregister();
+               break;
        default:
-               /* This would indicate bug in HAL, as unregister should not be
-                * called in init failed */
+               /*
+                * This would indicate bug in HAL, as unregister should not be
+                * called in init failed
+                */
                DBG("service %u not supported", m->service_id);
                status = HAL_STATUS_FAILED;
                goto failed;
@@ -166,7 +217,8 @@ static void service_unregister(const void *buf, uint16_t len)
        info("Service ID=%u unregistered", m->service_id);
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
+                                                               status);
 }
 
 static const struct ipc_handler cmd_handlers[] = {
@@ -204,6 +256,11 @@ static void stop_bluetooth(void)
        g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, quit_eventloop, NULL);
 }
 
+static void ipc_disconnected(void *data)
+{
+       stop_bluetooth();
+}
+
 static void adapter_ready(int err, const bdaddr_t *addr)
 {
        if (err < 0) {
@@ -220,7 +277,16 @@ static void adapter_ready(int err, const bdaddr_t *addr)
 
        info("Adapter initialized");
 
-       ipc_init();
+       hal_ipc = ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH),
+                                               HAL_SERVICE_ID_MAX, true,
+                                               ipc_disconnected, NULL);
+       if (!hal_ipc) {
+               error("Failed to initialize IPC");
+               exit(EXIT_FAILURE);
+       }
+
+       ipc_register(hal_ipc, HAL_SERVICE_ID_CORE, cmd_handlers,
+                                               G_N_ELEMENTS(cmd_handlers));
 }
 
 static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
@@ -294,12 +360,19 @@ static guint setup_signalfd(void)
 
 static gboolean option_version = FALSE;
 static gint option_index = -1;
+static gboolean option_dbg = FALSE;
+static gboolean option_mgmt_dbg = FALSE;
 
 static GOptionEntry options[] = {
        { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
                                "Show version information and exit", NULL },
        { "index", 'i', 0, G_OPTION_ARG_INT, &option_index,
                                "Use specified controller", "INDEX"},
+       { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_dbg,
+                               "Enable debug logs", NULL},
+       { "mgmt-debug", 0, 0, G_OPTION_ARG_NONE, &option_mgmt_dbg,
+                               "Enable mgmt debug logs", NULL},
+
        { NULL }
 };
 
@@ -309,7 +382,7 @@ static void cleanup_services(void)
 
        DBG("");
 
-       for (i = HAL_SERVICE_ID_BLUETOOTH; i < HAL_SERVICE_ID_MAX; i++) {
+       for (i = HAL_SERVICE_ID_BLUETOOTH; i < HAL_SERVICE_ID_MAX + 1; i++) {
                if (!services[i])
                        continue;
 
@@ -317,7 +390,7 @@ static void cleanup_services(void)
                case HAL_SERVICE_ID_BLUETOOTH:
                        bt_bluetooth_unregister();
                        break;
-               case HAL_SERVICE_ID_SOCK:
+               case HAL_SERVICE_ID_SOCKET:
                        bt_socket_unregister();
                        break;
                case HAL_SERVICE_ID_HIDHOST:
@@ -326,9 +399,21 @@ static void cleanup_services(void)
                case HAL_SERVICE_ID_A2DP:
                        bt_a2dp_unregister();
                        break;
+               case HAL_SERVICE_ID_AVRCP:
+                       bt_avrcp_unregister();
+                       break;
                case HAL_SERVICE_ID_PAN:
                        bt_pan_unregister();
                        break;
+               case HAL_SERVICE_ID_HANDSFREE:
+                       bt_handsfree_unregister();
+                       break;
+               case HAL_SERVICE_ID_GATT:
+                       bt_gatt_unregister();
+                       break;
+               case HAL_SERVICE_ID_HEALTH:
+                       bt_health_unregister();
+                       break;
                }
 
                services[i] = false;
@@ -344,15 +429,29 @@ static bool set_capabilities(void)
        header.version = _LINUX_CAPABILITY_VERSION;
        header.pid = 0;
 
-       /* CAP_NET_ADMIN: Allow use of MGMT interface
+       /*
+        * CAP_NET_ADMIN: Allow use of MGMT interface
         * CAP_NET_BIND_SERVICE: Allow use of privileged PSM
-        * CAP_NET_RAW: Allow use of bnep ioctl calls */
+        * CAP_NET_RAW: Allow use of bnep ioctl calls
+        */
        cap.effective = cap.permitted =
                CAP_TO_MASK(CAP_NET_RAW) |
                CAP_TO_MASK(CAP_NET_ADMIN) |
                CAP_TO_MASK(CAP_NET_BIND_SERVICE);
        cap.inheritable = 0;
 
+       /* don't clear capabilities when dropping root */
+       if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+               error("%s: prctl(): %s", __func__, strerror(errno));
+               return false;
+       }
+
+       /* Android bluetooth user UID=1002 */
+       if (setuid(1002) < 0) {
+               error("%s: setuid(): %s", __func__, strerror(errno));
+               return false;
+       }
+
        /* TODO: Move to cap_set_proc once bionic support it */
        if (capset(&header, &cap) < 0) {
                error("%s: capset(): %s", __func__, strerror(errno));
@@ -402,7 +501,10 @@ int main(int argc, char *argv[])
        if (!signal)
                return EXIT_FAILURE;
 
-       __btd_log_init("*", 0);
+       if (option_dbg || option_mgmt_dbg)
+               __btd_log_init("*", 0);
+       else
+               __btd_log_init(NULL, 0);
 
        if (!set_capabilities()) {
                __btd_log_cleanup();
@@ -419,7 +521,7 @@ int main(int argc, char *argv[])
                return EXIT_FAILURE;
        }
 
-       if (!bt_bluetooth_start(option_index, adapter_ready)) {
+       if (!bt_bluetooth_start(option_index, option_mgmt_dbg, adapter_ready)) {
                __btd_log_cleanup();
                g_source_remove(bluetooth_start_timeout);
                g_source_remove(signal);
@@ -429,9 +531,6 @@ int main(int argc, char *argv[])
        /* Use params: mtu = 0, flags = 0 */
        start_sdp_server(0, 0);
 
-       ipc_register(HAL_SERVICE_ID_CORE, cmd_handlers,
-                                               G_N_ELEMENTS(cmd_handlers));
-
        DBG("Entering main loop");
 
        event_loop = g_main_loop_new(NULL, FALSE);
@@ -445,12 +544,15 @@ int main(int argc, char *argv[])
 
        cleanup_services();
 
-       ipc_cleanup();
        stop_sdp_server();
        bt_bluetooth_cleanup();
        g_main_loop_unref(event_loop);
 
-       ipc_unregister(HAL_SERVICE_ID_CORE);
+       /* If no adapter was initialized, hal_ipc is NULL */
+       if (hal_ipc) {
+               ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE);
+               ipc_cleanup(hal_ipc);
+       }
 
        info("Exit");
 
diff --git a/android/mcap-lib.c b/android/mcap-lib.c
new file mode 100644 (file)
index 0000000..b04eaac
--- /dev/null
@@ -0,0 +1,3177 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *  Copyright (C) 2010 Signove
+ *  Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "bluetooth/l2cap.h"
+#include "btio/btio.h"
+#include "src/log.h"
+
+#include "mcap-lib.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
+
+#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 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 */
+       uint8_t         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;
+};
+
+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 gboolean csp_caps_initialized = FALSE;
+struct csp_caps _caps;
+
+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 int 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 int 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;
+       int 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(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(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, &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, &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(const 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(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(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, 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, 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, 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);
+}
+
+
+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)
+{
+       /*
+        * FIXME: btd_adapter_read_clock(...) always return FALSE, current
+        * code doesn't support CSP (Clock Synchronization Protocol). To avoid
+        * build dependancy on struct 'btd_adapter', removing this code.
+        */
+
+       return FALSE;
+}
+
+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;
+       btres = 0;
+
+       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;
+
+       btclock = 0;
+       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;
+
+       btclock = 0;
+       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);
+       cur_btclock = 0;
+
+       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/android/mcap-lib.h b/android/mcap-lib.h
new file mode 100644 (file)
index 0000000..548d672
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *  Copyright (C) 2010 Signove
+ *  Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#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    65535
+
+/* 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)
+
+#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 */
+
+/*
+ * 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;
+
+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;
+
+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_csp;
+struct mcap_mdl_op_cb;
+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);
+
+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 */
+       int                     ref;                    /* Reference counter */
+
+       gboolean                csp_enabled;            /* CSP: functionality enabled */
+};
+
+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 */
+       int                     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 */
+};
+
+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 */
+       int                     ref;            /* References counter */
+};
+
+struct sync_info_ind_data {
+       uint32_t        btclock;
+       uint64_t        timestamp;
+       uint16_t        accuracy;
+};
+
+/************ 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(const 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);
+
+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);
index 6b098b2..9eab932 100644 (file)
@@ -1,19 +1,22 @@
 /*
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
  *
+ *  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.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
- *  This program is distributed in the hope that it will be useful,
+ *
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 #include <unistd.h>
 #include <fcntl.h>
 #include <glib.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <linux/if_bridge.h>
 
 #include "btio/btio.h"
 #include "lib/bluetooth.h"
 #include "lib/bnep.h"
 #include "lib/sdp.h"
 #include "lib/sdp_lib.h"
-#include "src/glib-helper.h"
+#include "src/uuid-helper.h"
 #include "profiles/network/bnep.h"
+#include "src/log.h"
 
-#include "log.h"
-#include "pan.h"
 #include "hal-msg.h"
+#include "ipc-common.h"
 #include "ipc.h"
 #include "utils.h"
 #include "bluetooth.h"
+#include "pan.h"
+
+#define SVC_HINT_NETWORKING 0x02
+
+#define BNEP_BRIDGE "bt-pan"
+#define BNEP_PANU_INTERFACE "bt-pan"
+#define BNEP_NAP_INTERFACE "bt-pan%d"
 
 static bdaddr_t adapter_addr;
 GSList *devices = NULL;
 uint8_t local_role = HAL_PAN_ROLE_NONE;
+static struct ipc *hal_ipc = NULL;
 
 struct pan_device {
        char            iface[16];
@@ -54,9 +73,124 @@ struct pan_device {
        uint8_t         conn_state;
        uint8_t         role;
        GIOChannel      *io;
+       struct bnep     *session;
        guint           watch;
 };
 
+static struct {
+       uint32_t        record_id;
+       GIOChannel      *io;
+       bool            bridge;
+} nap_dev = {
+       .record_id = 0,
+       .io = NULL,
+       .bridge = false,
+};
+
+static int set_forward_delay(int sk)
+{
+       unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0 , 0, 0 };
+       struct ifreq ifr;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, BNEP_BRIDGE, IFNAMSIZ);
+       ifr.ifr_data = (char *) args;
+
+       if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) {
+               error("pan: setting forward delay failed: %d (%s)",
+                                                       errno, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int nap_create_bridge(void)
+{
+       int sk, err;
+
+       DBG("%s", BNEP_BRIDGE);
+
+       if (nap_dev.bridge)
+               return 0;
+
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -EOPNOTSUPP;
+
+       if (ioctl(sk, SIOCBRADDBR, BNEP_BRIDGE) < 0) {
+               err = -errno;
+               if (err != -EEXIST) {
+                       close(sk);
+                       return -EOPNOTSUPP;
+               }
+       }
+
+       err = set_forward_delay(sk);
+       if (err < 0)
+               ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE);
+
+       close(sk);
+
+       nap_dev.bridge = err == 0;
+
+       return err;
+}
+
+static int bridge_if_down(void)
+{
+       struct ifreq ifr;
+       int sk, err;
+
+       sk = socket(AF_INET, SOCK_DGRAM, 0);
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, BNEP_BRIDGE, IF_NAMESIZE - 1);
+
+       ifr.ifr_flags &= ~IFF_UP;
+
+       /* Bring down the interface */
+       err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+
+       close(sk);
+
+       if (err < 0) {
+               error("pan: Could not bring down %s", BNEP_BRIDGE);
+               return err;
+       }
+
+       return 0;
+}
+
+static int nap_remove_bridge(void)
+{
+       int sk, err;
+
+       DBG("%s", BNEP_BRIDGE);
+
+       if (!nap_dev.bridge)
+               return 0;
+
+       bridge_if_down();
+
+       sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (sk < 0)
+               return -EOPNOTSUPP;
+
+       err = ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE);
+       if (err < 0)
+               err = -errno;
+
+       close(sk);
+
+       if (err < 0)
+               return err;
+
+       nap_dev.bridge = false;
+
+       return 0;
+}
+
 static int device_cmp(gconstpointer s, gconstpointer user_data)
 {
        const struct pan_device *dev = s;
@@ -65,24 +199,38 @@ static int device_cmp(gconstpointer s, gconstpointer user_data)
        return bacmp(&dev->dst, dst);
 }
 
-static void pan_device_free(struct pan_device *dev)
+static void pan_device_free(void *data)
 {
-       local_role = HAL_PAN_ROLE_NONE;
+       struct pan_device *dev = data;
 
        if (dev->watch > 0) {
+               bnep_server_delete(BNEP_BRIDGE, dev->iface, &dev->dst);
                g_source_remove(dev->watch);
-               dev->watch = 0;
        }
 
        if (dev->io) {
+               g_io_channel_shutdown(dev->io, FALSE, NULL);
                g_io_channel_unref(dev->io);
-               dev->io = NULL;
        }
 
-       devices = g_slist_remove(devices, dev);
+       if (dev->session)
+               bnep_free(dev->session);
+
        g_free(dev);
 }
 
+static void pan_device_remove(struct pan_device *dev)
+{
+       devices = g_slist_remove(devices, dev);
+
+       if (g_slist_length(devices) == 0) {
+               local_role = HAL_PAN_ROLE_NONE;
+               nap_remove_bridge();
+       }
+
+       pan_device_free(dev);
+}
+
 static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state)
 {
        struct hal_ev_pan_conn_state ev;
@@ -101,8 +249,10 @@ static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state)
        ev.remote_role = dev->role;
        ev.status = HAL_STATUS_SUCCESS;
 
-       ipc_send_notif(HAL_SERVICE_ID_PAN, HAL_EV_PAN_CONN_STATE, sizeof(ev),
-                                                                       &ev);
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CONN_STATE,
+                                                       sizeof(ev), &ev);
+       if (dev->conn_state == HAL_PAN_STATE_DISCONNECTED)
+               pan_device_remove(dev);
 }
 
 static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state)
@@ -114,27 +264,28 @@ static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state)
        ev.state = state;
        ev.local_role = local_role;
        ev.status = HAL_STATUS_SUCCESS;
+
        memset(ev.name, 0, sizeof(ev.name));
-       memcpy(ev.name, dev->iface, sizeof(dev->iface));
 
-       ipc_send_notif(HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE, sizeof(ev),
-                                                                       &ev);
+       if (local_role == HAL_PAN_ROLE_NAP)
+               memcpy(ev.name, BNEP_BRIDGE, sizeof(BNEP_BRIDGE));
+       else
+               memcpy(ev.name, dev->iface, sizeof(dev->iface));
+
+       ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE,
+                                                       sizeof(ev), &ev);
 }
 
-static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
-                                                               gpointer data)
+static void bnep_disconn_cb(void *data)
 {
        struct pan_device *dev = data;
 
        DBG("%s disconnected", dev->iface);
 
        bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
-       pan_device_free(dev);
-
-       return FALSE;
 }
 
-static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data)
+static void bnep_conn_cb(char *iface, int err, void *data)
 {
        struct pan_device *dev = data;
 
@@ -143,7 +294,6 @@ static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data)
        if (err < 0) {
                error("bnep connect req failed: %s", strerror(-err));
                bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
-               pan_device_free(dev);
                return;
        }
 
@@ -153,17 +303,12 @@ static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data)
 
        bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED);
        bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED);
-
-       dev->watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                                       bnep_watchdog_cb, dev);
-       g_io_channel_unref(dev->io);
-       dev->io = NULL;
 }
 
 static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
 {
        struct pan_device *dev = data;
-       uint16_t src, dst;
+       uint16_t l_role, r_role;
        int perr, sk;
 
        DBG("");
@@ -173,21 +318,33 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
                goto fail;
        }
 
-       src = (local_role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU;
-       dst = (dev->role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU;
+       l_role = (local_role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP :
+                                                               BNEP_SVC_PANU;
+       r_role = (dev->role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU;
+
        sk = g_io_channel_unix_get_fd(dev->io);
 
-       perr = bnep_connect(sk, src, dst, bnep_conn_cb, dev);
+       dev->session = bnep_new(sk, l_role, r_role, BNEP_PANU_INTERFACE);
+       if (!dev->session)
+               goto fail;
+
+       perr = bnep_connect(dev->session, bnep_conn_cb, dev);
        if (perr < 0) {
                error("bnep connect req failed: %s", strerror(-perr));
                goto fail;
        }
 
+       bnep_set_disconnect(dev->session, bnep_disconn_cb, dev);
+
+       if (dev->io) {
+               g_io_channel_unref(dev->io);
+               dev->io = NULL;
+       }
+
        return;
 
 fail:
        bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
-       pan_device_free(dev);
 }
 
 static void bt_pan_connect(const void *buf, uint16_t len)
@@ -256,10 +413,10 @@ static void bt_pan_connect(const void *buf, uint16_t len)
        devices = g_slist_append(devices, dev);
        bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING);
 
-       status =  HAL_STATUS_SUCCESS;
+       status = HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status);
 }
 
 static void bt_pan_disconnect(const void *buf, uint16_t len)
@@ -282,40 +439,251 @@ static void bt_pan_disconnect(const void *buf, uint16_t len)
 
        dev = l->data;
 
-       if (dev->watch) {
-               g_source_remove(dev->watch);
-               dev->watch = 0;
-       }
+       if (dev->conn_state == HAL_PAN_STATE_CONNECTED && dev->session)
+               bnep_disconnect(dev->session);
+
+       bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT,
+                                                                       status);
+}
+
+static gboolean nap_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct pan_device *dev = user_data;
 
-       bnep_if_down(dev->iface);
-       bnep_kill_connection(&dst);
+       DBG("disconnected");
 
        bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
-       pan_device_free(dev);
 
-       status = HAL_STATUS_SUCCESS;
+       return FALSE;
+}
+static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct pan_device *dev = user_data;
+       uint8_t packet[BNEP_MTU];
+       struct bnep_setup_conn_req *req = (void *) packet;
+       uint16_t src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED;
+       int sk, n, err;
+
+       if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+               error("Hangup or error or inval on BNEP socket");
+               return FALSE;
+       }
+
+       sk = g_io_channel_unix_get_fd(chan);
+
+       /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+       n = read(sk, packet, sizeof(packet));
+       if (n  < 0) {
+               error("read(): %s(%d)", strerror(errno), errno);
+               goto failed;
+       }
+
+       /* Highest known control command id BNEP_FILTER_MULT_ADDR_RSP 0x06 */
+       if (req->type == BNEP_CONTROL &&
+                       req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+               error("cmd not understood");
+               bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_CMD_NOT_UNDERSTOOD,
+                                                               req->ctrl);
+               goto failed;
+       }
+
+       if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) {
+               error("cmd is not BNEP_SETUP_CONN_REQ %02X %02X", req->type,
+                                                               req->ctrl);
+               goto failed;
+       }
+
+       rsp = bnep_setup_decode(req, &dst_role, &src_role);
+       if (rsp) {
+               error("bnep_setup_decode failed");
+               goto failed;
+       }
+
+       rsp = bnep_setup_chk(dst_role, src_role);
+       if (rsp) {
+               error("benp_setup_chk failed");
+               goto failed;
+       }
+
+       err = nap_create_bridge();
+       if (err < 0) {
+               error("pan: Failed to create bridge: %s (%d)", strerror(-err),
+                                                                       -err);
+               goto failed;
+       }
+
+       if (bnep_server_add(sk, dst_role, BNEP_BRIDGE, dev->iface,
+                                                       &dev->dst) < 0) {
+               nap_remove_bridge();
+               error("server_connadd failed");
+               rsp = BNEP_CONN_NOT_ALLOWED;
+               goto failed;
+       }
+
+       rsp = BNEP_SUCCESS;
+       bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
+
+       dev->watch = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                                       nap_watchdog_cb, dev);
+       g_io_channel_unref(dev->io);
+       dev->io = NULL;
+
+       bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED);
+       bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED);
+
+       return FALSE;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, status);
+       bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
+       pan_device_remove(dev);
+
+       return FALSE;
+}
+
+static void nap_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+       struct pan_device *dev = user_data;
+
+       DBG("");
+
+       if (err) {
+               error("%s", err->message);
+               bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+               return;
+       }
+
+       g_io_channel_set_close_on_unref(chan, TRUE);
+       dev->watch = g_io_add_watch(chan,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               nap_setup_cb, dev);
+}
+
+static void nap_confirm_cb(GIOChannel *chan, gpointer data)
+{
+       struct pan_device *dev;
+       bdaddr_t dst;
+       char address[18];
+       GError *err = NULL;
+
+       DBG("");
+
+       bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID);
+       if (err) {
+               error("%s", err->message);
+               g_error_free(err);
+               return;
+       }
+
+       DBG("incoming connect request from %s", address);
+       dev = g_new0(struct pan_device, 1);
+       bacpy(&dev->dst, &dst);
+       local_role = HAL_PAN_ROLE_NAP;
+       dev->role = HAL_PAN_ROLE_PANU;
+
+       strncpy(dev->iface, BNEP_NAP_INTERFACE, 16);
+       dev->iface[15] = '\0';
+
+       dev->io = g_io_channel_ref(chan);
+       g_io_channel_set_close_on_unref(dev->io, TRUE);
+
+       if (!bt_io_accept(dev->io, nap_connect_cb, dev, NULL, &err)) {
+               error("bt_io_accept: %s", err->message);
+               g_error_free(err);
+               goto failed;
+       }
+
+       devices = g_slist_append(devices, dev);
+       bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING);
+
+       return;
+
+failed:
+       bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+}
+
+static void destroy_nap_device(void)
+{
+       DBG("");
+
+       nap_remove_bridge();
+
+       if (nap_dev.io) {
+               g_io_channel_shutdown(nap_dev.io, FALSE, NULL);
+               g_io_channel_unref(nap_dev.io);
+               nap_dev.io = NULL;
+       }
+}
+
+static int register_nap_server(void)
+{
+       GError *gerr = NULL;
+
+       DBG("");
+
+       nap_dev.io = bt_io_listen(NULL, nap_confirm_cb, NULL, NULL, &gerr,
+                                       BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                                       BT_IO_OPT_PSM, BNEP_PSM,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                                       BT_IO_OPT_OMTU, BNEP_MTU,
+                                       BT_IO_OPT_IMTU, BNEP_MTU,
+                                       BT_IO_OPT_INVALID);
+
+       if (!nap_dev.io) {
+               destroy_nap_device();
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
 static void bt_pan_enable(const void *buf, uint16_t len)
 {
        const struct hal_cmd_pan_enable *cmd = buf;
        uint8_t status;
+       int err;
+
+       DBG("");
+
+       if (local_role == cmd->local_role) {
+               status = HAL_STATUS_SUCCESS;
+               goto reply;
+       }
+
+       /* destroy existing server */
+       destroy_nap_device();
 
        switch (cmd->local_role) {
-       case HAL_PAN_ROLE_PANU:
        case HAL_PAN_ROLE_NAP:
-               DBG("Not Implemented");
-               status  = HAL_STATUS_FAILED;
                break;
+       case HAL_PAN_ROLE_NONE:
+               status = HAL_STATUS_SUCCESS;
+               goto reply;
        default:
                status = HAL_STATUS_UNSUPPORTED;
-               break;
+               goto reply;
+       }
+
+       local_role = cmd->local_role;
+       err = register_nap_server();
+       if (err < 0) {
+               status = HAL_STATUS_FAILED;
+               destroy_nap_device();
+               goto reply;
        }
 
-       ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status);
+       status = HAL_STATUS_SUCCESS;
+
+reply:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status);
 }
 
 static void bt_pan_get_role(const void *buf, uint16_t len)
@@ -325,8 +693,8 @@ static void bt_pan_get_role(const void *buf, uint16_t len)
        DBG("");
 
        rsp.local_role = local_role;
-       ipc_send_rsp_full(HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, sizeof(rsp),
-                                                               &rsp, -1);
+       ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE,
+                                                       sizeof(rsp), &rsp, -1);
 }
 
 static const struct ipc_handler cmd_handlers[] = {
@@ -340,21 +708,121 @@ static const struct ipc_handler cmd_handlers[] = {
        { bt_pan_disconnect, false, sizeof(struct hal_cmd_pan_disconnect) },
 };
 
-bool bt_pan_register(const bdaddr_t *addr)
+static sdp_record_t *pan_record(void)
 {
+       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 = 0x0001, type = 0xfffe;
+       uint32_t rate = 0;
+       const char *desc = "Network Access Point", *name = "Network Service";
+       sdp_record_t *record;
+       uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806,  /* ARP */ };
+       sdp_data_t *head, *pseq, *data;
+
+       record = sdp_record_alloc();
+       if (!record)
+               return NULL;
+
+       record->attrlist = NULL;
+       record->pattern = NULL;
+
+       sdp_uuid16_create(&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, &type);
+       sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+                                                       SDP_UINT32, &rate);
+
+       sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+       root = sdp_list_append(NULL, &root_uuid);
+       sdp_set_browse_groups(record, root);
+
+       sdp_uuid16_create(&l2cap, L2CAP_UUID);
+       proto[0] = sdp_list_append(NULL, &l2cap);
+       p = sdp_data_alloc(SDP_UINT16, &psm);
+       proto[0] = sdp_list_append(proto[0], p);
+       apseq = sdp_list_append(NULL, proto[0]);
+
+       sdp_uuid16_create(&bnep, BNEP_UUID);
+       proto[1] = sdp_list_append(NULL, &bnep);
+       v = sdp_data_alloc(SDP_UINT16, &version);
+       proto[1] = sdp_list_append(proto[1], v);
+
+       head = sdp_data_alloc(SDP_UINT16, &ptype[0]);
+       data = sdp_data_alloc(SDP_UINT16, &ptype[1]);
+       sdp_seq_append(head, data);
+
+       pseq = sdp_data_alloc(SDP_SEQ16, head);
+       proto[1] = sdp_list_append(proto[1], pseq);
+       apseq = sdp_list_append(apseq, proto[1]);
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+       sdp_add_lang_attr(record);
+       sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security);
+
+       sdp_data_free(p);
+       sdp_data_free(v);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(root, NULL);
+       sdp_list_free(aproto, NULL);
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(svclass, NULL);
+       sdp_list_free(pfseq, NULL);
+
+       return record;
+}
+
+bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
+{
+       sdp_record_t *rec;
        int err;
 
        DBG("");
 
        bacpy(&adapter_addr, addr);
 
+       rec = pan_record();
+       if (!rec) {
+               error("Failed to allocate PAN record");
+               return false;
+       }
+
+       if (bt_adapter_add_record(rec, SVC_HINT_NETWORKING) < 0) {
+               error("Failed to register PAN record");
+               sdp_record_free(rec);
+               return false;
+       }
+
        err = bnep_init();
-       if (err) {
+       if (err < 0) {
                error("bnep init failed");
+               bt_adapter_remove_record(rec->handle);
+               return false;
+       }
+
+       err = register_nap_server();
+       if (err < 0) {
+               error("Failed to register NAP");
+               bt_adapter_remove_record(rec->handle);
+               bnep_cleanup();
                return false;
        }
 
-       ipc_register(HAL_SERVICE_ID_PAN, cmd_handlers,
+       nap_dev.record_id = rec->handle;
+
+       hal_ipc = ipc;
+       ipc_register(hal_ipc, HAL_SERVICE_ID_PAN, cmd_handlers,
                                                G_N_ELEMENTS(cmd_handlers));
 
        return true;
@@ -364,7 +832,16 @@ void bt_pan_unregister(void)
 {
        DBG("");
 
+       g_slist_free_full(devices, pan_device_free);
+       devices = NULL;
+       local_role = HAL_PAN_ROLE_NONE;
+
        bnep_cleanup();
 
-       ipc_unregister(HAL_SERVICE_ID_PAN);
+       ipc_unregister(hal_ipc, HAL_SERVICE_ID_PAN);
+       hal_ipc = NULL;
+
+       bt_adapter_remove_record(nap_dev.record_id);
+       nap_dev.record_id = 0;
+       destroy_nap_device();
 }
index 3178d88..cfbea96 100644 (file)
@@ -2,24 +2,24 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
-bool bt_pan_register(const bdaddr_t *addr);
+bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
 void bt_pan_unregister(void);
diff --git a/android/pics-a2dp.txt b/android/pics-a2dp.txt
new file mode 100644 (file)
index 0000000..81debcd
--- /dev/null
@@ -0,0 +1,86 @@
+A2DP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_1_1  True            Role: Source (C.1)
+TSPC_A2DP_1_2  False (*)       Role: Sink (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+               A2DP SRC Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_2_1  True            SRC: Initiate connection establishment (M)
+TSPC_A2DP_2_2  True            SRC: Accept connection establishment (M)
+TSPC_A2DP_2_3  True            SRC: Initiate start streaming (M)
+TSPC_A2DP_2_4  True            SRC: Accept start streaming (M)
+TSPC_A2DP_2_5  True            SRC: Send audio stream (M)
+TSPC_A2DP_2_6  True            SRC: Initiate connection release (M)
+TSPC_A2DP_2_7  True            SRC: Accept connection release (M)
+TSPC_A2DP_2_8  True (*)        SRC: Initiate suspend (O)
+TSPC_A2DP_2_9  True (*)        SRC: Accept suspend (O)
+TSPC_A2DP_2_10 True            SRC: SBC encoder (M)
+TSPC_A2DP_2_10a        False           SRC: Encode Audio Stream (O)
+TSPC_A2DP_2_11 False           SRC: SBC Configurations in 16 KHz sampling (O)
+TSPC_A2DP_2_12 False           SRC: SBC Configurations in 32 KHz sampling (O)
+TSPC_A2DP_2_13 True            SRC: SBC Configurations in 44.1 KHz sampling
+                                       (C.1)
+TSPC_A2DP_2_14 False (*)       SRC: SBC Configurations in 48 KHz sampling
+                                       (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one of the values shall be supported.
+-------------------------------------------------------------------------------
+
+
+               Supported Codecs in SRC
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_3_1  True            SRC: SBC encoder Codec (M)
+TSPC_A2DP_3_2  False           SRC: Additional encoder Codec (O)
+-------------------------------------------------------------------------------
+
+
+               A2DP Sink Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_4_1  False           SNK: Initiate connection establishment (O)
+TSPC_A2DP_4_2  False (*)       SNK: Accept connection establishment (M)
+TSPC_A2DP_4_3  False           SNK: Initiate start streaming (O)
+TSPC_A2DP_4_4  False (*)       SNK: Accept start streaming (M)
+TSPC_A2DP_4_5  False (*)       SNK: Receive audio stream (M)
+TSPC_A2DP_4_6  False           SNK: Initiate connection release (O)
+TSPC_A2DP_4_7  False (*)       SNK: Accept connection release (M)
+TSPC_A2DP_4_8  False           SNK: Initiate suspend (O)
+TSPC_A2DP_4_9  False           SNK: Accept suspend (O)
+TSPC_A2DP_4_10 False (*)       SNK: SBC decoder (M)
+TSPC_A2DP_4_10a        False (*)       SNK: Decode Audio Stream (O)
+TSPC_A2DP_4_11 False           SNK: SBC Configurations in 16 KHz sampling (O)
+TSPC_A2DP_4_12 False           SNK: SBC Configurations in 32 KHz sampling (O)
+TSPC_A2DP_4_13 False (*)       SNK: SBC Configurations in 44.1 KHz sampling (M)
+TSPC_A2DP_4_14 False (*)       SNK: SBC Configurations in 48 KHz sampling (M)
+-------------------------------------------------------------------------------
+
+
+               Supported codecs in SNK
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_5_1  False (*)       SNK: SBC decoder Codec (M)
+TSPC_A2DP_5_2  False           SNK: Additional decoder Coded (O)
+TSPC_ALL       False           Enable all test cases when set to False.
+-------------------------------------------------------------------------------
diff --git a/android/pics-avctp.txt b/android/pics-avctp.txt
new file mode 100644 (file)
index 0000000..3490db5
--- /dev/null
@@ -0,0 +1,75 @@
+AVCTP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+               Protocol Version
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_0_1 False           AVCTP 1.0 (C.1)
+TSPC_AVCTP_0_2 False           AVCTP 1.2 (C.1)
+TSPC_AVCTP_0_3 False           AVCTP 1.3 (C.1)
+TSPC_AVCTP_0_4 True (*)        AVCTP 1.4 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support only one Protocol Version.
+-------------------------------------------------------------------------------
+
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_1_1 True (*)        Controller (C.1)
+TSPC_AVCTP_1_2 True (*)        Target (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+               Controller Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_2_1 False           Message fragmentation (O)
+TSPC_AVCTP_2_2 True            Transaction label management (M)
+TSPC_AVCTP_2_3 True            Packet type field management (M)
+TSPC_AVCTP_2_4 True            Message type field management (M)
+TSPC_AVCTP_2_5 True            PID field management (M)
+TSPC_AVCTP_2_6 True            IPID field mangement (M)
+TSPC_AVCTP_2_7 True            Message information management (M)
+TSPC_AVCTP_2_8 False           Event registration for message reception (O)
+TSPC_AVCTP_2_9 False           Event registration for connection request (O)
+TSPC_AVCTP_2_10        False           Event registration for disconnection (O)
+TSPC_AVCTP_2_11        False           Connect request (O)
+TSPC_AVCTP_2_12        False           Disconnect request (O)
+TSPC_AVCTP_2_13        False           Send message (O)
+TSPC_AVCTP_2_14        False           Support for multiple AVCTP channel establishment
+                                       (O)
+-------------------------------------------------------------------------------
+
+
+               Target Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_AVCTP_3_1 False           Message fragmentation (O)
+TSPC_AVCTP_3_2 True            Transaction label management (M)
+TSPC_AVCTP_3_3 True            Packet type field management (M)
+TSPC_AVCTP_3_4 True            Message type field management (M)
+TSPC_AVCTP_3_5 True            PID field management (M)
+TSPC_AVCTP_3_6 True            IPID field management (M)
+TSPC_AVCTP_3_7 True            Message information management (M)
+TSPC_AVCTP_3_8 True (*)        Event registration for message reception (O)
+TSPC_AVCTP_3_9 True (*)        Event registration for connection request (O)
+TSPC_AVCTP_3_10        True (*)        Event registration for disconnection request (O)
+TSPC_AVCTP_3_11        True (*)        Connect request (O)
+TSPC_AVCTP_3_12        True (*)        Disconnect request (O)
+TSPC_AVCTP_3_13        True (*)        Send message (O)
+TSPC_AVCTP_ALL False           Enables all test cases when set to TRUE
+-------------------------------------------------------------------------------
diff --git a/android/pics-avrcp.txt b/android/pics-avrcp.txt
new file mode 100644 (file)
index 0000000..969be6a
--- /dev/null
@@ -0,0 +1,626 @@
+AVRCP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory if such role selected
+O - optional
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+SPC_AVRCP_1_1     True         Role: Controller (CT) (C.1)
+TSPC_AVRCP_1_2    True         Role: Target (TG) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+               Controller Features
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_2_1    False (*)    CT: Initiating connection establishment (M)
+TSPC_AVRCP_2_2    False (*)    CT: Accepting connection establishment (M)
+TSPC_AVRCP_2_3    False (*)    CT: Initiating connection release (M)
+TSPC_AVRCP_2_4    False (*)    CT: Accepting connection release (M)
+TSPC_AVRCP_2_5    False                CT: Sending UNIT INFO (O)
+TSPC_AVRCP_2_6    False                CT: Sending SUBUNIT INFO (O)
+TSPC_AVRCP_2_7    False (*)    CT: Sending PASS THROUGH command category 1
+                                       (C.1)
+TSPC_AVRCP_2_8    False                CT: Sending PASS THROUGH command category 2
+                                       (C.1)
+TSPC_AVRCP_2_9    False                CT: Sending PASS THROUGH command category 3
+                                       (C.1)
+TSPC_AVRCP_2_10   False                CT: Sending PASS THROUGH command category 4
+                                       (C.1)
+TSPC_AVRCP_2_11   False                CT: Get Capabilities (O)
+TSPC_AVRCP_2_12   False                CT: List Player Application Setting
+                                       Attributes (C.9)
+TSPC_AVRCP_2_13   False                CT: List Player Application Setting Values (O)
+TSPC_AVRCP_2_14   False                CT: Get Current Player Application Setting
+                                       (C.10)
+TSPC_AVRCP_2_15   False                CT: Set Player Application Setting Value (C.10)
+TSPC_AVRCP_2_16   False                CT: Get Player Application Setting
+                                               Attribute (O)
+TSPC_AVRCP_2_17   False                CT: Get Player Application Setting Value (O)
+TSPC_AVRCP_2_18   False                CT: Inform Displayable Character Set (O)
+TSPC_AVRCP_2_19   False                CT: Inform Battery Status of CT (O)
+TSPC_AVRCP_2_20   False                CT: Get Element Attributes (O)
+TSPC_AVRCP_2_21   False                CT: Get Play Status (O)
+TSPC_AVRCP_2_22   False                CT: Register Notification (C.11)
+TSPC_AVRCP_2_23   False                CT: Request Continuing Response (C.2)
+TSPC_AVRCP_2_24   False                CT: Abort Continuing Response (C.2)
+TSPC_AVRCP_2_25   False                CT: Next Group (C.12)
+TSPC_AVRCP_2_26   False                CT: Previous Group (C.12)
+TSPC_AVRCP_2_27   False                CT: Media Player Selection (O)
+TSPC_AVRCP_2_28   False                CT: SetAddressedPlayer (O)
+TSPC_AVRCP_2_29   False                CT: GetFolderItems(MediaPlayerList) (C.5)
+TSPC_AVRCP_2_29b  False                CT: GetTotalNumberOfItems(MediaPlayerList) (C.5)
+TSPC_AVRCP_2_30   False                CT: EVENT_AVAILABLE_PLAYERS_CHANGED (O)
+TSPC_AVRCP_2_31   False                CT: EVENT_ADDRESSED_PLAYER_CHANGED (O)
+TSPC_AVRCP_2_32   False                CT: Browsing (O)
+TSPC_AVRCP_2_33   False                CT: SetBrowsedPlayer (C.4)
+TSPC_AVRCP_2_34   False                CT: ChangePath (C.4)
+TSPC_AVRCP_2_35   False                CT: GetFolderItems(Filesystem) (C.4)
+TSPC_AVRCP_2_35b  False                CT: GetTotalNumberOfItems(Filesystem) (C.4)
+TSPC_AVRCP_2_36   False                CT: GetItemAttributes (O)
+TSPC_AVRCP_2_37   False                CT: PlayItem(Filesystem) (C.4)
+TSPC_AVRCP_2_38   False                CT: EVENT_UIDS_CHANGED (O)
+TSPC_AVRCP_2_39   False                CT: Searching (O)
+TSPC_AVRCP_2_40   False                CT: Search (C.7)
+TSPC_AVRCP_2_41   False                CT: GetFolderItems(Search Results) (C.7)
+TSPC_AVRCP_2_41b  False                CT: GetTotalNumberOfItems(Search Results) (C.7)
+TSPC_AVRCP_2_42   False                CT: PlayItem(SearchResultList) (C.7)
+TSPC_AVRCP_2_43   False                CT: NowPlaying (C.8)
+TSPC_AVRCP_2_44   False                CT: GetFolderItems(NowPlayingList) (C.8)
+TSPC_AVRCP_2_44b  False                CT: GetTotalNumberOfItems(NowPlayingList) (C.8)
+TSPC_AVRCP_2_45   False                CT: PlayItem(NowPlayingList) (C.8)
+TSPC_AVRCP_2_46   False                CT: AddToNowPlaying (O)
+TSPC_AVRCP_2_47   False                CT: EVENT_NOW_PLAYING_CONTENT_CHANGED (O)
+TSPC_AVRCP_2_48   False                CT: Playable Folders (O)
+TSPC_AVRCP_2_49   True (*)     CT: Absolute Volume (C.3)
+TSPC_AVRCP_2_50   True (*)     CT: SetAbsoluteVolume (C.3)
+TSPC_AVRCP_2_51   True (*)     CT: NotifyVolumeChange (C.3)
+TSPC_AVRCP_2_52   False (*)    CT: Discoverable Mode (M)
+TSPC_AVRCP_2_53   False                CT: PASSTHROUGH operation supporting press
+                                       and hold (O)
+TSPC_AVRCP_2_54   False                CT: Cover Art (O)
+TSPC_AVRCP_2_55   False                CT: GetCapabilities, Cover Art (C.10)
+TSPC_AVRCP_2_56   False                CT: GetImageProperties, Cover Art (C.10)
+TSPC_AVRCP_2_57   False                CT: GetImage, Cover Art (C.9)
+TSPC_AVRCP_2_58   False                CT: GetLinkedThumbnail, CoverArt (C.9)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined categories
+       (TSPC_AVRCP_2_7 through TSPC_AVRCP_2_10).
+C.2: Mandatory if TSPC_AVRCP_2_20 is supported, otherwise Optional.
+C.3: Mandatory if TSPC_AVRCP_2_8 is supported, otherwise Excluded.
+C.4: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded.
+C.5: Mandatory if TSPC_AVRCP_2_27 is supported, otherwise Excluded.
+C.7: Mandatory if item TSPC_AVRCP_2_39 is supported, Excluded otherwise.
+C.8: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded.
+C.9: Mandatory to support if Player Application Settings feature is supported.
+       If any item TSPC_AVRCP_2_13 through TSPC_AVRCP_2_15 is supported it is
+       required to claim support for this feature in accordance with Player
+       Application Settings support requirements, otherwise Optional.
+C.10: Mandatory to support either Get or Set Player Application Settings
+       (TSPC_AVRCP_2_14 or TSPC_AVRCP_2_15) if List Player Application Setting
+       Attributes (TSPC_AVRCP_2_12) is supported. Either TSPC_AVRCP_2_14
+       or TSPC_AVRCP_2_15 must be supported if Player Application Settings
+       feature is supported, in accordance with Player Application Settings
+       support requirements.
+C.11: Mandatory if TSPC_AVRCP_2_7 or (TSPC_AVRCP_2_8 AND TSPC_AVRCP_2_49)
+       or TSPC_AVRCP_2_9 is supported, otherwise Optional.
+C.12: Mandatory if Basic Group Navigation Feature supported. If any item
+       TSPC_AVRCP_2_25 or TSPC_AVRCP_2_26 is supported it is mandatory to
+       support both, in accordance with Basic Group Navigation support
+       requirements, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Controller Profile Version
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_2b_1   False                CT: AVRCP v1.0 (C.1)
+TSPC_AVRCP_2b_2   False                CT: AVRCP v1.3 (C.1)
+TSPC_AVRCP_2b_3   False                CT: AVRCP v1.4 (C.1)
+TSPC_AVRCP_2b_4   False                CT: AVRCP v1.5 (C.1)
+TSPC_AVRCP_2b_5   False                CT: AVRCP v1.6 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions if
+       Controller role supported (SPC_AVRCP_1_1).
+-------------------------------------------------------------------------------
+
+
+               Operation_id of Category 1 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_3_1    False                CT: category 1 - Operation id: 0 (C.1)
+TSPC_AVRCP_3_2    False                CT: category 1 - Operation id: 1 (C.1)
+TSPC_AVRCP_3_3    False                CT: category 1 - Operation id: 2 (C.1)
+TSPC_AVRCP_3_4    False                CT: category 1 - Operation id: 3 (C.1)
+TSPC_AVRCP_3_5    False                CT: category 1 - Operation id: 4 (C.1)
+TSPC_AVRCP_3_6    False                CT: category 1 - Operation id: 5 (C.1)
+TSPC_AVRCP_3_7    False                CT: category 1 - Operation id: 6 (C.1)
+TSPC_AVRCP_3_8    False                CT: category 1 - Operation id: 7 (C.1)
+TSPC_AVRCP_3_9    False                CT: category 1 - Operation id: 8 (C.1)
+TSPC_AVRCP_3_10   False                CT: category 1 - Operation id: 9 (C.1)
+TSPC_AVRCP_3_11   False                CT: category 1 - Operation id: dot (C.1)
+TSPC_AVRCP_3_12   False                CT: category 1 - Operation id: enter (C.1)
+TSPC_AVRCP_3_13   False                CT: category 1 - Operation id: clear (C.1)
+TSPC_AVRCP_3_14   False                CT: category 1 - Operation id: sound_select
+                                       (C.1)
+TSPC_AVRCP_3_15   False                CT: category 1 - Operation id: input_select
+                                       (C.1)
+TSPC_AVRCP_3_16   False                CT: category 1 - Operation id:
+                                       display_information (C.1)
+TSPC_AVRCP_3_17   False                CT: category 1 - Operation id: help (C.1)
+TSPC_AVRCP_3_18   False                CT: category 1 - Operation id: power (C.1)
+TSPC_AVRCP_3_19   False (*)    CT: category 1 - Operation id: play (C.1)
+TSPC_AVRCP_3_20   False (*)    CT: category 1 - Operation id: stop (C.1)
+TSPC_AVRCP_3_21   False (*)    CT: category 1 - Operation id: pause (C.1)
+TSPC_AVRCP_3_22   False                CT: category 1 - Operation id: record (C.1)
+TSPC_AVRCP_3_23   False                CT: category 1 - Operation id: rewind (C.1)
+TSPC_AVRCP_3_24   False                CT: category 1 - Operation id: fast_forward
+                                       (C.1)
+TSPC_AVRCP_3_25   False                CT: category 1 - Operation id: eject (C.1)
+TSPC_AVRCP_3_26   False                CT: category 1 - Operation id: forward (C.1)
+TSPC_AVRCP_3_27   False                CT: category 1 - Operation id: backward (C.1)
+TSPC_AVRCP_3_28   False                CT: category 1 - Operation id: angle (C.1)
+TSPC_AVRCP_3_29   False                CT: category 1 - Operation id: subpicture (C.1)
+TSPC_AVRCP_3_30   False                CT: category 1 - Operation id: F1 (C.1)
+TSPC_AVRCP_3_31   False                CT: category 1 - Operation id: F2 (C.1)
+TSPC_AVRCP_3_32   False                CT: category 1 - Operation id: F3 (C.1)
+TSPC_AVRCP_3_33   False                CT: category 1 - Operation id: F4 (C.1)
+TSPC_AVRCP_3_33a  False                CT: category 1 - Operation id: F5 (C.1)
+TSPC_AVRCP_3_34   False                CT: category 1 - Operation id: vendor_unique
+                                       (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these operation_ids if the device
+       supports category 1 (TSPC_AVRCP_2_7).
+-------------------------------------------------------------------------------
+
+
+               Operation_id of category 2 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_4_1    False                CT: category 2 - Operation id: 0 (C.2)
+TSPC_AVRCP_4_2    False                CT: category 2 - Operation id: 1 (C.2)
+TSPC_AVRCP_4_3    False                CT: category 2 - Operation id: 2 (C.2)
+TSPC_AVRCP_4_4    False                CT: category 2 - Operation id: 3 (C.2)
+TSPC_AVRCP_4_5    False                CT: category 2 - Operation id: 4 (C.2)
+TSPC_AVRCP_4_6    False                CT: category 2 - Operation id: 5 (C.2)
+TSPC_AVRCP_4_7    False                CT: category 2 - Operation id: 6 (C.2)
+TSPC_AVRCP_4_8    False                CT: category 2 - Operation id: 7 (C.2)
+TSPC_AVRCP_4_9    False                CT: category 2 - Operation id: 8 (C.2)
+TSPC_AVRCP_4_10   False                CT: category 2 - Operation id: 9 (C.2)
+TSPC_AVRCP_4_11   False                CT: category 2 - Operation id: dot (C.2)
+TSPC_AVRCP_4_12   False                CT: category 2 - Operation id: enter (C.2)
+TSPC_AVRCP_4_13   False                CT: category 2 - Operation id: clear (C.2)
+TSPC_AVRCP_4_14   False                CT: category 2 - Operation id: sound_select
+                                       (C.2)
+TSPC_AVRCP_4_15   False                CT: category 2 - Operation id: input_select
+                                       (C.2)
+TSPC_AVRCP_4_16   False                CT: category 2 - Operation id:
+                                       display_information (C.2)
+TSPC_AVRCP_4_17   False                CT: category 2 - Operation id: help (C.2)
+TSPC_AVRCP_4_18   False                CT: category 2 - Operation id: power (C.2)
+TSPC_AVRCP_4_19   False (*)    CT: category 2 - Operation id: volume_up (C.2)
+TSPC_AVRCP_4_20   False (*)    CT: category 2 - Operation id: volume_down (C.2)
+TSPC_AVRCP_4_21   False                CT: category 2 - Operation id: mute (C.2)
+TSPC_AVRCP_4_22   False                CT: category 2 - Operation id: F1 (C.2)
+TSPC_AVRCP_4_23   False                CT: category 2 - Operation id: F2 (C.2)
+TSPC_AVRCP_4_24   False                CT: category 2 - Operation id: F3 (C.2)
+TSPC_AVRCP_4_25   False                CT: category 2 - Operation id: F4 (C.2)
+TSPC_AVRCP_4_25a  False                CT: category 2 - Operation id: F5 (C.2)
+TSPC_AVRCP_4_26   False                CT: category 2 - Operation id: vendor_unique
+                                       (C.2)
+-------------------------------------------------------------------------------
+C.2: Mandatory to support at least one of these operation_ids if the device
+       supports category 2 (TSPC_AVRCP_2_8).
+-------------------------------------------------------------------------------
+
+
+               Operation_id of category 3 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_5_1    False                CT: category 3 - Operation id: 0 (C.3)
+TSPC_AVRCP_5_2    False                CT: category 3 - Operation id: 1 (C.3)
+TSPC_AVRCP_5_3    False                CT: category 3 - Operation id: 2 (C.3)
+TSPC_AVRCP_5_4    False                CT: category 3 - Operation id: 3 (C.3)
+TSPC_AVRCP_5_5    False                CT: category 3 - Operation id: 4 (C.3)
+TSPC_AVRCP_5_6    False                CT: category 3 - Operation id: 5 (C.3)
+TSPC_AVRCP_5_7    False                CT: category 3 - Operation id: 6 (C.3)
+TSPC_AVRCP_5_8    False                CT: category 3 - Operation id: 7 (C.3)
+TSPC_AVRCP_5_9    False                CT: category 3 - Operation id: 8 (C.3)
+TSPC_AVRCP_5_10   False                CT: category 3 - Operation id: 9 (C.3)
+TSPC_AVRCP_5_11   False                CT: category 3 - Operation id: dot (C.3)
+TSPC_AVRCP_5_12   False                CT: category 3 - Operation id: enter (C.3)
+TSPC_AVRCP_5_13   False                CT: category 3 - Operation id: clear (C.3)
+TSPC_AVRCP_5_14   False                CT: category 3 - Operation id: channel up (C.3)
+TSPC_AVRCP_5_15   False                CT: category 3 - Operation id: channel down
+                                       (C.3)
+TSPC_AVRCP_5_16   False                CT: category 3 - Operation id: previous channel
+                                       (C.3)
+TSPC_AVRCP_5_17   False                CT: category 3 - Operation id: sound_select
+                                       (C.3)
+TSPC_AVRCP_5_18   False                CT: category 3 - Operation id: input_select
+                                       (C.3)
+TSPC_AVRCP_5_19   False                CT: category 3 - Operation id:
+                                       display_information (C.3)
+TSPC_AVRCP_5_20   False                CT: category 3 - Operation id: help (C.3)
+TSPC_AVRCP_5_21   False                CT: category 3 - Operation id: power (C.3)
+TSPC_AVRCP_5_22   False                CT: category 3 - Operation id: angle (C.3)
+TSPC_AVRCP_5_23   False                CT: category 3 - Operation id: subpicture(C.3)
+TSPC_AVRCP_5_24   False                CT: category 3 - Operation id: F1 (C.3)
+TSPC_AVRCP_5_25   False                CT: category 3 - Operation id: F2 (C.3)
+TSPC_AVRCP_5_26   False                CT: category 3 - Operation id: F3 (C.3)
+TSPC_AVRCP_5_27   False                CT: category 3 - Operation id: F4 (C.3)
+TSPC_AVRCP_5_27a  False                CT: category 3 - Operation id: F5 (C.3)
+TSPC_AVRCP_5_28   False                CT: category 3 - Operation id: vendor_unique
+                                       (C.3)
+-------------------------------------------------------------------------------
+C.3: Mandatory to support at least one of these operation_ids if the device
+       supports category 3 (TSPC_AVRCP_2_9).
+-------------------------------------------------------------------------------
+
+
+               Operation_id of category 4 for CT
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_6_1    False                CT: category 4 - Operation id: select (C.4)
+TSPC_AVRCP_6_2    False                CT: category 4 - Operation id: up (C.4)
+TSPC_AVRCP_6_3    False                CT: category 4 - Operation id: down (C.4)
+TSPC_AVRCP_6_4    False                CT: category 4 - Operation id: left (C.4)
+TSPC_AVRCP_6_5    False                CT: category 4 - Operation id: right (C.4)
+TSPC_AVRCP_6_6    False                CT: category 4 - Operation id: right up (C.4)
+TSPC_AVRCP_6_7    False                CT: category 4 - Operation id: right down (C.4)
+TSPC_AVRCP_6_8    False                CT: category 4 - Operation id: left up (C.4)
+TSPC_AVRCP_6_9    False                CT: category 4 - Operation id: left down (C.4)
+TSPC_AVRCP_6_10   False                CT: category 4 - Operation id: root menu (C.4)
+TSPC_AVRCP_6_11   False                CT: category 4 - Operation id: setup menu (C.4)
+TSPC_AVRCP_6_12   False                CT: category 4 - Operation id: contents menu
+                                       (C.4)
+TSPC_AVRCP_6_13   False                CT: category 4 - Operation id: favorite menu
+                                       (C.4)
+TSPC_AVRCP_6_14   False                CT: category 4 - Operation id: exit (C.4)
+TSPC_AVRCP_6_15   False                CT: category 4 - Operation id: 0 (C.4)
+TSPC_AVRCP_6_16   False                CT: category 4 - Operation id: 1 (C.4)
+TSPC_AVRCP_6_17   False                CT: category 4 - Operation id: 2 (C.4)
+TSPC_AVRCP_6_18   False                CT: category 4 - Operation id: 3 (C.4)
+TSPC_AVRCP_6_19   False                CT: category 4 - Operation id: 4 (C.4)
+TSPC_AVRCP_6_20   False                CT: category 4 - Operation id: 5 (C.4)
+TSPC_AVRCP_6_21   False                CT: category 4 - Operation id: 6 (C.4)
+TSPC_AVRCP_6_22   False                CT: category 4 - Operation id: 7 (C.4)
+TSPC_AVRCP_6_23   False                CT: category 4 - Operation id: 8 (C.4)
+TSPC_AVRCP_6_24   False                CT: category 4 - Operation id: 9 (C.4)
+TSPC_AVRCP_6_25   False                CT: category 4 - Operation id: dot (C.4)
+TSPC_AVRCP_6_26   False                CT: category 4 - Operation id: enter (C.4)
+TSPC_AVRCP_6_27   False                CT: category 4 - Operation id: clear (C.4)
+TSPC_AVRCP_6_28   False                CT: category 4 - Operation id:
+                                       display_information (C.4)
+TSPC_AVRCP_6_29   False                CT: category 4 - Operation id: help (C.4)
+TSPC_AVRCP_6_30   False                CT: category 4 - Operation id: page up (C.4)
+TSPC_AVRCP_6_31   False                CT: category 4 - Operation id: page down (C.4)
+TSPC_AVRCP_6_32   False                CT: category 4 - Operation id: power (C.4)
+TSPC_AVRCP_6_33   False                CT: category 4 - Operation id: F1 (C.4)
+TSPC_AVRCP_6_34   False                CT: category 4 - Operation id: F2 (C.4)
+TSPC_AVRCP_6_35   False                CT: category 4 - Operation id: F3 (C.4)
+TSPC_AVRCP_6_36   False                CT: category 4 - Operation id: F4 (C.4)
+TSPC_AVRCP_6_36a  False                CT: category 4 - Operation id: F5 (C.4)
+TSPC_AVRCP_6_37   False                CT: category 4 - Operation id: vendor_unique
+                                       (C.4)
+-------------------------------------------------------------------------------
+C.4: Mandatory to support at least one of these operation_ids if the device
+       supports category 4 (TSPC_AVRCP_2_9).
+-------------------------------------------------------------------------------
+
+
+               Target Features
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_7_1    True (*)     TG: Initiating connection establishment (O)
+TSPC_AVRCP_7_2    True         TG: Accept connection establishment (M)
+TSPC_AVRCP_7_3    True         TG: Initiating connection release (M)
+TSPC_AVRCP_7_4    True         TG: Accepting connection release (M)
+TSPC_AVRCP_7_5    True         TG: Receiving UNIT INFO (M)
+TSPC_AVRCP_7_6    True         TG: Receiving SUBUNIT INFO (M)
+TSPC_AVRCP_7_7    True         TG: Receiving PASS THROUGH command category 1
+                                       (C.1)
+TSPC_AVRCP_7_8    True (*)     TG: Receiving PASS THROUGH command category 2
+                                       (C.1)
+TSPC_AVRCP_7_9    False                TG: Receiving PASS THROUGH command category 3
+                                       (C.1)
+TSPC_AVRCP_7_10   False                TG: Receiving PASS THROUGH command category 4
+                                       (C.1)
+TSPC_AVRCP_7_11   True (*)     TG: Get Capabilities Response (C.3)
+TSPC_AVRCP_7_12   False                TG: List Player Application Settings (C.14)
+TSPC_AVRCP_7_13   False                TG: List Player Application Setting Values
+                                       (C.14)
+TSPC_AVRCP_7_14   False                TG: Get Current Player Application Settings
+                                       (C.14)
+TSPC_AVRCP_7_15   False                TG: Set Player Application Setting Value (C.14)
+TSPC_AVRCP_7_16   False                TG: Get Player Application Setting Attribute
+                                       (O)
+TSPC_AVRCP_7_17   False                TG: Get Player Application Setting Value (O)
+TSPC_AVRCP_7_18   False                TG: Inform Displayable Character Set (O)
+TSPC_AVRCP_7_19   False                TG: Inform Battery Status Of CT Response (O)
+TSPC_AVRCP_7_20   True (*)     TG: Get Element Attributes Response (C.3)
+TSPC_AVRCP_7_21   True (*)     TG: Get Play Status Response (C.2)
+TSPC_AVRCP_7_22   True (*)     TG: Register Notification Response (C.12)
+TSPC_AVRCP_7_23   True (*)     TG: Notify Event Response:
+                                       PLAYBACK_STATUS_CHANGED (C.4)
+TSPC_AVRCP_7_24   True (*)     TG: Notify Event Response: TRACK_CHANGED (C.4)
+TSPC_AVRCP_7_25   False                TG: Notify Event Response: TRACK_REACHED_END (O)
+TSPC_AVRCP_7_26   False                TG: Notify Event Response: TRACK_REACHED_START
+                                       (O)
+TSPC_AVRCP_7_27   False                TG: Notify Event Response: PLAYBACK_POS_CHANGED
+                                       (O)
+TSPC_AVRCP_7_28   False                TG: Notify Event Response: BATT_STATUS_CHANGED
+                                       (O)
+TSPC_AVRCP_7_29   False                TG: Notify Event Response: SYSTEM_STATUS_CHANGED
+                                       (O)
+TSPC_AVRCP_7_30   False                TG: Notify Event Response:
+                                       PLAYER_APPLICATION_SETTING_CHANGED (O)
+TSPC_AVRCP_7_31   True (*)     TG: Request ContinuingResponse (C.2)
+TSPC_AVRCP_7_32   True (*)     TG: Abort ContinuingResponse Response (C.2)
+TSPC_AVRCP_7_34   False                TG: Next Group (C.15)
+TSPC_AVRCP_7_35   False                TG: Previous Group (C.15)
+TSPC_AVRCP_7_36   False                TG: Media Player Selection (C.8)
+TSPC_AVRCP_7_37   False                TG: SetAddressedPlayer (C.8)
+TSPC_AVRCP_7_38   False                TG: GetFolderItems(MediaPlayerList) (C.8)
+TSPC_AVRCP_7_38b  False                TG: GetTotalNumberOfItems(MediaPlayerList) (C.8)
+TSPC_AVRCP_7_39   False                TG: EVENT_AVAILABLE_PLAYERS_CHANGED (C.8)
+TSPC_AVRCP_7_40   False                TG: EVENT_ADDRESSED_PLAYER_CHANGED (C.8)
+TSPC_AVRCP_7_41   False                TG: Supports Multiple Players (O)
+TSPC_AVRCP_7_42   False                TG: Browsing (O)
+TSPC_AVRCP_7_42a  False                TG: Supports initiation of browsing channel
+                                       establishment (O)
+TSPC_AVRCP_7_43   False                TG: SetBrowsedPlayer (C.6)
+TSPC_AVRCP_7_44   False                TG: ChangePath (C.6)
+TSPC_AVRCP_7_45   False                TG: GetFolderItems(Filesystem) (C.6)
+TSPC_AVRCP_7_45b  False                TG: GetTotalNumberOfItems(Filesystem) (C.6)
+TSPC_AVRCP_7_46   False                TG: GetItemAttributes (C.6)
+TSPC_AVRCP_7_47   False                TG: PlayItem(Filesystem) (C.6)
+TSPC_AVRCP_7_48   False                TG: EVENT_UIDS_CHANGED (C.9)
+TSPC_AVRCP_7_49   False                TG: Database Aware Players (O)
+TSPC_AVRCP_7_50   False                TG: Searching (O)
+TSPC_AVRCP_7_51   False                TG: Search (C.10)
+TSPC_AVRCP_7_52   False                TG: GetFolderItems(Search Results) (C.10)
+TSPC_AVRCP_7_52b  False                TG: GetTotalNumberOfItems(Search Results) (C.10)
+TSPC_AVRCP_7_53   False                TG: PlayItem(SearchResultList) (C.10)
+TSPC_AVRCP_7_54   False                TG: NowPlaying (C.11)
+TSPC_AVRCP_7_55   False                TG: GetFolderItems(NowPlayingList) (C.11)
+TSPC_AVRCP_7_55b  False                TG: GetTotalNumberOfItems(NowPlayingList) (C.11)
+TSPC_AVRCP_7_56   False                TG: PlayItem(NowPlayingList) (C.11)
+TSPC_AVRCP_7_57   False                TG: AddToNowPlaying (O)
+TSPC_AVRCP_7_58   False                TG: EVENT_NOW_PLAYING_CONTENT_CHANGED (C.11)
+TSPC_AVRCP_7_59   False                TG: Playable Folders (O)
+TSPC_AVRCP_7_60   False                TG: Absolute Volume (C.5)
+TSPC_AVRCP_7_61   False                TG: SetAbsoluteVolume (C.5)
+TSPC_AVRCP_7_62   False                TG: NotifyVolumeChange (C.5)
+TSPC_AVRCP_7_63   False                TG: Error Response (O)
+TSPC_AVRCP_7_64   False                TG: General Reject (C.13)
+TSPC_AVRCP_7_65   True         TG: Discoverable Mode (M)
+TSPC_AVRCP_7_66   False                TG: PASSTHROUGH operation supporting press
+                                       and hold (O)
+TSPC_AVRCP_7_67   False                TG: Cover Art (O)
+TSPC_AVRCP_7_68   False                TG: GetCapabilities, Cover Art (C.12)
+TSPC_AVRCP_7_69   False                TG: GetImageProperties, Cover Art (C.12)
+TSPC_AVRCP_7_70   False                TG: GetImage, Cover Art (C.12)
+TSPC_AVRCP_7_71   False                TG: GetLinkedThumbnail, Cover Art (C.12)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the categories. Supported
+       operation_id's are shown in Table 8 to Table 11.
+C.2: Mandatory if 7/20 is supported, otherwise Optional.
+C.3: Mandatory if 7/7 is supported, otherwise Optional.
+C.4: Mandatory if 7/22 and 7/20 is supported, otherwise Optional.
+C.5: Mandatory if 7/8 is supported, otherwise Excluded.
+C.6: Mandatory if 7/42 is supported, otherwise Excluded.
+C.7: Mandatory if 7/36 is supported, otherwise Excluded.
+C.8: Mandatory if (7/7 or 7/9) is supported, otherwise Excluded.
+C.9: Mandatory if 7/49 is supported, otherwise Optional.
+C.10: Mandatory if 7/50 is supported, otherwise Excluded.
+C.11: Mandatory if 7/42 is supported, otherwise Optional.
+C.12: Mandatory if 7/7 or (7/8 AND 7/60) or 7/9 is supported, otherwise Optional
+C.13: Mandatory if 7/7 or 7/9 or 7/42 is supported, otherwise Optional.
+C.14: Mandatory if Player Application Settings Feature supported. If any item
+       7/12 – 7/15 is supported, all items 7/12 – 7/15 shall be supported,
+       in accordance with Player Application Settings Feature support
+       requirements, otherwise Excluded.
+C.15: Mandatory if Basic Group Navigation Feature supported. If any item
+       7/34 or 7/35 is supported it is mandatory to support both,
+       in accordance with Basic Group Navigation support requirements,
+       otherwise Excluded.
+-------------------------------------------------------------------------------
+
+               Target Profile Version
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_7b_1   False                TG: AVRCP v1.0 (C.1)
+TSPC_AVRCP_7b_2   False                TG: AVRCP v1.3 (C.1)
+TSPC_AVRCP_7b_3   False                TG: AVRCP v1.4 (C.1)
+TSPC_AVRCP_7b_4   True (*)     TG: AVRCP v1.5 (C.1)
+TSPC_AVRCP_7b_5   False                TG: AVRCP v1.6 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+               operation_id of category 1 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_8_1    False                TG: category 1 - Operation id: 0 (O)
+TSPC_AVRCP_8_2    False                TG: category 1 - Operation id: 1 (O)
+TSPC_AVRCP_8_3    False                TG: category 1 - Operation id: 2 (O)
+TSPC_AVRCP_8_4    False                TG: category 1 - Operation id: 3 (O)
+TSPC_AVRCP_8_5    False                TG: category 1 - Operation id: 4 (O)
+TSPC_AVRCP_8_6    False                TG: category 1 - Operation id: 5 (O)
+TSPC_AVRCP_8_7    False                TG: category 1 - Operation id: 6 (O)
+TSPC_AVRCP_8_8    False                TG: category 1 - Operation id: 7 (O)
+TSPC_AVRCP_8_9    False                TG: category 1 - Operation id: 8 (O)
+TSPC_AVRCP_8_10   False                TG: category 1 - Operation id: 9 (O)
+TSPC_AVRCP_8_11   False                TG: category 1 - Operation id: dot (O)
+TSPC_AVRCP_8_12   False                TG: category 1 - Operation id: enter (O)
+TSPC_AVRCP_8_13   False                TG: category 1 - Operation id: clear (O)
+TSPC_AVRCP_8_14   False                TG: category 1 - Operation id: sound select (O)
+TSPC_AVRCP_8_15   False                TG: category 1 - Operation id: input select (O)
+TSPC_AVRCP_8_16   False                TG: category 1 - Operation id: display
+                                       information (O)
+TSPC_AVRCP_8_17   False                TG: category 1 - Operation id: help (O)
+TSPC_AVRCP_8_18   False                TG: category 1 - Operation id: power (O)
+TSPC_AVRCP_8_19   True         TG: category 1 - Operation id: play (M)
+TSPC_AVRCP_8_20   True         TG: category 1 - Operation id: stop (M)
+TSPC_AVRCP_8_21   True         TG: category 1 - Operation id: pause (O)
+TSPC_AVRCP_8_22   False                TG: category 1 - Operation id: record (O)
+TSPC_AVRCP_8_23   True (*)     TG: category 1 - Operation id: rewind (O)
+TSPC_AVRCP_8_24   True (*)     TG: category 1 - Operation id: fast forward (O)
+TSPC_AVRCP_8_25   False                TG: category 1 - Operation id: eject (O)
+TSPC_AVRCP_8_26   True (*)     TG: category 1 - Operation id: forward (O)
+TSPC_AVRCP_8_27   True (*)     TG: category 1 - Operation id: backward (O)
+TSPC_AVRCP_8_28   False                TG: category 1 - Operation id: angle (O)
+TSPC_AVRCP_8_29   False                TG: category 1 - Operation id: subpicture (O)
+TSPC_AVRCP_8_30   False                TG: category 1 - Operation id: F1 (O)
+TSPC_AVRCP_8_31   False                TG: category 1 - Operation id: F2 (O)
+TSPC_AVRCP_8_32   False                TG: category 1 - Operation id: F3 (O)
+TSPC_AVRCP_8_33   False                TG: category 1 - Operation id: F4 (O)
+TSPC_AVRCP_8_33a  False                TG: category 1 - Operation id: F5 (O)
+TSPC_AVRCP_8_34   False                TG: category 1 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+
+
+               operation_id of category 2 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_9_1    False                TG: category 2 - Operation id: 0 (O)
+TSPC_AVRCP_9_2    False                TG: category 2 - Operation id: 1 (O)
+TSPC_AVRCP_9_3    False                TG: category 2 - Operation id: 2 (O)
+TSPC_AVRCP_9_4    False                TG: category 2 - Operation id: 3 (O)
+TSPC_AVRCP_9_5    False                TG: category 2 - Operation id: 4 (O)
+TSPC_AVRCP_9_6    False                TG: category 2 - Operation id: 5 (O)
+TSPC_AVRCP_9_7    False                TG: category 2 - Operation id: 6 (O)
+TSPC_AVRCP_9_8    False                TG: category 2 - Operation id: 7 (O)
+TSPC_AVRCP_9_9    False                TG: category 2 - Operation id: 8 (O)
+TSPC_AVRCP_9_10   False                TG: category 2 - Operation id: 9 (O)
+TSPC_AVRCP_9_11   False                TG: category 2 - Operation id: dot (O)
+TSPC_AVRCP_9_12   False                TG: category 2 - Operation id: enter (O)
+TSPC_AVRCP_9_13   False                TG: category 2 - Operation id: clear (O)
+TSPC_AVRCP_9_14   False                TG: category 2 - Operation id: sound select (O)
+TSPC_AVRCP_9_15   False                TG: category 2 - Operation id: input select (O)
+TSPC_AVRCP_9_16   False                TG: category 2 - Operation id: display
+                                       information (O)
+TSPC_AVRCP_9_17          False         TG: category 2 - Operation id: help (O)
+TSPC_AVRCP_9_18          False         TG: category 2 - Operation id: power (O)
+TSPC_AVRCP_9_19   True         TG: category 2 - Operation id: volume up (C.2)
+TSPC_AVRCP_9_20   True         TG: category 2 - Operation id: volume down (C.2)
+TSPC_AVRCP_9_21   False                TG: category 2 - Operation id: mute (O)
+TSPC_AVRCP_9_24   False                TG: category 2 - Operation id: F1 (O)
+TSPC_AVRCP_9_25   False                TG: category 2 - Operation id: F2 (O)
+TSPC_AVRCP_9_26   False                TG: category 2 - Operation id: F3 (O)
+TSPC_AVRCP_9_27   False                TG: category 2 - Operation id: F4 (O)
+TSPC_AVRCP_9_27a  False                TG: category 2 - Operation id: F5 (O)
+TSPC_AVRCP_9_28   False                TG: category 2 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+C.2: Mandatory to support if the device supports category 2 (TSPC_AVRCP_7_8).
+-------------------------------------------------------------------------------
+
+
+               operation_id of category 3 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_10_1   False                TG: category 3 - Operation id: 0 (O)
+TSPC_AVRCP_10_2   False                TG: category 3 - Operation id: 1 (O)
+TSPC_AVRCP_10_3   False                TG: category 3 - Operation id: 2 (O)
+TSPC_AVRCP_10_4   False                TG: category 3 - Operation id: 3 (O)
+TSPC_AVRCP_10_5   False                TG: category 3 - Operation id: 4 (O)
+TSPC_AVRCP_10_6   False                TG: category 3 - Operation id: 5 (O)
+TSPC_AVRCP_10_7   False                TG: category 3 - Operation id: 6 (O)
+TSPC_AVRCP_10_8   False                TG: category 3 - Operation id: 7 (O)
+TSPC_AVRCP_10_9   False                TG: category 3 - Operation id: 8 (O)
+TSPC_AVRCP_10_10  False                TG: category 3 - Operation id: 9 (O)
+TSPC_AVRCP_10_11  False                TG: category 3 - Operation id: dot (O)
+TSPC_AVRCP_10_12  False                TG: category 3 - Operation id: enter (O)
+TSPC_AVRCP_10_13  False                TG: category 3 - Operation id: clear (O)
+TSPC_AVRCP_10_14  False                TG: category 3 - Operation id: channel up (C.3)
+TSPC_AVRCP_10_15  False                TG: category 3 - Operation id: channel down
+                                       (C.3)
+TSPC_AVRCP_10_16  False                TG: category 3 - Operation id: previous channel
+                                       (O)
+TSPC_AVRCP_10_17  False                TG: category 3 - Operation id: sound select (O)
+TSPC_AVRCP_10_18  False                TG: category 3 - Operation id: input select (O)
+TSPC_AVRCP_10_19  False                TG: category 3 - Operation id: display
+                                       information (O)
+TSPC_AVRCP_10_20  False                TG: category 3 - Operation id: help (O)
+TSPC_AVRCP_10_21  False                TG: category 3 - Operation id: power (O)
+TSPC_AVRCP_10_21a False                TG: category 3 - Operation id: angle (O)
+TSPC_AVRCP_10_21b False                TG: category 3 - Operation id: subpicture (O)
+TSPC_AVRCP_10_22  False                TG: category 3 - Operation id: F1 (O)
+TSPC_AVRCP_10_23  False                TG: category 3 - Operation id: F2 (O)
+TSPC_AVRCP_10_24  False                TG: category 3 - Operation id: F3 (O)
+TSPC_AVRCP_10_25  False                TG: category 3 - Operation id: F4 (O)
+TSPC_AVRCP_10_25a False                TG: category 3 - Operation id: F5 (O)
+TSPC_AVRCP_10_26  False                TG: category 3 - Operation id: vendor unique (O)
+-------------------------------------------------------------------------------
+C.3: Mandatory to support if the device supports category 3 (TSPC_AVRCP_7_9).
+-------------------------------------------------------------------------------
+
+
+               operation_id of category 4 for TG
+-------------------------------------------------------------------------------
+Parameter Name    Selected     Description
+-------------------------------------------------------------------------------
+TSPC_AVRCP_11_1   False                TG: category 4 - Operation id: select (C.4)
+TSPC_AVRCP_11_2   False                TG: category 4 - Operation id: up (C.4)
+TSPC_AVRCP_11_3   False                TG: category 4 - Operation id: down (C.4)
+TSPC_AVRCP_11_4   False                TG: category 4 - Operation id: left (C.4)
+TSPC_AVRCP_11_5   False                TG: category 4 - Operation id: right (C.4)
+TSPC_AVRCP_11_6   False                TG: category 4 - Operation id: right up (O)
+TSPC_AVRCP_11_7   False                TG: category 4 - Operation id: right down (O)
+TSPC_AVRCP_11_8   False                TG: category 4 - Operation id: left up (O)
+TSPC_AVRCP_11_9   False                TG: category 4 - Operation id: left down (O)
+TSPC_AVRCP_11_10  False                TG: category 4 - Operation id: root menu (C.4)
+TSPC_AVRCP_11_11  False                TG: category 4 - Operation id: setup menu (O)
+TSPC_AVRCP_11_12  False                TG: category 4 - Operation id: contents menu (O)
+TSPC_AVRCP_11_13  False                TG: category 4 - Operation id: favorite menu (O)
+TSPC_AVRCP_11_14  False                TG: category 4 - Operation id: exit (O)
+TSPC_AVRCP_11_15  False                TG: category 4 - Operation id: 0 (O)
+TSPC_AVRCP_11_16  False                TG: category 4 - Operation id: 1 (O)
+TSPC_AVRCP_11_17  False                TG: category 4 - Operation id: 2 (O)
+TSPC_AVRCP_11_18  False                TG: category 4 - Operation id: 3 (O)
+TSPC_AVRCP_11_19  False                TG: category 4 - Operation id: 4 (O)
+TSPC_AVRCP_11_20  False                TG: category 4 - Operation id: 5 (O)
+TSPC_AVRCP_11_21  False                TG: category 4 - Operation id: 6 (O)
+TSPC_AVRCP_11_22  False                TG: category 4 - Operation id: 7 (O)
+TSPC_AVRCP_11_23  False                TG: category 4 - Operation id: 8 (O)
+TSPC_AVRCP_11_24  False                TG: category 4 - Operation id: 9 (O)
+TSPC_AVRCP_11_25  False                TG: category 4 - Operation id: dot (O)
+TSPC_AVRCP_11_26  False                TG: category 4 - Operation id: enter (O)
+TSPC_AVRCP_11_27  False                TG: category 4 - Operation id: clear (O)
+TSPC_AVRCP_11_28  False                TG: category 4 - Operation id: disply (O)
+TSPC_AVRCP_11_29  False                TG: category 4 - Operation id: help (O)
+TSPC_AVRCP_11_30  False                TG: category 4 - Operation id: page up (O)
+TSPC_AVRCP_11_31  False                TG: category 4 - Operation id: page down (O)
+TSPC_AVRCP_11_32  False                TG: category 4 - Operation id: power (O)
+TSPC_AVRCP_11_33  False                TG: category 4 - Operation id: F1 (O)
+TSPC_AVRCP_11_34  False                TG: category 4 - Operation id: F2 (O)
+TSPC_AVRCP_11_35  False                TG: category 4 - Operation id: F3 (O)
+TSPC_AVRCP_11_36  False                TG: category 4 - Operation id: F4 (O)
+TSPC_AVRCP_11_36a False                TG: category 4 - Operation id: F5 (O)
+TSPC_AVRCP_11_37  False                TG: category 4 - Operation id: vendor unique (O)
+TSPC_AVRCP_ALL    False                Enables all test cases when set to TRUE.
+-------------------------------------------------------------------------------
+C.4: Mandatory to support if the device supports category 4 (TSPC_AVRCP_7_10).
+-------------------------------------------------------------------------------
index e8c914a..fe1c03a 100644 (file)
@@ -1,21 +1,13 @@
 DID PICS for the PTS tool.
 
+PTS version: 5.1
+
 * - different than PTS defaults
 # - not yet implemented/supported
 
 M - mandatory
 O - optional
 
-               Version
--------------------------------------------------------------------------------
-Parameter Name Selected        Description
--------------------------------------------------------------------------------
-TSPC_DID_0_1   False           Device ID 1.2 (C.1)
-TSPC_DID_0_2   True            Device ID 1.3 (C.1)
--------------------------------------------------------------------------------
-C.1: It is mandatory to support one of the profile versions.
--------------------------------------------------------------------------------
-
 
                SDP Requirements
 -------------------------------------------------------------------------------
@@ -29,14 +21,3 @@ TSPC_DID_1_5 True            Primary Record (M)
 TSPC_DID_1_6   True            Vendor ID Source (M)
 TSPC_ALL       False           Turns on all the test cases
 -------------------------------------------------------------------------------
-
-
-               Required PIXIT settings
--------------------------------------------------------------------------------
-Parameter Name                 Value
--------------------------------------------------------------------------------
-TSPX_ClientExecutableURL       False
-TSPX_ServiceDescription                False
-TSPX_DocumentationURL          False
--------------------------------------------------------------------------------
-Other should be set according to Tester's test environment.
index cd274e8..cd48332 100644 (file)
@@ -1,7 +1,8 @@
 GAP PICS for the PTS tool.
 
+PTS version: 5.1
+
 * - different than PTS defaults
-# - not yet implemented/supported
 
 M - mandatory
 O - optional
@@ -34,12 +35,18 @@ Note - Only one transport shall be supported.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_0A_1  False           Core Specification Addendum 3 (CSA3),
-                                       GAP Connection Parameters Changes,
+TSPC_GAP_0A_1  True (*)        Core Specification Addendum 3 (CSA3), GAP
+                                       Connection Parameters Changes,
                                        Authentication and Lost Bond Changes,
-                                       Private Addressing Changes,
-                                       Dual Mode Addressing Changes,
+                                       Private Addressing Changes, Dual Mode
+                                       Addressing Changes,
                                        Adopted 24 July 2012 (C.1)
+TSPC_GAP_0A_2  True (*)        Core Specification Addendum 4 (CSA4)
+TSPC_GAP_0A_3  True (*)        Core Spec version 4.1 (Core v4.1) GAP Connection
+                                       Parameters Changes, Authentication and
+                                       Lost Bond Changes, Private Addressing
+                                       Changes, Dual Mode Addressing Changes,
+                                       Adopted 03 December 2013
 -------------------------------------------------------------------------------
 C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded.
 -------------------------------------------------------------------------------
@@ -50,12 +57,14 @@ C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded.
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
 TSPC_GAP_1_1   True (*)        Non-discoverable mode (C.1)
-TSPC_GAP_1_2   False           Limited-discoverable Mode (O)
+TSPC_GAP_1_2   True (*)        Limited-discoverable Mode (O)
 TSPC_GAP_1_3   True (*)        General-discoverable mode (O)
 TSPC_GAP_1_4   True (*)        Non-connectable mode (O)
 TSPC_GAP_1_5   True            Connectable mode (M)
 TSPC_GAP_1_6   False           Non-bondable mode (O)
 TSPC_GAP_1_7   True (*)        Bondable mode (C.2)
+TSPC_GAP_1_8   False           Non-Synchronizable Mode (O)
+TSPC_GAP_1_9   False           Synchronizable Mode (O)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_GAP_0_2 is supported, otherwise Optional.
 C.2: Mandatory if TSPC_GAP_3_5 is supported, otherwise Optional.
@@ -75,7 +84,8 @@ TSPC_GAP_2_6  False           Security mode 3 (C.7)
 TSPC_GAP_2_7   True (*)        Security mode 4 (C.4)
 TSPC_GAP_2_8   True (*)        Support of Authenticated link key (C.6)
 TSPC_GAP_2_9   True (*)        Support of Unauthenticated link key (C.6)
-TSPC_GAP_2_10  False           No security (C.6)
+TSPC_GAP_2_10  True (*)        No security (C.6)
+TSPC_GAP_2_11  False           Secure Connections Only Mode (O)
 -------------------------------------------------------------------------------
 C.1: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6) is supported, otherwise
        Optional.
@@ -105,7 +115,7 @@ C.7: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional.
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
 TSPC_GAP_3_1   True (*)        Initiation of general inquiry (C.1)
-TSPC_GAP_3_2   False           Initiation of limited inquiry (C.1)
+TSPC_GAP_3_2   True (*)        Initiation of limited inquiry (C.1)
 TSPC_GAP_3_3   True (*)        Initiation of name discover (O)
 TSPC_GAP_3_4   True (*)        Initiation of device discovery (O)
 TSPC_GAP_3_5   True (*)        Initiation of general bonding (O)
@@ -128,6 +138,8 @@ TSPC_GAP_4_5        True (*)        Support connection establishment as initiator
                                        (O)
 TSPC_GAP_4_6   True (*)        Support connection establishment as acceptor
                                        (O)
+TSPC_GAP_4_7   True (*)        Support synchronization establishment
+                                       as receiver (O)
 -------------------------------------------------------------------------------
 
 
@@ -138,7 +150,7 @@ Parameter Name      Selected        Description
 TSPC_GAP_5_1   False (*)       Broadcaster (C.1)
 TSPC_GAP_5_2   False           Observer (C.1)
 TSPC_GAP_5_3   False (*)       Peripheral (C.1)
-TSPC_GAP_5_4   True (*#)       Central (C.1)
+TSPC_GAP_5_4   True (*       Central (C.1)
 -------------------------------------------------------------------------------
 C.1: It is mandatory to support at least one of the defined roles.
 Note: 'LE Roles' is applicable for LE-only configurations, but it appears that
@@ -180,7 +192,7 @@ Parameter Name      Selected        Description
 -------------------------------------------------------------------------------
 TSPC_GAP_8A_1  False           AD Type-Service UUID (O)
 TSPC_GAP_8A_2  False           AD Type-Local Name (O)
-TSPC_GAP_8A_3  False (*)       AD Type-Flags (M)
+TSPC_GAP_8A_3  False (*)       AD Type-Flags (O)
 TSPC_GAP_8A_4  False           AD Type-Manufacturer Specific Data (O)
 TSPC_GAP_8A_5  False           AD Type-TX Power Level (O)
 TSPC_GAP_8A_6  False           AD Type-Security Manager Out of Band (OOB) (C.1)
@@ -188,6 +200,12 @@ TSPC_GAP_8A_7      False           AD Type-Security manager TK Value (O)
 TSPC_GAP_8A_8  False           AD Type-Slave Connection Interval Range (O)
 TSPC_GAP_8A_9  False           AD Type-Service Solicitation (O)
 TSPC_GAP_8A_10 False           AD Type-Service Data (O)
+TSPC_GAP_8A_11 False           AD Type-Appearance (O)
+TSPC_GAP_8A_12 False           AD Type-Public Target Address (O)
+TSPC_GAP_8A_13 False           AD Type-Random Target Address (O)
+TSPC_GAP_8A_14 False           AD Type-Advertising Interval (O)
+TSPC_GAP_8A_15 False           AD Type-LE Bluetooth Device Address (O)
+TSPC_GAP_8A_16 False           AD Type –LE Role (O)
 -------------------------------------------------------------------------------
 C.1: Optional if TSPC_SM_2_4 (OOB supported) is supported, otherwise Excluded.
 -------------------------------------------------------------------------------
@@ -207,8 +225,11 @@ Parameter Name     Selected        Description
 -------------------------------------------------------------------------------
 TSPC_GAP_10_1  False (*)       Broadcaster: Broadcast Mode
 TSPC_GAP_11_1  False           Broadcaster: Privacy Feature
+TSPC_GAP_11_1A False (*)       Broadcaster: Privacy Feature v1.1 (O)
 TSPC_GAP_11_2  False           Broadcaster: Resolvable Private Address
                                        Generation Procedure
+TSPC_GAP_11_3  False (*)       Broadcaster: Non-Resolvable Private Address
+                                       Generation Procedure (O)
 -------------------------------------------------------------------------------
 
 
@@ -216,7 +237,7 @@ TSPC_GAP_11_2       False           Broadcaster: Resolvable Private Address
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_12_1  True (#)        Observer: Receiver
+TSPC_GAP_12_1  False (*)       Observer: Receiver
 TSPC_GAP_12_2  False           Observer: Transmitter
 -------------------------------------------------------------------------------
 
@@ -225,8 +246,8 @@ TSPC_GAP_12_2       False           Observer: Transmitter
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_13_1  True (#)        Observer: Standby
-TSPC_GAP_13_2  True (#)        Observer: Scanning
+TSPC_GAP_13_1  False (*)       Observer: Standby
+TSPC_GAP_13_2  False (*)       Observer: Scanning
 -------------------------------------------------------------------------------
 
 
@@ -234,7 +255,7 @@ TSPC_GAP_13_2       True (#)        Observer: Scanning
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_14_1  True (#)        Observer: Passive Scanning
+TSPC_GAP_14_1  False (*)       Observer: Passive Scanning
 TSPC_GAP_14_2  False           Observer: Active Scanning
 -------------------------------------------------------------------------------
 
@@ -243,7 +264,7 @@ TSPC_GAP_14_2       False           Observer: Active Scanning
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_15_1  True (#)        Observer: Non-Connectable Mode
+TSPC_GAP_15_1  False (*)       Observer: Non-Connectable Mode
 -------------------------------------------------------------------------------
 
 
@@ -251,7 +272,7 @@ TSPC_GAP_15_1       True (#)        Observer: Non-Connectable Mode
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_16_1  True (#)        Observer: Observation Procedure
+TSPC_GAP_16_1  False (*)       Observer: Observation Procedure
 -------------------------------------------------------------------------------
 
 
@@ -260,6 +281,7 @@ TSPC_GAP_16_1       True (#)        Observer: Observation Procedure
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
 TSPC_GAP_17_1  False           Observer: Privacy Feature (O)
+TSPC_GAP_17_1A False           Observer: Privacy Feature v1.1 (O)
 TSPC_GAP_17_2  False           Observer: Non-Resolvable Private Address
                                        Generation Procedure (C.1)
 TSPC_GAP_17_3  False           Observer: Resolvable Private Address Resolution
@@ -303,8 +325,9 @@ TSPC_GAP_19_3       False           Peripheral: Connection, Slave Role
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_20_1  False           Peripheral: Connectable Undirected Event
-TSPC_GAP_20_2  False           Peripheral: Connectable Directed Event
+TSPC_GAP_20_1  False           Peripheral: Connectable Undirected Event (C.1)
+TSPC_GAP_20_2  False           Peripheral: Connectable Directed Event (C.2)
+TSPC_GAP_20_2A False           Peripheral: Low Duty Directed Advertising (C.3)
 TSPC_GAP_20_3  False           Peripheral: Non-Connectable Undirected Event
 TSPC_GAP_20_4  False           Peripheral: Scannable Undirected Event
 -------------------------------------------------------------------------------
@@ -324,6 +347,12 @@ TSPC_GAP_20A_7     False           AD Type-Security manager TK Value (C.1)
 TSPC_GAP_20A_8 False           AD Type-Slave Connection Interval Range (C.1)
 TSPC_GAP_20A_9 False           AD Type-Service Solicitation (C.1)
 TSPC_GAP_20A_10        False           AD Type-Service Data (C.1)
+TSPC_GAP_20A_11        False           AD Type –Appearance (C.1)
+TSPC_GAP_20A_12        False           AD Type-Public Target Address (C.1)
+TSPC_GAP_20A_13        False           AD Type-Random Target Address (C.1)
+TSPC_GAP_20A_14        False           AD Type-Advertising Interval (C.1)
+TSPC_GAP_20A_15        False           AD Type-LE Bluetooth Device Address (C.1)
+TSPC_GAP_20A_16        False           AD Type – LE Role (C.1)
 -------------------------------------------------------------------------------
 C.1: Optional if (TSPC_GAP_20_1 or TSPC_GAP_20_3 or TSPC_GAP_20_4) is
        supported, otherwise Excluded.
@@ -346,6 +375,11 @@ TSPC_GAP_21_3      False           Peripheral: Encryption Procedure (O)
 TSPC_GAP_21_4  False (*)       Peripheral: Feature Exchange Procedure (M)
 TSPC_GAP_21_5  False (*)       Peripheral: Version Exchange Procedure (M)
 TSPC_GAP_21_6  False (*)       Peripheral: Termination Procedure (M)
+TSPC_GAP_21_7  False (*)       Peripheral: LE Ping Procedure (C.3)
+TSPC_GAP_21_8  False (*)       Peripheral: Slave Initiated Feature Exchange
+                                       Procedure (C.4)
+TSPC_GAP_21_9  False (*)       Peripheral: Connection Parameter Request
+                                       Procedure (C.5)
 -------------------------------------------------------------------------------
 
 
@@ -427,10 +461,13 @@ C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported,
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
 TSPC_GAP_26_1  False           Peripheral: Privacy Feature (O)
+TSPC_GAP_26_1A False           Peripheral: Privacy Feature v1.1 (O)
 TSPC_GAP_26_2  False           Peripheral: Non-Resolvable Private Address
                                        Generation Procedure (C.1)
 TSPC_GAP_26_3  False           Peripheral: Resolvable Private Address
                                        Generation Procedure (C.2)
+TSPC_GAP_26_4  False           Peripheral: Resolvable Private Address
+                                       Generation Procedure (C.4)
 -------------------------------------------------------------------------------
 C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded.
 C.2: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded.
@@ -462,8 +499,8 @@ C.2: Optional if TSPC_GAP_26_1 and TSPC_GAP_27_3 are supported,
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_28_1  True (*#)       Central: Transmitter (M)
-TSPC_GAP_28_2  True (*#)       Central: Receiver (M)
+TSPC_GAP_28_1  True (*       Central: Transmitter (M)
+TSPC_GAP_28_2  True (*       Central: Receiver (M)
 -------------------------------------------------------------------------------
 
 
@@ -471,10 +508,10 @@ TSPC_GAP_28_2     True (*#)       Central: Receiver (M)
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_29_1  True (*#)       Central: Standby (M)
-TSPC_GAP_29_2  True (*#)       Central: Scanning (M)
-TSPC_GAP_29_3  True (*#)       Central: Initiating (M)
-TSPC_GAP_29_4  True (*#)       Central: Connection, Master Role (M)
+TSPC_GAP_29_1  True (*       Central: Standby (M)
+TSPC_GAP_29_2  True (*       Central: Scanning (M)
+TSPC_GAP_29_3  True (*       Central: Initiating (M)
+TSPC_GAP_29_4  True (*       Central: Connection, Master Role (M)
 -------------------------------------------------------------------------------
 
 
@@ -482,8 +519,8 @@ TSPC_GAP_29_4       True (*#)       Central: Connection, Master Role (M)
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_30_1  True (*#)       Central: Passive Scanning (O)
-TSPC_GAP_30_2  True (*#)       Central: Active Scanning (C.1)
+TSPC_GAP_30_1  True (*       Central: Passive Scanning (O)
+TSPC_GAP_30_2  True (*       Central: Active Scanning (C.1)
 -------------------------------------------------------------------------------
 C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_38_4) is supported.
        Optional if TSPC_GAP_30_1 and (TSPC_GAP_5_4 OR TSPC_GAP_38_4)
@@ -495,12 +532,17 @@ C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_38_4) is supported.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_31_1  True (*#)       Central: Connection Update Procedure (M)
-TSPC_GAP_31_2  True (*#)       Central: Channel Map Update Procedure (M)
-TSPC_GAP_31_3  True (*#)       Central: Encryption Procedure (O)
-TSPC_GAP_31_4  True (*#)       Central: Feature Exchange Procedure (M)
-TSPC_GAP_31_5  True (*#)       Central: Version Exchange Procedure (M)
-TSPC_GAP_31_6  True (*#)       Central: Termination Procedure (M)
+TSPC_GAP_31_1  True () Central: Connection Update Procedure (M)
+TSPC_GAP_31_2  True () Central: Channel Map Update Procedure (M)
+TSPC_GAP_31_3  True (*)        Central: Encryption Procedure (O)
+TSPC_GAP_31_4  True () Central: Feature Exchange Procedure (M)
+TSPC_GAP_31_5  True () Central: Version Exchange Procedure (M)
+TSPC_GAP_31_6  True () Central: Termination Procedure (M)
+TSPC_GAP_31_7  True (*)        Central: LE Ping Procedure (C.1)
+TSPC_GAP_31_8  True (*)        Central: Slave Initiated Feature Exchange
+                                       Procedure (C.2)
+TSPC_GAP_31_9  True (*)        Central: Connection Parameter Request Procedure
+                                       (C.3)
 -------------------------------------------------------------------------------
 
 
@@ -508,9 +550,9 @@ TSPC_GAP_31_6       True (*#)       Central: Termination Procedure (M)
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_32_1  False           Central: Limited Discovery Procedure (C.2)
-TSPC_GAP_32_2  True (*#)       Central: General Discovery Procedure (C.1)
-TSPC_GAP_32_3  True (*#)       Central: Name Discovery Procedure (C.3)
+TSPC_GAP_32_1  True (*)        Central: Limited Discovery Procedure (C.2)
+TSPC_GAP_32_2  True (*       Central: General Discovery Procedure (C.1)
+TSPC_GAP_32_3  True (*       Central: Name Discovery Procedure (C.3)
 -------------------------------------------------------------------------------
 C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_1) is supported, else Excluded.
 C.2: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_2) is supported,
@@ -524,17 +566,17 @@ C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_4) is supported,
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_33_1  True (*#)       Central: Auto Connection Establishment
+TSPC_GAP_33_1  True (*       Central: Auto Connection Establishment
                                        Procedure (C.3)
-TSPC_GAP_33_2  True (*#)       Central: General Connection Establishment
+TSPC_GAP_33_2  True (*       Central: General Connection Establishment
                                        Procedure (C.1)
-TSPC_GAP_33_3  True (*#)       Central: Selective Connection Establishment
+TSPC_GAP_33_3  True (*       Central: Selective Connection Establishment
                                        Procedure (C.3)
-TSPC_GAP_33_4  True (*#)       Central: Direct Connectin Establishment
+TSPC_GAP_33_4  True (*       Central: Direct Connectin Establishment
                                        Procedure (C.2)
-TSPC_GAP_33_5  True (*#)       Central: Connection Parameter Update Procedure
+TSPC_GAP_33_5  True (*       Central: Connection Parameter Update Procedure
                                        (C.2)
-TSPC_GAP_33_6  True (*#)       Central: Terminate Connection Procedure
+TSPC_GAP_33_6  True (*       Central: Terminate Connection Procedure
                                        (C.2)
 -------------------------------------------------------------------------------
 C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) and TSPC_GAP_36_1 is
@@ -550,9 +592,9 @@ C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported,
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_34_1  False           Central: Non-Bondable Mode (C.1)
-TSPC_GAP_34_2  True (*#)       Central: Bondable Mode (C.2)
-TSPC_GAP_34_3  True (*#)       Central: Bonding Procedure (O)
+TSPC_GAP_34_1  True (*)        Central: Non-Bondable Mode (C.1)
+TSPC_GAP_34_2  True (*       Central: Bondable Mode (C.2)
+TSPC_GAP_34_3  True (*       Central: Bonding Procedure (O)
 -------------------------------------------------------------------------------
 C.1: Mandatory if (TSPC_GAP_5_4 or 39/5) is supported, otherwise Excluded.
 C.2: Optional if (TSPC_GAP_5_4 or 39/6) is supported, otherwise Excluded.
@@ -563,15 +605,15 @@ C.2: Optional if (TSPC_GAP_5_4 or 39/6) is supported, otherwise Excluded.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_35_1  True (*#)       Central: Security Mode 1 (O)
-TSPC_GAP_35_2  True (*#)       Central: Security Mode 2 (O)
-TSPC_GAP_35_3  True (*#)       Central: Authentication Procedure (O)
-TSPC_GAP_35_4  True (*#)       Central: Authorization Procedure (O)
-TSPC_GAP_35_5  True (*#)       Central: Connection Data Signing Procedure (O)
-TSPC_GAP_35_6  True (*#)       Central: Authenticate Signed Data Procedure (O)
-TSPC_GAP_35_7  False           Central: Authenticated Pairing
+TSPC_GAP_35_1  True (*       Central: Security Mode 1 (O)
+TSPC_GAP_35_2  True (*       Central: Security Mode 2 (O)
+TSPC_GAP_35_3  True (*       Central: Authentication Procedure (O)
+TSPC_GAP_35_4  True (*       Central: Authorization Procedure (O)
+TSPC_GAP_35_5  True (*       Central: Connection Data Signing Procedure (O)
+TSPC_GAP_35_6  True (*       Central: Authenticate Signed Data Procedure (O)
+TSPC_GAP_35_7  True (*)        Central: Authenticated Pairing
                                        (LE security mode 1 level 3) (C.1)
-TSPC_GAP_35_8  False           Central: Unauthenticated Pairing
+TSPC_GAP_35_8  True (*)        Central: Unauthenticated Pairing
                                        (LE security mode 1 level 2) (C.1)
 -------------------------------------------------------------------------------
 C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded.
@@ -582,13 +624,16 @@ C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_36_1  False           Central: Privacy Feature (C.3)
-TSPC_GAP_36_2  False           Central: Non-Resolvable Private Address
+TSPC_GAP_36_1  True (*)        Central: Privacy Feature (C.3)
+TSPC_GAP_36_1A True (*)        Central: Privacy Feature v1.1 (C.4)
+TSPC_GAP_36_2  True (*)        Central: Non-Resolvable Private Address
                                        Generation Procedure (C.1)
-TSPC_GAP_36_3  False           Central: Resolvable Private Address Resolution
+TSPC_GAP_36_3  True (*)        Central: Resolvable Private Address Resolution
                                        Procedure (C.2)
-TSPC_GAP_36_4  False           Central: Write to Privacy Characteristic
+TSPC_GAP_36_4  True (*)        Central: Write to Privacy Characteristic
                                        (Enable/Disable Privacy) (O)
+TSPC_GAP_36_5  True (*)        Central: Resolvable Private Address Generation
+                                       Procedure (C.6)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_GAP_36_1 and TSPC_GAP_30_2 are supported,
        otherwise Excluded.
@@ -600,8 +645,8 @@ C.2: Mandatory if TSPC_GAP_36_1 is supported, otherwise Excluded.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_37_1  True (*#)       Central: Device Name (M)
-TSPC_GAP_37_2  True (*#)       Central: Appearance (M)
+TSPC_GAP_37_1  True (*       Central: Device Name (M)
+TSPC_GAP_37_2  True (*       Central: Appearance (M)
 -------------------------------------------------------------------------------
 
 
@@ -610,9 +655,9 @@ TSPC_GAP_37_2       True (*#)       Central: Appearance (M)
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
 TSPC_GAP_38_1  False           BR/EDR/LE: Broadcaster (C.1)
-TSPC_GAP_38_2  True (*#)       BR/EDR/LE: Observer (C.1)
+TSPC_GAP_38_2  False           BR/EDR/LE: Observer (C.1)
 TSPC_GAP_38_3  False           BR/EDR/LE: Peripheral (C.1)
-TSPC_GAP_38_4  True (*#)       BR/EDR/LE: Central (C.1)
+TSPC_GAP_38_4  True (*       BR/EDR/LE: Central (C.1)
 -------------------------------------------------------------------------------
 C.1: It is mandatory to support at least one of the defined roles.
 This table is applicable for BR/EDR/LE configurations. For LE-only
@@ -624,12 +669,12 @@ configurations, see 'LE Roles' table for role declarations.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_39_1  True (*#)       Central BR/EDR/LE: Non-Discoverable Mode (C.1)
-TSPC_GAP_39_2  True (*#)       Central BR/EDR/LE: Discoverable Mode (C.2)
-TSPC_GAP_39_3  True (*#)       Central BR/EDR/LE: Non-Connectable Mode (C.3)
-TSPC_GAP_39_4  True (#)        Central BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_39_1  True (*       Central BR/EDR/LE: Non-Discoverable Mode (C.1)
+TSPC_GAP_39_2  True (*       Central BR/EDR/LE: Discoverable Mode (C.2)
+TSPC_GAP_39_3  True (*       Central BR/EDR/LE: Non-Connectable Mode (C.3)
+TSPC_GAP_39_4  True () Central BR/EDR/LE: Connectable Mode (M)
 TSPC_GAP_39_5  False           Central BR/EDR/LE: Non-Bondable Mode (C.4)
-TSPC_GAP_39_6  True (*#)       Central BR/EDR/LE: Bondable Mode (C.5)
+TSPC_GAP_39_6  True (*       Central BR/EDR/LE: Bondable Mode (C.5)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
 C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR,
@@ -644,11 +689,11 @@ C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_40_1  True (*#)       Central BR/EDR/LE: General Discovery (C.1)
-TSPC_GAP_40_2  False           Central BR/EDR/LE: Limited Discovery (C.2)
-TSPC_GAP_40_3  True (*#)       Central BR/EDR/LE: Device Type Discovery (C.3)
-TSPC_GAP_40_4  True (*#)       Central BR/EDR/LE: Name Discovery (C.4)
-TSPC_GAP_40_5  True (*#)       Central BR/EDR/LE: Link Establishment (C.5)
+TSPC_GAP_40_1  True (*       Central BR/EDR/LE: General Discovery (C.1)
+TSPC_GAP_40_2  True (*)        Central BR/EDR/LE: Limited Discovery (C.2)
+TSPC_GAP_40_3  True (*       Central BR/EDR/LE: Device Type Discovery (C.3)
+TSPC_GAP_40_4  True (*       Central BR/EDR/LE: Name Discovery (C.4)
+TSPC_GAP_40_5  True (*       Central BR/EDR/LE: Link Establishment (C.5)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_GAP_3_1 is supported over BR/EDR, otherwise Excluded.
 C.2: Mandatory if TSPC_GAP_3_2 is supported over BR/EDR, otherwise Excluded.
@@ -664,7 +709,7 @@ C.5: Mandatory if (TSPC_GAP_4_1 or TSPC_GAP_4_2) is supported over BR/EDR,
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_41_1  True (#)        Central BR/EDR/LE: Security Aspects (M)
+TSPC_GAP_41_1  True () Central BR/EDR/LE: Security Aspects (M)
 -------------------------------------------------------------------------------
 
 
@@ -672,15 +717,12 @@ TSPC_GAP_41_1     True (#)        Central BR/EDR/LE: Security Aspects (M)
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_42_1  False           Peripheral BR/EDR/LE: Non-Discoverable Mode
-                               (C.1)
-TSPC_GAP_42_2  False           Peripheral BR/EDR/LE: Discoverable Mode
-                               (C.2)
-TSPC_GAP_42_3  False           Peripheral BR/EDR/LE: Non-Connectable Mode
-                               (C.3)
-TSPC_GAP_42_4  False (*)       Peripheral BR/EDR/LE: Connectable Mode  (M)
-TSPC_GAP_42_5  False           Peripheral BR/EDR/LE: Non-Bondable Mode (C.4)
-TSPC_GAP_42_6  False           Peripheral BR/EDR/LE: Bondable Mode (C.5)
+TSPC_GAP_42_1  False           Peripheral BR/EDR/LE: Non-Discoverable Mode (See Spec)
+TSPC_GAP_42_2  False           Peripheral BR/EDR/LE: Discoverable Mode (See Spec)
+TSPC_GAP_42_3  False           Peripheral BR/EDR/LE: Non-Connectable Mode (See Spec)
+TSPC_GAP_42_4  False (*)       Peripheral BR/EDR/LE: Connectable Mode (M)
+TSPC_GAP_42_5  False           Peripheral BR/EDR/LE: Non-Bondable Mode (See Spec)
+TSPC_GAP_42_6  False           Peripheral BR/EDR/LE: Bondable Mode (See Spec)
 -------------------------------------------------------------------------------
 C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded.
 C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR,
@@ -695,20 +737,42 @@ C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_GAP_43_1  False (*)       Peripheral BR/EDR/LE: Security Aspects (M)
+TSPC_GAP_43_1  False (*)       Peripheral BR/EDR/LE: Non-Discoverable Mode
 -------------------------------------------------------------------------------
 
 
+               Central Simultaneous BR/EDR and LE Transports
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_SM_1_1    False           Master Role (Initiator)
-TSPC_SM_1_2    False           Slave Role (Responder)
-TSPC_SM_2_4    False           OOB supported (O)
-TSPC_ALL       False           Turns on all
+TSPC_GAP_44_1  True (*)        Central BR/EDR/LE: Simultaneous BR/EDR and LE
+                                       Transports – BR/EDR Slave to the same
+                                       device (O)
+TSPC_GAP_44_2  True (*)        Central BR/EDR/LE: Simultaneous BR/EDR and LE
+                                       Transports – BR/EDR Master to the same
+                                       device (O)
+-------------------------------------------------------------------------------
+
+
+               Peripheral Simultaneous BR/EDR and LE Transports
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_45_1  False           Simultaneous BR/EDR and LE Transports – BR/EDR
+                                       Slave to the same device (C.1)
+TSPC_GAP_45_2  False           Simultaneous BR/EDR and LE Transports – BR/EDR
+                                       Master to the same device (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if ((SUM ICS 31/14 (Core Spec Version 4.1) or SUM ICS 31/15
+(Core Spec Version 4.1+HS)) is supported, otherwise Excluded.
 -------------------------------------------------------------------------------
 
 
 -------------------------------------------------------------------------------
-No special PIXIT settings required. All should be set according to Tester's
-test environment.
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_SM_1_1    True (*)        Master Role (Initiator)
+TSPC_SM_1_2    True (*)        Slave Role (Responder)
+TSPC_SM_2_4    True (*)        OOB supported (O)
+TSPC_ALL       False           Turns on all
+-------------------------------------------------------------------------------
diff --git a/android/pics-gatt.txt b/android/pics-gatt.txt
new file mode 100644 (file)
index 0000000..f3e8199
--- /dev/null
@@ -0,0 +1,322 @@
+GATT PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+               Generic Attribute Profile Role
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GATT_1_1  True            Generic Attribute Profile Client (C.1)
+TSPC_GATT_1_2  True            Generic Attribute Profile Server (C.2)
+TSPC_GATT_1A_1 False (*)       Complete GATT client (C.3)
+TSPC_GATT_1A_2 False (*)       Complete GATT server (C.4)
+-------------------------------------------------------------------------------
+C.1: Optional to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory
+       to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
+C.2: Mandatory to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is
+       mandatory to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
+C.3: Optional IF TSPC_GATT_1_1 is supported, otherwise Excluded
+C.4: Optional IF TSPC_GATT_1_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+               ATT Bearer Transport
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GATT_2_1  True            Attribute Protocol Supported over BR/EDR
+                                       (L2CAP fixed channel support) (C.1)
+TSPC_GATT_2_2  True            Attribute Protocol Supported over LE (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (SUM ICS 12/2 OR SUM ICS 12/9) is supported, otherwise
+       Excluded
+C.2: Mandatory IF (SUM ICS 12/7 OR SUM ICS 12/9) is supported, otherwise
+       Excluded
+-------------------------------------------------------------------------------
+
+
+
+               Generic Attribute Profile Support
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GATT_3_1  True            Client: Exchange MTU (C.1)
+TSPC_GATT_3_2  True            Client: Discover All Primary Services (C.1)
+TSPC_GATT_3_3  True            Client: Discover Primary Services Service
+                                       UUID (C.1)
+TSPC_GATT_3_4  True            Client: Find Included Services (C.1)
+TSPC_GATT_3_5  True            Client: Discover All characteristics of a
+                                       Service (C.1)
+TSPC_GATT_3_6  True            Client: Discover Characteristics by UUID (C.1)
+TSPC_GATT_3_7  True            Client: Discover All Characteristic Descriptors
+                                       (C.1)
+TSPC_GATT_3_8  True            Client: Read Characteristic Value (C.1)
+TSPC_GATT_3_9  True            Client: Read using Characteristic UUID (C.1)
+TSPC_GATT_3_10 True            Client: Read Long Characteristic Values (C.1)
+TSPC_GATT_3_11 True            Client: Read Multiple Characteristic
+                                       Values (C.1)
+TSPC_GATT_3_12 True            Client: Write without Response (C.1)
+TSPC_GATT_3_13 True            Client: Signed Write Without Response (C.1)
+TSPC_GATT_3_14 True            Client: Write Characteristic Value (C.1)
+TSPC_GATT_3_15 True            Client: Write Long Characteristic Values (C.1)
+TSPC_GATT_3_16 True            Client: Characteristic Value Reliable
+                                       Writes (C.1)
+TSPC_GATT_3_17 True            Client: Notifications (C.1)
+TSPC_GATT_3_18 True            Client: Indications (M)
+TSPC_GATT_3_19 True            Client: Read Characteristic Descriptors (C.1)
+TSPC_GATT_3_20 True            Client: Read long Characteristic Descriptors
+                                       (C.1)
+TSPC_GATT_3_21 True            Client: Write Characteristic Descriptors (C.1)
+TSPC_GATT_3_22 True            Client: Write Long Characteristic Descriptors
+                                       (C.1)
+TSPC_GATT_3_23 True            Client: Service Changed Characteristic (M)
+TSPC_GATT_3B_1 True            Client: Primary Service Declaration (M)
+TSPC_GATT_3B_2 True            Client: Secondary Service Declaration (M)
+TSPC_GATT_3B_3 True            Client: Include Declaration (M)
+TSPC_GATT_3B_4 True            Client: Characteristic Declaration (M)
+TSPC_GATT_3B_5 True            Client: Characteristic Value Declaration (M)
+TSPC_GATT_3B_6 True            Client: Characteristic Extended Properties (M)
+TSPC_GATT_3B_7 True            Client: Characteristic User Description
+                                       Descriptor (M)
+TSPC_GATT_3B_8 True            Client: Client Characteristic Configuration
+                                       Descriptor (M)
+TSPC_GATT_3B_9 True            Client: Server Characteristic Configuration
+                                       Descriptor (M)
+TSPC_GATT_3B_10        True            Client: Characteristic Format Descriptor (M)
+TSPC_GATT_3B_11        True            Client: Characteristic Aggregate Format
+                                       Descriptor (M)
+TSPC_GATT_3B_12        True            Client: Characteristic Format: Boolean (M)
+TSPC_GATT_3B_13        True            Client: Characteristic Format: 2Bit (M)
+TSPC_GATT_3B_14        True            Client: Characteristic Format: nibble (M)
+TSPC_GATT_3B_15        True            Client: Characteristic Format: Uint8 (M)
+TSPC_GATT_3B_16        True            Client: Characteristic Format: Uint12 (M)
+TSPC_GATT_3B_17        True            Client: Characteristic Format: Uint16 (M)
+TSPC_GATT_3B_18        True            Client: Characteristic Format: Uint24 (M)
+TSPC_GATT_3B_19        True            Client: Characteristic Format: Uint32 (M)
+TSPC_GATT_3B_20        True            Client: Characteristic Format: Uint48 (M)
+TSPC_GATT_3B_21        True            Client: Characteristic Format: Uint64 (M)
+TSPC_GATT_3B_22        True            Client: Characteristic Format: Uint128 (M)
+TSPC_GATT_3B_23        True            Client: Characteristic Format: Sint8 (M)
+TSPC_GATT_3B_24        True            Client: Characteristic Format: Sint12 (M)
+TSPC_GATT_3B_25        True            Client: Characteristic Format: Sint16 (M)
+TSPC_GATT_3B_26        True            Client: Characteristic Format: Sint24 (M)
+TSPC_GATT_3B_27        True            Client: Characteristic Format: Sint32 (M)
+TSPC_GATT_3B_28        True            Client: Characteristic Format: Sint48 (M)
+TSPC_GATT_3B_29        True            Client: Characteristic Format: Sint64 (M)
+TSPC_GATT_3B_30        True            Client: Characteristic Format: Sint128 (M)
+TSPC_GATT_3B_31        True            Client: Characteristic Format: Float32 (M)
+TSPC_GATT_3B_32        True            Client: Characteristic Format: Float64 (M)
+TSPC_GATT_3B_33        True            Client: Characteristic Format: SFLOAT (M)
+TSPC_GATT_3B_34        True            Client: Characteristic Format: FLOAT (M)
+TSPC_GATT_3B_35        True            Client: Characteristic Format: Duint16 (M)
+TSPC_GATT_3B_36        True            Client: Characteristic Format: utf8s (M)
+TSPC_GATT_3B_37        True            Client: Characteristic Format: utf16s (M)
+TSPC_GATT_3B_38        True            Client: Characteristic Format: struct (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_1A_1 is supported, otherwise Optional
+-------------------------------------------------------------------------------
+
+
+
+               Generic Attribute Profile Support, by Server
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GATT_4_1  True            Server: Exchange MTU (C.4)
+TSPC_GATT_4_2  True            Server: Discover All Primary Services (M)
+TSPC_GATT_4_3  True            Server: Discover Primary Services Service
+                                       UUID (M)
+TSPC_GATT_4_4  True            Server: Find Included Services (M)
+TSPC_GATT_4_5  True            Server: Discover All characteristics of
+                                       a Service (M)
+TSPC_GATT_4_6  True            Server: Discover Characteristics by UUID (M)
+TSPC_GATT_4_7  True            Server: Discover All Characteristic
+                                       Descriptors (M)
+TSPC_GATT_4_8  True            Server: Read Characteristic Value (M)
+TSPC_GATT_4_9  True            Server: Read using Characteristic UUID (M)
+TSPC_GATT_4_10 True            Server: Read Long Characteristic Values (C.4)
+TSPC_GATT_4_11 True            Server: Read Multiple Characteristic
+                                       Values (C.4)
+TSPC_GATT_4_12 True            Server: Write without Response (C.2)
+TSPC_GATT_4_13 False (*)       Server: Signed Write Without Response (C.4)
+TSPC_GATT_4_14 True            Server: Write Characteristic Value (C.3)
+TSPC_GATT_4_15 True            Server: Write Long Characteristic Values (C.4)
+TSPC_GATT_4_16 True            Server: Characteristic Value Reliable
+                                       Writes (C.4)
+TSPC_GATT_4_17 True            Server: Notifications (C.4)
+TSPC_GATT_4_18 True            Server: Indications (C.1)
+TSPC_GATT_4_19 True            Server: Read Characteristic Descriptors (C.4)
+TSPC_GATT_4_20 True            Server: Read long Characteristic
+                                       Descriptors (C.4)
+TSPC_GATT_4_21 True            Server: Write Characteristic Descriptors (C.4)
+TSPC_GATT_4_22 True            Server: Write Long Characteristic
+                                       Descriptors (C.4)
+TSPC_GATT_4_23 True            Server: Service Changed Characteristic (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF service definitions on the server can be added, changed, or
+       removed, otherwise Optional
+C.2: Mandatory IF GATT TSPC_GATT_4_13 is supported, otherwise Optional
+C.3: Mandatory IF GATT TSPC_GATT_4_15 is supported, otherwise Optional
+C.4: Mandatory IF GATT TSPC_GATT_1A_2 is supported, otherwise Optional
+-------------------------------------------------------------------------------
+
+
+               Profile Attribute Types and Characteristic Formats
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GATT_4B_1 True            Server: Primary Service Declaration (M)
+TSPC_GATT_4B_2 True            Server: Secondary Service Declaration (M)
+TSPC_GATT_4B_3 True            Server: Include Declaration (M)
+TSPC_GATT_4B_4 True            Server: Characteristic Declaration (M)
+TSPC_GATT_4B_5 True            Server: Characteristic Value Declaration (M)
+TSPC_GATT_4B_6 True            Server: Characteristic Extended Properties (M)
+TSPC_GATT_4B_7 True            Server: Characteristic User Description
+                                       Descriptor (M)
+TSPC_GATT_4B_8 True            Server: Client Characteristic Configuration
+                                       Descriptor (M)
+TSPC_GATT_4B_9 True            Server: Server Characteristic Configuration
+                                       Descriptor (M)
+TSPC_GATT_4B_10        True            Server: Characteristic Format Descriptor (M)
+TSPC_GATT_4B_11        True            Server: Characteristic Aggregate Format
+                                       Descriptor (M)
+TSPC_GATT_4B_12        True            Server: Characteristic Format: Boolean (M)
+TSPC_GATT_4B_13        True            Server: Characteristic Format: 2Bit (M)
+TSPC_GATT_4B_14        True            Server: Characteristic Format: nibble (M)
+TSPC_GATT_4B_15        True            Server: Characteristic Format: Uint8 (M)
+TSPC_GATT_4B_16        True            Server: Characteristic Format: Uint12 (M)
+TSPC_GATT_4B_17        True            Server: Characteristic Format: Uint16 (M)
+TSPC_GATT_4B_18        True            Server: Characteristic Format: Uint24 (M)
+TSPC_GATT_4B_19        True            Server: Characteristic Format: Uint32 (M)
+TSPC_GATT_4B_20        True            Server: Characteristic Format: Uint48 (M)
+TSPC_GATT_4B_21        True            Server: Characteristic Format: Uint64 (M)
+TSPC_GATT_4B_22        True            Server: Characteristic Format: Uint128 (M)
+TSPC_GATT_4B_23        True            Server: Characteristic Format: Sint8 (M)
+TSPC_GATT_4B_24        True            Server: Characteristic Format: Sint12 (M)
+TSPC_GATT_4B_25        True            Server: Characteristic Format: Sint16 (M)
+TSPC_GATT_4B_26        True            Server: Characteristic Format: Sint24 (M)
+TSPC_GATT_4B_27        True            Server: Characteristic Format: Sint32 (M)
+TSPC_GATT_4B_28        True            Server: Characteristic Format: Sint48 (M)
+TSPC_GATT_4B_29        True            Server: Characteristic Format: Sint64 (M)
+TSPC_GATT_4B_30        True            Server: Characteristic Format: Sint128 (M)
+TSPC_GATT_4B_31        True            Server: Characteristic Format: Float32 (M)
+TSPC_GATT_4B_32        True            Server: Characteristic Format: Float64 (M)
+TSPC_GATT_4B_33        True            Server: Characteristic Format: SFLOAT (M)
+TSPC_GATT_4B_34        True            Server: Characteristic Format: FLOAT (M)
+TSPC_GATT_4B_35        True            Server: Characteristic Format: Duint16 (M)
+TSPC_GATT_4B_36        True            Server: Characteristic Format: utf8s (M)
+TSPC_GATT_4B_37        True            Server: Characteristic Format: utf16s (M)
+TSPC_GATT_4B_38        True            Server: Characteristic Format: struct (M)
+-------------------------------------------------------------------------------
+
+
+               Generic Attribute Profile Service
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GATT_6_2  True            Discover GATT Services using Service Discovery
+                                       Profile (C.1)
+TSPC_GATT_6_3  True            Publish SDP record for GATT services support
+                                       via BR/EDR (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_1_1 is supported, otherwise Excluded
+C.2: Mandatory IF TSPC_GATT_1_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+               Attribute Protocol Transport Security
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GATT_7_1  True            Security Mode 4 (C.1)
+TSPC_GATT_7_2  True            LE Security Mode 1 (C.2)
+TSPC_GATT_7_3  True            LE Security Mode 2 (C.2)
+TSPC_GATT_7_4  True            LE Authentication Procedure (C.2)
+TSPC_GATT_7_5  True            LE connection data signing procedure (C.2)
+TSPC_GATT_7_6  True            LE Authenticate signed data procedure (C.2)
+TSPC_GATT_7_7  False (*)       LE Authorization Procedure (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF TSPC_GATT_2_1 is supported, otherwise Excluded
+C.2: Optional IF TSPC_GATT_2_2 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+               Attribute Protocol Client Messages
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_ATT_3_1   True            Attribute Error Response (M)
+TSPC_ATT_3_2   False (*)       Exchange MTU Request (O)
+TSPC_ATT_3_4   False (*)       Find Information Request (O)
+TSPC_ATT_3_6   False (*)       Find by Type Value Request (O)
+TSPC_ATT_3_8   False (*)       Read by Type Request (O)
+TSPC_ATT_3_10  False (*)       Read Request (O)
+TSPC_ATT_3_12  False (*)       Read Blob Request (O)
+TSPC_ATT_3_14  False (*)       Read Multiple Request (O)
+TSPC_ATT_3_16  False (*)       Read by Group Type Request (O)
+TSPC_ATT_3_17  False (*)       Read by Group Type Response (C.6)
+TSPC_ATT_3_18  False (*)       Write Request (O)
+TSPC_ATT_3_20  False (*)       Write Command (O)
+TSPC_ATT_3_21  False (*)       Signed Write Command (O)
+TSPC_ATT_3_22  False (*)       Prepare Write Request (O)
+TSPC_ATT_3_24  False (*)       Execute Write Request (C.8)
+TSPC_ATT_3_26  True            Handle Value Notification (M)
+-------------------------------------------------------------------------------
+C.6: Mandatory IF TSPC_ATT_3_16 is supported, otherwise Excluded
+C.8: Mandatory IF TSPC_ATT_3_22 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+               Attribute Protocol Server Messages
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_ATT_4_1   True            Attribute Error Response (M)
+TSPC_ATT_4_3   True            Exchange MTU Response (M)
+TSPC_ATT_4_5   True            Find Information Response (M)
+TSPC_ATT_4_7   True            Find by Type Value Response (M)
+TSPC_ATT_4_8   True            Read by Type Request (M)
+TSPC_ATT_4_9   True            Read by Type Response (M)
+TSPC_ATT_4_11  True            Read Response (M)
+TSPC_ATT_4_15  False (*)       Read Multiple Response (C.2)
+TSPC_ATT_4_17  True            Read by Group Type Response (M)
+TSPC_ATT_4_19  False (*)       Write Response (C.3)
+TSPC_ATT_4_20  False (*)       Write Command (O)
+TSPC_ATT_4_21  False (*)       Signed Write Command (O)
+TSPC_ATT_4_23  False (*)       Prepare Write Response (C.4)
+TSPC_ATT_4_25  False (*)       Execute Write Response (C.5)
+TSPC_ATT_4_26  False (*)       Handle Value Notification (O)
+-------------------------------------------------------------------------------
+C.2: Mandatory IF TSPC_ATT_4_14 is supported, otherwise Excluded
+C.3: Mandatory IF TSPC_ATT_4_18 is supported, otherwise Excluded
+C.4: Mandatory IF TSPC_ATT_4_22 is supported, otherwise Excluded
+C.5: Mandatory IF TSPC_ATT_4_27 is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+               Attribute Protocol Transport
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_ATT_5_2   False (*)       LE Security Mode 1 (C.2)
+TSPC_ATT_5_4   False (*)       LE Authentication Procedure (C.2)
+TSPC_ATT_5_7   False (*)       LE Authorization Procedure (C.2)
+-------------------------------------------------------------------------------
+C.2: Optional to support if 2/2 (Attribute Protocol Supported over LE),
+       otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+               Device Configuration
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_0_2   False (*)       LE (C.2)
+-------------------------------------------------------------------------------
+C.2: Mandatory IF (SUM ICS 34/2 (LE GAP) AND NOT SUM ICS 32/3 (BR/EDR GAP))
+       is supported, otherwise Excluded
+-------------------------------------------------------------------------------
diff --git a/android/pics-hdp.txt b/android/pics-hdp.txt
new file mode 100644 (file)
index 0000000..3d8020d
--- /dev/null
@@ -0,0 +1,307 @@
+HDP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+               Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_0_1   False           HDP 1.0 (C.1)
+TSPC_HDP_0_2   True            HDP 1.1(C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support only one Profile version.
+-------------------------------------------------------------------------------
+
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_1_1   True            Supports Source Role (C.1, C.2)
+TSPC_HDP_1_2   True (*)        Supports Sink Role (C.1, C.3)
+-------------------------------------------------------------------------------
+C.1: At least one of the defined roles is Mandatory.
+C.2: Mandatory if TSPC_MCAP_1_1 is supported, otherwise Excluded.
+C.3: Mandatory if TSPC_MCAP_1_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               GAP Features - Source
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_2_1   True            Supports General-discoverable Mode (M)
+TSPC_HDP_2_2   True            Supports bondable Mode (M) (C.1)
+TSPC_HDP_2_3   True            Supports Response to Authentication requests (M)
+TSPC_HDP_2_4   True            Supports Initiation of Authentication (M) (C.2)
+TSPC_HDP_2_5   True            Supports Acceptance of Encryption request (M)
+TSPC_HDP_2_6   True            Supports Initiation of Encryption (M) (C.3)
+TSPC_HDP_2_7   True (*)        Supports General Inquiry (C.5) (C.4)
+TSPC_HDP_2_8   True            Supports Acceptance of Bonding requests (M)
+TSPC_HDP_2_9   True (*)        Supports Initiation of Bonding (O)
+TSPC_HDP_2_10  True (*)        Supports Extended Inquiry Response (C.7)
+TSPC_HDP_2_11  True (*)        Supports use of Health Class of Device (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional.
+C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional.
+C.3: Mandatory if Bluetooth version 2.1 + EDR is claimed, otherwise Optional.
+C.4: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional.
+C.5: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional.
+C.6: Mandatory if Bluetooth Core Specification 2.1 + EDR or later
+       (Not SUM ICS 31/4) and Table 2/1 (Supports General-discoverable Mode)
+       is supported, otherwise Optional if Bluetooth Core Specification 2.1
+       + EDR or later (Not SUM ICS 31/4) is supported, otherwise Excluded.
+C.7: Mandatory if Bluetooth Core specification 2.1 + EDR or later is supported,
+       otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               L2CAP Features - Source
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_3_1   True            Supports Reliable Control Channel (C.1)
+TSPC_HDP_3_2   True            Uses FCS for Control Channel (M)
+TSPC_HDP_3_3   True            Supports Reliable Data Channel (C.1)
+TSPC_HDP_3_4   True            Can send data using SAR in ERTM (C.2)
+TSPC_HDP_3_5   False           Uses FCS for Reliable Data Channel (O)
+TSPC_HDP_3_6   False           Supports FCS option of "No FCS" for Reliable
+                               Data Channel (C.3)
+TSPC_HDP_3_7   True            Supports Streaming Data Channel (C.4)
+TSPC_HDP_3_8   False           Can send data using SAR in SM (C.5)
+TSPC_HDP_3_9   False           Uses FCS for Steaming Data Channel (C.6)
+TSPC_HDP_3_10  False           Supports FCS option of "No FCS" for Streaming
+                               (C.7)
+TSPC_HDP_3_11  True            Maximum number of simultaneous Data Channels
+                               supported (DCmax) per MCL (C.8)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_L2CAP_2_12 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_L2CAP_2_22 is supported, otherwise Excluded.
+C.3: Optional if TSPC_L2CAP_2_14 is supported, otherwise Excluded.
+C.4: Optional if TSPC_L2CAP_2_13 is supported, otherwise Excluded.
+C.5: Mandatory if TSPC_HDP_3_7 and TSPC_L2CAP_2_23 are supported, otherwise
+       Excluded.
+C.6: Optional if TSPC_HDP_3_7 is supported, otherwise Excluded.
+C.7: Optional if TSPC_HDP_3_7 and TSPC_L2CAP_2_14 are supported, otherwise
+       Excluded.
+C.8: >=2 if Table TSPC_HDP_3_7 is claimed, otherwise >=1.
+-------------------------------------------------------------------------------
+
+
+               SDP Attributes - Source
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_4_1   True            Supports advertisement of HDP Service Record
+                               (C.1) (C.4)
+TSPC_HDP_4_2   True            Service Class ID List (C.2)
+TSPC_HDP_4_3   True            Protocol Descriptor List (C.2)
+TSPC_HDP_4_4   True            Bluetooth Profile Descriptor List (C.2)
+TSPC_HDP_4_5   False           Additional Protocol Descriptor Lists (C.2)
+TSPC_HDP_4_6   True (*)        Service Name (O)
+TSPC_HDP_4_7   True (*)        Service Description (O)
+TSPC_HDP_4_8   True (*)        Provider Name (O)
+TSPC_HDP_4_9   True            HDP Supported Features (MDEP List) (C.3)
+TSPC_HDP_4_10  True            MCAP Data Exchange Specification (C.3)
+TSPC_HDP_4_11  True            MCAP Supported Procedures (C.3)
+TSPC_HDP_4_12  False           Service Record State (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HDP_6_3 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_HDP_4_1 is supported, otherwise Optional.
+C.3: Mandatory if TSPC_HDP_4_1 is supported, otherwise Excluded.
+C.4: Mandatory to support SDP Server Role (SDP 1b/1) if this item is supported.
+-------------------------------------------------------------------------------
+
+
+               Device Identification - Source
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_5_1   True            Device Identification Profile v1.3 or later
+                               (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HDP_4_1 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+               HDP Features - Source
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_6_1   True            Supports Standard Op Codes (M)
+TSPC_HDP_6_2   True (*)        Supports Initiate creation of Control and Data
+                               Channels (C.3) (C.7) (C.1 - MCAP Status)
+TSPC_HDP_6_3   True            Supports Accept creation of Control and Data
+                               Channels (C.3) (C.8) (C.1 - MCAP Status)
+TSPC_HDP_6_4   False           Supports Initiate Reconnection of MDL (O)
+                               (C.2 - MCAP Status)
+TSPC_HDP_6_5   True            Supports Accept Reconnection of MDL (C.4)
+TSPC_HDP_6_6   False           Supports Clock Synchronization Protocol (O)
+TSPC_HDP_6_7   False (*)       Supports Sync-Slave (C.5)
+TSPC_HDP_6_8   False           Supports Sync-Master (C.6)
+-------------------------------------------------------------------------------
+C.1: If TSPC_HDP_6_1 is supported, at least one is Mandatory, otherwise
+       Excluded.
+C.2: Optional if TSPC_HDP_6_1 is supported, otherwise Excluded.
+C.3: Mandatory to support at least one.
+C.4: Mandatory if TSPC_HDP_6_3 is supported, otherwise Excluded.
+
+C.5: Mandatory if TSPC_HDP_6_6 is supported, otherwise Excluded.
+C.6: Optional if TSPC_HDP_6_6 is supported, otherwise Excluded.
+C.7: Mandatory to support SDP Client Role (SDP 1b/2) if this item is supported.
+C.8: Mandatory to support SDP Server Role (SDP 1b/1) if this item is supported.
+-------------------------------------------------------------------------------
+
+
+               Data Exchange Features - Source
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_7_1   False           Supports Initiation of Echo Test (O)
+TSPC_HDP_7_2   True            Supports Acceptance of Echo Test (M)
+TSPC_HDP_7_3   True            Supports IEEE 11073-20601 (M)
+TSPC_HDP_7_4   True            Supports IEEE 11073-20601 Agent Role (C.1)
+TSPC_HDP_7_5   True (*)        Supports IEEE 11073-20601 Manager Role (C.1)
+TSPC_HDP_7_6   False           Supports Initiation of Association Release (O)
+-------------------------------------------------------------------------------
+C.1: If TSPC_HDP_7_3 is supported, at least one is Mandatory, otherwise
+       Excluded.
+-------------------------------------------------------------------------------
+
+
+               GAP Features - Sink
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_8_1   True            Supports General-discoverable Mode (M)
+TSPC_HDP_8_2   True            Supports Bondable Mode (M) (C.1)
+TSPC_HDP_8_3   True            Supports Response to Authentitaction requests
+                               (M)
+TSPC_HDP_8_4   True            Supports Initiation of Authentication (M) (C.2)
+TSPC_HDP_8_5   True            Supports Acceptance of Encryption request (M)
+TSPC_HDP_8_6   True            Supports Initiation of Encryption (M) (C.3)
+TSPC_HDP_8_7   True            Supports General Inquiry (M) C.4)
+TSPC_HDP_8_8   True            Supports Acceptance of Bonding requests (M)
+TSPC_HDP_8_9   Ture (*)        Supports Initiation of Bonding (O)
+TSPC_HDP_8_10  True (*)        Supports Extended Inquiry Response (C.5) (C.6)
+TSPC_HDP_8_11  True (*)        Supports use of Health Class of Device (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional.
+C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional.
+C.3: Mandatory if Bluetooth version 2.1 + EDR is claimed (Not SUM ICS 31/4),
+       otherwise Optional.
+C.4: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional.
+C.5: Mandatory if Bluetooth Core Specification 2.1 + EDR or later
+       (Not SUM ICS 31/4) and TSPC_HDP_8_1 is supported, otherwise Optional
+       if Bluetooth Core Specification 2.1 + EDR or later is supported,
+       otherwise Excluded.
+C.6: Mandatory if Bluetooth Core specification 2.1 + EDR or later
+       (Not SUM ICS 31/4) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+
+               L2CAP Features - Sink
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_9_1   True            Supports Reliable Control Channel (C.1)
+TSPC_HDP_9_2   True            Uses FCS for Control Channel (M)
+TSPC_HDP_9_3   True            Supports Reliable Data Channel (C.1)
+TSPC_HDP_9_4   True            Can send data using SAR in ERTM (C.2)
+TSPC_HDP_9_5   False           Uses FCS for Reliable Data Channel (O)
+TSPC_HDP_9_6   False           Supports FCS option of "No FCS" for Reliable
+                               Data Channel (C.3)
+TSPC_HDP_9_7   True            Supports Streaming Data Channel (C.4)
+TSPC_HDP_9_8   True            Can send data using SAR in SM (C.5)
+TSPC_HDP_9_9   False           Uses FCS for Steaming Data Channel (O)
+TSPC_HDP_9_10  False           Supports FCS option of "No FCS" for Streaming
+                               Data Channel (C.3)
+TSPC_HDP_9_11  True            Maximum number of simultaneous Data Channels
+                               supported (DCmax) per MCL (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_L2CAP_2_12 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_L2CAP_2_22 is supported, otherwise Excluded.
+C.3: Optional if TSPC_L2CAP_2_14 is supported, otherwise Excluded.
+C.4: Mandatory if TSPC_L2CAP_2_13 is supported, otherwise Excluded.
+C.5: Mandatory if TSPC_L2CAP_2_23 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               SDP Attributes - Sink
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_10_1  True            Supports advertisement of HDP Service Record (C.1)
+TSPC_HDP_10_2  True            Service Class ID List (M)
+TSPC_HDP_10_3  True            Protocol Descriptor List (M)
+TSPC_HDP_10_4  True            Bluetooth Profile Descriptor List (M)
+TSPC_HDP_10_5  True            Additional Protocol Descriptor Lists (M)
+TSPC_HDP_10_6  True (*)        Service Name (O)
+TSPC_HDP_10_7  False           Service Description (O)
+TSPC_HDP_10_8  False           Provider Name (O)
+TSPC_HDP_10_9  True            HDP Supported Features (MDEP List) (M)
+TSPC_HDP_10_10 True            MCAP Data Exchange Specification (M)
+TSPC_HDP_10_11 True            MCAP Supported Procedures (M)
+TSPC_HDP_10_12 False           Service Record State (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support 10/1 and SDP Server Role (SDP 1b/1).
+-------------------------------------------------------------------------------
+
+
+               Device Identification - Sink
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_11_1  True            Device Identification Profile v1.3 or later
+                               (M) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if 1/2 is supported.
+-------------------------------------------------------------------------------
+
+
+               HDP Features - Sink
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_12_1  True            Supports Standard Op Codes (M)
+TSPC_HDP_12_2  True            Supports Initiate creation of Control and Data
+                               Channels (C.1) (C.5)
+TSPC_HDP_12_3  True            Supports Accept creation of Control and Data
+                               Channels (C.1) (C.6)
+TSPC_HDP_12_4  False           Supports Initiate Reconnection of MDL (O) (C.2)
+TSPC_HDP_12_5  True            Supports Accept Reconnection of MDL (M)
+TSPC_HDP_12_6  False           Supports Clock Synchronization Protocol (O)
+TSPC_HDP_12_7  False           Supports Sync-Slave (C.3)
+TSPC_HDP_12_8  False           Supports Sync-Master (C.6)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HDP_12_1 is supported, otherwise Excluded.
+C.2: Optional if TSPC_HDP_12_1 is supported, otherwise Excluded.
+C.3: Mandatory if TSPC_HDP_12_6 is supported, otherwise Excluded.
+C.4: Optional if TSPC_HDP_12_6 is supported, otherwise Excluded.
+C.5: Mandatory to support 12/2 and SDP Client Role (SDP 1b/2).
+C.6: Mandatory to support 12/3 and SDP Server Role (SDP 1b/1).
+-------------------------------------------------------------------------------
+
+
+               Data Exchange Features - Sink
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HDP_13_1  False           Supports Initiation of Echo Test (O)
+TSPC_HDP_13_2  True            Supports Acceptance of Echo Test (M)
+TSPC_HDP_13_3  True            Supports IEEE 11073-20601 (M)
+TSPC_HDP_13_4  True (*)        Supports IEEE 11073-20601 Agent Role (C.1)
+TSPC_HDP_13_5  False           Supports IEEE 11073-20601 Manager Role (C.1)
+TSPC_HDP_13_6  False           Supports Initiation of Association Release (O)
+-------------------------------------------------------------------------------
+C.1: If TSPC_HDP_13_3 is supported, at least one is Mandatory, otherwise
+       Excluded.
+-------------------------------------------------------------------------------
diff --git a/android/pics-hfp.txt b/android/pics-hfp.txt
new file mode 100644 (file)
index 0000000..1a13edc
--- /dev/null
@@ -0,0 +1,217 @@
+HFP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Version
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HFP_0_1   False           Version: Hands-Free Profile v1.5 (O.1)
+TSPC_HFP_0_2   True (*)        Version: Hands-Free Profile v1.6 (O.1)
+-------------------------------------------------------------------------------
+O.1: It is mandatory to support only one of the adopted versions.
+-------------------------------------------------------------------------------
+
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HFP_1_1   True (*)        Role: Audio Gateway (AG) (O.1)
+TSPC_HFP_1_2   False           Role: Hands-Free (HF) (O.1)
+-------------------------------------------------------------------------------
+O.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+               Audio Gateway Role
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HFP_2_1   True            Connection management (M)
+TSPC_HFP_2_1a  True (*)        SLC initiation during active ongoing call (O)
+TSPC_HFP_2_2   True            Phone Status Information (M)
+TSPC_HFP_2_3   True            Audio connection handling (M)
+TSPC_HFP_2_3a  False           Audio connection establishment independent of
+                                       call processing (O)
+TSPC_HFP_2_3b  True (*)        eSCO support in Audio Connection (C.10)
+TSPC_HFP_2_3c  True (*)        Codec negotiation (C.7)
+TSPC_HFP_2_4a  False           Accept an incoming voice call
+                                       (in-band ring) (C.1)
+TSPC_HFP_2_4b  True (*)        Accept an incoming voice call
+                                       (no in-band ring) (C.1)
+TSPC_HFP_2_4c  False           Capability to change the "in-band ring"
+                                       settings (O)
+TSPC_HFP_2_5   True (*)        Reject an incoming voice call (O)
+TSPC_HFP_2_6   True            Terminate a call (M)
+TSPC_HFP_2_7   True            Audio connection transfer during an ongoing
+                                       call (M)
+TSPC_HFP_2_7a  True (*)        HF-initiated Audio transfer to AG during
+                                       ongoing call (O)
+TSPC_HFP_2_8   True            Place a call with a phone number supplied by
+                                       the HF (M)
+TSPC_HFP_2_9   True            Place a call using memory dialing (M)
+TSPC_HFP_2_10  True            Place a call to the last number dialed (M)
+TSPC_HFP_2_11  True            Call waiting notification (M)
+TSPC_HFP_2_12  True (*)        Three Way Calling (O)
+TSPC_HFP_2_12a True (*)        User Busy (AT+CHLD value 0) (C.3)
+TSPC_HFP_2_12b True (*)        Call Hold Handling (AT+CHLD value 1,2) (C.2)
+TSPC_HFP_2_12c True (*)        Three Way Call (AT+CHLD value 3) (C.3)
+TSPC_HFP_2_12d False           Explicit Call Transfer (AT+CHLD value 4) (C.3)
+TSPC_HFP_2_13  True            Calling Line Identification (CLI) (M)
+TSPC_HFP_2_14  True (*)        Echo canceling (EC) and Noise reduction (NR) (O)
+TSPC_HFP_2_15  True (*)        Voice recognition activation (O)
+TSPC_HFP_2_15a True (*)        Initiate voice recognition from AG (C.6)
+TSPC_HFP_2_15b True (*)        Autonomous voice deactivation (C.6)
+TSPC_HFP_2_16  False           Attach a phone number to a voice tag (O)
+TSPC_HFP_2_17  True            Ability to transmit DTMF codes (M)
+TSPC_HFP_2_18a True (*)        Remote audio volume control – speaker (O)
+TSPC_HFP_2_18b False           Remote audio volume control – microphone (O)
+TSPC_HFP_2_18c True (*)        Volume Level Synchronization – speaker and
+                                       microphone (C.5)
+TSPC_HFP_2_19  False           Response and hold (O)
+TSPC_HFP_2_20  True            Subscriber Number Information (M)
+TSPC_HFP_2_21a True            Enhanced Call Status (C.4)
+TSPC_HFP_2_21b False           Enhanced Call Control (C.3)
+TSPC_HFP_2_21c True (*)        Enhanced Call Status with limited network
+                                       notification (C.4)
+TSPC_HFP_2_22  False           Support for automatic link loss recovery (O)
+TSPC_HFP_2_23  True (*)        Individual Indicator Activation (C.9)
+TSPC_HFP_2_24  True (*)s       Wide Band Speech service (C.8)
+TSPC_HFP_2_25  False           Support roaming function (O)
+-------------------------------------------------------------------------------
+C.1:  The AG must support one of item TSPC_HFP_2_4a or TSPC_HFP_2_4b
+C.2:  Mandatory if TSPC_HFP_2_12is TRUE; otherwise excluded
+C.3:  Optional if TSPC_HFP_2_12 is TRUE; otherwise excluded
+C.4:  The AG must support one of item TSPC_HFP_2_21a or TSPC_HFP_2_21c
+C.5:  Mandatory if TSPC_HFP_2_18a or TSPC_HFP_2_18b; otherwise optional
+C.6:  Optional if TSPC_HFP_2_15 is supported, otherwise excluded
+C.7:  Mandatory if TSPC_HFP_2_24 otherwise excluded
+C.8:  Excluded if TSPC_HFP_0_1 otherwise optional
+C.9:  Excluded if TSPC_HFP_0_1 otherwise mandatory
+C.10: Mandatory if TSPC_HFP_2_24 otherwise optional
+-------------------------------------------------------------------------------
+
+
+               Hands-Free Role
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HFP_3_1   False (*)       Connection Management (M)
+TSPC_HFP_3_2a  False (*)       Phone Status Information ("service" and "call"
+                                       indicators) (M)
+TSPC_HFP_3_2b  False           Phone Status Information ("callsetup"
+                                       indicators) (O)
+TSPC_HFP_3_2c  False           Accept indicator of signal strength (O)
+TSPC_HFP_3_2d  False           Accept indicator of roaming state ("roam:") (O)
+TSPC_HFP_3_2e  False           Accept indicator of battery level ("battchg") (O)
+TSPC_HFP_3_2f  False           Accept indicator of operator selection (O)
+TSPC_HFP_3_3   False (*)       Audio connection handling (M)
+TSPC_HFP_3_3a  False           Audio connection establishment independent
+                                       of call processing (O)
+TSPC_HFP_3_3b  False           eSCO support in Audio Connection (C.7)
+TSPC_HFP_3_3c  False           Codec negotiation (C.5)
+TSPC_HFP_3_4a  False (*)       Accept an incoming voice call (in-band ring) (M)
+TSPC_HFP_3_4b  False (*)       Accept an incoming voice call (no in-band
+                                       ring) (M)
+TSPC_HFP_3_4c  False           Accept an incoming voice call (in-band ring
+                                       muting) (O)
+TSPC_HFP_3_5   False (*)       Reject an incoming voice call (M)
+TSPC_HFP_3_6   False (*)       Terminate a call (M)
+
+TSPC_HFP_3_7   False (*)       Audio connection transfer during an ongoing
+                                       call (M)
+TSPC_HFP_3_7a  False           HF-initiated Audio transfer to AG during
+                                       ongoing call (O)
+TSPC_HFP_3_8   False           Place a call with a phone number supplied by
+                                       the HF (O)
+TSPC_HFP_3_9   False           Place a call using memory dialing (O)
+TSPC_HFP_3_10  False           Place a call to the last number dialed (O)
+TSPC_HFP_3_11  False           Call waiting notification (O)
+TSPC_HFP_3_12  False           Three Way Calling (O)
+TSPC_HFP_3_12a False           Three way calling (AT+CHLD values 0) (C.2)
+TSPC_HFP_3_12b False           Three way calling (AT+CHLD values 1 and 2) (C.1)
+TSPC_HFP_3_12c False           Three way calling (AT+CHLD value 3) (C.2)
+TSPC_HFP_3_12d False           Three way calling (AT+CHLD value 4) (C.2)
+TSPC_HFP_3_12e False           Originate new call with established call in
+                                       progress (C.2)
+TSPC_HFP_3_13  False           Calling Line Identification (CLI) (O)
+TSPC_HFP_3_14  False           Echo cancelling (EC) and Noise reduction (NR) (O)
+TSPC_HFP_3_15  False           Voice recognition activation/deactivation (O)
+TSPC_HFP_3_16  False           Attach a phone number to a voice tag (O)
+TSPC_HFP_3_17  False           Ability to transmit DTMF codes (O)
+TSPC_HFP_3_18a False           Remote audio volume control – speaker (O)
+TSPC_HFP_3_18b False           Remote audio volume control – microphone (O)
+TSPC_HFP_3_18c False           Volume Level Synchronization – speaker (C.3)
+TSPC_HFP_3_18d False           Volume Level Synchronization – microphone (C.4)
+TSPC_HFP_3_18e False           HF informs AG about local changes of audio
+                                       volume (O)
+TSPC_HFP_3_18f False           HF informs AG about local changes of
+                                       microphone gain (O)
+TSPC_HFP_3_19  False           Response and hold (O)
+TSPC_HFP_3_20  False           Subscriber Number Information (O)
+TSPC_HFP_3_21a False           Enhanced Call Status (O)
+TSPC_HFP_3_21b False           Enhanced Call Control (C.2)
+TSPC_HFP_3_22  False           Support for automatic link loss recovery (O)
+TSPC_HFP_3_23  False           Individual Indicator Activation (C.6)
+TSPC_HFP_3_24  False           Wide Band Speech service (C.6)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HFP_3_12; otherwise excluded
+C.2: Optional if TSPC_HFP_3_12; otherwise excluded
+C.3: Mandatory if TSPC_HFP_3_18a or TSPC_HFP_3_18b, otherwise optional
+C.4: Mandatory if TSPC_HFP_3_18a, otherwise optional
+C.5: Mandatory if TSPC_HFP_3_24 otherwise excluded
+C.6: Excluded if TSPC_HFP_0_1 otherwise optional
+C.7: Mandatory if TSPC_HFP_3_24 otherwise optional
+-------------------------------------------------------------------------------
+
+
+               Audio Coding Requirements
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HFP_4_1   True            CVSD audio coding over SCO (M)
+TSPC_HFP_4_2   True (*)        mSBC audio coding over eSCO (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if Wide band speech service is supported TSPC_HFP_2_24 or
+       TSPC_HFP_3_24, otherwise excluded
+-------------------------------------------------------------------------------
+
+
+               Supplementary Interoperability Verification
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HFP_8_1   True (*)        Multiple audio transfers during call –
+                                       AG and HF initiated (C.1)
+TSPC_HFP_8_2   True (*)        Audio transfer by SLC release during
+                                       an active call (C.1)
+TSPC_HFP_8_3   True (*)        Audio transfer by powering ON HF (O)
+TSPC_HFP_8_4   True (*)        SLC during SDP response (O)
+TSPC_HFP_8_5   True (*)        Handle dynamic server channel number for HFP
+                                       service (O)
+TSPC_HFP_8_6   False           HF disallows connections in non-discoverable
+                                       mode (C.2)
+TSPC_HFP_8_7   True (*)        HF connects to AG during incoming call (O)
+TSPC_HFP_8_8   True (*)        Link loss during incoming call (C.3)
+TSPC_HFP_8_9   True (*)        SLC release during incoming call (C.3)
+TSPC_HFP_8_10  True (*)        Voice Recognition Activation (C.4)
+TSPC_HFP_8_11  True (*)        Place outgoing call by dialing number on
+                                       the AG (O)
+TSPC_HFP_8_12  True (*)        Active call termination – NO CARRIER signal
+                                       (C.5)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_HFP_2_7a or TSPC_HFP_3_7a is supported,
+       otherwise excluded
+C.2: Optional if TSPC_HFP_1_2 is supported, otherwise excluded
+C.3: Optional if TSPC_HFP_1_1 is supported, otherwise excluded
+C.4: Optional if TSPC_HFP_2_15 or TSPC_HFP_3_15 is supported,
+       otherwise excluded
+C.5: Optional if TSPC_HFP_2_6 is supported, otherwise excluded
+-------------------------------------------------------------------------------
index ba50626..f1645cc 100644 (file)
@@ -1,5 +1,7 @@
 HID PICS for the PTS tool.
 
+PTS version: 5.1
+
 * - different than PTS defaults
 # - not yet implemented/supported
 
@@ -283,8 +285,3 @@ TSPC_ALL    False           Enables all test cases when set to true.
 -------------------------------------------------------------------------------
 M.1: Mandatory IF (TSPC_HID_1_2) is supported.
 -------------------------------------------------------------------------------
-
-
--------------------------------------------------------------------------------
-No special PIXIT settings required. All should be set according to Tester's
-test environment.
diff --git a/android/pics-hsp.txt b/android/pics-hsp.txt
new file mode 100644 (file)
index 0000000..3c4962c
--- /dev/null
@@ -0,0 +1,103 @@
+HSP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Version
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HSP_0_1   False           Version: Headset Profile v1.1 (C.1)
+TSPC_HSP_0_2   True (*)        Version: Headset Profile v1.2 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support one and only one of these versions.
+-------------------------------------------------------------------------------
+
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HSP_1_1   True (*)        Role: Audio Gateway (AG) (C.1)
+TSPC_HSP_1_2   False           Role: Headset (HS) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+               Audio Gateway Role
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HSP_2_1   True            Incoming audio connection establishment (M)
+TSPC_HSP_2_2   True (*)        Ring (AT command) (C.3)
+TSPC_HSP_2_3   False           Inband ring tone (O)
+TSPC_HSP_2_4   True (*)        Outgoing audio connection establishment (O)
+TSPC_HSP_2_5   True (*)        Audio connection release from HS (C.5)
+TSPC_HSP_2_6   True            Audio connection release from AG (M)
+TSPC_HSP_2_7   True            Audio connection transfer: AG to HS (M)
+TSPC_HSP_2_8   True            Audio connection transfer: HS to AG (M)
+TSPC_HSP_2_9   True (*)        Remote audio volume control (C.1)
+TSPC_HSP_2_10  True (*)        HS informs AG about local changes of audio
+                                       volume (O)
+TSPC_HSP_2_11  True (*)        Audio volume setting storage by HS (O)
+TSPC_HSP_2_12  False           Remote microphone gain control (C.2)
+TSPC_HSP_2_13  False           HS informs AG about local changes of microphone
+                                       gain (O)
+TSPC_HSP_2_14  False           Microphone gain setting storage by HS (O)
+TSPC_HSP_2_15  True            Connection handling with Detach/Page (M)
+TSPC_HSP_2_16  False           Connection handling with Park Mode (C.4)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HSP_2_10 is supported, otherwise optional
+C:2: Mandatory if TSPC_HSP_2_13 is supported, otherwise optional
+C.3: Excluded if TSPC_HSP_2_3 and TSPC_HSP_4_1 ("Show that in-band
+       ringing and RING are mutually exclusive") are supported,
+       otherwise optional
+C.4: Excluded if TSPC_HSP_0_2 is supported, otherwise optional
+C.5: Mandatory if TSPC_HSP_0_1 is supported, otherwise optional
+-------------------------------------------------------------------------------
+
+
+               Headset Application Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HSP_3_1   False (*)       Incoming audio connection establishment (M)
+TSPC_HSP_3_2   False (*)       Ring (AT command) (M)
+TSPC_HSP_3_3   False (*)       Inband ring tone (M)
+TSPC_HSP_3_4   False (*)       Outgoing audio connection establishment (M)
+TSPC_HSP_3_5   False (*)       Audio connection release from HS (M)
+TSPC_HSP_3_6   False (*)       Audio connection release from AG (M)
+TSPC_HSP_3_7   False (*)       Audio connection transfer: AG to HS (M)
+TSPC_HSP_3_8   False (*)       Audio connection transfer: HS to AG (M)
+TSPC_HSP_3_9   False           Remote audio volume control (C.1)
+TSPC_HSP_3_10  False           HS informs AG about local changes of audio
+                                        volume (O)
+TSPC_HSP_3_11  False           Audio volume setting storage by HS (O)
+TSPC_HSP_3_12  False           Remote microphone gain control (C.2)
+TSPC_HSP_3_13  False           HS informs AG about local changes of microphone
+                                        gain (O)
+TSPC_HSP_3_14  False (*)       Microphone gain setting storage by HS (O)
+TSPC_HSP_3_15  False           Connection handling with Detach/Page (M)
+TSPC_HSP_3_16  False           Connection handling with Park Mode (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_HSP_3_10 is supported, otherwise optional
+C.2: Mandatory if TSPC_HSP_2_13 is supported, otherwise optional
+C.3: Excluded if TSPC_HSP_0_2 is supported, otherwise optional
+-------------------------------------------------------------------------------
+
+
+               Errata Service Releases
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HSP_4_1   False           Show that in-band ringing and RING are
+                                       mutually exclusive (C.1)
+-------------------------------------------------------------------------------
+C.1: Excluded if TSPC_HSP_0_2 is supported, otherwise optional
+-------------------------------------------------------------------------------
diff --git a/android/pics-iopt.txt b/android/pics-iopt.txt
new file mode 100644 (file)
index 0000000..a1289ef
--- /dev/null
@@ -0,0 +1,223 @@
+IOPT PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Profiles
+-------------------------------------------------------------------------------
+Parameter Name                         Selected        Description
+-------------------------------------------------------------------------------
+TSPC_support_                                          Support for: Advanced
+AdvancedAudioDistributionProfile_Sink  False           Audio Distribution
+                                                       Profile. Role: Sink
+
+TSPC_support_                                          Support for: Advanced
+AdvancedAudioDistributionProfile_Source        True (*)        Audio Distribution
+                                                       Profile. Role: Source
+
+TSPC_support_AVRemoteControlProfile_CT True (*)        Support for: Audio\Video
+                                                       Remote Control Profile.
+                                                       Role: Controller
+
+TSPC_support_AVRemoteControlProfile_TG True (*)        Support for: Audio\Video
+                                                       Remote Control Profile.
+                                                       Role: Target
+
+TSPC_support_BasicImagingProfile_CLIENT        False           Support for: Basic
+                                                       Imaging Profile.
+                                                       Role: Client
+
+TSPC_support_BasicImagingProfile_                      Support for: Basic
+SERVER_ImagingAutomaticArchive         False           Imaging Profile. Role:
+                                                       Server Functionality:
+                                                       Imaging autoarchive
+
+TSPC_support_BasicImagingProfile_      False           Support for: Basic
+SERVER_ImagingReferencedObjects                                Imaging Profile. Role:
+                                                       Server Functionality:
+                                                       Imaging ref. objects
+
+TSPC_support_BasicImagingProfile_      False           Support for: Basic
+SERVER_ImagingResponder                                        Imaging Profile. Role:
+                                                       Server Functionality:
+                                                       Imaging responder
+
+TSPC_support_                          False           Support for: Basic
+BasicPrintingProfile_PRINTER                           Printing Profile. Role:
+                                                       Printer
+
+TSPC_support_                                          Support for: Basic
+BasicPriProfile_PRINTER_ReflectedUI    False           Printing Profile. Role:
+                                                       Printer Functionality:
+                                                       Reflected UI
+
+TSPC_support_BasicPrintingProfile_                     Support for: Basic
+SENDER_Referenced_objects_Service      False           Printing Profile. Role:
+                                                       Sender Functionality:
+                                                       Refe. objects service
+
+TSPC_support_DialUpNetworkingProfile_DT        False           Support for: Dial-Up
+                                                       Networking Profile.
+                                                       Role: Data Terminal
+
+TSPC_support_DialUpNetworkingProfile_GW        False           Support for: Dial-Up
+                                                       Networking Profile.
+                                                       Role: Gateway
+
+TSPC_support_                          True (*)        Support for: Extended
+ExtendedServiceDiscoveryProfile_IP_LAP                 SDP. Version: IP-LAP
+
+TSPC_support_                          True (*)        Support for: Extended
+ExtendedServiceDiscoveryProfile_IP_PAN                 SDP. Version: IP-PAN
+
+TSPC_support_                          True (*)        Support for: Extended
+ExtendedServiceDiscoveryProfile_L2CAP                  SDP. Version: L2CAP
+
+TSPC_support_FAXProfile_DE             False           Support for: FAX Profile
+                                                       Role: Data Terminal
+
+TSPC_support_FAXProfile_GW             False           Support for: FAX Profile
+                                                       Role: Gateway
+
+TSPC_support_FileTransferProfile_CLIENT        False           Support for: FTP
+                                                       Role: Client
+
+TSPC_support_FileTransferProfile_SERVER        False           Support for: FTP
+                                                       Role: Server
+
+TSPC_support_HealthDeviceProfile_Sink  False (#)       Support for: HDP
+                                                       Role: Sink
+
+TSPC_support_HealthDeviceProfile_Source        False (#)       Support for: HDP
+                                                       Role: Source
+
+TSPC_support_NewHandsFreeProfile_AG    False           Support for: HFP
+                                                       Role: Audio gateway
+
+TSPC_support_NewHandsFreeProfile_HF    False           Support for: HFP
+                                                       Role: Hands-Free unit
+
+TSPC_support_                          False           Support for: Hard Copy
+HardCopyReplacementProfile_                            cable Repl. Profile
+CLIENT_CR_RegisterNotofication_support                 Role: Client
+                                                       Functionality: CR
+                                                       register notification
+                                                       support
+
+TSPC_support_                          False           Support for: Hard Copy
+HardCopyReplacementProfile_CLIENT_print                        cable Repl. Profile.
+                                                       Role: Client
+                                                       Functionality: Print
+
+TSPC_support_                          False           Support for: Hard Copy
+HardCopyReplacementProfile_CLIENT_scan                 cable Repl. Profile.
+                                                       Role: Client
+                                                       Functionality: Scan
+
+TSPC_support_                          False           Support for: Hard Copy
+HardCopyReplacementProfile_SERVER_print                        cable Repl. Profile.
+                                                       Role: Server
+                                                       Functionality: Print
+
+TSPC_support_                          False           Support for: Hard Copy
+HardCopyReplacementProfile_SERVER_scan                 cable Repl. Profile.
+                                                       Role: Server
+                                                       Functionality: Scan
+
+TSPC_support_HeadsetProfile_AG         True (*)        Support for: HSP
+                                                       Role: Audio Gateway
+
+TSPC_support_HeadsetProfile_HS         False           Support for: HSP
+                                                       Role: Headset
+
+TSPC_support_                          False           Support for: HID
+HumanInterfaceDeviceProfile                            Role: Device
+
+TSPC_support_HID_Host                  True (*)        Support for: HID
+                                                       Role: Host
+
+TSPC_support_LANAccessProfile_DT       False           Support for: LAN Access
+                                                       Profile. Role: Data
+                                                       Terminal
+
+TSPC_support_LANAccessProfile_LAP      False           Support for: LAN Access
+                                                       Profile. Role: LAN
+                                                       Access Point
+
+TSPC_support_MessaeAccessProfile_MCE   False           Support for: MAP
+                                                       Role: MCE
+
+TSPC_support_MessageAccessProfile_MSE  True (*)        Support for: MAP
+                                                       Role: MSE
+
+TSPC_support_ObjectPushProfile_CLIENT  True (*)        Support for: OPP
+                                                       Role: Client
+
+TSPC_support_ObjectPushProfile_SERVER  True (*)        Support for: OPP
+                                                       Role: Server
+
+TSPC_support_                          False           Support for: PAN
+PersonalAreaNetworkProfile_GN                          Role: GN
+
+TSPC_support_                          True (*)        Support for: PAN
+PersonalAreaNetworkProfile_NAP                         Role: NAP
+
+TSPC_support_                          True (*)        Support for: PAN
+PersonalAreaNetworkProfile_PANU                                Role: PANU
+
+TSPC_support_PhonebookAccessProfile_PCE        False           Support for: PBAP
+                                                       Role: PCE
+
+TSPC_support_PhonebookAccessProfile_PSE        True (*)        Support for: PBAP
+                                                       Role: PSE
+
+TSPC_support_SerialPortProfile_Service False           Support for: SPP
+                                                       Role: Dev B
+
+TSPC_support_                          False           Support for: Service
+ServiceDiscoveryApplicationProfile                     Discovery Application
+                                                       Profile
+
+TSPC_support_SIMAccessProfile_CLIENT   False           Support for: SIM access
+                                                       Profile. Role: Client
+
+TSPC_support_SIMAccessProfile_SERVER   False           Support for: SIM access
+                                                       Profile. Role: Server
+
+TSPC_support_                          False           Support for:
+SynchronizationProfile_CLIENT                          Synchronization Profile
+                                                       Role: Client
+
+TSPC_support_                          False           Support for:
+SynchronizationProfile_SERVER                          Synchronization Profile
+                                                       Role: Server
+
+TSPC_support_UDIProfile_MT             False           Support for: UDI Profile
+                                                       Role: MT
+
+TSPC_support_UDIProfile_TA             False           Support for: UDI Profile
+                                                       Role: TA
+
+TSPC_support_                          False           Support for: Video
+VideoDistributionProfile_Sink                          distribution Profile
+                                                       Role: Sink
+
+TSPC_support_                          False           Support for: Video
+VideoDistributionProfile_Source                                distribution Profile
+                                                       Role: Source
+
+TSPC_support_WAPOverBluetooth_CLIENT   False           Support for: WAP over
+                                                       Bluetooth Profile
+                                                       Role: Client
+
+TSPC_support_WAPOverBluetooth_PROXY    False           Support for: WAP over
+                                                       Bluetooth Profile
+                                                       Role: PROXY
+
+TSPC_support_GNSS_SERVER               False           Support for: GNSS
+                                                       Role: Server
diff --git a/android/pics-l2cap.txt b/android/pics-l2cap.txt
new file mode 100644 (file)
index 0000000..096fbf4
--- /dev/null
@@ -0,0 +1,159 @@
+L2CAP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_L2CAP_1_1 True            Data Channel Initiator (C.1)
+TSPC_L2CAP_1_2 True            Data Channel Acceptor (C.1)
+TSPC_L2CAP_1_3 True (#)        LE Master (C.2)
+TSPC_L2CAP_1_4 True (#)        LE Slave (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF BR/EDR or BR/EDR/LE is claimed, ELSE Excluded.
+C.2: Mandatory to support (at least one of TSPC_L2CAP_1_3 or TSPC_L2CAP_1_4)
+       IF LE or BR/EDR/LE claimed, ELSE Excluded.
+-------------------------------------------------------------------------------
+
+
+               General Operation
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_L2CAP_2_1 True            Support of L2CAP signaling channel (C.20)
+TSPC_L2CAP_2_2 True            Support of configuration process (C.20)
+TSPC_L2CAP_2_4 True            Support of command echo request (C.21)
+TSPC_L2CAP_2_3  True            Support of connection oriented data
+                                        channel (C.20)
+TSPC_L2CAP_2_5 True            Support of command echo response (C.20)
+TSPC_L2CAP_2_6 True (*)        Support of command information request (C.21)
+TSPC_L2CAP_2_7 True            Support of command information response (C.20)
+TSPC_L2CAP_2_8 False           Support of a channel group (C.21)
+TSPC_L2CAP_2_9 False           Support of packet for connectionless
+                                       channel (C.21)
+TSPC_L2CAP_2_10        False           Support retransmission mode (C.21)
+TSPC_L2CAP_2_11        False           Support flow control mode(C.21)
+TSPC_L2CAP_2_12        True (*)        Enhanced Retransmission Mode (C.1, C.13)
+TSPC_L2CAP_2_13        True (*)        Streaming Mode (C.1, C.14)
+TSPC_L2CAP_2_14        True (*)        FCS Option (C.2)
+TSPC_L2CAP_2_15        True (*)        Generate Local Busy Condition (C.3)
+TSPC_L2CAP_2_16        True (*)        Send Reject (C.3)
+TSPC_L2CAP_2_17        True (*)        Send Selective Reject (C.3)
+TSPC_L2CAP_2_18        True (*)        Mandatory use of ERTM (C.4)
+TSPC_L2CAP_2_19        True (*)        Mandatory use of Streaming Mode (C.5)
+TSPC_L2CAP_2_20        True (*)        Optional use of ERTM (C.4)
+TSPC_L2CAP_2_21        True (*)        Optional use of Streaming Mode (C.5)
+TSPC_L2CAP_2_22        True (*)        Send data using SAR in ERTM (C.6)
+TSPC_L2CAP_2_23        True (*)        Send data using SAR in Streaming Mode (C.7)
+TSPC_L2CAP_2_24        True (*)        Actively request Basic Mode for a PSM that
+                                       supports the use of ERTM or Streaming
+                                       Mode (C.8)
+TSPC_L2CAP_2_25        True (*)        Supports performing L2CAP channel mode
+                                       configuration fallback from SM
+                                        to ERTM (C.9)
+TSPC_L2CAP_2_26        True (*)        Supports sending more than one unacknowledged
+                                       I-Frame when operating in ERTM (C.10)
+TSPC_L2CAP_2_27        True (*)        Supports sending more than three unacknowledged
+                                       I-Frame when operating in ERTM (C.10)
+TSPC_L2CAP_2_28        True (*)        Supports configuring the peer TxWindow
+                                       greater than 1 (C.11)
+TSPC_L2CAP_2_29        False           AMP Support (C.12)
+TSPC_L2CAP_2_30        True (*)        Fixed Channel Support (C.12)
+TSPC_L2CAP_2_31        False           AMP Manager Support (C.12)
+TSPC_L2CAP_2_32        False           ERTM over AMP (C.12)
+TSPC_L2CAP_2_33        False           Streaming Mode Source over AMP Support (C.15)
+TSPC_L2CAP_2_34        False           Streaming Mode Sink over AMP Support (C.15)
+TSPC_L2CAP_2_35        False           Unicast Connectionless Data, Reception (C.1, C.16)
+TSPC_L2CAP_2_36        False           Ability to transmit an unencrypted packet over
+                                       a Unicast connectionless L2CAP
+                                       channel (C.16)
+TSPC_L2CAP_2_37        False           Ability to transmit an encrypted packet over
+                                       a Unicast connectionless L2CAP
+                                       channel (C.16)
+TSPC_L2CAP_2_38        False           Extended Flow Specification for BR/EDR (C.8)
+TSPC_L2CAP_2_39        False           Extended Window Size (C.8)
+TSPC_L2CAP_2_40        True (*)        Support of Low Energy signaling channel (C.17)
+TSPC_L2CAP_2_41        True (*)        Support of command reject (C.17)
+TSPC_L2CAP_2_42        True (*)        Send Connection Parameter Update Request (C.18)
+TSPC_L2CAP_2_43        True (*)        Send Connection Parameter Update Response (C.19)
+TSPC_L2CAP_2_44        False           Extended Flow Specification for AMP (C.22)
+TSPC_L2CAP_2_45        False           Send disconnect request command (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 OR
+       TSPC_L2CAP_2_35 IF BR/EDR BR/EDR/LE AND SUM_ICS 31/7 (CSA1) OR
+       SUM_ICS 31/8 (3.0) OR SUM_ICS 31/9 (3.0+HS) OR SUM_ICS 31/10 (4.0))
+       is supported, ELSE Excluded
+C.2: Optional IF TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 is claimed, ELSE Excluded.
+C.3: Optional IF TSPC_L2CAP_2_12 AND TSPC_L2CAP_2_28 is claimed, ELSE Excluded.
+C.4: IF TSPC_L2CAP_2_12 is claimed THEN either TSPC_L2CAP_2_18
+       OR TSPC_L2CAP_2_20 are Mandatory, ELSE Excluded.
+C.5: IF TSPC_L2CAP_2_13 is claimed THEN either TSPC_L2CAP_2_19
+       OR TSPC_L2CAP_2_21 are Mandatory, ELSE Excluded.
+C.6: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded.
+C.7: Optional IF TSPC_L2CAP_2_13 is claimed, ELSE Excluded.
+C.8: Optional IF TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 is claimed, ELSE Excluded.
+C.9: Mandatory IF TSPC_L2CAP_2_12 AND TSPC_L2CAP_2_13 AND TSPC_L2CAP_2_21
+       is claimed, ELSE Excluded.
+C.10: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded.
+C.11: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded.
+C.12: Mandatory IF SUM_ICS 31/9 (3.0 + HS) is claimed, ELSE Optional.
+C.13: Mandatory IF SUM_ICS 31/9 (3.0 + HS) is claimed, ELSE Optional.
+C.14: Optional IF SUM_ICS 31/8 OR 31/9 OR 31/10 OR 31/11 is claimed, ELSE Excluded.
+C.15: Optional IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded.
+C.16: Optional IF (SUM_ICS 31/8 OR SUM_ICS 31/9 OR 31/10 OR 31/11) is claimed,
+       ELSE Excluded.
+C.17: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded.
+C.18: Optional IF (SUM_ICS 31/10 AND 1/4) is claimed, ELSE Excluded.
+C.19: Mandatory IF (SUM_ICS 31/10 AND 1/3) is claimed, ELSE Excluded.
+C.20: Mandatory IF LE OR BR/EDR/LE, is claimed, ELSE Excluded
+C.21: Optional IF LE OR BR/EDR/LE, is claimed, ELSE Excluded
+C.22: Mandatory IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded.
+-------------------------------------------------------------------------------
+
+
+               Configurable Parameters
+-------------------------------------------------------------------------------
+Parameter      Name Selected   Description
+-------------------------------------------------------------------------------
+TSPC_L2CAP_3_1 True            Support of RTX timer (M)
+TSPC_L2CAP_3_2 True            Support of ERTX timer (C.4)
+TSPC_L2CAP_3_3 True            Support minimum MTU size 48 octets (C.4)
+TSPC_L2CAP_3_4 True (*)        Support MTU size larger than 48 octets (C.5)
+TSPC_L2CAP_3_5 True            Support of flush timeout value for reliable
+                                       channel (C.4)
+TSPC_L2CAP_3_6 False           Support of flush timeout value for unreliable
+                                       channel (C.5)
+TSPC_L2CAP_3_7 False           Support of bi-directional quality of service
+                                       (QoS) option field (C.1)
+TSPC_L2CAP_3_8 False           Negotiate QoS service type (C.5)
+TSPC_L2CAP_3_9 False           Negotiate and support service type ‘No
+                                       traffic’ (C.2)
+TSPC_L2CAP_3_10        False           Negotiate and support service type ‘Best
+                                       effort’ (C.3)
+TSPC_L2CAP_3_11        False           Negotiate and support service type
+                                       ‘Guaranteed’ (C.2)
+TSPC_L2CAP_3_12        True (*)        Support minimum MTU size 23 octets (C.6)
+TSPC_L2CAP_3_13        False           Negotiate and support service type ‘No traffic’
+                                       for Extended Flow Specification (C.7)
+TSPC_L2CAP_3_14        False           Negotiate and support service type ‘Best Effort'
+                                       for Extended Flow Specification (C.8)
+TSPC_L2CAP_3_15        False           Negotiate and support service type ‘Guaranteed’
+                                       for Extended Flow Specification (C.9)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_L2CAP_3_8 is supported, ELSE Optional.
+C.2: Optional if TSPC_L2CAP_3_8 is supported, ELSE Excluded.
+C.3: Mandatory if TSPC_L2CAP_3_8 is supported, ELSE Excluded.
+C.4: Mandatory IF BR/EDR OR BR/EDR/LE is claimed, ELSE Excluded.
+C.5: Optional IF BR/EDR OR BR/EDR/LE is claimed, ELSE Excluded.
+C.6: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded.
+C.7: Optional if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded.
+C.8: Mandatory if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded.
+C.9: Optional if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded.
+-------------------------------------------------------------------------------
diff --git a/android/pics-map.txt b/android/pics-map.txt
new file mode 100644 (file)
index 0000000..1e0695f
--- /dev/null
@@ -0,0 +1,175 @@
+MAP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+       Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MAP_0_1   False           Role: Map 1.0 (C1)
+TSPC_MAP_0_2   True (*)        Role: Map 1.1 (C1)
+TSPC_MAP_0_3   False           Role: Map 1.2 (C1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support only one Profile version.
+-------------------------------------------------------------------------------
+
+
+       Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MAP_1_1   True (*)        Role: Messaging Server Equipment (C1)
+TSPC_MAP_1_2   False           Role: Messaging Client Equipment (C1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+       Supported features MCE
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MAP_2_1   False           MCE: Message Notification (C1)
+TSPC_MAP_2_1a  False           MCE: SendEvent (C4)
+TSPC_MAP_2_2   False           MCE: Message Browsing (C1)
+TSPC_MAP_2_2a  False           MCE: SetFolder (C5)
+TSPC_MAP_2_2b  False           MCE: GetFoldersListing (C5)
+TSPC_MAP_2_2c  False           MCE: GetMessagesListing (C5)
+TSPC_MAP_2_2d  False           MCE: GetMessage (O)
+TSPC_MAP_2_2e  False           MCE: SetMessageStatus (O)
+TSPC_MAP_2_2f  False           MCE: UpdateInbox (O)
+TSPC_MAP_2_2g  False           MCE: Filtering (O)
+TSPC_MAP_2_2h  False           MCE: Multiple simultaneous MAS instances (O)
+TSPC_MAP_2_3   False           MCE: Message Uploading (O)
+TSPC_MAP_2_3a  False           MCE: SetFolder (C6)
+TSPC_MAP_2_3b  False           MCE: GetFoldersListing (C6)
+TSPC_MAP_2_3c  False           MCE: PushMessage (C6)
+TSPC_MAP_2_4   False           MCE: Message Delete (O)
+TSPC_MAP_2_4a  False           MCE: SetMessageStatus (C7)
+TSPC_MAP_2_5   False           MCE: Notification Registration (C2)
+TSPC_MAP_2_5a  False           MCE: SetNotificationRegistration off (O)
+TSPC_MAP_2_5b  False           MCE: SetNotificationRegistration on (C8)
+TSPC_MAP_2_6   False           MCE: Supported Message Types
+TSPC_MAP_2_6a  False (*)       MCE: EMAIL (C3)
+TSPC_MAP_2_6b  False (*)       MCE: SMS_GSM (C3)
+TSPC_MAP_2_6c  False (*)       MCE: SMS_CDMA (C3)
+TSPC_MAP_2_6d  False (*)       MCE: MMS (C3)
+TSPC_MAP_2_7   False           MCE: Instance Information (Not Supported)
+TSPC_MAP_2_7a  False (*)       MCE: GetMASInstanceInformation (Not Supported)
+TSPC_MAP_2_8   False           MCE: Extended MAP-Event-Report (Not Supported)
+TSPC_MAP_2_8a  False (*)       MCE: MAP-Event-Report: Version 1.1
+                                       (Not Supported)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined features TSPC_MAP_2_1 or
+       TSPC_MAP_2_2.
+C.2: Mandatory to support TSPC_MAP_2_5 if TSPC_MAP_2_1 is supported.
+C.3: Mandatory to support at least one of the defined message types
+       TSPC_MAP_2_6a to TSPC_MAP_2_6d IF TSPC_MAP_2_2 or TSPC_MAP_2_3 is
+       supported.
+C.4: Support of functionality TSPC_MAP_2_1a mandatory IF related feature
+       TSPC_MAP_2_1 supported.
+C.5: Support of functionality mandatory IF TSPC_MAP_2_2 supported.
+C.6: Support of functionality mandatory IF TSPC_MAP_2_3 supported.
+C.7: Support of functionality mandatory IF TSPC_MAP_2_4 supported.
+C.8: Mandatory to support IF TSPC_MAP_2_5 (Notification Registration) is
+       supported, otherwise excluded.
+C.9: Optional to support IF TSPC_MAP_0_3 (MAP v1.2) is supported, otherwise
+       excluded.
+C.10: Mandatory to support IF TSPC_MAP_0_3 (MAP v1.2) and TSPC_MAP_2_1
+       (Message Notification) is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+       Supported features MSE
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MAP_3_1   True            MSE: Message Notification (M)
+TSPC_MAP_3_1a  True            MSE: SendEvent (M)
+TSPC_MAP_3_2   True            MSE: Message Browsing (M)
+TSPC_MAP_3_2a  True            MSE: SetFolder (M)
+TSPC_MAP_3_2b  True            MSE: GetFoldersListing (M)
+TSPC_MAP_3_2c  True            MSE: GetMessagesListing (M)
+TSPC_MAP_3_2d  True            MSE: GetMessage (M)
+TSPC_MAP_3_2e  True            MSE: SetMessageStatus (M)
+TSPC_MAP_3_2f  True            MSE: UpdateInbox (M)
+TSPC_MAP_3_2g  False           MSE: Multiple simultaneous MAS instances (O)
+TSPC_MAP_3_3   True            MSE: Message Uploading (M)
+TSPC_MAP_3_3a  True            MSE: SetFolder (M)
+TSPC_MAP_3_3b  True            MSE: GetFoldersListing (M)
+TSPC_MAP_3_3c  True            MSE: PushMessage (M)
+TSPC_MAP_3_4   True            MSE: Message Delete (M)
+TSPC_MAP_3_4a  True            MSE: SetMessageStatus (M)
+TSPC_MAP_3_5   True            MSE: Notification Registration (M)
+TSPC_MAP_3_5a  True            MSE: SetNotificationRegistration (M)
+TSPC_MAP_3_6   False           MSE: Supported Message Types
+TSPC_MAP_3_6a  False           MSE: EMAIL (C1)
+TSPC_MAP_3_6b  True            MSE: SMS_GSM (C1)
+TSPC_MAP_3_6c  False           MSE: SMS_CDMA (C1)
+TSPC_MAP_3_6d  False (*)       MSE: MMS (C1)
+TSPC_MAP_3_7   False           MSE: Instance Information (Not Supported)
+TSPC_MAP_3_7a  False (*)       MSE: GetMASInstanceInformation (Not Supported)
+TSPC_MAP_3_8   False           MSE: Extended MAP-Event-Report (Not Supported)
+TSPC_MAP_3_8a  False (*)       MSE: MAP-Event-Report: Version 1.1
+                                       (Not Supported)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of the defined message types
+       TSPC_MAP_3_6a to TSPC_MAP_3_6d IF TSPC_MAP_3_2 or TSPC_MAP_3_3
+       is supported.
+C.2: Mandatory to support IF TSPC_MAP_0_3 (MAP v1.2) is supported,
+       otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+       GOEP v2.0 or later Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MAP_7b_1  False           GOEP v2.0 or later (C1)
+TSPC_MAP_7b_2  False           GOEP v2 Backwards Compatibility (C1)
+TSPC_MAP_7b_3  False           OBEX over L2CAP (C1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded.
+-------------------------------------------------------------------------------
+
+
+       MCE OBEX Header Support
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MAP_10_1  False (*)       Name (M)
+TSPC_MAP_10_2  False (*)       Typr (M)
+TSPC_MAP_10_3  False (*)       Body (M)
+TSPC_MAP_10_4  False (*)       End of Body (M)
+TSPC_MAP_10_5  False (*)       Target (M)
+TSPC_MAP_10_6  False (*)       Who (M)
+TSPC_MAP_10_7  False (*)       Connection ID (M)
+TSPC_MAP_10_8  False (*)       Application Parameters (M)
+TSPC_MAP_10_9  False           SRM (C2)
+TSPC_MAP_10_10 False           Receive SRMP (C2)
+TSPC_MAP_10_11 False           Send SRMP (C2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded.
+C.2: Optional if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded.
+-------------------------------------------------------------------------------
+
+
+       GetMessagesListing Filtering Parameter Support
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MAP_20_1  False (*)       MCE: FilterMessageType (O)
+TSPC_MAP_20_2  False (*)       MCE: FilterPeriodBegin (O)
+TSPC_MAP_20_3  False (*)       MCE: FilterPeriodEnd (O)
+TSPC_MAP_20_4  False (*)       MCE: FilterReadStatus (O)
+TSPC_MAP_20_5  False (*)       MCE: FilterRecipient (O)
+TSPC_MAP_20_6  False (*)       MCE: FilterOriginator (O)
+TSPC_MAP_20_7  False (*)       MCE: FilterPriority (O)
+TSPC_ALL       False (*)       Turns on all the test cases
+-------------------------------------------------------------------------------
diff --git a/android/pics-mcap.txt b/android/pics-mcap.txt
new file mode 100644 (file)
index 0000000..3b1086a
--- /dev/null
@@ -0,0 +1,141 @@
+MCAP PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Protocols
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_1_A_1        True (*)        Supports Standard Op Codes (C.1)
+TSPC_MCAP_1_A_2        False           Supports Clock Synchronization Protocol (C.1)
+-------------------------------------------------------------------------------
+C.1: Support for at least one of the defined protocols is Mandatory.
+-------------------------------------------------------------------------------
+
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_1_1  True (*)        Supports Source Role (C.1)
+TSPC_MCAP_1_2  True (*)        Supports Sink Role (C.1)
+TSPC_MCAP_1_3  False           Supports Sync-Slave Role (C.2)
+TSPC_MCAP_1_4  False           Supports Sync-Master Role (C.3)
+-------------------------------------------------------------------------------
+C.1: If support for TSPC_MCAP_1_A_1 is supported, at least one of the
+       defined roles is Mandatory otherwise Excluded.
+C.2: Mandatory if TSPC_MCAP_1_A_2 is supported, otherwise Excluded.
+C.3: Optional if TSPC_MCAP_1_A_2 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               L2CAP Features - Source
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_2_1  True (*)        Supports L2CAP Control Channel (M)
+TSPC_MCAP_2_2  True (*)        Supports at least one L2CAP Data Channel (M)
+TSPC_MCAP_2_3  True (*)        Maximum number of simultaneous L2CAP Data
+                               Channels supported (DCmax) per MCL (M)
+TSPC_MCAP_2_4  False           Can support multiple simultaneous MCLs with
+                               Standard Op Codes (O)
+-------------------------------------------------------------------------------
+
+
+               Connection Management - Source
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_3_1                  This row inentionally left blank
+TSPC_MCAP_3_2  True (*)        Initiate creation of Control and Data Channels
+                               (C.1)
+TSPC_MCAP_3_3  True (*)        Accept creation of Control and Data Channels
+                               (C.1)
+TSPC_MCAP_3_4  True (*)        Initiate Disconnection of MCL (M)
+TSPC_MCAP_3_5  True (*)        Accept Disconnection of MCL (M)
+TSPC_MCAP_3_6  True (*)        Initiate Disconnection of MDL (M)
+TSPC_MCAP_3_7  True (*)        Accept Disconnection of MDL (M)
+TSPC_MCAP_3_8  False           Initiate Reconnection of MDL (O)
+TSPC_MCAP_3_9  False           Accept Reconnection of MDL (C.2)
+TSPC_MCAP_3_10 False           Initiate Deletion of MDL (O)
+TSPC_MCAP_3_11 True (*)        Accept Deletion of MDL (M)
+TSPC_MCAP_3_12 False           Initiate Delete of All MDLs using 0xFFFF (O)
+TSPC_MCAP_3_13 True (*)        Accept Delete of All MDLs using 0xFFFF (M)
+TSPC_MCAP_3_14 False           Send MDL Abort request (O)
+TSPC_MCAP_3_15 True (*)        Accept MDL Abort request (M)
+-------------------------------------------------------------------------------
+C.1: Support for at least one of TSPC_MCAP_3_2 or TSPC_MCAP_3_3 is Mandatory.
+C.2: Mandatory if TSPC_MCAP_3_3 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               L2CAP Features - Sink
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_4_1  True (*)        Supports L2CAP Control Channel (M)
+TSPC_MCAP_4_2  True (*)        Supports at least one L2CAP Data Channel (M)
+TSPC_MCAP_4_3  True (*)        Maximum number of simultaneous L2CAP Data
+                               Channels supported (DCmax) per MCL (M)
+TSPC_MCAP_4_4  False           Can support multiple simultaneous MCLs with
+                               Standard Op Codes (O)
+-------------------------------------------------------------------------------
+
+
+               Connection Management - Sink
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_5_1                  This row intentionally left blank
+TSPC_MCAP_5_2  True (*)        Initiate creation of Control and Data Channels
+                               (M)
+TSPC_MCAP_5_3  True (*)        Accept creation of Control and Data Channels
+                               (M)
+TSPC_MCAP_5_4  True (*)        Initiate Disconnection of MCL (M)
+TSPC_MCAP_5_5  True (*)        Accept Disconnection of MCL (M)
+TSPC_MCAP_5_6  True (*)        Initiate Disconnection of MDL (M)
+TSPC_MCAP_5_7  True (*)        Accept Disconnection of MDL (M)
+TSPC_MCAP_5_8  False           Initiate Reconnection of MDL (O)
+TSPC_MCAP_5_9  True (*)        Accept Reconnection of MDL (M)
+TSPC_MCAP_5_10 False           Initiate Deletion of MDL (O)
+TSPC_MCAP_5_11 True (*)        Accept Deletion of MDL (M)
+TSPC_MCAP_5_12 False           Initiate Delete of All MDLs using 0xFFFF (O)
+TSPC_MCAP_5_13 True (*)        Accept Delete of All MDLs using 0xFFFF (M)
+TSPC_MCAP_5_14 False           Send MDL Abort request (O)
+TSPC_MCAP_5_15 True (*)        Accept MDL Abort request (M)
+-------------------------------------------------------------------------------
+
+
+               Clock Synchronization Features - Sync-Slave
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_6_1  False (*)       Accept MD_SYNC_CAP_REQ and MD_SYNC_SET_REQ and
+                               Initiate MD_SYNC_INFO_IND (C.1)
+TSPC_MCAP_6_2                  This row intentionally left blank
+TSPC_MCAP_6_3  False           Can support multiple simultaneous MCLs with CSP
+                               (O)
+TSPC_MCAP_6_4  False           Can access Bluetooth Clock (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if MCAP TSPC_MCAP_1_A_2 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Clock Synchronization Features - Sync-Master
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MCAP_7_1                  This row intentionally left blank
+TSPC_MCAP_7_2  False (*)       Initiate MD_SYNC_CAP_REQ and MD_SYNC_SET_REQ
+                               and Accept MD_SYNC_INFO_IND (C.1)
+TSPC_MCAP_7_3  False           Can support multiple simultaneous MCLs with CSP (O)
+TSPC_MCAP_7_4  False (*)       Can access Bluetooth Clock (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support IF TSPC_MCAP_1_A_2 is supported.
+-------------------------------------------------------------------------------
diff --git a/android/pics-mps.txt b/android/pics-mps.txt
new file mode 100644 (file)
index 0000000..e12cad0
--- /dev/null
@@ -0,0 +1,339 @@
+MPS PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MPS_0_1   True            MPS v1.0 (M)
+-------------------------------------------------------------------------------
+
+
+               Profile Version Requirements
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MPS_1_1   True (*)        A2DP 1.2 or later (O)
+TSPC_MPS_1_2   True (*)        AVRCP 1.3 or later (O)
+TSPC_MPS_1_3   False           DUN 1.1 or later (O)
+TSPC_MPS_1_4   True (*)        HFP 1.5 or later (O)
+TSPC_MPS_1_5   False           PAN 1.0 or later (O)
+TSPC_MPS_1_6   False           PBAP 1.1 or later (O)
+-------------------------------------------------------------------------------
+
+
+               Profile Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MPS_2_1   True (*)        A2DP Source (SRC) (C.1)
+TSPC_MPS_2_2   False           A2DP Sink (SNK) (C.1)
+TSPC_MPS_2_3   True (*)        AVRCP Controller (CT) (C.1)
+TSPC_MPS_2_4   True (*)        AVRCP Target (TG) (C.1)
+TSPC_MPS_2_5   False           DUN Gateway (GW) (C.1)
+TSPC_MPS_2_6   False           DUN Data Terminal (DT) (C.1)
+TSPC_MPS_2_7   True (*)        HFP Audio Gateway (AG) (C.1)
+TSPC_MPS_2_8   False           HFP Hands-Free (HF) (C.1)
+TSPC_MPS_2_9   False           PAN Network Access Point (NAP) (C.1)
+TSPC_MPS_2_10  False           PAN Group Ad-hoc Network (GN) (C.1)
+TSPC_MPS_2_11  False           PAN User (PANU) (C.1)
+TSPC_MPS_2_12  False           PBAP PCE (C.1)
+TSPC_MPS_2_13  False           PBAP PSE (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to declare each role as supported within the represented Profile
+       otherwise Excluded. The roles declared shall match that of the roles
+       supported within the Profile.
+-------------------------------------------------------------------------------
+
+
+               Profile Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MPS_3_1   True (*)        Receiving PASS THROUGH command in Category 1
+                               (AVRCP - TG)  (C.1)
+TSPC_MPS_3_2   True (*)        Receiving PASS THROUGH command in Category 1
+                               (AVRCP - TG) - PAUSE (C.1)
+TSPC_MPS_3_3   False           Sending PASS THROUGH command in Category 1
+                               (AVRCP - CT) - PLAY (C.2)
+TSPC_MPS_3_4   False           Sending PASS THROUGH command in Category 1
+                               (AVRCP - CT) - PAUSE (C.2)
+TSPC_MPS_3_5   True (*)        Transfer Control - Suspend (GAVDP - Initiator)
+                               (C.3)
+TSPC_MPS_3_6   True (*)        Transfer Control - Suspend (GAVDP - Acceptor)
+                               (C.4)
+TSPC_MPS_3_7   False           Accept an incoming voice call (in-band ring)
+                               (C.5)
+TSPC_MPS_3_8   True (*)        Accept an incoming voice call (no in-band ring)
+                               (C.5)
+TSPC_MPS_3_9   False           Place a call with a phone number supplied by
+                               the HF (C.6)
+TSPC_MPS_3_10  True (*)        Register Notification: PLAYBACK_STATUS_CHANGED
+                               (C.7)
+TSPC_MPS_3_11  False           Ability to support parallel data and call
+                               operation (O)
+TSPC_MPS_3_12  False           PBAP Phone Book Download (C.8)
+TSPC_MPS_3_13  False           Ability to support multiple concurrent device
+                               connections (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MPS_2_1 (A2DP Source role) and TSPC_MPS_2_4 (AVRCP
+       Target role) are supported, otherwise Excluded.
+C.2: Mandatory if TSPC_MPS_2_2 (A2DP Sink role) and TSPC_MPS_2_3 (AVRCP
+       Controller role) are supported, otherwise Excluded.
+C.3: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_1 (A2DP Source
+       role) are supported; Optional if TSPC_MPS_1_4 (HFP 1.5 or later) and
+       TSPC_MPS_2_2 (A2DP Sink role) are supported, otherwise Excluded.
+C.4: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_1 (A2DP Source
+       role) or TSPC_MPS_2_2 (A2DP Sink role) are supported, otherwise
+       Excluded.
+C.5: Mandatory to support at least one if TSPC_MPS_1_4 (HFP 1.5 or later) is
+       supported, otherwise Excluded.
+C.6: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and HFP 3/8 (Place a call with
+       a phone number supplied by the HF) are supported, otherwise Excluded.
+C.7: Mandatory if TSPC_MPS_2_3 (AVRCP Controller role) is supported, otherwise
+       Excluded.
+C.8: Mandatory if TSPC_MPS_1_6 (PBAP 1.1 or later) and PBAP 2/1 (Phone Book
+       Download) are supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Device Capability Support
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MPS_4_1   True            Multiple Profiles Single Device (MPSD) (M)
+TSPC_MPS_4_2   False           Multiple Profiles Multiple Devices (MPMD) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_MPS_3_13 (Ability to support multiple concurrent device
+       connections), otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               MPSD scenarios
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MPS_6_1   True (*)        HFP-AG and A2DP-SRC Implementation Answer
+                               Incoming Call during Audio Streaming (C.1)
+TSPC_MPS_6_2   False           HFP-HF and A2DP-SNK Implementation Answer
+                               Incoming Call during Audio Streaming (C.2)
+TSPC_MPS_6_3   True (*)        HFP-AG and A2DP-SRC Implementation Outgoing
+                               Call during Audio Streaming (C.1)
+TSPC_MPS_6_4   False           HFP-HF and A2DP-SNK Implementation Outgoing
+                               Call during Audio Streaming (C.2)
+TSPC_MPS_6_5   True (*)        HFP-AG and A2DP-SRC Implementation Reject/Ignore
+                               Incoming Call during Audio Streaming (C.1)
+TSPC_MPS_6_6   False           HFP-HF and A2DP-SNK Implementation Reject/Ignore
+                               Incoming Call during Audio Streaming (C.2)
+TSPC_MPS_6_7   True (*)        HFP-AG and A2DP-SRC Implementation HFP Call
+                               Termination during AVP Connection (C.1)
+TSPC_MPS_6_8   False           HFP-HF and A2DP-SNK Implementation HFP Call
+                               Termination during AVP Connection (C.2)
+TSPC_MPS_6_9   True (*)        HFP-AG and A2DP-SRC Implementation Press Play
+                               on Audio Player during Active Call (C.1)
+TSPC_MPS_6_10  False           HFP-HF and A2DP-SNK Implementation Press Play
+                               on Audio Player during Active Call (C.2)
+TSPC_MPS_6_11  True (*)        HFP-AG and A2DP-SRC Implementation Start Audio
+                               Streaming after AVRCP Play Command (C.1)
+TSPC_MPS_6_12  False           HFP-HF and A2DP-SNK Implementation Start Audio
+                               Streaming after AVRCP Play Command (C.2)
+TSPC_MPS_6_13  True (*)        HFP-AG and A2DP-SRC Implementation Suspend Audio
+                               Streaming after AVRCP Pause/Stop (C.1)
+TSPC_MPS_6_14  False           HFP-HF and A2DP-SNK Implementation Suspend Audio
+                               Streaming after AVRCP Pause/Stop (C.2)
+TSPC_MPS_6_15  False           HFP-AG and DUN-GW Implementation Data
+                               Communication under PSDM (DUN) during Active
+                               Voice Call (C.3)
+TSPC_MPS_6_16  False           HFP-HF and DUN-DT Implementation Data
+                               Communication under PSDM (DUN) during Active
+                               Voice call (C.4)
+TSPC_MPS_6_17  False           HFP-AG and DUN-GW Implementation Outgoing Voice
+                               Call during Data Communication under PSDM (DUN)
+                               (C.3)
+TSPC_MPS_6_18  False           HFP-HF and DUN-DT Implementation Outgoing Voice
+                               Call during Data Communication under PSDM (DUN)
+                               (C.4)
+TSPC_MPS_6_19  False           HFP-AG and DUN-GW Implementation Incoming Voice
+                               Call during Data Communication under PSDM (DUN)
+                               (C.3)
+TSPC_MPS_6_20  False           HFP-HF and DUN-DT Implementation Incoming Voice
+                               Call during Data Communication under PSDM (DUN)
+                               (C.4)
+TSPC_MPS_6_21  False           A2DP-SRC and DUN-GW Implementation Start Audio
+                               Streaming during Data Communication under PSDM
+                               (DUN) (C.5)
+TSPC_MPS_6_22  False           A2DP-SNK and DUN-DT Implementation Start Audio
+                               Streaming during Data Communication under PSDM
+                               (DUN) (C.6)
+TSPC_MPS_6_23  False           A2DP-SRC and DUN-GW Implementation Data
+                               Communication Establishment under PSDM (DUN)
+                               during Audio Streaming (C.5)
+TSPC_MPS_6_24  False           A2DP-SNK and DUN-DT Implementation Data
+                               Communication Establishment under PSDM (DUN)
+                               during Audio Streaming (C.6)
+TSPC_MPS_6_25  False           HFP-AG and DUN-GW Implementation Terminate
+                               Voice Call/Data Call during Data Communication
+                               and Voice Call (C.5)
+TSPC_MPS_6_26  False           HFP-HF and DUN-DT Implementation Terminate
+                               Voice Call/Data Call during Data Communication
+                               and Voice Call (C.6)
+TSPC_MPS_6_27  False           HFP-AG and PAN-NAP Implementation Data
+                               Communication in Personal Area Network during
+                               Active Voice Call (C.7)
+TSPC_MPS_6_28  False           HFP-HF and PAN-PANU Implementation Data
+                               Communication in Personal Area Network during
+                               Active Voice Call (C.8)
+TSPC_MPS_6_29  False           HFP-AG and PAN-NAP Implementation Outgoing
+                               Voice Call during Data Communication in Personal
+                               Area Network (C.7)
+TSPC_MPS_6_30  False           HFP-HF and PAN-PANU Implementation Outgoing
+                               Voice Call during Data Communication in Personal
+                               Area Network (C.8)
+TSPC_MPS_6_31  False           HFP-AG and PAN-NAP Implementation Incoming Voice
+                               Call during Data Communication in Personal Area
+                               Network (C.7)
+TSPC_MPS_6_32  False           HFP-HF and PAN-PANU Implementation Incoming
+                               Voice Call during Data Communication in Personal
+                               Area Network (C.8)
+TSPC_MPS_6_33  False           A2DP-SRC and PAN-NAP Implementation Start Audio
+                               Streaming during Data Communication in Personal
+                               Area Network (C.9)
+TSPC_MPS_6_34  False           A2DP-SNK and PAN-PANU Implementation Start Audio
+                               Streaming during Data Communication in Personal
+                               Area Network (C.10)
+TSPC_MPS_6_35  False           A2DP-SRC and PAN-NAP Implementation Data
+                               Communication Establishment in Personal Area
+                               Network during Audio Streaming (C.9)
+TSPC_MPS_6_36  False           A2DP-SNK and PAN_PANU Implementation Data
+                               Communication Establishment in Personal Area
+                               Network during Audio Streaming (C.10)
+TSPC_MPS_6_37  False           A2DP-SRC_PBAP-Server Implementation Phonebook
+                               Download during Audio Streaming (C.11)
+TSPC_MPS_6_38  False           A2DP-SNK and PBAP-Client Implementation
+                               Phonebook Download during Audio Streaming (C.12)
+TSPC_MPS_6_39  False           HFP-AG and PBAP-Server Implementation PBAP and
+                               HFP Connection Behavior (C.13)
+-------------------------------------------------------------------------------
+C.1: Mandatory if 2/1 (A2DP Source role), 2/4 (AVRCP Target role) and 2/7
+       (HFP Audio Gateway role) are supported, otherwise Excluded.
+C.2: Mandatory if 2/2 (A2DP Sink role), 2/3 (AVRCP Controller role) and 2/8
+       (HFP Hands-Free role) are supported, otherwise Excluded.
+C.3: Mandatory if 2/5 (DUN Gateway role) and 2/7 (HFP Audio Gateway role)
+       are supported and 3/9 ( Ability to support parallel data and call
+       operation), otherwise Excluded.
+C.4: Mandatory if 2/6 (DUN Data Terminal role) and 2/8 (HFP Hands-Free role)
+       are supported, otherwise Excluded.
+C.5: Mandatory if 2/1 (A2DP Source role) and 2/5 (DUN Gateway role) are
+       supported, otherwise Excluded.
+C.6: Mandatory if 2/2 (A2DP Sink role) and 2/6 (DUN Data Terminal role) are
+       supported, otherwise Excluded.
+C.7: Mandatory if 2/7 (HFP Audio Gateway role) and 2/9 (PAN Network Access Point
+       role) and 3/11 (Ability to support parallel data and call operation) are
+       supported, otherwise Excluded.
+C.8: Mandatory if 2/8 (HFP Hands-Free role) and 2/11 (PAN User role) are
+       supported and 3/11, otherwise Excluded.
+C.9: Mandatory if 2/1 (A2DP Source role) and 2/9 (PAN Network Access Point role)
+       are supported, otherwise Excluded.
+C.10: Mandatory if 2/2 (A2DP Sink role) and 2/11 (PAN User role) are supported,
+       otherwise Excluded.
+C.11: Mandatory if 2/1 (A2DP Source role) and 2/13 (PBAP PSE role) are
+       supported, otherwise Excluded.
+C.12: Mandatory if 2/2 (A2DP Sink role) and 2/12 (PBAP PCE role) are supported,
+       otherwise Excluded.
+C.13: Mandatory if 2/7 (HFP Audio Gateway role) and 2/13 (PBAP PSE role) are
+       supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               MPMD Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MPS_7_1   False           HFP-HF and A2DP-SNK and AVRCP-CT Implementation
+                               Answer Incoming Call during Audio Streaming
+                               (C.1)
+TSPC_MPS_7_2   True (*)        A2DP-SRC and AVRCP-TG Implementation Answer
+                               Incoming Call during Audio Streaming (C.2)
+TSPC_MPS_7_3   False           HFP-HF and A2DP-SNK and AVRCP-CT Implementation
+                               Outgoing Call during Audio Streaming (C.1)
+TSPC_MPS_7_4   True (*)        A2DP-SRC and AVRCP-TG Implementation Outgoing
+                               Call during Audio Streaming (C.2)
+TSPC_MPS_7_5   False           HFP-HF and A2DP-SNK and AVRCP-CT Implementation
+                               Reject/Ignore Incoming Call during Audio
+                               Streaming (C.1)
+TSPC_MPS_7_6   True (*)        A2DP-SRC and AVRCP-TG Implementation
+                               Reject/Ignore Incoming Call during Audio
+                               Streaming (C.2)
+TSPC_MPS_7_7   False           HFP-HF and A2DP-SNK and AVRCP-CT Implementation
+                               HFP Call Termination during AVP Connection (C.1)
+TSPC_MPS_7_8   True (*)        A2DP-SRC and AVRCP-TG Implementation HFP Call
+                               Termination during AVP Connection (C.2)
+TSPC_MPS_7_9   False           HFP-HF and A2DP-SNK and AVRCP-CT Implementation
+                               Press Play on Audio Player during Active Call
+                               (C.1)
+TSPC_MPS_7_10  True (*)        A2DP-SRC and AVRCP-TG Implementation Press Play
+                               on Audio Player during Active Call (C.2)
+TSPC_MPS_7_11  True (*)        A2DP-SRC and AVRCP-TG Implementation Start Audio
+                               Streaming during Data Communication under PSDM
+                               (C.2)
+TSPC_MPS_7_12  False           A2DP-SNK and AVRCP-CT and DUN-DT Implementation
+                               Start Audio Streaming during Data Communication
+                               under PSDM (C.3)
+TSPC_MPS_7_13  True (*)        A2DP-SRC and AVRCP-TG Implementation Start
+                               Packet Data Communication during Audio Streaming
+                               (C.2)
+TSPC_MPS_7_14  False           A2DP-SNK and AVRCP-CT and DUN-DT Implementation
+                               Start Packet Data Communication during Audio
+                               Streaming (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if 2/2 (A2DP Sink role), 2/3 (AVRCP Controller role) and 2/8
+       (HFP Hands-Free role) are supported, otherwise Excluded.
+C.2: Mandatory if 2/1 (A2DP Source role) and 2/4 (AVRCP Target role) are
+       supported, otherwise Excluded.
+C.3: Mandatory if 2/2 (A2DP Sink role), 2/3 (AVRCP Controller role) and 2/6
+       (DUN Data Terminal role) supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               MPS Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MPS_8_1   True (*)        AVP Suspension (C.1)
+TSPC_MPS_8_2   True (*)        Profile (Dis-)Connection behavior (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if 1/1 (A2DP 1.2 or later) and 1/2 (AVRCP 1.3 or later) are
+       supported, otherwise Excluded.
+C.2: Mandatory if 1/1 (A2DP 1.2 or later), 1/2 (AVRCP 1.3 or later) and 1/4 (HFP
+       1.5 or later) are supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               MPS Dependencies
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MPS_9_1   True            Implements Bluetooth Core Specification v2.1
+                               + EDR or later (M)
+-------------------------------------------------------------------------------
+
+
+               MPS Requirements
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_MPS_10_1  True            SDP Record (M)
+TSPC_MPS_10_2  True (*)        Media Stream Suspension (C.1)
+TSPC_MPS_10_3  True (*)        Sniff Mode during Streaming (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if 1/1 (A2DP1.2 or later) and 1/4 (HFP 1.5 or later) are
+       supported, otherwise Excluded.
+C.2: Mandatory if 1/1 (A2DP 1.2 or later) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
index 11e61ff..0a5919e 100644 (file)
@@ -1,5 +1,7 @@
 OPP PICS for the PTS tool.
 
+PTS version: 5.1
+
 * - different than PTS defaults
 # - not yet implemented/supported
 
@@ -10,10 +12,10 @@ O - optional
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_OPP_1_1   True (*)        Role: Object Push Client.
-TSPC_OPP_1_2   True (*)        Role: Object Push Server.
+TSPC_OPP_1_1   True (*)        Role: Object Push Client (C.1)
+TSPC_OPP_1_2   True (*)        Role: Object Push Server (C.1)
 -------------------------------------------------------------------------------
-C.1: It is Mandatory to Support at least one of the defined roles.
+C.1: Mandatory to support at least one of the defined roles.
 -------------------------------------------------------------------------------
 
 
@@ -32,23 +34,18 @@ C.1: It is mandatory to support at least one of the profile versions.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_OPP_2_1   True            Client: Perform Service Discovery request.
-                                       (M.1)
+TSPC_OPP_2_1   True            Client: Perform Service Discovery request (M)
 TSPC_OPP_2_2   True            Client: Authentication/PIN exchange supported.
-                                       (M.1)
+                                       (M)
 TSPC_OPP_2_2a  True (*)        Client: Require Authentication/PIN by default.
                                        (O)
-TSPC_OPP_2_3   True            Client: Object Push is supported. (M.1)
-TSPC_OPP_2_4   True (*)        Client: vCard 2.1 format is supported for
-                                       Object Push. (C.3)
-TSPC_OPP_2_5   False           Client: vCalender 1.0 format is supported for
-                                       Object Push. (O)
-TSPC_OPP_2_6   False           Client: vMsg as defined in IrMC 1.1 is supported
-                                       for Object Push. (O)
-TSPC_OPP_2_7   False           Client: vNote as defined in IrMC 1.1 is
-                                       supported for Object Push. (O)
+TSPC_OPP_2_3   True            Client: Object Push (M)
+TSPC_OPP_2_4   True (*)        Client: vCard 2.1 (C.3)
+TSPC_OPP_2_5   False           Client: vCalender 1.0 (O)
+TSPC_OPP_2_6   False           Client: vMsg as defined in IrMC 1.1 (O)
+TSPC_OPP_2_7   False           Client: vNote as defined in IrMC 1.1 (O)
 TSPC_OPP_2_8   True (*)        Client: Support content formats other than those
-                                       declared in TSPC_OPP_2_44 through
+                                       declared in TSPC_OPP_2_4 through
                                        TSPC_OPP_2_7. (O)
 TSPC_OPP_2_8a  False           Client: Support specific set of other content
                                        formats. (C.4)
@@ -59,10 +56,10 @@ TSPC_OPP_2_9a       False           Client: Push multiple vCard objects using
 TSPC_OPP_2_9b  True (*)        Client: Push multiple vCard objects using the
                                        same PUT operation. (C.5)
 TSPC_OPP_2_10  False           Client: Push multiple vCalender objects. (O)
-TSPC_OPP_2_10a False           Client: Push multiple vMsg objects using
+TSPC_OPP_2_10a False           Client: Push multiple vCalendar objects using
                                        different PUT operations. (C.6)
-TSPC_OPP_2_10b False           Client: Push multiple vMsg objects using the
-                                       same PUT operation. (C.6)
+TSPC_OPP_2_10b False           Client: Push multiple vCalendar objects using
+                                       the same PUT operation. (C.6)
 TSPC_OPP_2_11  False           Client: Push multiple vMsg objects. (O)
 TSPC_OPP_2_11a False           Client: Push multiple vMsg objects using
                                        different PUT operations. (C.7)
@@ -73,17 +70,21 @@ TSPC_OPP_2_12a      False           Client: Push multiple vNote objects using
                                        different PUT operations. (C.8)
 TSPC_OPP_2_12b False           Client: Push multiple vNote objects using the
                                        same PUT operation. (C.8)
-TSPC_OPP_2_13  False           Client: Pull business card is supported. (O)
-TSPC_OPP_2_14  False           Client: vCard 2.1 format is supported for
-                                       Business Card Pull. (C.1)
-TSPC_OPP_2_15  False           Client: Exchange business card is supported. (O)
-TSPC_OPP_2_16  False           Client: vCard 2.1 format is supported for
-                                       Business Card Exchange (C.2)
+TSPC_OPP_2_13  False           Client: Pull business card (O)
+TSPC_OPP_2_14  False           Client: vCard 2.1 (C.1)
+TSPC_OPP_2_15  False           Client: Exchange business card (O)
+TSPC_OPP_2_16  False           Client: vCard 2.1 (C.2)
+TSPC_OPP_2_17  False           GOEP v2 (C.9)
+TSPC_OPP_2_18  False           GOEP v2 Backward Compability (C.9)
+TSPC_OPP_2_19  False           OBEX over L2CAP (C.9)
+TSPC_OPP_2_20  False           OBEX Reliable Session (C.10)
+TSPC_OPP_2_21  False           OBEX SRM (C.10)
+TSPC_OPP_2_22  False           Send OBEX SRMP header (C.10)
+TSPC_OPP_2_23  False           Receive OBEX SRMP header (C.11)
 -------------------------------------------------------------------------------
 C.1: Mandatory to Support IF (TSPC_OPP_2_13) Business Card Pull is supported.
 C.2: Mandatory to Support IF (TSPC_OPP_2_15) Business Card Exchange is
        supported.
-M.1: Mandatory to Support IF (TSPC_OPP_1_1) supported.
 C.3: vCard 2.1 support is required for devices containing phonebook
        applications. vCard 2.1 support optional for other devices.
 C.4: Mandatory to support one of TSPC_OPP_2_8a or TSPC_OPP_2_8b if TSPC_OPP_2_8
@@ -96,6 +97,9 @@ C.7: Mandatory to support at least one of TSPC_OPP_2_11a and TSPC_OPP_2_11b if
        TSPC_OPP_2_11 is supported. Otherwise, both items are excluded.
 C.8: Mandatory to support at least one of TSPC_OPP_2_12a and TSPC_OPP_2_12b if
        TSPC_OPP_2_12 is supported. Otherwise, both items are excluded.
+C.9: Mandatory if TSPC_OPP_1b_2 supported.
+C.10: Optional to support if TSPC_OPP_1b_2 supported else excluded.
+C.11: Mandatory if TSPC_OPP_17 and TSPC_OPP_21 supported else excluded.
 -------------------------------------------------------------------------------
 
 
@@ -119,17 +123,13 @@ TSPC_OPP_3_1      True            Server: Provide information on supported
                                        request. (M)
 TSPC_OPP_3_2   True            Server: Authentication/PIN exchange supported.
                                        (M)
-TSPC_OPP_3_3   True            Server: Object Push is supported. (M)
+TSPC_OPP_3_3   True            Server: Object Push (M)
 TSPC_OPP_3_3a  True (*)        Server: Receive multiple objects in the same
                                        PUT operation. (O)
-TSPC_OPP_3_4   True (*)        Server: vCard 2.1 format is supported for Object
-                                       Push. (C.3)
-TSPC_OPP_3_5   False           Server: vCalender 1.0 format is supported for
-                                       Object Push. (O)
-TSPC_OPP_3_6   False           Server: vMsg as defined in IrMC 1.1 is supported
-                                       for Object Push. (O)
-TSPC_OPP_3_7   False           Server: vNote as defined in IrMC 1.1 is
-                                       supported for Object Push. (O)
+TSPC_OPP_3_4   True (*)        Server: vCard 2.1 (C.3)
+TSPC_OPP_3_5   False           Server: vCalender 1.0 format (O)
+TSPC_OPP_3_6   False           Server: vMsg as defined in IrMC 1.1 (O)
+TSPC_OPP_3_7   False           Server: vNote as defined in IrMC 1.1 (O)
 TSPC_OPP_3_8   True (*)        Server: Support content formats other than those
                                        declared in TSPC_OPP_3_4 through
                                        TSPC_OPP_3_7. (O)
@@ -137,31 +137,37 @@ TSPC_OPP_3_8a     False           Server: Support specific set of other content
                                        formats. (C.4)
 TSPC_OPP_3_8b  True (*)        Server: Support all content formats. (C.4)
 TSPC_OPP_3_9   True (*)        Server: Object Push vCard reject. (O)
-TSPC_OPP_3_10  False           Server: Object Push vCal rejectt. (O)
+TSPC_OPP_3_10  False           Server: Object Push vCal reject. (O)
 TSPC_OPP_3_11  False           Server: Object Push vMsg reject. (O)
 TSPC_OPP_3_12  False           Server: Object Push vNote reject. (O)
-TSPC_OPP_3_13  False           Server: Business card pull is supported. (O.1)
-TSPC_OPP_3_14  False           Server: vCard 2.1 format is supported for
-                                       Business Card Pull. (C.1)
+TSPC_OPP_3_13  False           Server: Business card pull (O.1)
+TSPC_OPP_3_14  False           Server: vCard 2.1 (C.1)
 TSPC_OPP_3_15  False           Server: Business card pull reject. (O)
-TSPC_OPP_3_16  False           Server: Business card exchange is supported.
-                                       (O.2)
-TSPC_OPP_3_17  False           Server: vCard 2.1 format is supported for
-                                       Business Card Exchange (C.2)
+TSPC_OPP_3_16  False           Server: Business card exchange (O.2)
+TSPC_OPP_3_17  False           Server: vCard 2.1 (C.2)
 TSPC_OPP_3_18  False           Server: Business card exchange reject. (O)
+TSPC_OPP_3_19  False           GOEP v2 (C.5)
+TSPC_OPP_3_20  False           GOEP v2 Backward Compability (C.5)
+TSPC_OPP_3_21  False           OBEX over L2CAP (C.5)
+TSPC_OPP_3_22  False           OBEX Reliable Session (C.16)
+TSPC_OPP_3_23  False           OBEX SRM (C.6)
+TSPC_OPP_3_24  False           Send OBEX SRMP header (C.6)
+TSPC_OPP_3_25  False           Receive OBEX SRMP header (C.7)
 -------------------------------------------------------------------------------
-M.1: Mandatory to Support IF (TSPC_OPP_1_2) supported.
-C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is
-       supported.
 O.1: IF NOT Supported, an error message must be sent on request for Business
        Card Pull.
 O.2: IF NOT Supported, an error message must be sent on request for Business
        Card Exchange.
 C.1: Mandatory to Support IF (TSPC_OPP_3_13) Business Card Pull is supported.
+C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is
+       supported.
 C.3: vCard 2.1 support is required for devices containing phonebook
        applications. vCard 2.1 support optional for other devices.
 C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8
        is supported. Otherwise, both items are excluded.
+C.5: Mandatory if TSPC_OPP_2b_2 supported.
+C.6: Optional to support if TSPC_OPP_2b_2 supported, else excluded.
+C.7: Mandatory if TSPC_OPP_3_19 and TSPC_OPP_3_23 supported else excluded.
 -------------------------------------------------------------------------------
 
 
@@ -169,21 +175,13 @@ C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_OPP_4_1   False           Abort-Push Operation is supported. (O)
-TSPC_OPP_4_2   False           Disconnect of OBEX session should be tested.
+TSPC_OPP_4_1   False           Abort-Push Operation (O)
+TSPC_OPP_4_2   False           Intentionally Left Blank (N/A)
 TSPC_OPP_4_3   False           Multiple vCards transferred as a single vObject
-                                       is supported. (C.1)
-TSPC_OPP_4_4   False           Multiple vCards transfer is supported. (C.1)
-TSPC_OPP_4_5   False           vCards with multiple Phone Number Fields is
-                                       supported. (C.1)
-TSPC_OPP_4_6   False           Server supports Push vCal to Different Time
-                                       Zone. (C.1)
-TSPC_ALL       False           Turn on all test cases.
+                                       (C.1)
+TSPC_OPP_4_4   False           Multiple vCards transfer (C.1)
+TSPC_OPP_4_5   False           vCards with multiple Phone Number Fields (C.1)
+TSPC_OPP_4_6   False           Push vCal to Different Time Zone Server (C.1)
 -------------------------------------------------------------------------------
 C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded.
 -------------------------------------------------------------------------------
-
-
--------------------------------------------------------------------------------
-No special PIXIT settings required. All should be set according to Tester's
-test environment.
index 406b6a9..f78fc10 100644 (file)
@@ -1,5 +1,7 @@
 PAN PICS for the PTS tool.
 
+PTS version: 5.1
+
 * - different than PTS defaults
 # - not yet implemented/supported
 
@@ -10,14 +12,14 @@ O - optional
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_PAN_1_1   True (*#)       Role: Network Access Point (O.1)
+TSPC_PAN_1_1   True (*       Role: Network Access Point (O.1)
 TSPC_PAN_1_2   False           Role: Group Ad-hoc Network (O.1)
-TSPC_PAN_1_3   True (*#)       Role: PAN User (O.1)
-TSPC_PAN_1a_1  True (#)        BNEP: BNEP Connection Setup (M)
-TSPC_PAN_1a_2  True (#)        BNEP: BNEP Data Packet Reception (M)
-TSPC_PAN_1a_3  True (#)        BNEP: BNEP Data Packet Transmission (M)
-TSPC_PAN_1a_4  True (#)        BNEP: BNEP Control Message Processing (M)
-TSPC_PAN_1a_5  True (#)        BNEP: BNEP Extension Header Processing (M)
+TSPC_PAN_1_3   True (*       Role: PAN User (O.1)
+TSPC_PAN_1a_1  True            BNEP: BNEP Connection Setup (M)
+TSPC_PAN_1a_2  True            BNEP: BNEP Data Packet Reception (M)
+TSPC_PAN_1a_3  True            BNEP: BNEP Data Packet Transmission (M)
+TSPC_PAN_1a_4  True            BNEP: BNEP Control Message Processing (M)
+TSPC_PAN_1a_5  True            BNEP: BNEP Extension Header Processing (M)
 TSPC_PAN_1a_6  False           BNEP: Network Protocol Filter Message
                                        Transmission (O)
 TSPC_PAN_1a_7  False           BNEP: Multicast Address Filter Message
@@ -31,21 +33,21 @@ O.1: It is mandatory to support at least one of the defined roles.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_PAN_2_1   True (#)        NAP: Support BNEP (M)
-TSPC_PAN_2_2   True (#)        NAP: Support BNEP Forwarding (M)
+TSPC_PAN_2_1   True            NAP: Support BNEP (M)
+TSPC_PAN_2_2   True            NAP: Support BNEP Forwarding (M)
 TSPC_PAN_2_3   False           NAP: Support Layer 2-Bridging between PAN and
                                        External Network (C.1)
-TSPC_PAN_2_4   True (*#)       NAP: Support IP forwarding between PAN and
+TSPC_PAN_2_4   True (*       NAP: Support IP forwarding between PAN and
                                        External Network (C.1)
 TSPC_PAN_2_5   False           NAP: Support BNEP Packet Filtering (O)
-TSPC_PAN_2_6   True (*#)       NAP: Support IPv4 (C.2)
-TSPC_PAN_2_6a  True (*#)       NAP: Supports operable routable IPv4 address (O)
+TSPC_PAN_2_6   False           NAP: Support IPv4 (C.2)
+TSPC_PAN_2_6a  False           NAP: Supports operable routable IPv4 address (O)
 TSPC_PAN_2_6b  False           NAP: Support link-local address configuration
                                        for IPv4 (C.4)
 TSPC_PAN_2_7   False           NAP: Support ping client for IPv4 (O)
 TSPC_PAN_2_8   False           NAP: Support DHCP Client for IPv4 (O)
 TSPC_PAN_2_9   False           NAP: Support DNS/LLMNR Resolver for IPv4 (O)
-TSPC_PAN_2_9a  True (#)        NAP: Support LLMNR Sender for IPv4 (C.5)
+TSPC_PAN_2_9a  False           NAP: Support LLMNR Sender for IPv4 (C.5)
 TSPC_PAN_2_9b  False           NAP: Support LLMNR Responder for IPv4 (O)
 TSPC_PAN_2_10  False           NAP: Support HTTP Client for IPv4 (O)
 TSPC_PAN_2_11  False           NAP: Support WAP Client for IPv4 (O)
@@ -56,8 +58,8 @@ TSPC_PAN_2_14a        False (*)       NAP: Support LLMNR Sender for IPv6 (C.6)
 TSPC_PAN_2_14b False           NAP: Support LLMNR Responder for IPv6 (O)
 TSPC_PAN_2_15  False           NAP: Support HTTP Client for IPv6 (O)
 TSPC_PAN_2_16  False           NAP: Support WAP Client for IPv6 (O)
-TSPC_PAN_2_17  True (#)        NAP: Supports Connectable Mode (M)
-TSPC_PAN_2_18  True (#)        NAP: NAP Service Record (M)
+TSPC_PAN_2_17  True            NAP: Supports Connectable Mode (M)
+TSPC_PAN_2_18  True            NAP: NAP Service Record (M)
 TSPC_PAN_2_19  False           NAP: Support at least three PANUs (O)
 TSPC_PAN_2_20  False           NAP: Support at least two PANUs (O)
 -------------------------------------------------------------------------------
@@ -71,7 +73,7 @@ C.3: Mandatory to support IF any IPv6-based transport protocol OR
        (TSPC_PAN_2_13-16) is supported, ELSE Optional.
 C.4: Mandatory if TSPC_PAN_2_6 is supported and TSPC_PAN_2_6a is not supported,
        otherwise optional.
-C.5: Mandatory if item (TSPC_PAN_2_6) or item supported.
+C.5: Mandatory if item (TSPC_PAN_2_6) supported.
 C.6: Mandatory if item (TSPC_PAN_2_12) supported
 -------------------------------------------------------------------------------
 
@@ -117,12 +119,12 @@ C.4: Mandatory to support if (TSPC_PAN_3_10) is supported.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_PAN_4_1   True (#)        PANU: Support BNEP (M)
-TSPC_PAN_4_2   True (*#)       PANU: Support IPv4 (C.1)
-TSPC_PAN_4_3   True (*#)       PANU: Support ping client for IPv4 (O)
-TSPC_PAN_4_4   True (*#)       PANU: Support DHCP client for  IPv4 (O)
+TSPC_PAN_4_1   True            PANU: Support BNEP (M)
+TSPC_PAN_4_2   True (*       PANU: Support IPv4 (C.1)
+TSPC_PAN_4_3   False           PANU: Support ping client for IPv4 (O)
+TSPC_PAN_4_4   False           PANU: Support DHCP client for  IPv4 (O)
 TSPC_PAN_4_5   False           PANU: Support DNS/LLMNR Resolver for IPv4 (O)
-TSPC_PAN_4_5a  True (#)        PANU: Support LLMNR Sender for IPv4 (C.2)
+TSPC_PAN_4_5a  True            PANU: Support LLMNR Sender for IPv4 (C.2)
 TSPC_PAN_4_5b  False           PANU: Support LLMNR Responder for IPv4 (O)
 TSPC_PAN_4_6   False           PANU: Support HTTP Client for IPv4 (O)
 TSPC_PAN_4_7   False           PANU: Support WAP Client for IPv4 (O)
@@ -144,8 +146,3 @@ C.1: PAN User devices must support at least One of items (TSPC_PAN_4_2) or
 C.2: Mandatory to support if (TSPC_PAN_4_2) is supported.
 C.3: Mandatory to support if (TSPC_PAN_4_8) is supported.
 -------------------------------------------------------------------------------
-
-
--------------------------------------------------------------------------------
-No special PIXIT settings required. All should be set according to Tester's
-test environment.
index 2af5fd8..f75a972 100644 (file)
@@ -1,5 +1,7 @@
 PBAP PICS for the PTS tool.
 
+PTS version: 5.1
+
 * - different than PTS defaults
 # - not yet implemented/supported
 
@@ -12,8 +14,7 @@ Parameter Name        Selected        Description
 -------------------------------------------------------------------------------
 TSPC_PBAP_0_1  False           Role: PBAP 1.0 (C.1)
 TSPC_PBAP_0_2  True (*)        Role: PBAP 1.1 (C.1)
-TSPC_PBAP_0_3  False           Role: Reserve
-TSPC_PBAP_0_4  False (*)       Role: PBAP 1.2 (C.1)
+TSPC_PBAP_0_3  False           Role: PBAP 1.2 (C.1)
 -------------------------------------------------------------------------------
 C.1: Mandatory to support one and only one major profile version.
 -------------------------------------------------------------------------------
@@ -116,7 +117,7 @@ Parameter Name      Selected        Description
 TSPC_PBAP_6_1  False (*)       PCE: Connect (M)
 TSPC_PBAP_6_2  False (*)       PCE: Disconnect (M)
 TSPC_PBAP_6_3  False (*)       PCE: Get (M)
-TSPC_PBAP_6_4  False (*)       PCE: Abort (M)
+TSPC_PBAP_6_4  False           PCE: Abort (O)
 TSPC_PBAP_6_5  False (*)       PCE: SetPath (C.1)
 TSPC_PBAP_6_6  False           PCE: Support for OBEX authentication initiation
                                        (C.2)
@@ -195,11 +196,11 @@ TSPC_PBAP_9_10a   False           PSE: Referencing Contacts (C.3)
 TSPC_PBAP_9_12 False           PSE: Contact Image Default Format (C.1)
 TSPC_PBAP_9_12a        False           PSE: Able to request Contact Images (C.2)
 TSPC_PBAP_9_13 False           PSE: Supported Phonebook Objects
-TSPC_PBAP_9_13a        False (*)       PSE: Telecom/pb (M)
-TSPC_PBAP_9_13b        False           PSE: Telecom/ich (O)
-TSPC_PBAP_9_13c        False           PSE: Telecom/och (O)
-TSPC_PBAP_9_13d        False           PSE: Telecom/mch (O)
-TSPC_PBAP_9_13e        False (*)       PSE: Telecom/cch (O)
+TSPC_PBAP_9_13a        True            PSE: Telecom/pb (M)
+TSPC_PBAP_9_13b        True  (*)       PSE: Telecom/ich (O)
+TSPC_PBAP_9_13c        True  (*)       PSE: Telecom/och (O)
+TSPC_PBAP_9_13d        True  (*)       PSE: Telecom/mch (O)
+TSPC_PBAP_9_13e        True            PSE: Telecom/cch (O)
 TSPC_PBAP_9_13f        False           PSE: Telecom/spd (C.2)
 TSPC_PBAP_9_13g        False           PSE: Telecom/fav (C.2)
 TSPC_PBAP_9_13h        False (*)       PSE: SIM1/Telecom/pb (O)
@@ -353,7 +354,7 @@ TSPC_PBAP_19_2      True            PSE: Initiate LMP-Authentication (M)
 TSPC_PBAP_19_3 False           PSE: Security mode 1 (C.2)
 TSPC_PBAP_19_4 False           PSE: Security mode 2 (C.1)
 TSPC_PBAP_19_5 False           PSE: Security mode 3 (C.1)
-TSPC_PBAP_19_6 False           PSE: Security mode 4 (C.1)
+TSPC_PBAP_19_6 True (*)        PSE: Security mode 4 (C.1)
 -------------------------------------------------------------------------------
 C.1: At least one of TSPC_PBAP_19_3, TSPC_PBAP_19_4, TSPC_PBAP_19_5 or
        TSPC_PBAP_19_6 (security mode 2, 3, or 4) shall be supported.
@@ -413,7 +414,7 @@ C.3: Optional if TSPC_PBAP_10_2 is supported, otherwise excluded.
 -------------------------------------------------------------------------------
 Parameter Name Selected        Description
 -------------------------------------------------------------------------------
-TSPC_PBAP_25_1 False (*)       PCE: GOEP v2.0 or later (M)
+TSPC_PBAP_25_1 False           PCE: GOEP v2.0 or later (M)
 TSPC_PBAP_25_2 False (*)       PCE: GOEP v2 Backwards Compatibility (M)
 TSPC_PBAP_25_3 False           PCE: OBEX over L2CAP (M)
 TSPC_PBAP_25_4 False           PCE: OBEX SRM (M)
@@ -435,12 +436,7 @@ TSPC_PBAP_26_3     False           PSE: OBEX over L2CAP (M)
 TSPC_PBAP_26_4 False           PSE: OBEX SRM (M)
 TSPC_PBAP_26_5 False (*)       PSE: Send OBEX SRMP header (C.1)
 TSPC_PBAP_26_6 False           PSE: Receive OBEX SRMP header (M)
-TSPC_ALL       False (*)       Turns on all the test cases
+TSPC_ALL       False           Turns on all the test cases
 -------------------------------------------------------------------------------
 C.1: Optional if TSPC_PBAP_26_4 (OBEX SRM) is supported, otherwise Excluded.
 -------------------------------------------------------------------------------
-
-
--------------------------------------------------------------------------------
-No special PIXIT settings required. All should be set according to Tester's
-test environment.
diff --git a/android/pics-rfcomm.txt b/android/pics-rfcomm.txt
new file mode 100644 (file)
index 0000000..09cea18
--- /dev/null
@@ -0,0 +1,54 @@
+
+* - different than BITE defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+               Protocol Version
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+F.1.0.1                False           RFCOMM 1.1 with TS 07.10 (C.1)
+F.1.0.2                True            RFCOMM 1.2 with TS 07.10 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support one and only one of the protocol versions.
+-------------------------------------------------------------------------------
+
+
+               Supported Procedures
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+F.1.1.1                True            Initialize RFCOMM Session (C.2)
+F.1.1.2                True            Respond to Initialization of an RFCOMM
+                               Session (C.1)
+F.1.1.3                True            Shutdown RFCOMM Session (M)
+F.1.1.4                True            Respond to a Shutdown of an RFCOMM Session (M)
+F.1.1.5                True            Establish DLC (C.2)
+F.1.1.6                True            Respond to Establishment of a DLC (C.1)
+F.1.1.7                True            Disconnect DLC (M)
+F.1.1.8                True            Respond to Disconnection of a DLC (M)
+F.1.1.9                True            Respond to and send MSC Command (M)
+F.1.1.10       True            Initiate Transfer Information (M)
+F.1.1.11       True            Respond to Test Command (M)
+F.1.1.12       True            Send Test Command (O)
+F.1.1.13       True            React to Aggregate Flow Control (M)
+F.1.1.14       True            Respond to RLS Command (M)
+F.1.1.15       False (*)       Send RLS Command (O)
+F.1.1.16       True            Respond to PN Command (M)
+F.1.1.17       True            Send PN Command (C.3)
+F.1.1.18       True            Send Non-Supported Command (NSC) response (C.4)
+F.1.1.19       True            Respond to RPN Command (M)
+F.1.1.18                       Send RPN Command (O)
+F.1.1.21       True            Closing Multiplexer by First Sending a DISC
+                               Command (O)
+F.1.1.22       True            Support of Credit Based Flow Control (M)
+F.1.1.23       True            IUT Responds to Establishment of a DLC (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if SPP 1/2 (Device B) is supported, otherwise Excluded.
+C.2: Mandatory if SPP 1/1 (Device A) is supported, otherwise Excluded.
+C.3: Mandatory if SPP 1/1 (Device A) is supported, otherwise Optional
+C.4: Mandatory to support if 0/2 (RFCOMM 1.2) is supported, otherwise Optional.
+-------------------------------------------------------------------------------
diff --git a/android/pics-sdp.txt b/android/pics-sdp.txt
new file mode 100644 (file)
index 0000000..cb98130
--- /dev/null
@@ -0,0 +1,137 @@
+
+* - different than BITE defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+               Support Different Size Capabilities on UUID
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+E.1.1          True            Support for 128 bit UUID (M)
+E.1.2          True            Support for 32 bit UUID (M)
+E.1.3          True            Support for 16 bit UUID (M)
+-------------------------------------------------------------------------------
+
+
+               Roles
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+E.1b.1         True            Support for server role (C.1)
+E.1b.2         True            Support for client role (C.1)
+-------------------------------------------------------------------------------
+C.1 Mandatory to support at least one of the roles
+-------------------------------------------------------------------------------
+
+
+               Valid Service Search Request
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+E.2.1          True            Support for respond on search of single
+                               Service, using ServiceSearchRequest (C.2)
+E.2.2          True            Support for respond on search of Service,
+                               using continuation state (O)
+E.2.3          True            Search for services using the continuation
+                               state (C.1)
+-------------------------------------------------------------------------------
+C.1 Mandatory to support if the client role is supported (1b/2)
+C.2 Mandatory to support if the server role is supported (1b/1)
+-------------------------------------------------------------------------------
+
+
+               Invalid Service Search Request
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+E.3.1          True            Support for error response on Service search
+                               request (M)
+-------------------------------------------------------------------------------
+
+
+               Valid Service Attribute Request
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+E.4.1          True            Support for respond on search of
+                               Attribute(s) (M)
+E.4.2          True            Support for respond on search of
+                               Attribute, using continuation state (O)
+E.4.3          True            Support for respond on search on
+                               attribute AdditionalProtocolDescriptorList (O)
+-------------------------------------------------------------------------------
+
+
+               Invalid Service Attribute Request
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+E.5.1          True            Support for error response on Attribute
+                               search request (M)
+-------------------------------------------------------------------------------
+
+
+               Valid Service Search Attribute Request
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+E.6.1          True            Support for respond on search for Service(s)
+                               and Attribute(s) (M)
+E.6.2          True            Support for respond on search of Attribute,
+                               using continuation state (O)
+E.6.3          True            Support for respond on search on attribute
+                               AdditionalProtocolDescriptorList on existing
+                               service (O)
+-------------------------------------------------------------------------------
+
+
+               Invalid Service Search Attribute Request
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+E.7.1          True            Support for error response on Service and
+                               Attribute request (M)
+-------------------------------------------------------------------------------
+
+
+               Service Browsing
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+E.8.1          True            Support for browsing, using
+                               SDP_ServiceSearchRequest and
+                               SDP_ServiceAttributeRequest (O)
+E.8.2          True            Support for browsing, using
+                               SDP_ServiceSearchAttributeRequest (O)
+-------------------------------------------------------------------------------
+
+
+               Attributes Present in IUT
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+E.9.1          True            ServiceID (O)
+E.9.2          True            ProtocolDescriptorList (O)
+E.9.3          True            ServiceRecordState (O)
+E.9.4          True            ServiceInfoTimeToLive (O)
+E.9.5          True            BrowseGroupList (O)
+E.9.6          True            LanguageBaseAttributeIdList (O)
+E.9.7          True            ServiceAvailability (O)
+E.9.8          True            IconURL (O)
+E.9.9          True            ServiceName (O)
+E.9.10         True            ServiceDescription (O)
+E.9.11         True            ProviderName (O)
+E.9.12         True            VersionNumberList (O)
+E.9.13         True            ServiceDataBaseState (O)
+E.9.14         True            BluetoothProfileDescriptorList (O)
+E.9.15         True            DocumentationURL (O)
+E.9.16         True            ClientExecutableURL (O)
+E.9.17         True            AdditionalProtocolDescriptorList (C.1)
+E.9.18         True            ServiceRecordHandle (M)
+E.9.19         True            ServiceClassIDList (M)
+-------------------------------------------------------------------------------
+C.1: Optional if 9/2 is supported, otherwise excluded
+-------------------------------------------------------------------------------
diff --git a/android/pics-sm.txt b/android/pics-sm.txt
new file mode 100644 (file)
index 0000000..3060d40
--- /dev/null
@@ -0,0 +1,91 @@
+SM PICS for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+
+M - mandatory
+O - optional
+
+               Connection Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_SM_1_1    True            Master Role (Initiator) (C.1)
+TSPC_SM_1_2    True            Slave Role (Responder) (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one of these features shall be supported.
+-------------------------------------------------------------------------------
+
+
+               Security Properties
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_SM_2_1    True            Authenticated MITM protection (O)
+TSPC_SM_2_2    True            Unauthenticated no MITM protection (C.1)
+TSPC_SM_2_3    True            No security requirements (M)
+TSPC_SM_2_4    True            OOB supported (O)
+-------------------------------------------------------------------------------
+C.1: If TSPC_SM_2_1 is supported then Mandatory, else Optional
+-------------------------------------------------------------------------------
+
+
+               Encryption Key Size
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_SM_3_1    True            Encryption Key Size Negotiation (M)
+-------------------------------------------------------------------------------
+
+
+               Pairing Method
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_SM_4_1    True            Just Works (O)
+TSPC_SM_4_2    True            Passkey Entry (C.1)
+TSPC_SM_4_3    True            Out of Band (C.1)
+-------------------------------------------------------------------------------
+C.1: If TSPC_SM_2_1 is supported, at least one of these features shall be
+       supported.
+-------------------------------------------------------------------------------
+
+
+               Security Initiation
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_SM_5_1    True            Encryption Setup using STK (C.3)
+TSPC_SM_5_2    True            Encryption Setup using LTK (O)
+TSPC_SM_5_3    True            Slave Initiated Security (C.1)
+TSPC_SM_5_4    True            Slave Initiated Security – Master response(C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_SM_1_2 is supported, otherwise Excluded
+C.2: Mandatory if TSPC_SM_1_1 is supported, otherwise Excluded
+C.3: Mandatory IF TSPC_SM_2_1 OR TSPC_SM_2_1 OR TSPC_SM_2_4 is supported,
+       otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+               Signing Algorithm
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_SM_6_1    True            Signing Algorithm - Generation (O)
+TSPC_SM_6_2    True            Signing Algorithm - Resolving (O)
+-------------------------------------------------------------------------------
+
+
+               Key Distribution
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_SM_7_1    True            Encryption Key (C.1)
+TSPC_SM_7_2    True            Identity Key (C.2)
+TSPC_SM_7_3    True            Signing Key (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if GAP (24/2 OR 42/6) is supported, ELSE Optional
+C.2: Mandatory if GAP (26/3) is supported, ELSE Optional
+C.3: Mandatory if GAP (25/6 OR 35/6) is supported, ELSE Optional
+-------------------------------------------------------------------------------
diff --git a/android/pics-spp.txt b/android/pics-spp.txt
new file mode 100644 (file)
index 0000000..6057d45
--- /dev/null
@@ -0,0 +1,64 @@
+
+* - different than BITE defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+
+               Profile Version
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+K.5.0.1                False           SPP v1.1 (C.1)
+K.5.0.2                True            SPP v1.2 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support only one Profile version.
+-------------------------------------------------------------------------------
+
+               Device Role
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+K.5.1.1                True            Device A (DevA) (C.1)
+K.5.1.2                True            Device B (DevB) (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+               Support of SPP Service
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+K.5.2.1                True            Support of Serial Profile Service (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory for devices that support Serial Profile for serial cable
+     emulation as a Bluetooth service. Irrelevant of devices that only
+     support Serial Profile for usage by another application profile
+     e.g. Fax Profile, Dun Profile, Hands free Profile, etc.
+-------------------------------------------------------------------------------
+
+
+               Application Procedures
+-------------------------------------------------------------------------------
+Item           Selected        Description
+-------------------------------------------------------------------------------
+K.5.3.1                True            Establish link and set up virtual serial
+                               connection (C.1)
+K.5.3.2                True            Accept link and virtual serial connection
+                               establishment (C.2)
+K.5.3.3                True            Register Service record for application in
+                               local SDP database (C.2)
+K.5.3.4                True            No release in Sniff mode. Sniff mode enabled
+                               in the Link Manager (O)
+K.5.3.5                True            No release in Hold mode. Hold mode enabled
+                               in the Link Manager (O)
+K.5.3.6                True            No release in Park mode. Park mode enabled
+                               in the Link Manager (O)
+K.5.3.7                True            No release after Master/Slave switch. M/S
+                               switch enabled in the Link manager (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory for Device A, Irrelevant for Device B.
+C.2: Mandatory for Device B, Irrelevant for Device A.
+-------------------------------------------------------------------------------
diff --git a/android/pixit-a2dp.txt b/android/pixit-a2dp.txt
new file mode 100644 (file)
index 0000000..807ce3b
--- /dev/null
@@ -0,0 +1,30 @@
+A2DP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                 Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled          FALSE
+TSPX_bd_addr_iut               112233445566 (*&)
+TSPX_SRC_class_of_device       080418
+TSPX_SNK_class_of_device       04041C
+TSPX_pin_code                  0000
+TSPX_delete_link_key           FALSE
+TSPX_time_guard                        300000
+TSPX_use_implicit_send         TRUE
+TSPX_media_directory           C:\Program Files\Bluetooth SIG\Bluetooth PTS\
+                                       bin\audio (#)
+TSPX_no_avrcp                  TRUE
+TSPX_auth_password             0000
+TSPX_auth_user_id              PTS
+TSPX_rfcomm_channel            8
+TSPX_l2cap_psm                 1011
+TSPX_no_confirmations          FALSE
+TSPX_cover_art_uuid            3EEE
+-------------------------------------------------------------------------------
diff --git a/android/pixit-avctp.txt b/android/pixit-avctp.txt
new file mode 100644 (file)
index 0000000..a9b3f4f
--- /dev/null
@@ -0,0 +1,39 @@
+AVCTP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                 Value
+-------------------------------------------------------------------------------
+TSPX_avctp_psm                 0017
+TSPX_avctp_profile_id          110E
+TSPX_connect_avdtp             TRUE
+TSPX_avctp_tester_command_data
+TSPX_avctp_tester_response_data
+TSPX_avctp_iut_command_data
+TSPX_avctp_iut_response_data
+TSPX_bd_addr_iut               11223344556677 (*&)
+TSPX_pin_code                  0000
+TSPX_delete_link_key           FALSE
+TSPX_security_enabled          FALSE
+TSPX_class_of_device           20050C
+TSPX_player_feature_bitmask    0000000000000007FFF00070000000000
+TSPX_avrcp_version
+TSPX_establish_avdtp_stream    TRUE
+TSPX_tester_av_role
+TSPX_time_guard                        300000
+TSPX_avrcp_only                        FALSE
+TSPX_use_implicit_send         TRUE
+TSPX_media_directory           C:\Program Files\Bluetooth SIG\Bluetooth PTS\
+                                       bin\audio (#)
+TSPX_no_confirmations          FALSE
+TSPX_auth_password             0000
+TSPX_auth_user_id              PTS
+TSPX_rfcomm_channel            8
+TSPX_l2cap_psm                 1011
+-------------------------------------------------------------------------------
diff --git a/android/pixit-avrcp.txt b/android/pixit-avrcp.txt
new file mode 100644 (file)
index 0000000..ceae884
--- /dev/null
@@ -0,0 +1,36 @@
+AVRCP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                 Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled          FALSE
+TSPX_bd_addr_iut               112233445566 (*&)
+TSPX_class_of_device           20050C
+TSPX_player_feature_bitmask    0000000000000007FFF00070000000000
+TSPX_pin_code                  0000
+TSPX_delete_link_key           FALSE
+TSPX_time_guard                        300000
+TSPX_avrcp_only                        FALSE
+TSPX_search_string             tomorrow
+TSPX_max_avc_fragments         10
+TSPX_establish_avdtp_stream    TRUE
+TSPX_use_implicit_send         TRUE
+TSPX_avrcp_version
+TSPX_tester_av_role
+TSPX_media_directory           C:\Program Files\Bluetooth SIG\Bluetooth PTS\
+                                       bin\audio (#)
+TSPX_auth_password             0000
+TSPX_auth_user_id              PTS
+TSPX_rfcomm_channel            8
+TSPX_l2cap_psm                 1011
+TSPX_no_confirmations          FALSE
+TSPX_no_cover_art_folder
+TSPX_cover_art_folder
+-------------------------------------------------------------------------------
diff --git a/android/pixit-did.txt b/android/pixit-did.txt
new file mode 100644 (file)
index 0000000..c40f938
--- /dev/null
@@ -0,0 +1,24 @@
+DID PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled                                  False
+TSPX_ClientExecutableURL                               False (*)
+TSPX_ServiceDescription                                        False (*)
+TSPX_DocumentationURL                                  False (*)
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_class_of_device_pts                               200404
+TSPX_device_search_time                                        30
+TSPX_delete_link_key                                   False
+TSPX_pin_code                                          0000
+TSPX_time_guard                                                200000
+TSPX_use_implicit_send                                 True
+TSPX_secure_simple_pairing_pass_key_confirmation       False
+-------------------------------------------------------------------------------
diff --git a/android/pixit-gap.txt b/android/pixit-gap.txt
new file mode 100644 (file)
index 0000000..494e24d
--- /dev/null
@@ -0,0 +1,57 @@
+GAP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_bd_addr_PTS                                       000000000000
+TSPX_broadcaster_class_of_device                       100104
+TSPX_observer_class_of_device                          100104
+TSPX_peripheral_class_of_device                                100104
+TSPX_central_class_of_device                           100104
+TSPX_security_enabled                                  True
+TSPX_delete_link_key                                   True
+TSPX_pin_code                                          0000
+TSPX_time_guard                                                300000
+TSPX_use_implicit_send                                 True
+TSPX_use_dynamic_pin                                   False
+TSPX_secure_simple_pairing_pass_key_confirmation       False
+TSPX_using_public_device_address                       True
+TSPX_using_random_device_address                       False
+TSPX_lim_adv_timeout                                   30720
+TSPX_gen_disc_adv_min                                  30720
+TSPX_lim_disc_scan_min                                 10240
+TSPX_gen_disc_scan_min                                 10240
+TSPX_database_file                                     Database-GAP.sig
+TSPX_iut_rx_mtu                                                23
+TSPX_iut_private_address_interval                      10000
+TSPX_iut_privacy_enabled                               False
+TSPX_psm                                               1001
+TSPX_iut_valid_connection_interval_min                 00C8
+TSPX_iut_valid_conneciton_interval_max                 0960
+TSPX_iut_valid_connection_latency                      0007
+TSPX_iut_valid_timeout_multiplier                      0960
+TSPX_iut_connection_parameter_timeout                  30000
+TSPX_iut_invalid_connection_interval_min               0000
+TSPX_iut_invalid_conneciton_interval_max               0000
+TSPX_iut_invalid_connection_latency                    0000
+TSPX_iut_invalid_timeout_multiplier                    0000
+TSPX_LE_scan_interval                                  0010
+TSPX_LE_scan_window                                    0010
+TSPX_con_interval_min                                  0032
+TSPX_con_interval_max                                  0046
+TSPX_con_latency                                       0000
+TSPX_supervision_timeout                               07D0
+TSPX_minimum_ce_length                                 0000
+TSPX_maximum_ce_length                                 0000
+TSPX_pairing_before_service_request                    False
+TSPX_iut_mandates_mitm                                 False
+TSPX_encryption_before_service_request                 False
+TSPX_tester_appearance                                 0000
+-------------------------------------------------------------------------------
diff --git a/android/pixit-gatt.txt b/android/pixit-gatt.txt
new file mode 100644 (file)
index 0000000..c073452
--- /dev/null
@@ -0,0 +1,31 @@
+GATT PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_security_enabled                                  FALSE
+TSPX_delete_link_key                                   TRUE
+TSPX_time_guard                                                180000
+TSPX_selected_handle                                   0012
+TSPX_use_implicit_send                                 TRUE
+TSPX_secure_simple_pairing_pass_key_confirmation       FALSE
+TSPX_iut_use_dynamic_bd_addr                           FALSE
+TSPX_iut_setup_att_over_br_edr                         FALSE
+TSPX_tester_database_file                              [Path to GATT Test
+                                                               Database]
+TSPX_iut_is_client_periphral                           FALSE
+TSPX_iut_is_server_central                             FALSE
+TSPX_mtu_size                                          23
+TSPX_pin_code                                          0000
+TSPX_use_dynamic_pin                                   FALSE
+TSPX_delete_ltk                                                FALSE
+TSPX_characteristic_readable                           FALSE
+TSPX_tester_appearance                                 0000
+-------------------------------------------------------------------------------
diff --git a/android/pixit-hdp.txt b/android/pixit-hdp.txt
new file mode 100644 (file)
index 0000000..24cdcec
--- /dev/null
@@ -0,0 +1,32 @@
+HDP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled                                  TRUE (*)
+TSPX_delete_link_key                                   FALSE
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_sink_device_class_of_device                       00900
+TSPX_source_device_class_of_device                     00900
+TSPX_pin_code                                          0000
+TSPX_use_dynamic_pin                                   FALSE
+TSPX_use_implicit_send                                 TRUE
+TSPX_MCAP_l2cap_psm_control                            1001
+TSPX_MCAP_l2cap_psm_data                               1003
+TSPX_MCAP_sync_lead_time                               0BB8
+TSPX_MCAP_timestamp_native_resolution                  2233
+TSPX_MCAP_timestamp_native_accuracy                    1400
+TSPX_MCAP_timestamp_required_accuracy                  6400
+TSPX_DC_max                                    1
+TSPX_secure_simple_pairing_pass_key_confirmation       FALSE
+TSPX_time_guard                                                6000000
+TSPX_ieee_device_specialization                                10417
+TSPX_ieee_standard_configuration                       TRUE
+TSPX_MCAP_bluetooth_clock_access_resolution            55
+-------------------------------------------------------------------------------
diff --git a/android/pixit-hfp.txt b/android/pixit-hfp.txt
new file mode 100644 (file)
index 0000000..ebf73c8
--- /dev/null
@@ -0,0 +1,38 @@
+HFP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to the respective phone numbers
+^ - should be set according to the reported phone number's type
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled                                  TRUE
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_hf_class_of_device                                        200408
+TSPX_ag_class_of_device                                        400204
+TSPX_packet_type_sco                                   00A0
+TSPX_phone_number                                      1234567 (#)
+TSPX_second_phone_number                               7654321 (#)
+TSPX_phone_number_type                                 145 (*^)
+TSPX_second_phone_number_type                          145 (*^)
+TSPX_phone_number_memory                               1
+TSPX_phone_number_memory_invalid_index                 9999
+TSPX_scan_all_memory_dial_locations                    FALSE
+TSPX_pin_code                                          0000
+TSPX_time_guard                                                300000
+TSPX_use_implicit_send                                 TRUE
+TSPX_verbose_implicit_send                             FALSE
+TSPX_delete_link_key                                   FALSE
+TSPX_server_channel_tester                             01
+TSPX_server_channel_iut                                        00
+TSPX_verify_CLIP_information                           TRUE
+TSPX_no_fail_verdict                                   FALSE
+TSPX_network_supports_correct_call_and_callstatus      TRUE
+TSPX_secure_simple_pairing_pass_key_confirmation       FALSE
+TSPX_AG_match_tester_BRSF_codec_negotiation_bit                FALSE
+-------------------------------------------------------------------------------
diff --git a/android/pixit-hid.txt b/android/pixit-hid.txt
new file mode 100644 (file)
index 0000000..44e4363
--- /dev/null
@@ -0,0 +1,30 @@
+HID PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled                                  True
+TSPX_delete_link_key                                   True
+TSPX_query_iut_sdp                                     True
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_pointing_device_class_of_device                   002580
+TSPX_keyboard_device_class_of_device                   002540
+TSPX_host_class_of_device                              100108
+TSPX_pts_device_role                                   MOUSE
+TSPX_pin_code                                          0000
+TSPX_use_dynamic_pin_code                              False
+TSPX_time_guard                                                6000000
+TSPX_hid_data_interval                                 1000
+TSPX_use_implicit_send                                 True
+TSPX_verbose_implicit_send                             False
+TSPX_no_fail_verdicts                                  False
+TSPX_time_reconnect                                    30000
+TSPX_secure_simple_pairing_pass_key_confirmation       False
+TSPX_hid_report_id                                     1
+-------------------------------------------------------------------------------
diff --git a/android/pixit-hsp.txt b/android/pixit-hsp.txt
new file mode 100644 (file)
index 0000000..2d5b14b
--- /dev/null
@@ -0,0 +1,30 @@
+HSP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled                                  TRUE
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_hs_class_of_device                                        200404
+TSPX_ag_class_of_device                                        400204
+TSPX_packet_type_sco                                   00A0
+TSPX_pin_code                                          0000
+TSPX_time_guard                                                20000000
+TSPX_use_implicit_send                                 TRUE
+TSPX_verbose_implicit_send                             FALSE
+TSPX_delete_link_key                                   FALSE
+TSPX_server_channel_tester                             01
+TSPX_server_channel_iut                                        00
+TSPX_no_fail_verdict                                   FALSE
+TSPX_remote_audio_volume_control                       TRUE
+TSPX_secure_simple_pairing_pass_key_confirmation       FALSE
+TSPX_inband_ring_only                                  FALSE
+TSPX_no_ring_or_inband_ring_tone                       FALSE
+TSPX_iut_establish_audio_before_RING                   FALSE
+-------------------------------------------------------------------------------
diff --git a/android/pixit-iopt.txt b/android/pixit-iopt.txt
new file mode 100644 (file)
index 0000000..a4bb581
--- /dev/null
@@ -0,0 +1,23 @@
+IOPT PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_security_enabled                                  FALSE
+TSPX_delete_link_key                                   FALSE
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_class_of_device_pts                               200404
+TSPX_class_of_device_test_pts_initiator                        TRUE
+TSPX_limited_inquiry_used                              FALSE
+TSPX_pin_code                                          0000
+TSPX_time_guard                                                200000
+TSPX_device_search_time                                        20
+TSPX_use_implicit_send                                 TRUE
+TSPX_secure_simple_pairing_pass_key_confirmation       FALSE
+-------------------------------------------------------------------------------
diff --git a/android/pixit-l2cap.txt b/android/pixit-l2cap.txt
new file mode 100644 (file)
index 0000000..914a995
--- /dev/null
@@ -0,0 +1,41 @@
+L2CAP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_client_class_of_device                            100104
+TSPX_server_class_of_device                            100104
+TSPX_security_enabled                                  TRUE
+TSPX_delete_link_key                                   FALSE
+TSPX_pin_code                                          0000
+TSPX_flushto                                           FFFF
+TSPX_inmtu                                             02A0
+TSPX_no_fail_verditcs                                  FALSE
+TSPX_oumtu                                             02A0
+TSPX_iut_role_initiator                                TRUE
+TSPX_psm                                               1011 (*)
+TSPX_time_guard                                        180000
+TSPX_timer_ertx                                        120000
+TSPX_timer_ertx_max                                    300000
+TSPX_timer_ertx_min                                    60000
+TSPX_timer_rtx                                         10000
+TSPX_timer_rtx_max                                     60000
+TSPX_timer_rtx_min                                     1000
+TSPX_rfc_mode_tx_window_size                           08
+TSPX_rfc_mode_max_transmit                             03
+TSPX_rfc_mode_retransmission_timeout                   07D0
+TSPX_rfc_mode_monitor_timeout                          2EE0
+TSPX_rfc_mode_maximum_pdu_size                         02A0
+TSPX_extended_window_size                              0012
+TSPX_use_implicit_send                                 TRUE
+TSPX_use_dynamic_pin                                   FALSE
+TSPX_iut_SDU_size_in_bytes                             144
+TSPX_secure_simple_pairing_pass_key_confirmation       FALSE
+-------------------------------------------------------------------------------
diff --git a/android/pixit-map.txt b/android/pixit-map.txt
new file mode 100644 (file)
index 0000000..ba687c1
--- /dev/null
@@ -0,0 +1,47 @@
+MAP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to tester's phone number
+$ - should be set to IUT e-mail address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_auth_password                                     0000
+TSPX_auth_user_id                                      PTS
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_client_class_of_device                            100204
+TSPX_delete_link_key                                   FALSE
+TSPX_get_object_name                                   put.gif
+TSPX_initial_path
+TSPX_l2cap_psm                                         1001
+TSPX_no_confirmations                                  FALSE
+TSPX_pin_code                                          0000
+TSPX_rfcomm_channel                                    8
+TSPX_secure_simple_pairing_pass_key_confirmation       FALSE
+TSPX_security_enabled                                  TRUE
+TSPX_server_class_of_device                            100204
+TSPX_time_guard                                                300000
+TSPX_use_implicit_send                                 TRUE
+TSPX_Message_Access_rfcomm_channel                     1
+TSPX_Message_Notification_rfcomm_channel               2
+TSPX_SPP_rfcomm_channel                                        03
+TSPX_filter_period_begin                               20100101T000000
+TSPX_filter_period_end                                 20111231T125959
+TSPX_filter_recipient                                  PTS
+TSPX_filter_originator                                 PTS
+TSPX_default_message_upload_folder_in_msg              draft
+TSPX_default_test_folder_in_msg                                inbox
+TSPX_use_fixed_MASInstanceID                           FALSE
+TSPX_MASInstanceID_SMS                                 0
+TSPX_MASInstanceID_MMS                                 0
+TSPX_MASInstanceID_Email                               0
+TSPX_message_notification_l2cap_psm                    1003
+TSPX_message_notification_rfcomm_channel               9
+TSPX_upload_msg_phonenumber                            123456789 (#)
+TSPX_upload_msg_emailaddress                           IUT-email ($)
+-------------------------------------------------------------------------------
diff --git a/android/pixit-mcap.txt b/android/pixit-mcap.txt
new file mode 100644 (file)
index 0000000..ee67a1a
--- /dev/null
@@ -0,0 +1,37 @@
+MCAP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_delete_link_key                                   FALSE
+TSPX_MCAP_DC_max                                       2
+TSPX_MCAP_l2cap_psm_control                            1003
+TSPX_MCAP_l2cap_psm_control_B
+TSPX_MCAP_l2cap_psm_data                               1005
+TSPX_MCAP_l2cap_psm_data_B
+TSPX_MCAP_bluetooth_clock_access_resolution            55
+TSPX_MCAP_create_mdl_configuration
+TSPX_MCAP_data_channel_setup_mode
+TSPX_MCAP_iut_initiate_connection                      TRUE (*)
+TSPX_MCAP_mdep_id                                      12
+TSPX_MCAP_profile_name
+TSPX_MCAP_sync_lead_time                               0BB8
+TSPX_tester_role
+TSPX_MCAP_timestamp_native_accuracy                    1400
+TSPX_MCAP_timestamp_native_resolution                  2233
+TSPX_MCAP_timestamp_required_accuracy                  6400
+TSPX_host_class_of_device                              100108
+TSPX_pin_code                                          0000
+TSPX_security_enabled                                  TRUE (*)
+TSPX_time_guard                                                6000000
+TSPX_use_dynamic_pin                                   FALSE
+TSPX_use_implicit_send                                 TRUE
+TSPX_verbose_implicit_send                             TRUE
+-------------------------------------------------------------------------------
diff --git a/android/pixit-mps.txt b/android/pixit-mps.txt
new file mode 100644 (file)
index 0000000..c59c913
--- /dev/null
@@ -0,0 +1,46 @@
+MPS PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+^ - should be set accordingly
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_avrcp_revision
+TSPX_class_of_device                                   20050C
+TSPX_establish_avdtp_stream                            TRUE
+TSPX_iut_establishes_initial_condition                 FALSE
+TSPX_tester_device
+TSPX_media_directory
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_delete_link_key                                   FALSE
+TSPX_pin_code                                          0000
+TSPX_security_enabled                                  FALSE
+TSPX_time_guard                                                300000
+TSPX_use_implicit_send                                 TRUE
+TSPX_auth_password                                     0000
+TSPX_auth_user_id                                      PTS
+TSPX_l2cap_psm                                         1003
+TSPX_rfcomm_channel                                    8
+TSPX_no_confirmations                                  FALSE
+TSPX_AG_match_tester_BRSF_codec_negotiation_bit                FALSE
+TSPX_network_supports_correct_call_and_callstatus      TRUE
+TSPX_no_fail_verdicts                                  FALSE
+TSPX_packet_type_sco                                   00A0
+TSPX_phone_number                                      1234567 (^)
+TSPX_phone_number_memory                               1
+TSPX_phone_number_memory_invalid_index                 9999
+TSPX_phone_number_type                                 129
+TSPX_scan_all_memory_dial_locations                    FALSE
+TSPX_second_phone_number                               1234567 (^)
+TSPX_second_phone_type                                 129
+TSPX_secure_simple_pairing_pass_key_confirmation       FALSE
+TSPX_server_channel_iut                                        00
+TSPX_server_channel_tester                             01
+TSPX_verbose_implicit_send                             FALSE
+TSPX_verify_CLIP_information                           TRUE
+-------------------------------------------------------------------------------
diff --git a/android/pixit-opp.txt b/android/pixit-opp.txt
new file mode 100644 (file)
index 0000000..7b0f4f2
--- /dev/null
@@ -0,0 +1,27 @@
+OPP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_supported_extension                               jpg (*)
+TSPX_unsupported_extension                             pts
+TSPX_client_class_of_device                            100104
+TSPX_server_class_of_device                            100104
+TSPX_auth_password                                     0000
+TSPX_auth_user_id                                      PTS
+TSPX_l2cap_psm                                         1003
+TSPX_rfcomm_channel                                    8
+TSPX_no_confirmations                                  FALSE
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_delete_link_key                                   FALSE
+TSPX_pin_code                                          0000
+TSPX_security_enabled                                  FALSE
+TSPX_time_guard                                                300000
+TSPX_use_implicit_send                                 TRUE
+-------------------------------------------------------------------------------
diff --git a/android/pixit-pan.txt b/android/pixit-pan.txt
new file mode 100644 (file)
index 0000000..9053715
--- /dev/null
@@ -0,0 +1,30 @@
+PAN PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT or PTS Bluetooth address respectively
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_GN_class_of_device                                        020104
+TSPX_NAP_class_of_device                               020300
+TSPX_PANU_class_of_device                              020104
+TSPX_time_guard                                                300000
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_security_enabled                                  False
+TSPX_pin_code                                          0000
+TSPX_delete_link_key                                   False
+TSPX_use_implicit_send                                 True
+TSPX_iut_ip_address                                    192.168.167.152
+TSPX_iut_port_number                                   4242
+TSPX_PTS_ip_address                                    192.168.168.100
+TSPX_PTS_port_number                                   4242
+TSPX_bd_addr_PTS                                       112233445566 (*&)
+TSPX_checksum                                          E851
+TSPX_secure_simple_pairing_pass_key_confirmation       False
+TSPX_iut_friendly_bt_name                              gprs-pc
+TSPX_PTS_role_when_iut_is_PANU                         default
+-------------------------------------------------------------------------------
diff --git a/android/pixit-pbap.txt b/android/pixit-pbap.txt
new file mode 100644 (file)
index 0000000..fc89e15
--- /dev/null
@@ -0,0 +1,35 @@
+PBAP PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_auth_password                                     0000
+TSPX_auth_user_id                                      PTS
+TSPX_security_enabled                                  True
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_pin_code                                          0000
+TSPX_time_guard                                                100000
+TSPX_use_implicit_send                                 True
+TSPX_client_class_of_device                            100204
+TSPX_server_class_of_device                            100204
+TSPX_PSE_vCardSelector                                 0000000000000001
+TSPX_delete_link_key                                   False
+TSPX_PBAP_profile_version                              1
+TSPX_PBAP_supported_repositories                       1
+TSPX_PBAP_rfcomm_channel                               1
+TSPX_telecom_folder_path                               telecom
+TSPX_secure_simple_pairing_pass_key_confirmation       False
+TSPX_check_downloaded_contents_after_disconnect                False
+TSPX_SPP_rfcomm_channel                                        03
+TSPX_l2cap_psm                                         1005
+TSPX_rfcomm_channel                                    2
+TSPX_obex_auth_password                                        0000
+TSPX_no_confirmations                                  False
+TSPX_PSE_vCardSelector                                 0000000000000001
+-------------------------------------------------------------------------------
diff --git a/android/pixit-sm.txt b/android/pixit-sm.txt
new file mode 100644 (file)
index 0000000..607a3ce
--- /dev/null
@@ -0,0 +1,70 @@
+SM PIXIT for the PTS tool.
+
+PTS version: 5.1
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+
+               Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name                                         Value
+-------------------------------------------------------------------------------
+TSPX_bd_addr_iut                                       112233445566 (*&)
+TSPX_SMP_pin_code                                      111111
+TSPX_OOB_Data                                          000000000000FE12036E5A
+                                                       889F4D
+TSPX_peer_addr_type                                    00
+TSPX_own_addr_type                                     00
+TSPX_conn_interval_min                                 0190
+TSPX_conn_interval_max                                 0190
+TSPX_con_latency                                       0000
+TSPX_client_class_of_device                            100104
+TSPX_server_class_of_device                            100104
+TSPX_security_enabled                                  TRUE
+TSPX_delete_link_key                                   TRUE
+TSPX_pin_code                                          1234
+TSPX_ATTR_HANDLE                                       0000
+TSPX_ATTR_VALUE                                                000000000000000
+TSPX_delay_variation_in                                        FFFFFFFF
+TSPX_delay_variation_out                               FFFFFFFF
+TSPX_flushto                                           FFFF
+TSPX_inmtu                                             02A0
+TSPX_inquiry_length                                    17
+TSPX_latency_in                                                FFFFFFFF
+TSPX_latency_out                                       FFFFFFFF
+TSPX_linkto                                            3000
+TSPX_max_nbr_retransmission                            10
+TSPX_no_fail_verdicts                                  FALSE
+TSPX_outmtu                                            02A0
+TSPX_tester_role_optional                              L2CAP_ROLE_INITIATOR
+TSPX_page_scan_mode                                    00
+TSPX_page_scan_repetition_mode                         00
+TSPX_peak_bandwidth_in                                 00000000
+TSPX_peak_bandwidth_out                                        00000000
+TSPX_psm                                               0011
+TSPX_service_type_in                                   01
+TSPX_service_type_out                                  01
+TSPX_support_retransmissions                           TRUE
+TSPX_time_guard                                                180000
+TSPX_timer_ertx                                                120000
+TSPX_timer_ertx_max                                    300000
+TSPX_timer_ertx_min                                    60000
+TSPX_timer_rtx                                         10000
+TSPX_timer_rtx_max                                     60000
+TSPX_timer_rtx_min                                     1000
+TSPX_token_bucket_size_in                              00000000
+TSPX_token_bucket_size_out                             00000000
+TSPX_token_rate_in                                     00000000
+TSPX_token_rate_out                                    00000000
+TSPX_rfc_mode_mode                                     03
+TSPX_rfc_mode_tx_window_size                           08
+TSPX_rfc_mode_max_transmit                             03
+TSPX_rfc_mode_retransmission_timeout                   07D0
+TSPX_rfc_mode_monitor_timeout                          2EE0
+TSPX_rfc_mode_maximum_pdu_size                         02A0
+TSPX_extended_window_size                              0012
+TSPX_use_implicit_send                                 TRUE
+TSPX_use_dynamic_pin                                   FALSE
+TSPX_iut_SDU_size_in_bytes                             144
+TSPX_secure_simple_pairing_pass_key_confirmation       FALSE
+-------------------------------------------------------------------------------
diff --git a/android/pts-a2dp.txt b/android/pts-a2dp.txt
new file mode 100644 (file)
index 0000000..3670dd0
--- /dev/null
@@ -0,0 +1,60 @@
+PTS test results for A2DP
+
+PTS version: 5.1
+Tested: 07-April-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+               Source (SRC)
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_SRC_CC_BV_09_I      PASS    Start streaming
+TC_SRC_CC_BV_10_I      N/A
+TC_SRC_REL_BV_01_I     PASS    Connect to PTS from IUT. When requested
+                               disconnect from IUT
+TC_SRC_REL_BV_02_I     PASS
+TC_SRC_SET_BV_01_I     PASS    Connect to PTS (open a2dp)
+TC_SRC_SET_BV_02_I     PASS
+TC_SRC_SET_BV_03_I     PASS    Start streaming
+TC_SRC_SET_BV_04_I     PASS    Start streaming
+TC_SRC_SET_BV_05_I     PASS    IUT must be moved out of range
+TC_SRC_SET_BV_06_I     PASS    IUT must be moved out of range
+TC_SRC_SUS_BV_01_I     PASS    Stop streaming
+TC_SRC_SUS_BV_02_I     PASS
+TC_SRC_SDP_BV_01_I     PASS
+TC_SRC_AS_BV_01_I      PASS    Requires checking if the output on the IUT is
+                               correct
+-------------------------------------------------------------------------------
+
+
+               Sink (SNK)
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_SNK_CC_BV_01_I      N/A
+TC_SNK_CC_BV_02_I      N/A
+TC_SNK_CC_BV_03_I      N/A
+TC_SNK_CC_BV_04_I      N/A
+TC_SNK_CC_BV_05_I      N/A
+TC_SNK_CC_BV_06_I      N/A
+TC_SNK_CC_BV_07_I      N/A
+TC_SNK_CC_BV_08_I      N/A
+TC_SNK_REL_BV_01_I     N/A
+TC_SNK_REL_BV_02_I     N/A
+TC_SNK_SET_BV_01_I     N/A
+TC_SNK_SET_BV_02_I     N/A
+TC_SNK_SET_BV_03_I     N/A
+TC_SNK_SET_BV_04_I     N/A
+TC_SNK_SET_BV_05_I     N/A
+TC_SNK_SET_BV_06_I     N/A
+TC_SNK_SUS_BV_01_I     N/A
+TC_SNK_SUS_BV_02_I     N/A
+TC_SNK_SDP_BV_02_I     N/A
+TC_SNK_AS_BV_01_I      N/A
+-------------------------------------------------------------------------------
diff --git a/android/pts-avctp.txt b/android/pts-avctp.txt
new file mode 100644 (file)
index 0000000..d8e7230
--- /dev/null
@@ -0,0 +1,42 @@
+PTS test results for AVCTP
+
+PTS version: 5.1
+Tested: 07-April-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+               Controller (CT)
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_CT_CCM_BV_01_C      N/A
+TC_CT_CCM_BV_02_C      N/A
+TC_CT_CCM_BV_03_C      N/A
+TC_CT_CCM_BV_04_C      N/A
+TC_CT_CCM_BI_01_C      N/A
+TC_CT_NFR_BV_01_C      N/A
+TC_CT_NFR_BV_04_C      PASS    haltest: rc set_volume 5
+TC_CT_FRA_BV_01_C      N/A
+TC_CT_FRA_BV_04_C      N/A
+-------------------------------------------------------------------------------
+
+
+               Target (TG)
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_TG_CCM_BV_01_C      PASS
+TC_TG_CCM_BV_02_C      PASS
+TC_TG_CCM_BV_03_C      PASS
+TC_TG_CCM_BV_04_C      PASS
+TC_TG_NFR_BV_02_C      PASS
+TC_TG_NFR_BV_03_C      PASS
+TC_TG_NFR_BI_01_C      PASS
+TC_TG_FRA_BV_02_C      N/A     Fragmentation not supported
+TC_TG_FRA_BV_03_C      N/A     Fragmentation not supported
+-------------------------------------------------------------------------------
diff --git a/android/pts-avrcp.txt b/android/pts-avrcp.txt
new file mode 100644 (file)
index 0000000..b05bdbf
--- /dev/null
@@ -0,0 +1,199 @@
+PTS test results for AVRCP
+
+PTS version: 5.1
+Tested: 07-April-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+               Controller (CT)
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_CT_BGN_BV_01_I      N/A
+TC_CT_BGN_BV_02_I      N/A
+TC_CT_CEC_BV_01_I      N/A
+TC_CT_CEC_BV_02_I      N/A
+TC_CT_CFG_BV_01_C      N/A
+TC_CT_CON_BV_01_C      N/A
+TC_CT_CON_BV_03_C      N/A
+TC_CT_CRC_BV_01_I      N/A
+TC_CT_CRC_BV_02_I      N/A
+TC_CT_ICC_BV_01_I      N/A
+TC_CT_ICC_BV_02_I      N/A
+TC_CT_MCN_CB_BV_01_C   N/A
+TC_CT_MCN_CB_BV_01_I   N/A
+TC_CT_MCN_CB_BV_02_I   N/A
+TC_CT_MCN_CB_BV_03_I   N/A
+TC_CT_MCN_CB_BV_04_C   N/A
+TC_CT_MCN_CB_BV_04_I   N/A
+TC_CT_MCN_CB_BV_05_I   N/A
+TC_CT_MCN_CB_BV_06_I   N/A
+TC_CT_MCN_CB_BV_07_C   N/A
+TC_CT_MCN_CB_BV_07_I   N/A
+TC_CT_MCN_NP_BV_01_C   N/A
+TC_CT_MCN_NP_BV_01_I   N/A
+TC_CT_MCN_NP_BV_02_I   N/A
+TC_CT_MCN_NP_BV_03_C   N/A
+TC_CT_MCN_NP_BV_03_I   N/A
+TC_CT_MCN_NP_BV_04_I   N/A
+TC_CT_MCN_NP_BV_05_C   N/A
+TC_CT_MCN_NP_BV_05_I   N/A
+TC_CT_MCN_NP_BV_06_I   N/A
+TC_CT_MCN_NP_BV_07_I   N/A
+TC_CT_MCN_NP_BV_08_C   N/A
+TC_CT_MCN_SRC_BV_01_C  N/A
+TC_CT_MCN_SRC_BV_01_I  N/A
+TC_CT_MCN_SRC_BV_02_I  N/A
+TC_CT_MCN_SRC_BV_03_C  N/A
+TC_CT_MCN_SRC_BV_03_I  N/A
+TC_CT_MCN_SRC_BV_04_I  N/A
+TC_CT_MCN_SRC_BV_05_C  N/A
+TC_CT_MDI_BV_01_C      N/A
+TC_CT_MDI_BV_03_C      N/A
+TC_CT_MPS_BV_01_C      N/A
+TC_CT_MPS_BV_01_I      N/A
+TC_CT_MPS_BV_02_I      N/A
+TC_CT_MPS_BV_03_C      N/A
+TC_CT_MPS_BV_03_I      N/A
+TC_CT_MPS_BV_08_C      N/A
+TC_CT_NFY_BV_01_C      N/A
+TC_CT_PAS_BV_01_C      N/A
+TC_CT_PAS_BV_03_C      N/A
+TC_CT_PAS_BV_05_C      N/A
+TC_CT_PAS_BV_07_C      N/A
+TC_CT_PAS_BV_09_C      N/A
+TC_CT_PAS_BV_11_C      N/A
+TC_CT_PTH_BV_01_C      N/A
+TC_CT_PTH_BV_02_C      N/A
+TC_CT_PTT_BV_01_I      N/A
+TC_CT_PTT_BV_02_I      N/A
+TC_CT_PTT_BV_03_I      N/A
+TC_CT_PTT_BV_04_I      N/A
+TC_CT_PTT_BV_05_I      N/A
+TC_CT_RCR_BV_01_C      N/A
+TC_CT_RCR_BV_03_C      N/A
+TC_CT_VLH_BI_03_C      PASS    Send SetAbsolute Volume command by pressing
+                               volume up or down buttons then adb logcat and
+                               check VOLUME_CHANGED value
+TC_CT_VLH_BI_04_C      PASS    adb logcat: check VOLUME_CHANGED value
+TC_CT_VLH_BV_01_C      PASS    Send SetAbsolute Volume command by pressing
+                               volume up or down buttons
+TC_CT_VLH_BV_01_I      PASS    adb logcat: check VOLUME_CHANGED value
+TC_CT_VLH_BV_02_I      PASS    Send SetAbsolute Volume command by pressing
+                               volume up or down buttons
+TC_CT_VLH_BV_03_C      PASS
+-------------------------------------------------------------------------------
+
+
+               Target (TG)
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_TG_BGN_BV_01_I      N/A
+TC_TG_BGN_BV_02_I      N/A
+TC_TG_CEC_BV_01_I      PASS
+TC_TG_CEC_BV_02_I      PASS
+TC_TG_CFG_BI_01_C      PASS
+TC_TG_CFG_BV_02_C      PASS
+TC_TG_CON_BV_02_C      N/A
+TC_TG_CON_BV_04_C      N/A
+TC_TG_CON_BV_05_C      N/A
+TC_TG_CRC_BV_01_I      PASS
+TC_TG_CRC_BV_02_I      PASS    Disconnect from PTS
+TC_TG_ICC_BV_01_I      PASS
+TC_TG_ICC_BV_02_I      PASS
+TC_TG_INV_BI_01_C      PASS
+TC_TG_INV_BI_02_C      N/A
+TC_TG_MCN_CB_BI_01_C   N/A
+TC_TG_MCN_CB_BI_02_C   N/A
+TC_TG_MCN_CB_BI_03_C   N/A
+TC_TG_MCN_CB_BI_04_C   N/A
+TC_TG_MCN_CB_BI_05_C   N/A
+TC_TG_MCN_CB_BV_01_I   N/A
+TC_TG_MCN_CB_BV_02_C   N/A
+TC_TG_MCN_CB_BV_02_I   N/A
+TC_TG_MCN_CB_BV_03_C   N/A
+TC_TG_MCN_CB_BV_03_I   N/A
+TC_TG_MCN_CB_BV_04_I   N/A
+TC_TG_MCN_CB_BV_05_C   N/A
+TC_TG_MCN_CB_BV_05_I   N/A
+TC_TG_MCN_CB_BV_06_C   N/A
+TC_TG_MCN_CB_BV_06_I   N/A
+TC_TG_MCN_CB_BV_07_I   N/A
+TC_TG_MCN_CB_BV_08_C   N/A
+TC_TG_MCN_CB_BV_09_C   N/A
+TC_TG_MCN_CB_BV_10_C   N/A
+TC_TG_MCN_CB_BV_11_C   N/A
+TC_TG_MCN_NP_BI_01_C   N/A
+TC_TG_MCN_NP_BI_02_C   N/A
+TC_TG_MCN_NP_BV_01_I   N/A
+TC_TG_MCN_NP_BV_02_C   N/A
+TC_TG_MCN_NP_BV_02_I   N/A
+TC_TG_MCN_NP_BV_03_I   N/A
+TC_TG_MCN_NP_BV_04_C   N/A
+TC_TG_MCN_NP_BV_04_I   N/A
+TC_TG_MCN_NP_BV_05_I   N/A
+TC_TG_MCN_NP_BV_06_C   N/A
+TC_TG_MCN_NP_BV_06_I   N/A
+TC_TG_MCN_NP_BV_07_C   N/A
+TC_TG_MCN_NP_BV_07_I   N/A
+TC_TG_MCN_NP_BV_09_C   N/A
+TC_TG_MCN_SRC_BV_01_I  N/A
+TC_TG_MCN_SRC_BV_02_C  N/A
+TC_TG_MCN_SRC_BV_02_I  N/A
+TC_TG_MCN_SRC_BV_03_I  N/A
+TC_TG_MCN_SRC_BV_04_C  N/A
+TC_TG_MCN_SRC_BV_04_I  N/A
+TC_TG_MCN_SRC_BV_06_C  N/A
+TC_TG_MDI_BV_02_C      PASS
+TC_TG_MDI_BV_04_C      PASS
+TC_TG_MDI_BV_05_C      PASS
+TC_TG_MPS_BI_01_C      N/A
+TC_TG_MPS_BI_02_C      N/A
+TC_TG_MPS_BV_01_I      N/A
+TC_TG_MPS_BV_02_C      N/A
+TC_TG_MPS_BV_02_I      N/A
+TC_TG_MPS_BV_03_I      N/A
+TC_TG_MPS_BV_04_C      N/A
+TC_TG_MPS_BV_05_C      N/A
+TC_TG_MPS_BV_06_C      N/A
+TC_TG_MPS_BV_07_C      N/A
+TC_TG_MPS_BV_09_C      N/A
+TC_TG_MPS_BV_10_C      N/A
+TC_TG_NFY_BI_01_C      PASS
+TC_TG_NFY_BV_02_C      PASS    Change track when requested
+TC_TG_NFY_BV_03_C      N/A
+TC_TG_NFY_BV_04_C      PASS
+TC_TG_NFY_BV_05_C      PASS
+TC_TG_NFY_BV_06_C      N/A
+TC_TG_NFY_BV_07_C      N/A
+TC_TG_NFY_BV_08_C      PASS
+TC_TG_PAS_BI_01_C      N/A
+TC_TG_PAS_BI_02_C      N/A
+TC_TG_PAS_BI_03_C      N/A
+TC_TG_PAS_BI_04_C      N/A
+TC_TG_PAS_BI_05_C      N/A
+TC_TG_PAS_BV_02_C      N/A
+TC_TG_PAS_BV_04_C      N/A
+TC_TG_PAS_BV_06_C      N/A
+TC_TG_PAS_BV_08_C      N/A
+TC_TG_PAS_BV_10_C      N/A
+TC_TG_PTT_BV_01_I      PASS
+TC_TG_PTT_BV_02_I      PASS
+TC_TG_PTT_BV_03_I      N/A
+TC_TG_PTT_BV_04_I      N/A
+TC_TG_PTT_BV_05_I      N/A
+TC_TG_RCR_BV_02_C      PASS
+TC_TG_RCR_BV_04_C      PASS
+TC_TG_VLH_BI_01_C      N/A
+TC_TG_VLH_BI_02_C      N/A
+TC_TG_VLH_BV_01_I      N/A
+TC_TG_VLH_BV_02_C      N/A
+TC_TG_VLH_BV_02_I      N/A
+TC_TG_VLH_BV_04_C      N/A
+-------------------------------------------------------------------------------
diff --git a/android/pts-did.txt b/android/pts-did.txt
new file mode 100644 (file)
index 0000000..2a18d3e
--- /dev/null
@@ -0,0 +1,20 @@
+PTS test results for DID
+
+PTS version: 5.1
+Tested: 07-April-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name      Result  Notes
+-------------------------------------------------------------------------------
+TC_SDI_BV_1_I  PASS    IUT must be discoverable
+TC_SDI_BV_2_I  PASS    IUT must be discoverable
+TC_SDI_BV_3_I  PASS    IUT must be discoverable
+TC_SDI_BV_4_I  PASS    IUT must be discoverable
+-------------------------------------------------------------------------------
diff --git a/android/pts-gap.txt b/android/pts-gap.txt
new file mode 100644 (file)
index 0000000..d1f0402
--- /dev/null
@@ -0,0 +1,193 @@
+PTS test results for GAP
+
+PTS version: 5.1
+Tested: 16-May-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_MOD_NDIS_BV_01_C    PASS    IUT must be non-discoverable
+TC_MOD_LDIS_BV_01_C    PASS    btmgmt discov limited 30
+TC_MOD_LDIS_BV_02_C    PASS    btmgmt discov limited 30
+TC_MOD_LDIS_BV_03_C    PASS    btmgmt discov limited 30
+TC_MOD_GDIS_BV_01_C    PASS    IUT must be discoverable
+TC_MOD_GDIS_BV_02_C    PASS    IUT must be discoverable
+TC_MOD_NCON_BV_01_C    PASS    btmgmt connectable off
+TC_MOD_CON_BV_01_C     PASS    btmgmt connectable on
+TC_BROB_BCST_BV_01_C   N/A
+TC_BROB_BCST_BV_02_C   N/A
+TC_BROB_BCST_BV_03_C   N/A
+TC_BROB_OBSV_BV_01_C   N/A
+TC_BROB_OBSV_BV_02_C   N/A
+TC_BROB_OBSV_BV_03_C   N/A
+TC_BROB_OBSV_BV_04_C   N/A
+TC_BROB_OBSV_BV_05_C   N/A
+TC_DISC_NONM_BV_01_C   N/A
+TC_DISC_NONM_BV_02_C   N/A
+TC_DISC_LIMM_BV_01_C   N/A
+TC_DISC_LIMM_BV_02_C   N/A
+TC_DISC_LIMM_BV_03_C   N/A
+TC_DISC_LIMM_BV_04_C   N/A
+TC_DISC_GENM_BV_01_C   N/A
+TC_DISC_GENM_BV_02_C   N/A
+TC_DISC_GENM_BV_03_C   N/A
+TC_DISC_GENM_BV_04_C   N/A
+TC_DISC_LIMP_BV_01_C   PASS    btmgmt find -l
+                               PTS AD flags must have bit 1 unset and bit 0 set
+TC_DISC_LIMP_BV_02_C   PASS    btmgmt find -l
+                               PTS AD flags must have bit 1 set and bit 0 unset
+TC_DISC_LIMP_BV_03_C   PASS    btmgmt find -l
+                               PTS AD flags must have bit 1 and bit 0 unset
+TC_DISC_LIMP_BV_04_C   PASS    btmgmt find -l
+                               PTS AD flags must have bit 1 and bit 0 unset
+TC_DISC_LIMP_BV_05_C   PASS    btmgmt find -l
+                               PTS AD flags must have bit 1 and bit 0 unset
+TC_DISC_GENP_BV_01_C   PASS    btmgmt find -l
+                               PTS AD flags must have bit 1 set and bit 0 unset
+TC_DISC_GENP_BV_02_C   PASS    btmgmt find -l
+                               PTS AD flags must have bit 1 unset and bit 0 set
+TC_DISC_GENP_BV_03_C   PASS    btmgmt find -l
+                               PTS AD flags must have bit 1 and bit 0 unset
+TC_DISC_GENP_BV_04_C   PASS    btmgmt find -l
+                               PTS AD flags must have bit 1 and bit 0 unset
+TC_DISC_GENP_BV_05_C   PASS    btmgmt find -l
+                               PTS AD flags must have bit 1 and bit 0 unset
+TC_IDLE_GIN_BV_01_C    PASS    Start discovery from IUT
+TC_IDLE_LIN_BV_01_C    PASS    hcitool scan --iac=liac
+TC_IDLE_NAMP_BV_01_C   PASS    possible to PASS using haltest following steps:
+                               gattc - register client, connect to PTS, search
+                               all services, get characteristic and then read
+                               characteristic (name)
+TC_IDLE_NAMP_BV_02_C   PASS    haltest: gatts connect
+TC_CONN_NCON_BV_01_C   N/A
+TC_CONN_NCON_BV_02_C   N/A
+TC_CONN_NCON_BV_03_C   N/A
+TC_CONN_DCON_BV_01_C   N/A
+TC_CONN_DCON_BV_02_C   N/A
+TC_CONN_DCON_BV_03_C   N/A
+TC_CONN_UCON_BV_01_C   N/A
+TC_CONN_UCON_BV_02_C   N/A
+TC_CONN_UCON_BV_03_C   N/A
+TC_CONN_UCON_BV_04_C   N/A
+TC_CONN_UCON_BV_05_C   N/A
+TC_CONN_ACEP_BV_01_C   PASS    'gattc connect' prior to pressing OK on PTS
+TC_CONN_ACEP_BV_02_C   INC     Privacy feature - not implemented
+TC_CONN_GCEP_BV_01_C   PASS    'gattc connect' prior to pressing OK on PTS
+TC_CONN_GCEP_BV_02_C   PASS    'gattc connect' prior to pressing OK on PTS
+TC_CONN_GCEP_BV_03_C   INC     Privacy feature - not implemented
+TC_CONN_GCEP_BV_04_C   INC     Privacy feature - not implemented
+TC_CONN_SCEP_BV_01_C   PASS    'gattc connect' prior to pressing OK on PTS
+TC_CONN_SCEP_BV_02_C   INC     Privacy feature - not implemented
+TC_CONN_DCEP_BV_01_C   PASS    'gattc connect' prior to pressing OK on PTS
+TC_CONN_DCEP_BV_02_C   INC     Privacy feature - not implemented
+TC_CONN_DCEP_BV_03_C   PASS
+TC_CONN_DCEP_BV_04_C   INC     Privacy feature - not implemented
+TC_CONN_CPUP_BV_01_C   N/A
+TC_CONN_CPUP_BV_02_C   N/A
+TC_CONN_CPUP_BV_03_C   N/A
+TC_CONN_CPUP_BV_04_C   PASS
+TC_CONN_CPUP_BV_05_C   PASS
+TC_CONN_CPUP_BV_06_C   PASS    PTS issue - ID: 12187
+                               ETS + new pixit file should be added
+TC_CONN_TERM_BV_01_C   PASS
+TC_CONN_PRDA_BV_01_C   N/A
+TC_CONN_PRDA_BV_02_C   INC     Privacy feature - not implemented
+TC_BOND_NBON_BV_01_C   PASS
+TC_BOND_NBON_BV_02_C   PASS    haltest: gattc_register_client
+                               gattc connect <client_id> <address>
+                               bluetooth create_bond <address>
+                               bluetooth remove_bond <address>
+TC_BOND_NBON_BV_03_C   N/A
+TC_BOND_BON_BV_01_C    N/A
+TC_BOND_BON_BV_02_C    PASS
+TC_BOND_BON_BV_03_C    N/A
+TC_BOND_BON_BV_04_C    PASS    haltest: gattc_register_client
+                               gattc connect <client_id> <address>
+                               bluetooth remove_bond <address>
+                               gattc connect <client_id> <address>
+TC_SEC_AUT_BV_11_C     N/A
+TC_SEC_AUT_BV_12_C     INC     JIRA: BZ-29
+TC_SEC_AUT_BV_13_C     INC     JIRA: BZ-30
+TC_SEC_AUT_BV_14_C     N/A
+TC_SEC_AUT_BV_15_C     N/A
+TC_SEC_AUT_BV_16_C     INC     Not implemented
+TC_SEC_AUT_BV_17_C     PASS
+TC_SEC_AUT_BV_18_C     N/A
+TC_SEC_AUT_BV_19_C     PASS
+TC_SEC_AUT_BV_20_C     N/A
+TC_SEC_AUT_BV_21_C     INC     Not implemented
+TC_SEC_AUT_BV_22_C     N/A
+TC_SEC_AUT_BV_23_C     N/A
+TC_SEC_AUT_BV_24_C     INC     Not implemented
+TC_SEC_CSIGN_BV_01_C   INC     Not yet implemented - signed write
+TC_SEC_CSIGN_BV_02_C   INC     Not implemented
+TC_SEC_CSIGN_BI_01_C   INC     Not implemented
+TC_SEC_CSIGN_BI_02_C   INC     Not implemented
+TC_SEC_CSIGN_BI_03_C   INC     Not implemented
+TC_SEC_CSIGN_BI_04_C   INC     Not implemented
+TC_PRIV_CONN_BV_01_C   INC     Privacy feature - not implemented
+TC_PRIV_CONN_BV_02_C   INC     Privacy feature - not implemented
+TC_PRIV_CONN_BV_03_C   INC     Privacy feature - not implemented
+TC_PRIV_CONN_BV_04_C   INC     Privacy feature - not implemented
+TC_PRIV_CONN_BV_05_C   N/A
+TC_PRIV_CONN_BV_06_C   N/A
+TC_PRIV_CONN_BV_07_C   N/A
+TC_PRIV_CONN_BV_08_C   N/A
+TC_PRIV_CONN_BV_09_C   N/A
+TC_PRIV_CONN_BV_10_C   N/A
+TC_PRIV_CONN_BV_11_C   INC     Privacy feature - not implemented
+TC_ADV_BV_01_C         N/A
+TC_ADV_BV_02_C         N/A
+TC_ADV_BV_03_C         N/A
+TC_ADV_BV_04_C         N/A
+TC_ADV_BV_05_C         N/A
+TC_ADV_BV_06_C         N/A
+TC_ADV_BV_07_C         N/A
+TC_ADV_BV_08_C         N/A
+TC_ADV_BV_09_C         N/A
+TC_ADV_BV_10_C         N/A
+TC_ADV_BV_11_C         N/A
+TC_ADV_BV_12_C         N/A
+TC_ADV_BV_13_C         N/A
+TC_ADV_BV_14_C         N/A
+TC_ADV_BV_15_C         N/A
+TC_ADV_BV_16_C         N/A
+TC_GAT_BV_01_C         PASS    haltest:
+                               gattc register_client
+                               gattc connect
+TC_GAT_BV_02_C         N/A
+TC_GAT_BV_03_C         N/A
+TC_GAT_BV_04_C         N/A
+TC_GAT_BV_05_C         N/A
+TC_GAT_BV_06_C         N/A
+TC_GAT_BV_07_C         N/A
+TC_GAT_BV_08_C         N/A
+TC_DM_NCON_BV_01_C     N/A
+TC_DM_CON_BV_01_C      N/A
+TC_DM_NBON_BV_01_C     N/A
+TC_DM_BON_BV_01_C      PASS    haltest:
+                               create_bond and remove_bond when requested
+TC_DM_GIN_BV_01_C      PASS
+TC_DM_LIN_BV_01_C      PASS
+TC_DM_NAD_BV_01_C      PASS    Start discovery from IUT
+TC_DM_NAD_BV_02_C      PASS
+TC_DM_LEP_BV_01_C      N/A
+TC_DM_LEP_BV_02_C      N/A
+TC_DM_LEP_BV_03_C      N/A
+TC_DM_LEP_BV_04_C      PASS    l2test -n <PTS bdaddr>
+TC_DM_LEP_BV_05_C      PASS    btmgmt find -b
+                               l2test -n 00:1B:DC:06:06:22
+TC_DM_LEP_BV_06_C      PASS
+TC_DM_LEP_BV_07_C      N/A
+TC_DM_LEP_BV_08_C      N/A
+TC_DM_LEP_BV_09_C      N/A
+TC_DM_LEP_BV_10_C      N/A
+TC_DM_LEP_BV_11_C      N/A
+-------------------------------------------------------------------------------
diff --git a/android/pts-gatt.txt b/android/pts-gatt.txt
new file mode 100644 (file)
index 0000000..342db47
--- /dev/null
@@ -0,0 +1,119 @@
+PTS test results for GAP
+
+PTS version: 5.1
+Tested: 7-May-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_GAC_CL_BV_01_C      PASS    Send exchanged MTU
+TC_GAC_SR_BV_01_C      PASS    IUT should accept incoming GATT con request
+TC_GAD_CL_BV_01_C      PASS    Send discover all primary services command to
+                               PTS
+TC_GAD_CL_BV_02_C      PASS    Send discover primary services (UUID=1800)
+                               command to PTS
+TC_GAD_CL_BV_03_C      PASS    Send discover all include service to PTS
+TC_GAD_CL_BV_04_C      PASS    Discover all characteristics of service (UUID=
+                               180A)
+TC_GAD_CL_BV_05_C      PASS    Send discover characteristics by UUID
+TC_GAD_CL_BV_06_C      PASS    Send discover characteristics descriptor
+TC_GAD_CL_BV_07_C      PASS
+TC_GAD_CL_BV_08_C      PASS
+TC_GAD_SR_BV_01_C      INC
+TC_GAD_SR_BV_02_C      INC
+TC_GAD_SR_BV_03_C      INC
+TC_GAD_SR_BV_04_C      INC
+TC_GAD_SR_BV_05_C      INC
+TC_GAD_SR_BV_06_C      INC
+TC_GAD_SR_BV_07_C      INC
+TC_GAD_SR_BV_08_C      INC
+TC_GAR_CL_BV_01_C      PASS
+TC_GAR_CL_BI_01_C      PASS
+TC_GAR_CL_BI_02_C      PASS
+TC_GAR_CL_BI_03_C      PASS
+TC_GAR_CL_BI_04_C      PASS
+TC_GAR_CL_BI_05_C      PASS
+TC_GAR_CL_BV_03_C      PASS
+TC_GAR_CL_BI_06_C      PASS
+TC_GAR_CL_BI_07_C      PASS
+TC_GAR_CL_BI_09_C      PASS
+TC_GAR_CL_BI_10_C      PASS
+TC_GAR_CL_BI_11_C      PASS
+TC_GAR_CL_BV_04_C      PASS
+TC_GAR_CL_BI_12_C      PASS
+TC_GAR_CL_BI_13_C      PASS
+TC_GAR_CL_BI_14_C      PASS
+TC_GAR_CL_BI_15_C      INC
+TC_GAR_CL_BI_16_C      INC
+TC_GAR_CL_BI_17_C      INC
+TC_GAR_CL_BV_05_C      INC
+TC_GAR_CL_BI_18_C      INC
+TC_GAR_CL_BI_19_C      INC
+TC_GAR_CL_BI_20_C      INC
+TC_GAR_CL_BI_21_C      INC
+TC_GAR_CL_BI_22_C      INC
+TC_GAR_CL_BV_06_C      INC
+TC_GAR_CL_BI_23_C      INC
+TC_GAR_CL_BI_24_C      INC
+TC_GAR_CL_BI_25_C      INC
+TC_GAR_CL_BI_26_C      INC
+TC_GAR_CL_BI_27_C      INC
+TC_GAR_CL_BV_07_C      INC
+TC_GAR_CL_BI_28_C      INC
+TC_GAR_CL_BI_29_C      INC
+TC_GAR_CL_BI_30_C      INC
+TC_GAR_CL_BI_31_C      INC
+TC_GAR_CL_BI_32_C      INC
+TC_GAR_CL_BI_33_C      PASS
+TC_GAR_CL_BI_34_C      INC
+TC_GAR_CL_BI_35_C      PASS
+TC_GAR_SR_BV_01_C      INC
+TC_GAR_SR_BI_01_C      INC
+TC_GAR_SR_BI_02_C      INC
+TC_GAR_SR_BI_03_C      INC
+TC_GAR_SR_BI_04_C      INC
+TC_GAR_SR_BI_05_C      INC
+TC_GAR_SR_BV_03_C      INC
+TC_GAR_SR_BI_06_C      INC
+TC_GAR_SR_BI_07_C      INC
+TC_GAR_SR_BI_08_C      INC
+TC_GAR_SR_BI_09_C      INC
+TC_GAR_SR_BI_10_C      INC
+TC_GAR_SR_BI_11_C      INC
+TC_GAR_SR_BV_04_C      INC
+TC_GAR_SR_BI_12_C      INC
+TC_GAR_SR_BI_13_C      INC
+TC_GAR_SR_BI_14_C      INC
+TC_GAR_SR_BI_15_C      INC
+TC_GAR_SR_BI_16_C      INC
+TC_GAR_SR_BI_17_C      INC
+TC_GAR_SR_BV_05_C      INC
+TC_GAR_SR_BI_18_C      INC
+TC_GAR_SR_BI_19_C      INC
+TC_GAR_SR_BI_20_C      INC
+TC_GAR_SR_BI_21_C      INC
+TC_GAR_SR_BI_22_C      INC
+TC_GAR_SR_BV_06_C      INC
+TC_GAR_SR_BI_23_C      INC
+TC_GAR_SR_BI_24_C      INC
+TC_GAR_SR_BI_25_C      INC
+TC_GAR_SR_BI_26_C      INC
+TC_GAR_SR_BI_27_C      INC
+TC_GAR_SR_BV_07_C      INC
+TC_GAR_SR_BV_08_C      INC
+TC_GAR_SR_BI_28_C      INC
+TC_GAR_SR_BI_29_C      INC
+TC_GAR_SR_BI_30_C      INC
+TC_GAR_SR_BI_31_C      INC
+TC_GAR_SR_BI_32_C      INC
+TC_GAR_SR_BI_33_C      INC
+TC_GAR_SR_BI_34_C      INC
+TC_GAR_SR_BI_35_C      INC
+-------------------------------------------------------------------------------
diff --git a/android/pts-hdp.txt b/android/pts-hdp.txt
new file mode 100644 (file)
index 0000000..1dec18c
--- /dev/null
@@ -0,0 +1,72 @@
+PTS test results for HDP
+
+PTS version: 5.1
+Tested: not tested
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_SRC_CON_BV_01_I     INC
+TC_SRC_CON_BV_02_I     INC
+TC_SRC_CON_BV_03_I     INC
+TC_SRC_CON_BV_04_I     INC
+TC_SRC_CON_BV_05_I     INC
+TC_SRC_CON_BV_06_I     INC
+TC_SRC_CON_BV_07_I     INC
+TC_SRC_CON_BV_08_I     INC
+TC_SRC_CON_BV_09_I     INC
+TC_SRC_CON_BV_10_I     INC
+TC_SRC_CC_BV_01_C      INC
+TC_SRC_CC_BV_02_C      INC
+TC_SRC_CC_BV_03_C      INC
+TC_SRC_CC_BV_05_C      INC
+TC_SRC_CC_BV_07_C      INC
+TC_SRC_CC_BV_09_C      INC
+TC_SRC_CC_BI_12_C      INC
+TC_SRC_HCT_BV_01_I     INC
+TC_SRC_HCT_BV_02_I     INC
+TC_SRC_HCT_BV_03_I     N/A
+TC_SRC_HCT_BV_04_I     INC
+TC_SRC_HCT_BV_05_C     N/A
+TC_SRC_HCT_BV_06_C     INC
+TC_SRC_HCT_BV_07_C     INC
+TC_SRC_DE_BV_01_I      INC
+TC_SRC_DE_BV_02_I      INC
+TC_SRC_DEP_BV_01_I     INC
+TC_SRC_DEP_BV_02_I     N/A
+TC_SNK_CON_BV_01_I     INC
+TC_SNK_CON_BV_02_I     INC
+TC_SNK_CON_BV_03_I     INC
+TC_SNK_CON_BV_04_I     INC
+TC_SNK_CON_BV_05_I     INC
+TC_SNK_CON_BV_06_I     INC
+TC_SNK_CON_BV_07_I     INC
+TC_SNK_CON_BV_08_I     INC
+TC_SNK_CON_BV_09_I     INC
+TC_SNK_CON_BV_10_I     INC
+TC_SNK_CC_BV_01_C      INC
+TC_SNK_CC_BV_02_C      INC
+TC_SNK_CC_BV_04_C      INC
+TC_SNK_CC_BV_06_C      INC
+TC_SNK_CC_BV_08_C      INC
+TC_SNK_CC_BV_10_C      INC
+TC_SNK_CC_BI_11_C      INC
+TC_SNK_HCT_BV_01_I     INC
+TC_SNK_HCT_BV_02_I     INC
+TC_SNK_HCT_BV_03_I     N/A
+TC_SNK_HCT_BV_04_I     INC
+TC_SNK_HCT_BV_05_C     N/A
+TC_SNK_HCT_BV_06_C     INC
+TC_SNK_HCT_BV_07_C     INC
+TC_SNK_DE_BV_01_I      N/A
+TC_SNK_DE_BV_02_I      INC
+TC_SNK_DEP_BV_03_I     INC
+TC_SNK_DEP_BV_04_I     INC
+-------------------------------------------------------------------------------
diff --git a/android/pts-hfp.txt b/android/pts-hfp.txt
new file mode 100644 (file)
index 0000000..196fa14
--- /dev/null
@@ -0,0 +1,248 @@
+PTS test results for HFP
+
+PTS version: 5.1
+Tested: 25-Mar-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_AG_OOR_BV_01_I      PASS
+TC_AG_OOR_BV_02_I      PASS
+TC_AG_TRS_BV_01_I      PASS
+TC_AG_PSI_BV_01_I      PASS
+TC_AG_PSI_BV_02_I      N/A
+TC_AG_PSI_BV_03_I      PASS
+TC_AG_PSI_BV_04_I      PASS
+TC_AG_PSI_BV_05_I      PASS
+TC_AG_ACS_BV_02_I      N/A
+TC_AG_ACS_BV_04_I      PASS
+TC_AG_ACS_BV_06_I      N/A
+TC_AG_ACS_BV_08_I      PASS
+TC_AG_ACS_BV_10_I      N/A
+TC_AG_ACS_BV_11_I      PASS
+TC_AG_ACS_BI_14_I      PASS
+TC_AG_ACR_BV_01_I      PASS
+TC_AG_ACR_BV_02_I      PASS
+TC_AG_CLI_BV_01_I      PASS
+TC_AG_ICA_BV_01_I      N/A
+TC_AG_ICA_BV_02_I      N/A
+TC_AG_ICA_BV_04_I      PASS
+TC_AG_ICA_BV_05_I      N/A
+TC_AG_ICA_BV_06_I      PASS
+TC_AG_ICR_BV_01_I      PASS
+TC_AG_ICR_BV_02_I      PASS
+TC_AG_TCA_BV_01_I      PASS
+TC_AG_TCA_BV_02_I      PASS
+TC_AG_TCA_BV_03_I      PASS
+TC_AG_TCA_BV_04_I      PASS
+TC_AG_TCA_BV_05_I      PASS
+TC_AG_ATH_BV_03_I      PASS
+TC_AG_ATH_BV_04_I      PASS
+TC_AG_ATH_BV_05_I      PASS
+TC_AG_ATH_BV_06_I      PASS
+TC_AG_ATA_BV_01_I      PASS
+TC_AG_ATA_BV_02_I      PASS
+TC_AG_OCN_BV_01_I      PASS
+TC_AG_OCM_BV_01_I      PASS
+TC_AG_OCM_BV_02_I      PASS
+TC_AG_OCL_BV_01_I      PASS
+TC_AG_OCL_BV_02_I      PASS
+TC_AG_TWC_BV_01_I      PASS
+TC_AG_TWC_BV_02_I      PASS
+TC_AG_TWC_BV_03_I      PASS
+TC_AG_TWC_BV_04_I      PASS
+TC_AG_TWC_BV_05_I      PASS
+TC_AG_TWC_BV_06_I      N/A
+TC_AG_CIT_BV_01_I      PASS
+TC_AG_ENO_BV_01_I      PASS
+TC_AG_ENO_BV_02_I      N/A
+TC_AG_VRA_BV_01_I      PASS
+TC_AG_VRA_BV_02_I      PASS
+TC_AG_VRA_BI_01_I      PASS
+TC_AG_VRD_BV_01_I      PASS
+TC_AG_VTG_BV_01_I      N/A
+TC_AG_TDC_BV_01_I      PASS
+TC_AG_RSV_BV_01_I      PASS
+TC_AG_RSV_BV_02_I      PASS
+TC_AG_RSV_BV_03_I      PASS
+TC_AG_RMV_BV_01_I      N/A
+TC_AG_RMV_BV_02_I      N/A
+TC_AG_RMV_BV_03_I      N/A
+TC_AG_ECS_BV_01_I      PASS
+TC_AG_ECS_BV_02_I      PASS
+TC_AG_ECS_BV_03_I      PASS
+TC_AG_ECC_BV_01_I      N/A
+TC_AG_ECC_BV_02_I      N/A
+TC_AG_ECC_BI_03_I      PASS
+TC_AG_ECC_BI_04_I      PASS
+TC_AG_RHH_BV_01_I      N/A
+TC_AG_RHH_BV_02_I      N/A
+TC_AG_RHH_BV_03_I      N/A
+TC_AG_RHH_BV_04_I      N/A
+TC_AG_RHH_BV_05_I      N/A
+TC_AG_RHH_BV_06_I      N/A
+TC_AG_RHH_BV_07_I      N/A
+TC_AG_RHH_BV_08_I      N/A
+TC_AG_NUM_BV_01_I      PASS
+TC_AG_SLC_BV_01_C      PASS
+TC_AG_SLC_BV_02_C      PASS
+TC_AG_SLC_BV_03_C      PASS
+TC_AG_SLC_BV_04_C      PASS
+TC_AG_SLC_BV_05_I      PASS
+TC_AG_SLC_BV_06_I      PASS
+TC_AG_SLC_BV_07_I      PASS
+TC_AG_ACC_BV_08_I      INC     Possible PTS issue #12039
+TC_AG_ACC_BV_09_I      PASS
+TC_AG_ACC_BV_10_I      INC     Possible PTS issue #12039
+TC_AG_ACC_BV_11_I      INC     Possible PTS issue #12039
+TC_AG_ACC_BI_12_I      PASS
+TC_AG_ACC_BI_13_I      PASS
+TC_AG_ACC_BI_14_I      PASS
+TC_AG_ACC_BV_15_I      PASS
+TC_AG_WBS_BV_01_I      PASS
+TC_AG_DIS_BV_01_I      PASS
+TC_AG_SDP_BV_01_I      PASS
+TC_AG_IIA_BV_01_I      PASS
+TC_AG_IIA_BV_02_I      PASS
+TC_AG_IIA_BV_03_I      N/A
+TC_AG_IIA_BV_05_I      PASS
+TC_AG_IID_BV_01_I      PASS
+TC_AG_IID_BV_02_I      N/A
+TC_AG_IID_BV_03_I      PASS
+TC_AG_IID_BV_04_I      PASS
+TC_AG_IIC_BV_01_I      PASS
+TC_AG_IIC_BV_02_I      PASS
+TC_AG_IIC_BV_03_I      PASS
+
+TC_HF_OOR_BV_01_I      N/A
+TC_HF_OOR_BV_02_I      N/A
+TC_HF_TRS_BV_01_I      N/A
+TC_HF_PSI_BV_01_I      N/A
+TC_HF_PSI_BV_02_I      N/A
+TC_HF_PSI_BV_03_I      N/A
+TC_HF_PSI_BV_04_I      N/A
+TC_HF_ACS_BV_01_I      N/A
+TC_HF_ACS_BV_03_I      N/A
+TC_HF_ACS_BV_05_I      N/A
+TC_HF_ACS_BV_07_I      N/A
+TC_HF_ACS_BV_09_I      N/A
+TC_HF_ACS_BV_12_I      N/A
+TC_HF_ACS_BI_13_I      N/A
+TC_HF_ACR_BV_01_I      N/A
+TC_HF_ACR_BV_02_I      N/A
+TC_HF_CLI_BV_01_I      N/A
+TC_HF_ICA_BV_01_I      N/A
+TC_HF_ICA_BV_02_I      N/A
+TC_HF_ICA_BV_03_I      N/A
+TC_HF_ICA_BV_04_I      N/A
+TC_HF_ICA_BV_05_I      N/A
+TC_HF_ICA_BV_06_I      N/A
+TC_HF_ICA_BV_07_I      N/A
+TC_HF_ICR_BV_01_I      N/A
+TC_HF_ICR_BV_02_I      N/A
+TC_HF_TCA_BV_01_I      N/A
+TC_HF_TCA_BV_02_I      N/A
+TC_HF_TCA_BV_03_I      N/A
+TC_HF_TCA_BV_04_I      N/A
+TC_HF_ATH_BV_03_I      N/A
+TC_HF_ATH_BV_04_I      N/A
+TC_HF_ATH_BV_05_I      N/A
+TC_HF_ATH_BV_06_I      N/A
+TC_HF_ATH_BV_09_I      N/A
+TC_HF_ATA_BV_01_I      N/A
+TC_HF_ATA_BV_02_I      N/A
+TC_HF_ATA_BV_03_I      N/A
+TC_HF_OCN_BV_01_I      N/A
+TC_HF_OCM_BV_01_I      N/A
+TC_HF_OCM_BV_02_I      N/A
+TC_HF_OCL_BV_01_I      N/A
+TC_HF_OCL_BV_02_I      N/A
+TC_HF_TWC_BV_01_I      N/A
+TC_HF_TWC_BV_02_I      N/A
+TC_HF_TWC_BV_03_I      N/A
+TC_HF_TWC_BV_04_I      N/A
+TC_HF_TWC_BV_05_I      N/A
+TC_HF_TWC_BV_06_I      N/A
+TC_HF_CIT_BV_01_I      N/A
+TC_HF_ENO_BV_01_I      N/A
+TC_HF_VRA_BV_01_I      N/A
+TC_HF_VRA_BV_02_I      N/A
+TC_HF_VRA_BV_03_I      N/A
+TC_HF_VRD_BV_01_I      N/A
+TC_HF_VTG_BV_01_I      N/A
+TC_HF_TDC_BV_01_I      N/A
+TC_HF_RSV_BV_01_I      N/A
+TC_HF_RSV_BV_02_I      N/A
+TC_HF_RSV_BV_03_I      N/A
+TC_HF_RMV_BV_01_I      N/A
+TC_HF_RMV_BV_02_I      N/A
+TC_HF_RMV_BV_03_I      N/A
+TC_HF_ECS_BV_01_I      N/A
+TC_HF_ECS_BV_02_I      N/A
+TC_HF_ECS_BV_03_I      N/A
+TC_HF_ECC_BV_01_I      N/A
+TC_HF_ECC_BV_02_I      N/A
+TC_HF_RHH_BV_01_I      N/A
+TC_HF_RHH_BV_02_I      N/A
+TC_HF_RHH_BV_03_I      N/A
+TC_HF_RHH_BV_04_I      N/A
+TC_HF_RHH_BV_05_I      N/A
+TC_HF_RHH_BV_06_I      N/A
+TC_HF_RHH_BV_07_I      N/A
+TC_HF_RHH_BV_08_I      N/A
+TC_HF_NUM_BV_01_I      N/A
+TC_HF_NUM_BI_01_I      N/A
+TC_HF_SLC_BV_01_C      N/A
+TC_HF_SLC_BV_02_C      N/A
+TC_HF_SLC_BV_03_C      N/A
+TC_HF_SLC_BV_04_C      N/A
+TC_HF_SLC_BV_05_I      N/A
+TC_HF_SLC_BV_06_I      N/A
+TC_HF_SLC_BV_08_I      N/A
+TC_HF_ACC_BV_01_I      N/A
+TC_HF_ACC_BV_02_I      N/A
+TC_HF_ACC_BV_03_I      N/A
+TC_HF_ACC_BV_04_I      N/A
+TC_HF_ACC_BV_05_I      N/A
+TC_HF_ACC_BV_06_I      N/A
+TC_HF_ACC_BV_07_I      N/A
+TC_HF_WBS_BV_02_I      N/A
+TC_HF_WBS_BV_03_I      N/A
+TC_HF_DIS_BV_01_I      N/A
+TC_HF_DIS_BV_02_I      N/A
+TC_HF_SDP_BV_01_I      N/A
+TC_HF_SDP_BV_02_C      N/A
+TC_HF_SDP_BV_03_C      N/A
+TC_HF_ATAH_BV_01_I     N/A
+TC_HF_OCA_BV_01_I      N/A
+TC_HF_IIA_BV_04_I      N/A
+
+TC_AG_COD_BV_02_I      PASS
+TC_AG_ATAH_BV_03_I     PASS
+TC_AG_ATA_BV_03_I      PASS
+TC_AG_ATH_BV_09_I      PASS
+TC_AG_SDP_BV_02_C      PASS
+TC_AG_SDP_BV_03_C      PASS
+TC_AG_ICA_BV_07_I      PASS
+TC_AG_ICA_BV_08_I      PASS
+TC_AG_ICA_BV_09_I      PASS
+TC_AG_VRA_BV_03_I      PASS
+TC_AG_OCA_BV_01_I      PASS
+TC_AG_TCA_BV_06_I      PASS
+
+TC_HF_ATAH_BV_03_I     N/A
+TC_HF_ATA_BV_03_I      N/A
+TC_HF_ATH_BV_09_I      N/A
+TC_HF_SDP_BV_02_C      N/A
+TC_HF_SDP_BV_03_C      N/A
+TC_HF_DIS_BV_02_I      N/A
+TC_HF_ICA_BV_07_I      N/A
+TC_HF_VRA_BV_03_I      N/A
+TC_HF_OCA_BV_01_I      N/A
diff --git a/android/pts-hid.txt b/android/pts-hid.txt
new file mode 100644 (file)
index 0000000..a7990e1
--- /dev/null
@@ -0,0 +1,76 @@
+PTS test results for HID
+
+PTS version: 5.1
+Tested: 07-April-2014
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_HOS_HCE_BV_01_I     PASS
+TC_HOS_HCE_BV_02_I     PASS
+TC_HOS_HCE_BV_03_I     PASS
+TC_HOS_HCE_BV_04_I     PASS
+TC_HOS_HCR_BV_01_I     PASS
+TC_HOS_HCR_BV_02_I     PASS
+TC_HOS_HCR_BV_03_I     N/A
+TC_HOS_HCR_BV_04_I     N/A
+TC_HOS_HDT_BV_01_I     PASS
+TC_HOS_HDT_BV_02_I     PASS    from shell execute:
+                               haltest
+                               bluetooth enable
+                               hidhost connect <PTS bdaddr>
+                               hidhost send_data <PTS bdaddr> ff00
+TC_HOS_HDT_BV_03_I     N/A
+TC_HOS_HDT_BV_04_I     N/A
+TC_HOS_HID_BV_01_C     N/A
+TC_HOS_HID_BV_02_C     N/A
+TC_HOS_HID_BV_03_C     N/A
+TC_HOS_HID_BV_04_C     N/A
+TC_HOS_HID_BV_05_C     N/A
+TC_HOS_HID_BV_06_C     N/A
+TC_HOS_HID_BV_08_C     N/A
+TC_HOS_HID_BV_09_C     N/A
+TC_HOS_HID_BV_10_C     N/A
+TC_HOS_DAT_BV_01_C     PASS    from shell execute:
+                               haltest
+                               bluetooth enable
+                               hidhost connect <PTS bdaddr>
+                               hidhost send_data <PTS bdaddr> ff00
+TC_HOS_DAT_BV_02_C     N/A
+TC_HOS_DAT_BI_01_C     N/A
+TC_HOS_DAT_BI_02_C     N/A
+TC_DEV_HCE_BV_01_I     N/A
+TC_DEV_HCE_BV_02_I     N/A
+TC_DEV_HCE_BV_03_I     N/A
+TC_DEV_HCE_BV_04_I     N/A
+TC_DEV_HCE_BV_05_I     N/A
+TC_DEV_HCR_BV_01_I     N/A
+TC_DEV_HCR_BV_02_I     N/A
+TC_DEV_HCR_BV_03_I     N/A
+TC_DEV_HCR_BV_04_I     N/A
+TC_DEV_HDT_BV_01_I     N/A
+TC_DEV_HDT_BV_02_I     N/A
+TC_DEV_HDT_BV_03_I     N/A
+TC_DEV_HDT_BV_04_I     N/A
+TC_DEV_HID_BV_01_C     N/A
+TC_DEV_HID_BV_03_C     N/A
+TC_DEV_HID_BV_04_C     N/A
+TC_DEV_HID_BV_05_C     N/A
+TC_DEV_HID_BV_06_C     N/A
+TC_DEV_HID_BV_08_C     N/A
+TC_DEV_HID_BV_09_C     N/A
+TC_DEV_HID_BV_10_C     N/A
+TC_DEV_HID_BI_01_C     N/A
+TC_DEV_HID_BI_02_C     N/A
+TC_DEV_DAT_BV_01_C     N/A
+TC_DEV_SDD_BV_01_C     N/A
+TC_DEV_SDD_BV_02_C     N/A
+TC_DEV_SDD_BV_03_C     N/A
+TC_DEV_SDD_BV_04_I     N/A
+-------------------------------------------------------------------------------
diff --git a/android/pts-hsp.txt b/android/pts-hsp.txt
new file mode 100644 (file)
index 0000000..7da71e8
--- /dev/null
@@ -0,0 +1,41 @@
+PTS test results for HSP
+
+PTS version: 5.1
+Tested: 10-April-2014
+Android version: 4.4.2
+
+Results:
+PASS    test passed
+FAIL    test failed
+INC     test is inconclusive
+N/A     test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name               Result  Notes
+-------------------------------------------------------------------------------
+TC_AG_IAC_BV_01_I      PASS
+TC_AG_IAC_BV_02_I      N/A
+TC_AG_OAC_BV_01_I      PASS
+HSP_TC_AG_ACR_BV_01_I  PASS
+HSP_TC_AG_ACR_BV_02_I  PASS
+TC_AG_ACT_BV_01_I      PASS
+TC_AG_ACT_BV_02_I      PASS
+TC_AG_RAV_BV_01_I      PASS
+TC_AG_RAV_BV_02_I      PASS
+TC_AG_RAV_BV_03_I      PASS
+TC_AG_RAV_BV_04_I      N/A
+TC_AG_RAV_BV_05_I      N/A
+TC_AG_RAV_BV_06_I      N/A
+TC_HS_IAC_BV_01_I      N/A
+TC_HS_IAC_BV_02_I      N/A
+TC_HS_OAC_BV_01_I      N/A
+TC_HS_ACR_BV_01_I      N/A
+TC_HS_ACR_BV_02_I      N/A
+TC_HS_ACT_BV_01_I      N/A
+TC_HS_ACT_BV_02_I      N/A
+TC_HS_RAV_BV_01_I      N/A
+TC_HS_RAV_BV_02_I      N/A
+TC_HS_RAV_BV_03_I      N/A
+TC_HS_RAV_BV_04_I      N/A
+TC_HS_RAV_BV_05_I      N/A
+TC_HS_RAV_BV_06_I      N/A
diff --git a/android/pts-iopt.txt b/android/pts-iopt.txt
new file mode 100644 (file)
index 0000000..815740d
--- /dev/null
@@ -0,0 +1,23 @@
+PTS test results for IOPT
+
+PTS version: 5.1
+Tested: 08-May-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name      Result  Notes
+-------------------------------------------------------------------------------
+TC_COD_BV_01_I PASS    IUT must be discoverable
+TC_COD_BV_02_I N/A     Under PTS 5.1 test shall be disabled as there is
+                       matching test case in HFP test suit (test No. 15)
+                       PICS settings for HFP shall be disabled for IOPT
+TC_SDSS_BV_02_I        PASS
+TC_SDSA_BV_03_I        PASS
+TC_SDR_BV_04_I PASS
+-------------------------------------------------------------------------------
diff --git a/android/pts-l2cap.txt b/android/pts-l2cap.txt
new file mode 100644 (file)
index 0000000..b14614a
--- /dev/null
@@ -0,0 +1,165 @@
+PTS test results for L2CAP
+
+PTS version: 5.1
+Tested: 27-March-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_COS_CED_BV_01_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+                               i.e. l2test -n -P 4097 <bdaddr>
+TC_COS_CED_BV_03_C     PASS    l2test -y -N <frames to be sent>
+                               -P <PSM - not reserved> <bdaddr>
+TC_COS_CED_BV_04_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CED_BV_05_C     PASS    l2test -P <PSM - not reserved>
+TC_COS_CED_BV_07_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CED_BV_08_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CED_BV_09_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CED_BV_10_C     N/A
+TC_COS_CED_BV_11_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CED_BI_01_C     PASS
+
+TC_COS_CFD_BV_01_C     PASS    l2test -P <PSM - not reserved>
+TC_COS_CFD_BV_02_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CFD_BV_03_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CFD_BV_08_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CFD_BV_09_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CFD_BV_10_C     N/A
+TC_COS_CFD_BI_11_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CFD_BV_12_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_CFD_BV_13_C     N/A
+TC_COS_IEX_BV_01_C     PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_COS_IEX_BV_02_C     PASS
+
+TC_COS_ECH_BV_01_C     PASS
+TC_COS_ECH_BV_02_C     PASS    l2ping <bdaddr>
+TC_CLS_CLR_BV_01_C     N/A
+TC_CLS_UCD_BV_01_C     N/A
+TC_CLS_UCD_BV_02_C     N/A
+TC_CLS_UCD_BV_03_C     N/A
+TC_EXF_BV_01_C         PASS
+TC_EXF_BV_02_C         PASS
+TC_EXF_BV_03_C         PASS
+TC_EXF_BV_04_C         N/A
+TC_EXF_BV_05_C         PASS
+TC_EXF_BV_06_C         N/A
+
+TC_CMC_BV_01_C         PASS    l2test -X ertm -P <PSM - not reserved>
+TC_CMC_BV_02_C         PASS    l2test -X ertm -P <PSM - not reserved>
+TC_CMC_BV_03_C         PASS    l2test -X ertm -P <PSM - not reserved>
+TC_CMC_BV_04_C         PASS    l2test -X streaming <PSM - not reserved>
+TC_CMC_BV_05_C         PASS    l2test -X streaming <PSM - not reserved>
+TC_CMC_BV_06_C         PASS    l2test -X streaming <PSM - not reserved>
+TC_CMC_BV_07_C         PASS    l2test -X ertm <PSM - not reserved>
+TC_CMC_BV_08_C         PASS    l2test -X streaming -P <PSM - not reserved>
+TC_CMC_BV_09_C         PASS    l2test -X basic -P <PSM - not reserved>
+TC_CMC_BV_10_C         PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_CMC_BV_11_C         PASS    l2test -n -P <PSM - not reserved> <bdaddr>
+TC_CMC_BV_12_C         PASS    l2test -s -X ertm  -P <PSM - not reserved>
+                               -U <bdaddr>
+TC_CMC_BV_13_C         PASS    l2test -s -X ertm  -P <PSM - not reserved>
+                               -U <bdaddr>
+TC_CMC_BV_14_C         PASS    l2test -X streaming -P <PSM - not reserved>
+TC_CMC_BV_15_C         PASS    l2test -X streaming -P <PSM - not reserved>
+TC_CMC_BI_01_C         PASS    l2test -X ertm  -P <PSM - not reserved>
+TC_CMC_BI_02_C         PASS    l2test -X ertm  -P <PSM - not reserved>
+TC_CMC_BI_03_C         PASS    l2test -X streaming -P <PSM - not reserved>
+TC_CMC_BI_04_C         PASS    l2test -X streaming -P <PSM - not reserved>
+TC_CMC_BI_05_C         PASS    l2test -P <PSM - not reserved>
+TC_CMC_BI_06_C         PASS    l2test -P <PSM - not reserved>
+TC_FOC_BV_01_C         PASS    l2test -X ertm -P <PSM - not reserved> -F 0
+TC_FOC_BV_02_C         PASS    l2test -X ertm -P <PSM - not reserved> -F 0
+TC_FOC_BV_03_C         PASS    l2test -X ertm -P <PSM - not reserved> -F 0
+TC_FOC_BV_04_C         PASS    l2test -X ertm -P <PSM - not reserved> -F 0
+
+TC_OFS_BV_01_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -F 0
+                               -N 1
+TC_OFS_BV_02_C         PASS    l2test -X ertm -P <PSM - not reserved> -F 0
+TC_OFS_BV_03_C         PASS    l2test -X streaming -P <PSM - not reserved> -F 0
+TC_OFS_BV_04_C         PASS    l2test -X streaming -P <PSM - not reserved> -F 0
+TC_OFS_BV_05_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -N 1
+TC_OFS_BV_06_C         PASS    l2test -X ertm -P <PSM - not reserved>
+TC_OFS_BV_07_C         PASS    l2test -x -X streaming -P <PSM - not reserved>
+                               -F 0 -N 1
+TC_OFS_BV_08_C         PASS    l2test -X streaming -P <PSM - not reserved>
+TC_ERM_BV_01_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -N 3
+                               -Y 3
+TC_ERM_BV_02_C         PASS    l2test -X ertm -P <PSM - not reserved>
+TC_ERM_BV_03_C         PASS    l2test -X ertm -P <PSM - not reserved>
+TC_ERM_BV_05_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -N 2
+                               -Y 2
+TC_ERM_BV_06_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -N 2
+                               -Y 2
+TC_ERM_BV_07_C         PASS    l2test -X ertm -P <PSM - not reserved>
+TC_ERM_BV_08_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -N 1
+TC_ERM_BV_09_C         PASS    l2test -X ertm -P <PSM - not reserved>
+TC_ERM_BV_10_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -N 1
+TC_ERM_BV_11_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -N 1
+                               -Q 1
+TC_ERM_BV_12_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -R
+                               -N 1 -Q 1
+TC_ERM_BV_13_C         PASS
+TC_ERM_BV_14_C         PASS
+TC_ERM_BV_15_C         PASS    l2test -X ertm -P <PSM - not reserved> -N 4
+                               -Y 4
+TC_ERM_BV_16_C         N/A
+TC_ERM_BV_17_C         PASS    l2test -X ertm -P <PSM - not reserved>
+TC_ERM_BV_18_C         PASS
+TC_ERM_BV_19_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -N 1
+TC_ERM_BV_20_C         PASS    l2test -x -X ertm -P <PSM - not reserved> -N 1
+TC_ERM_BV_21_C         PASS    l2test -w -X ertm -P 4097 -N 3
+TC_ERM_BV_22_C         PASS
+TC_ERM_BV_23_C         PASS
+TC_ERM_BI_01_C         N/A
+TC_ERM_BI_02_C         PASS    l2test -X ertm -P <PSM - not reserved>
+TC_ERM_BI_03_C         PASS
+TC_ERM_BI_04_C         PASS
+TC_ERM_BI_05_C         PASS
+TC_STM_BV_01_C         PASS    l2test -x -X streaming -P <PSM - not reserved>
+                               -N 3 -Y 3
+TC_STM_BV_02_C         PASS    l2test -X streaming -P <PSM - not reserved>
+TC_STM_BV_03_C         PASS
+TC_STM_BV_11_C         N/A
+TC_STM_BV_12_C         N/A
+TC_STM_BV_13_C         N/A
+TC_FIX_BV_01_C         N/A
+TC_FIX_BV_02_C         N/A
+TC_EWC_BV_01_C         N/A
+TC_EWC_BV_02_C         N/A
+TC_EWC_BV_03_C         N/A
+TC_LSC_BV_01_C         N/A
+TC_LSC_BV_02_C         N/A
+TC_LSC_BV_03_C         N/A
+TC_LSC_BI_04_C         N/A
+TC_LSC_BI_05_C         N/A
+TC_LSC_BV_06_C         N/A
+TC_LSC_BV_07_C         N/A
+TC_LSC_BV_08_C         N/A
+TC_LSC_BV_09_C         N/A
+TC_LSC_BI_10_C         N/A
+TC_LSC_BI_11_C         N/A
+TC_LSC_BV_12_C         N/A
+TC_CCH_BV_01_C         N/A
+TC_CCH_BV_02_C         N/A
+TC_CCH_BV_03_C         N/A
+TC_CCH_BV_04_C         N/A
+TC_ECF_BV_01_C         N/A
+TC_ECF_BV_02_C         N/A
+TC_ECF_BV_03_C         N/A
+TC_ECF_BV_04_C         N/A
+TC_ECF_BV_05_C         N/A
+TC_ECF_BV_06_C         N/A
+TC_ECF_BV_07_C         N/A
+TC_ECF_BV_08_C         N/A
+TC_LE_CPU_BV_01_C      N/A
+TC_LE_CPU_BV_02_C      N/A
+TC_LE_CPU_BI_01_C      N/A
+TC_LE_CPU_BI_02_C      N/A
+TC_LE_REJ_BV_01_C      N/A
diff --git a/android/pts-map.txt b/android/pts-map.txt
new file mode 100644 (file)
index 0000000..b19a678
--- /dev/null
@@ -0,0 +1,98 @@
+PTS test results for MAP
+
+PTS version: 5.1
+Tested: 07-April-2014
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_MCE_MSM_BV_01_I     N/A
+TC_MCE_MSM_BV_02_I     N/A
+TC_MCE_MSM_BV_03_I     N/A
+TC_MCE_MSM_BV_04_I     N/A
+TC_MCE_MSM_BV_13_I     N/A
+TC_MCE_MSM_BV_14_I     N/A
+TC_MCE_MNR_BV_01_I     N/A
+TC_MCE_MNR_BV_02_I     N/A
+TC_MCE_MMB_BV_01_I     N/A
+TC_MCE_MMB_BV_02_I     N/A
+TC_MCE_MMB_BV_03_I     N/A
+TC_MCE_MMB_BV_19_I     N/A
+TC_MCE_MMB_BV_04_I     N/A
+TC_MCE_MMB_BV_17_I     N/A
+TC_MCE_MMB_BV_06_I     N/A
+TC_MCE_MMB_BV_07_I     N/A
+TC_MCE_MMB_BV_08_I     N/A
+TC_MCE_MMD_BV_01_I     N/A
+TC_MCE_MMU_BV_01_I     N/A
+TC_MCE_MMN_BV_01_I     N/A
+TC_MCE_MMN_BV_03_I     N/A
+TC_MCE_MMI_BV_01_I     N/A
+TC_MCE_MFB_BV_01_I     N/A
+TC_MCE_MFB_BV_03_I     N/A
+TC_MCE_MFB_BV_04_I     N/A
+TC_MCE_BC_BV_02_I      N/A
+TC_MCE_BC_BV_04_I      N/A
+TC_MCE_CON_BV_01_I     N/A
+TC_MCE_CON_BV_02_I     N/A
+TC_MCE_ROB_BV_01_I     N/A
+TC_MCE_SRM_BV_03_I     N/A
+TC_MCE_SRM_BV_07_I     N/A
+TC_MCE_SRMP_BI_01_I    N/A
+TC_MCE_SRMP_BV_01_I    N/A
+TC_MCE_SRMP_BV_04_I    N/A
+TC_MCE_SRMP_BV_05_I    N/A
+TC_MCE_SRMP_BV_06_I    N/A
+TC_MSE_MSM_BV_05_I     PASS    If prompted tester must accept obex request
+TC_MSE_MSM_BV_06_I     PASS    If prompted tester must accept obex request
+TC_MSE_MSM_BV_07_I     PASS    If prompted tester must accept obex request
+TC_MSE_MSM_BV_08_I     PASS    If prompted tester must accept obex request
+TC_MSE_MSM_BV_09_I     N/A
+TC_MSE_MSM_BV_10_I     N/A
+TC_MSE_MSM_BV_11_I     N/A
+TC_MSE_MSM_BV_12_I     N/A
+TC_MSE_MNR_BV_03_I     PASS    If prompted tester must accept obex request
+TC_MSE_MNR_BV_04_I     PASS    If prompted tester must accept obex request
+TC_MSE_MMB_BV_09_I     PASS    If prompted tester must accept obex request
+TC_MSE_MMB_BV_10_I     PASS    If prompted tester must accept obex request
+TC_MSE_MMB_BV_11_I     PASS    If prompted tester must accept obex request
+TC_MSE_MMB_BV_20_I     PASS    If prompted tester must accept obex request
+TC_MSE_MMB_BV_12_I     N/A
+TC_MSE_MMB_BV_18_I     N/A
+TC_MSE_MMB_BV_13_I     PASS    If prompted tester must accept obex request
+TC_MSE_MMB_BV_14_I     PASS    If prompted tester must accept obex request
+TC_MSE_MMB_BV_15_I     PASS    If prompted tester must accept obex request
+TC_MSE_MMB_BV_16_I     PASS    Android MAP server bug, fix provided at
+                               android-review.googlesource.com/#/c/82757
+TC_MSE_MMD_BV_02_I     PASS    If prompted tester must accept obex request
+TC_MSE_MMU_BV_02_I     PASS    If prompted tester must accept obex request,
+                               at least one SMS must be present in inbox
+TC_MSE_MMU_BV_03_I     PASS    If prompted tester must accept obex request,
+                               when asked by PTS tester must verify that SMS
+                               was successfully delivered onto network
+TC_MSE_MMN_BV_02_I     PASS    If prompted tester must accept obex request,
+                               when asked by PTS tester must send SMS to IUT
+TC_MSE_MMN_BV_04_I     N/A
+TC_MSE_MMI_BV_02_I     N/A
+TC_MSE_MFB_BV_02_I     N/A
+TC_MSE_MFB_BV_05_I     N/A
+TC_MSE_BC_BV_01_I      N/A
+TC_MSE_BC_BV_03_I      N/A
+TC_MSE_CON_BV_01_I     N/A
+TC_MSE_CON_BV_02_I     N/A
+TC_MSE_ROB_BV_01_I     N/A
+TC_MSE_SRM_BI_02_I     N/A
+TC_MSE_SRM_BI_03_I     N/A
+TC_MSE_SRM_BI_05_I     N/A
+TC_MSE_SRM_BV_04_I     N/A
+TC_MSE_SRM_BV_08_I     N/A
+TC_MSE_SRMP_BI_02_I    N/A
+TC_MSE_SRMP_BV_02_I    N/A
+TC_MSE_SRMP_BV_03_I    N/A
+-------------------------------------------------------------------------------
diff --git a/android/pts-mcap.txt b/android/pts-mcap.txt
new file mode 100644 (file)
index 0000000..9a77839
--- /dev/null
@@ -0,0 +1,77 @@
+PTS test results for MCAP
+
+PTS version: 5.1
+Tested: not tested
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_MCAP_CE_BV_01_C     INC
+TC_MCAP_CE_BV_02_C     INC
+TC_MCAP_CE_BV_03_C     INC
+TC_MCAP_CE_BV_04_C     INC
+TC_MCAP_CM_ABT_BV_01_C N/A
+TC_MCAP_CM_ABT_BV_02_C INC
+TC_MCAP_CM_ABT_BV_03_C N/A
+TC_MCAP_CM_DEL_BV_01_C N/A
+TC_MCAP_CM_DEL_BV_02_C INC
+TC_MCAP_CM_DEL_BV_02_C N/A
+TC_MCAP_CM_DEL_BV_04_C INC
+TC_MCAP_CM_DIS_BV_01_C INC
+TC_MCAP_CM_DIS_BV_02_C INC
+TC_MCAP_CM_DIS_BV_03_C INC
+TC_MCAP_CM_DIS_BV_04_C INC
+TC_MCAP_CM_DIS_BV_05_C INC
+TC_MCAP_CM_REC_BV_01_C N/A
+TC_MCAP_CM_REC_BV_02_C INC
+TC_MCAP_CM_REC_BV_03_C N/A
+TC_MCAP_CM_REC_BV_04_C INC
+TC_MCAP_CM_REC_BV_05_C N/A
+TC_MCAP_CM_REC_BV_06_C INC
+TC_MCAP_CS_ERR_BI_01_C N/A
+TC_MCAP_CS_ERR_BI_02_C N/A
+TC_MCAP_CS_ERR_BI_03_C N/A
+TC_MCAP_CS_ERR_BI_04_C N/A
+TC_MCAP_CS_I_BV_01_I   N/A
+TC_MCAP_CS_I_BV_02_I   N/A
+TC_MCAP_CS_I_BV_03_C   N/A
+TC_MCAP_CS_I_BV_04_C   N/A
+TC_MCAP_CS_R_BV_01_I   N/A
+TC_MCAP_CS_R_BV_02_I   N/A
+TC_MCAP_CS_R_BV_03_C   N/A
+TC_MCAP_CS_T_BV_04_C   N/A
+TC_MCAP_ERR_BI_01_C    INC
+TC_MCAP_ERR_BI_02_C    INC
+TC_MCAP_ERR_BI_03_C    INC
+TC_MCAP_ERR_BI_04_C    INC
+TC_MCAP_ERR_BI_05_C    INC
+TC_MCAP_ERR_BI_06_C    INC
+TC_MCAP_ERR_BI_07_C    INC
+TC_MCAP_ERR_BI_08_C    INC
+TC_MCAP_ERR_BI_09_C    INC
+TC_MCAP_ERR_BI_10_C    INC
+TC_MCAP_ERR_BI_11_C    INC
+TC_MCAP_ERR_BI_12_C    INC
+TC_MCAP_ERR_BI_13_C    INC
+TC_MCAP_ERR_BI_14_C    INC
+TC_MCAP_ERR_BI_15_C    INC
+TC_MCAP_ERR_BI_16_C    INC
+TC_MCAP_ERR_BI_17_C    INC
+TC_MCAP_ERR_BI_18_C    INC
+TC_MCAP_ERR_BI_19_C    INC
+TC_MCAP_ERR_BI_20_C    INC
+TC_MCAP_INV_BI_01_C    INC
+TC_MCAP_INV_BI_02_C    INC
+TC_MCAP_INV_BI_03_C    INC
+TC_MCAP_INV_BI_04_C    INC
+TC_MCAP_INV_BI_05_C    INC
+TC_MCAP_INV_BI_06_C    INC
+TC_MCAP_INV_BI_07_C    INC
+-------------------------------------------------------------------------------
diff --git a/android/pts-mps.txt b/android/pts-mps.txt
new file mode 100644 (file)
index 0000000..005318d
--- /dev/null
@@ -0,0 +1,53 @@
+PTS test results for MPS
+
+PTS version: 5.1
+Tested: not tested
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+NONE   test result is none
+
+-------------------------------------------------------------------------------
+Test Name                              Result  Notes
+-------------------------------------------------------------------------------
+TC_AG_SRC_HFAV_ACT_SD_BV_01_I          INC
+TC_AG_SRC_HFAV_ACT_SD_BV_02_I          INC
+TC_AG_SRC_HFAV_ACT_SD_BV_03_I          INC
+TC_AG_SRC_HFAV_CLH_SD_BV_01_I          INC
+TC_AG_SRC_HFAV_CLH_SD_BV_02_I          N/A
+TC_AG_SRC_HFAV_CLH_SD_BV_03_I          INC
+TC_AG_SRC_HFAV_CLH_SD_BV_04_I          INC
+TC_AG_SRC_HFAV_CLH_SD_BV_05_I          INC
+TC_AG_SRC_HFAV_CLH_SD_BV_06_I          INC
+TC_AVP_CTH_SD_BI_01_I                  INC
+TC_AVP_CTH_SD_BI_02_I                  INC
+TC_HF_SNK_HFAV_ACT_SD_BV_01_I          N/A
+TC_HF_SNK_HFAV_ACT_SD_BV_02_I          N/A
+TC_HF_SNK_HFAV_ACT_SD_BV_03_I          N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_01_I          N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_02_I          N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_03_I          N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_04_I          N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_05_I          N/A
+TC_HF_SNK_HFAV_CLH_SD_BV_06_I          N/A
+TC_HF_SNK_CT_HFAV_ACT_MD_BV_01_I       N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_01_I       N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_02_I       N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_03_I       N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_04_I       N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_05_I       N/A
+TC_HF_SNK_CT_HFAV_CLH_MD_BV_06_I       N/A
+TC_SDP_CTH_SD_BV_01_I                  INC
+TC_SRC_TG_HFAV_ACT_MD_BV_01_I          INC
+TC_SRC_TG_HFAV_CLH_MD_BV_01_I          INC
+TC_SRC_TG_HFAV_CLH_MD_BV_02_I          INC
+TC_SRC_TG_HFAV_CLH_MD_BV_03_I          INC
+TC_SRC_TG_HFAV_CLH_MD_BV_04_I          INC
+TC_SRC_TG_HFAV_CLH_MD_BV_05_I          INC
+TC_SRC_TG_HFAV_CLH_MD_BV_06_I          INC
+TC_PAIRING_HF_SNK_CT                   INC
+-------------------------------------------------------------------------------
diff --git a/android/pts-opp.txt b/android/pts-opp.txt
new file mode 100644 (file)
index 0000000..0a070ca
--- /dev/null
@@ -0,0 +1,133 @@
+PTS test results for OPP
+
+PTS version: 5.1
+Tested: 17-April-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+NONE   test result is none
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_CLIENT_BC_BV_02_I   N/A
+TC_CLIENT_BC_BV_04_I   N/A
+TC_CLIENT_BCE_BV_01_I   N/A
+TC_CLIENT_BCE_BV_03_I   N/A
+TC_CLIENT_BCE_BV_04_I   N/A
+TC_CLIENT_BCE_BV_05_I   N/A
+TC_CLIENT_BCE_BV_06_I   N/A
+TC_CLIENT_BCE_BV_07_I   N/A
+TC_CLIENT_BCP_BV_01_I   N/A
+TC_CLIENT_BCP_BV_02_I   N/A
+TC_CLIENT_BCP_BV_03_I   N/A
+TC_CLIENT_BCP_BV_04_I   N/A
+TC_CLIENT_BCP_BV_05_I   N/A
+TC_CLIENT_CON_BV_01_C  N/A
+TC_CLIENT_OPH_BI_01_C  PASS    PTS issue: ID: 12081 (bluetooth.org)
+                               ETS-12081 required
+TC_CLIENT_OPH_BV_01_I  PASS    PTS issue: ID: 12081.
+                               ETS-12160 required
+TC_CLIENT_OPH_BV_02_I  N/A
+TC_CLIENT_OPH_BV_03_I  PASS    ETS-12081 required (bluetooth.org)
+TC_CLIENT_OPH_BV_04_I  N/A
+TC_CLIENT_OPH_BV_05_I  PASS
+TC_CLIENT_OPH_BV_07_I  N/A
+TC_CLIENT_OPH_BV_08_I  N/A
+TC_CLIENT_OPH_BV_09_I  N/A
+TC_CLIENT_OPH_BV_10_I  N/A
+TC_CLIENT_OPH_BV_11_I  N/A
+TC_CLIENT_OPH_BV_12_I  N/A
+TC_CLIENT_OPH_BV_13_I  N/A
+TC_CLIENT_OPH_BV_14_I  N/A
+TC_CLIENT_OPH_BV_15_I  N/A
+TC_CLIENT_OPH_BV_16_I  N/A
+TC_CLIENT_OPH_BV_17_I  N/A
+TC_CLIENT_OPH_BV_18_I  N/A
+TC_CLIENT_OPH_BV_19_I  PASS    ETS-12081 required
+TC_CLIENT_OPH_BV_20_I  PASS
+TC_CLIENT_OPH_BV_22_I  PASS    ETS-12081 required
+TC_CLIENT_OPH_BV_23_I  PASS    ETS-12081 required
+TC_CLIENT_OPH_BV_24_I  N/A
+TC_CLIENT_OPH_BV_25_I  N/A
+TC_CLIENT_OPH_BV_26_I  N/A
+TC_CLIENT_SRM_BV_01_C  N/A
+TC_CLIENT_SRM_BV_03_C  N/A
+TC_CLIENT_SRM_BV_05_C  N/A
+TC_CLIENT_SRM_BV_07_C  N/A
+TC_CLIENT_SRMP_BI_01_C N/A
+TC_CLIENT_SRMP_BV_01_C N/A
+TC_CLIENT_SRMP_BV_04_C N/A
+TC_CLIENT_SRMP_BV_05_C N/A
+TC_CLIENT_SRMP_BV_06_C N/A
+TC_SERVER_BC_BV_01_I   N/A
+TC_SERVER_BC_BV_03_I   N/A
+TC_SERVER_BCE_BV_01_I  N/A
+TC_SERVER_BCE_BV_03_I  N/A
+TC_SERVER_BCE_BV_04_I  N/A
+TC_SERVER_BCE_BV_05_I  N/A
+TC_SERVER_BCE_BV_06_I  N/A
+TC_SERVER_BCE_BV_07_I  N/A
+TC_SERVER_BCP_BV_01_I  N/A
+TC_SERVER_BCP_BV_02_I  PASS    ETS-12081 required (bluetooth.org)
+TC_SERVER_BCP_BV_03_I  N/A
+TC_SERVER_BCP_BV_04_I  N/A
+TC_SERVER_BCP_BV_05_I  N/A
+TC_SERVER_CON_BV_02_C  N/A
+TC_SERVER_OPH_BV_01_I  PASS
+TC_SERVER_OPH_BV_02_I  PASS
+TC_SERVER_OPH_BV_03_I  PASS    ETS-12081 required
+                               IUT must be in the connectable mode. Tester must
+                                       accept incoming file
+TC_SERVER_OPH_BV_04_I  PASS    ETS-12081 required
+                               IUT must be in the connectable mode. Tester must
+                                       accept incoming file
+TC_SERVER_OPH_BV_05_I  PASS    ETS-12081 required
+                               IUT must be in the connectable mode. Tester must
+                                       reject incoming file
+TC_SERVER_OPH_BV_07_I  N/A
+TC_SERVER_OPH_BV_08_I  N/A
+TC_SERVER_OPH_BV_09_I  N/A
+TC_SERVER_OPH_BV_10_I  PASS    ETS-12081 required
+                               IUT must be in the connectable mode
+TC_SERVER_OPH_BV_11_I  N/A
+TC_SERVER_OPH_BV_12_I  N/A
+TC_SERVER_OPH_BV_13_I  N/A
+TC_SERVER_OPH_BV_14_I  PASS    ETS-12081 required
+                               IUT must be in the connectable mode
+TC_SERVER_OPH_BV_15_I  N/A
+TC_SERVER_OPH_BV_16_I  N/A
+TC_SERVER_OPH_BV_17_I  N/A
+TC_SERVER_OPH_BV_18_I  PASS    ETS-12081 required
+                               IUT must be in the connectable mode
+TC_SERVER_OPH_BV_19_I  PASS    ETS-12081 required
+                               IUT must be in the connectable mode
+TC_SERVER_OPH_BV_21_I  N/A
+TC_SERVER_OPH_BV_22_I  PASS    ETS-12081 required
+                               IUT must be in the connectable mode
+TC_SERVER_OPH_BV_23_I  PASS    ETS-12081 required
+                               IUT must be in the connectable mode
+TC_SERVER_OPH_BV_24_I  N/A
+TC_SERVER_OPH_BV_25_I  N/A
+TC_SERVER_OPH_BV_26_I  N/A
+TC_SERVER_ROB_BV_01_I  N/A
+TC_SERVER_ROB_BV_02_I  N/A
+TC_SERVER_SRM_BI_03_I  N/A
+TC_SERVER_SRM_BI_05_I  N/A
+TC_SERVER_SRM_BV_04_I  N/A
+TC_SERVER_SRM_BV_08_I  N/A
+TC_SERVER_SRMP_BV_02_I N/A
+TC_SERVER_SRMP_BV_03_I N/A
+TC_CLIENT_OPH_BV_27_I  N/A
+TC_CLIENT_OPH_BV_34_I  PASS
+TC_SERVER_OPH_BV_27_I  N/A
+TC_SERVER_OPH_BV_30_I  N/A
+TC_SERVER_OPH_BV_31_I  N/A
+TC_SERVER_OPH_BV_32_I  N/A
+TC_SERVER_OPH_BV_33_I  N/A
+TC_SERVER_OPH_BV_34_I  PASS    ETS-12081 required
+-------------------------------------------------------------------------------
diff --git a/android/pts-pan.txt b/android/pts-pan.txt
new file mode 100644 (file)
index 0000000..88f903d
--- /dev/null
@@ -0,0 +1,82 @@
+PTS test results for PAN
+
+PTS version: 5.1
+Tested: 07-April-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+--------------------------------------------------------------------------------
+Test Name                              Result  Notes
+--------------------------------------------------------------------------------
+TC_BNEP_GN_BROADCAST_0_BV_03_C         N/A
+TC_GN_Ipv4_Autonet_BV_01_I             N/A
+TC_GN_Ipv6_Autonet_BV_02_I             N/A
+TC_GN_IP_DHCP_BV_03_I                  N/A
+TC_GN_IP_LLMNR_BV_01_I                 N/A
+TC_GN_IP_LLMNR_BV_02_I                 N/A
+TC_GN_IP_DNS_BV_01_I                   N/A
+TC_GN_IP_APP_BV_01_I                   N/A
+TC_GN_IP_APP_BV_02_I                   N/A
+TC_GN_IP_APP_BV_03_I                   N/A
+TC_GN_IP_APP_BV_04_I                   N/A
+TC_GN_IP_APP_BV_05_I                   N/A
+TC_SDP_GN_BV_02_C                      N/A
+TC_MISC_GN_UUID_BV_01_C                        N/A
+TC_MISC_GN_UUID_BV_02_C                        N/A
+TC_BNEP_NAP_BROADCAST_0_BV_01_C                N/A
+TC_BNEP_NAP_BROADCAST_0_BV_02_C                N/A
+TC_BNEP_NAP_FORWARD_UNICAST_BV_05_C    N/A
+TC_BNEP_NAP_FORWARD_UNICAST_BV_06_C    N/A
+TC_BNEP_NAP_MULTICAST_0_BV_03_C                N/A
+TC_BNEP_NAP_MULTICAST_0_BV_04_C                N/A
+TC_BNEP_BRIDGE_RX_BV_02_I              PASS    Tester needs to send one
+                                               forward-unicast BNEP packet
+                                               with given data (5 times)
+                                               0x00, 0x01, 0xFF followed by
+                                               0x00, 0x01, 0x9E, 0x9F
+                                               sendip -d <hexdata>
+TC_BNEP_BRIDGE_TX_BV_01_I              PASS
+TC_NAP_Ipv4_Autonet_BV_01_I            N/A
+TC_NAP_Ipv6_Autonet_BV_02_I            N/A
+TC_NAP_IP_DHCP_BV_03_I                 N/A
+TC_NAP_IP_LLMNR_BV_01_I                        N/A
+TC_NAP_IP_LLMNR_BV_02_I                        N/A
+TC_NAP_IP_DNS_BV_01_I                  N/A
+TC_NAP_IP_APP_BV_01_I                  N/A
+TC_NAP_IP_APP_BV_02_I                  N/A
+TC_NAP_IP_APP_BV_03_I                  N/A
+TC_NAP_IP_APP_BV_04_I                  N/A
+TC_NAP_IP_APP_BV_05_I                  N/A
+TC_SDP_NAP_BV_01_C                     PASS
+TC_MISC_NAP_UUID_BV_01_C               PASS
+TC_MISC_NAP_UUID_BV_02_C               PASS
+TC_BNEP_PANU_BROADCAST_0_BV_04_C       N/A
+TC_PANU_Ipv4_Autonet_BV_01_I           PASS    When prompted connect to PTS.
+                                               From IUT: issue ARP request
+                                               iperf -c <addr> -p <port#>
+TC_PANU_Ipv6_Autonet_BV_02_I           N/A
+TC_PANU_IP_LLMNR_BV_01_I               PASS    When prompted connect to PTS.
+                                               From IUT: send LLMNR request
+                                               command
+                                               iperf -c <addr> -p <port#>
+TC_PANU_IP_LLMNR_BV_02_I               N/A
+TC_PANU_IP_DHCP_BV_03_I                        N/A
+TC_PANU_IP_DNS_BV_01_I                 N/A
+TC_PANU_IP_APP_BV_01_I                 N/A
+TC_PANU_IP_APP_BV_02_I                 N/A
+TC_PANU_IP_APP_BV_03_I                 N/A
+TC_PANU_IP_APP_BV_04_I                 N/A
+TC_PANU_IP_APP_BV_05_I                 PASS    When prompted, connect to PTS
+                                               and then when prompted terminate
+                                               connection (IUT side)
+TC_SDP_PANU_BV_01_C                    N/A
+TC_MISC_PANU_UUID_BV_01_C              N/A
+TC_MISC_PANU_UUID_BV_02_C              N/A
+TC_MISC_ROLE_BV_01_C                   N/A
+TC_MISC_ROLE_BV_BV_02_C                        N/A
+--------------------------------------------------------------------------------
diff --git a/android/pts-pbap.txt b/android/pts-pbap.txt
new file mode 100644 (file)
index 0000000..cd6dcb6
--- /dev/null
@@ -0,0 +1,143 @@
+PTS test results for PBAP
+
+PTS version: 5.1
+Tested: 08-April-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_PCE_SSM_BV_01_C     N/A
+TC_PCE_SSM_BV_02_C     N/A
+TC_PCE_SSM_BV_06_C     N/A
+TC_PCE_SSM_BV_08_C     N/A
+TC_PCE_SSM_BI_01_C     N/A
+TC_PCE_SSM_BV_09_C     N/A
+TC_PCE_SSM_BV_10_C     N/A
+TC_PCE_PBD_BV_01_C     N/A
+TC_PCE_PBD_BV_04_C     N/A
+TC_PCE_PBD_BV_38_C     N/A
+TC_PCE_PBD_BV_29_C     N/A
+TC_PCE_PBD_BV_40_C     N/A
+TC_PCE_PBD_BV_41_C     N/A
+TC_PCE_PBD_BV_42_C     N/A
+TC_PCE_PBD_BV_43_C     N/A
+TC_PCE_PBD_BV_44_C     N/A
+TC_PCE_PBD_BV_45_C     N/A
+TC_PCE_PBD_BV_46_C     N/A
+TC_PCE_PBD_BV_47_C     N/A
+TC_PCE_PBD_BV_48_C     N/A
+TC_PCE_PBB_BV_01_C     N/A
+TC_PCE_PBB_BV_02_C     N/A
+TC_PCE_PBB_BV_03_C     N/A
+TC_PCE_PBB_BV_05_C     N/A
+TC_PCE_PBB_BV_39_C     N/A
+TC_PCE_PBB_BV_40_C     N/A
+TC_PCE_PBB_BV_41_C     N/A
+TC_PCE_PBB_BV_42_C     N/A
+TC_PCE_PBB_BV_33_C     N/A
+TC_PCE_PBB_BV_34_C     N/A
+TC_PCE_PBB_BV_35_C     N/A
+TC_PCE_PBB_BV_36_C     N/A
+TC_PCE_PBB_BV_43_C     N/A
+TC_PCE_PBB_BV_37_C     N/A
+TC_PCE_PBB_BV_38_C     N/A
+TC_PCE_PBF_BV_01_I     N/A
+TC_PCE_PBF_BV_02_I     N/A
+TC_PCE_PBF_BV_03_I     N/A
+TC_PCE_PDF_BV_01_I     N/A
+TC_PCE_PDF_BV_06_I     N/A
+TC_PSE_SSM_BV_03_C     PASS    Tester must accept obex request
+TC_PSE_SSM_BV_05_C     PASS
+TC_PSE_SSM_BV_07_C     PASS    Tester must accept obex request with
+                               TSPX_auth_password set in PIXITs
+TC_PSE_SSM_BI_02_C     PASS
+TC_PSE_SSM_BI_03_C     N/A
+TC_PSE_SSM_BV_08_I     PASS    Tester must compare passkey on IUT and PTS
+TC_PSE_SSM_BV_11_C     N/A
+
+TC_PSE_PBD_BV_02_C     PASS    Tester must compare phone book size with the
+                               value given by PTS
+TC_PSE_PBD_BV_03_C     PASS    Tester must compare phone book size with the
+                               value given by PTS
+TC_PSE_PBD_BV_05_C     N/A
+TC_PSE_PBD_BI_01_C     FAIL    Android PBAP server bug
+TC_PSE_PBD_BV_06_C     N/A
+TC_PSE_PBD_BV_07_C     N/A
+TC_PSE_PBD_BV_08_C     N/A
+TC_PSE_PBD_BV_09_C     N/A
+TC_PSE_PBD_BV_10_C     N/A
+TC_PSE_PBD_BV_17_C     PASS
+TC_PSE_PBD_BV_18_C     N/A
+TC_PSE_PBD_BV_19_C     N/A
+TC_PSE_PBD_BV_20_C     N/A
+TC_PSE_PBD_BV_21_C     N/A
+TC_PSE_PBD_BV_22_C     N/A
+TC_PSE_PBD_BV_23_C     N/A
+TC_PSE_PBD_BV_24_C     N/A
+TC_PSE_PBD_BV_25_C     N/A
+TC_PSE_PBD_BV_26_C     N/A
+TC_PSE_PBD_BV_27_C     N/A
+TC_PSE_PBD_BV_28_C     N/A
+TC_PSE_PBD_BV_29_C     N/A
+TC_PSE_PBD_BV_30_C     N/A
+TC_PSE_PBD_BV_31_C     N/A
+TC_PSE_PBD_BV_32_C     N/A
+TC_PSE_PBD_BV_33_C     N/A
+TC_PSE_PBD_BV_34_C     N/A
+TC_PSE_PBD_BV_35_C     N/A
+TC_PSE_PBD_BV_36_C     PASS
+TC_PSE_PBD_BV_37_C     N/A
+TC_PSE_PBB_BV_06_C     PASS
+TC_PSE_PBB_BV_07_C     PASS
+TC_PSE_PBB_BV_08_C     PASS    Tester must compare phone book size with the
+                               value given by PTS
+TC_PSE_PBB_BV_09_C     PASS
+TC_PSE_PBB_BV_10_C     PASS    Tester must verify vcard content received by PTS
+TC_PSE_PBB_BV_11_C     PASS    Tester must verify number of new missed calls
+                               with value given by PTS
+TC_PSE_PBB_BI_01_C     PASS
+TC_PSE_PBB_BI_07_C     PASS
+TC_PSE_PBB_BV_12_C     PASS
+TC_PSE_PBB_BV_13_C     N/A
+TC_PSE_PBB_BV_14_C     N/A
+TC_PSE_PBB_BV_15_C     N/A
+TC_PSE_PBB_BV_16_C     N/A
+TC_PSE_PBB_BV_17_C     N/A
+TC_PSE_PBB_BV_18_C     N/A
+TC_PSE_PBB_BV_19_C     N/A
+TC_PSE_PBB_BV_20_C     N/A
+TC_PSE_PBB_BV_21_C     N/A
+TC_PSE_PBB_BV_22_C     N/A
+TC_PSE_PBB_BV_23_C     N/A
+TC_PSE_PBB_BV_24_C     N/A
+TC_PSE_PBB_BV_25_C     N/A
+TC_PSE_PBB_BV_26_C     N/A
+TC_PSE_PBB_BV_27_C     N/A
+TC_PSE_PBB_BV_44_C     N/A
+TC_PSE_PBB_BV_45_C     N/A
+TC_PSE_PBB_BV_46_C     N/A
+TC_PSE_PBB_BV_28_C     N/A
+TC_PSE_PBB_BV_29_C     N/A
+TC_PSE_PBB_BV_30_C     N/A
+TC_PSE_PBB_BV_31_C     PASS
+TC_PSE_PBB_BV_32_C     N/A
+TC_PSE_PBF_BV_01_I     PASS
+TC_PSE_PBF_BV_02_I     PASS    Tester must verify vcard content received by PTS
+TC_PSE_PDF_BV_01_I     PASS    Tester must compare phone book size with the
+                               value given by PTS
+TC_PSE_BC_BV_03_I      N/A
+TC_PSE_CON_BV_02_I     N/A
+TC_PSE_ROB_BV_01_I     N/A
+TC_PSE_SRM_BI_03_I     N/A
+TC_PSE_SRM_BI_05_I     N/A
+TC_PSE_SRM_BV_08_I     N/A
+TC_PSE_SRMP_BI_02_I    N/A
+TX_PSE_SRMP_BV_02_I    N/A
+-------------------------------------------------------------------------------
diff --git a/android/pts-sm.txt b/android/pts-sm.txt
new file mode 100644 (file)
index 0000000..0792de8
--- /dev/null
@@ -0,0 +1,63 @@
+PTS test results for SM
+
+PTS version: 5.1
+Tested: 14-May-2014
+Android version: 4.4.2
+
+Results:
+PASS   test passed
+FAIL   test failed
+INC    test is inconclusive
+N/A    test is disabled due to PICS setup
+NONE   test result is none
+
+-------------------------------------------------------------------------------
+Test Name              Result  Notes
+-------------------------------------------------------------------------------
+TC_PROT_BV_01_C                PASS
+TC_PROT_BV_02_C                FAIL    JIRA: BZ-24
+                               btmgmt advertising on
+TC_JW_BV_01_C          FAIL    JIRA: BZ-25
+TC_JW_BV_02_C          INC     JIRA: BZ-26
+TC_JW_BV_05_C          INC
+TC_JW_BI_01_C          PASS
+TC_JW_BI_02_C          INC
+TC_JW_BI_03_C          INC
+TC_JW_BI_04_C          FAIL
+TC_PKE_BV_01_C         FAIL
+TC_PKE_BV_02_C         INC
+TC_PKE_BV_04_C         FAIL
+TC_PKE_BV_05_C         INC
+TC_PKE_BI_01_C         INC
+TC_PKE_BI_02_C         PASS
+TC_PKE_BI_03_C         INC
+TC_OOB_BV_01_C         FAIL
+TC_OOB_BV_02_C         INC
+TC_OOB_BV_03_C         FAIL
+TC_OOB_BV_04_C         INC
+TC_OOB_BV_05_C         INC
+TC_OOB_BV_06_C         INC
+TC_OOB_BV_07_C         INC
+TC_OOB_BV_08_C         INC
+TC_OOB_BV_09_C         INC
+TC_OOB_BV_10_C         INC
+TC_OOB_BI_01_C         INC
+TC_OOB_BI_02_C         INC
+TC_EKS_BV_01_C         INC
+TC_EKS_BV_02_C         INC
+TC_EKS_BI_01_C         INC
+TC_EKS_BI_02_C         INC
+TC_SIGN_BV_01_C                INC
+TC_SIGN_BV_03_C                INC
+TC_SIGN_BI_01_C                INC
+TC_KDU_BV_01_C         INC
+TC_KDU_BV_02_C         INC
+TC_KDU_BV_03_C         INC
+TC_KDU_BV_04_C         INC
+TC_KDU_BV_05_C         INC
+TC_KDU_BV_06_C         INC
+TC_KDU_BV_07_C         INC
+TC_SIP_BV_01_C         INC
+TC_SIP_BV_02_C         FAIL
+TC_SIE_BV_01_C         INC
+-------------------------------------------------------------------------------
diff --git a/android/sco-msg.h b/android/sco-msg.h
new file mode 100644 (file)
index 0000000..df0d858
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+static const char BLUEZ_SCO_SK_PATH[] = "\0bluez_sco_socket";
+
+#define SCO_SERVICE_ID                 0
+
+#define SCO_STATUS_SUCCESS             IPC_STATUS_SUCCESS
+#define SCO_STATUS_FAILED              0x01
+
+#define SCO_OP_STATUS                  IPC_OP_STATUS
+
+#define SCO_OP_CONNECT                 0x01
+struct sco_rsp_connect {
+       uint16_t mtu;
+} __attribute__((packed));
index 9020874..99d6bec 100644 (file)
@@ -2,21 +2,21 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 "lib/sdp_lib.h"
 #include "src/sdp-client.h"
 #include "src/sdpd.h"
+#include "src/log.h"
 
-#include "bluetooth.h"
-#include "log.h"
 #include "hal-msg.h"
-#include "hal-ipc.h"
+#include "ipc-common.h"
 #include "ipc.h"
 #include "utils.h"
+#include "bluetooth.h"
 #include "socket.h"
 
-#define SPP_DEFAULT_CHANNEL    3
+#define RFCOMM_CHANNEL_MAX 30
+
 #define OPP_DEFAULT_CHANNEL    9
+#define HSP_AG_DEFAULT_CHANNEL 12
+#define HFP_AG_DEFAULT_CHANNEL 13
 #define PBAP_DEFAULT_CHANNEL   15
-#define MAS_DEFAULT_CHANNEL    16
+#define MAP_MAS_DEFAULT_CHANNEL        16
 
 #define SVC_HINT_OBEX 0x10
 
-static bdaddr_t adapter_addr;
-
-/* Simple list of RFCOMM server sockets */
-GList *servers = NULL;
+/* Hardcoded MAP stuff needed for MAS SMS Instance.*/
+#define DEFAULT_MAS_INSTANCE   0x00
 
-/* Simple list of RFCOMM connected sockets */
-GList *connections = NULL;
+#define MAP_MSG_TYPE_SMS_GSM   0x02
+#define MAP_MSG_TYPE_SMS_CDMA  0x04
+#define DEFAULT_MAS_MSG_TYPE   (MAP_MSG_TYPE_SMS_GSM | MAP_MSG_TYPE_SMS_CDMA)
 
+static struct ipc *hal_ipc = NULL;
 struct rfcomm_sock {
-       int fd;         /* descriptor for communication with Java framework */
-       int real_sock;  /* real RFCOMM socket */
        int channel;    /* RFCOMM channel */
+       BtIOSecLevel sec_level;
+
+       /* for socket to BT */
+       int bt_sock;
+       guint bt_watch;
 
-       guint rfcomm_watch;
-       guint stack_watch;
+       /* for socket to HAL */
+       int jv_sock;
+       guint jv_watch;
 
        bdaddr_t dst;
        uint32_t service_handle;
 
-       const struct profile_info *profile;
+       uint8_t *buf;
+       int buf_size;
 };
 
-static struct rfcomm_sock *create_rfsock(int sock, int *hal_fd)
-{
-       int fds[2] = {-1, -1};
+struct rfcomm_channel {
+       bool reserved;
        struct rfcomm_sock *rfsock;
+};
 
-       if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) {
-               error("socketpair(): %s", strerror(errno));
-               *hal_fd = -1;
-               return NULL;
+static bdaddr_t adapter_addr;
+
+static const uint8_t zero_uuid[16] = { 0 };
+
+/* Simple list of RFCOMM connected sockets */
+static GList *connections = NULL;
+
+static struct rfcomm_channel servers[RFCOMM_CHANNEL_MAX + 1];
+
+static int rfsock_set_buffer(struct rfcomm_sock *rfsock)
+{
+       socklen_t len = sizeof(int);
+       int rcv, snd, size, err;
+
+       err = getsockopt(rfsock->bt_sock, SOL_SOCKET, SO_RCVBUF, &rcv, &len);
+       if (err < 0) {
+               int err = -errno;
+               error("getsockopt(SO_RCVBUF): %s", strerror(-err));
+               return err;
        }
 
-       rfsock = g_new0(struct rfcomm_sock, 1);
-       rfsock->fd = fds[0];
-       *hal_fd = fds[1];
-       rfsock->real_sock = sock;
+       err = getsockopt(rfsock->bt_sock, SOL_SOCKET, SO_SNDBUF, &snd, &len);
+       if (err < 0) {
+               int err = -errno;
+               error("getsockopt(SO_SNDBUF): %s", strerror(-err));
+               return err;
+       }
 
-       return rfsock;
+       size = MAX(rcv, snd);
+
+       DBG("Set buffer size %d", size);
+
+       rfsock->buf = g_malloc(size);
+       rfsock->buf_size = size;
+
+       return 0;
 }
 
 static void cleanup_rfsock(gpointer data)
 {
        struct rfcomm_sock *rfsock = data;
 
-       DBG("rfsock: %p fd %d real_sock %d chan %u",
-               rfsock, rfsock->fd, rfsock->real_sock, rfsock->channel);
+       DBG("rfsock %p bt_sock %d jv_sock %d", rfsock, rfsock->bt_sock,
+                                                       rfsock->jv_sock);
 
-       if (rfsock->fd >= 0)
-               if (close(rfsock->fd) < 0)
-                       error("close() fd %d failed: %s", rfsock->fd,
+       if (rfsock->jv_sock >= 0)
+               if (close(rfsock->jv_sock) < 0)
+                       error("close() fd %d failed: %s", rfsock->jv_sock,
                                                        strerror(errno));
 
-       if (rfsock->real_sock >= 0)
-               if (close(rfsock->real_sock) < 0)
-                       error("close() fd %d: failed: %s", rfsock->real_sock,
+       if (rfsock->bt_sock >= 0)
+               if (close(rfsock->bt_sock) < 0)
+                       error("close() fd %d: failed: %s", rfsock->bt_sock,
                                                        strerror(errno));
 
-       if (rfsock->rfcomm_watch > 0)
-               if (!g_source_remove(rfsock->rfcomm_watch))
-                       error("rfcomm_watch source was not found");
+       if (rfsock->bt_watch > 0)
+               if (!g_source_remove(rfsock->bt_watch))
+                       error("bt_watch source was not found");
 
-       if (rfsock->stack_watch > 0)
-               if (!g_source_remove(rfsock->stack_watch))
+       if (rfsock->jv_watch > 0)
+               if (!g_source_remove(rfsock->jv_watch))
                        error("stack_watch source was not found");
 
        if (rfsock->service_handle)
                bt_adapter_remove_record(rfsock->service_handle);
 
+       if (rfsock->buf)
+               g_free(rfsock->buf);
+
        g_free(rfsock);
 }
 
-static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name)
+static struct rfcomm_sock *create_rfsock(int bt_sock, int *hal_sock)
 {
-       const char *service_name = "OBEX Object Push";
-       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];
-       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 fds[2] = {-1, -1};
+       struct rfcomm_sock *rfsock;
+
+       if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) {
+               error("socketpair(): %s", strerror(errno));
+               *hal_sock = -1;
+               return NULL;
+       }
+
+       rfsock = g_new0(struct rfcomm_sock, 1);
+       rfsock->jv_sock = fds[0];
+       *hal_sock = fds[1];
+       rfsock->bt_sock = bt_sock;
+
+       DBG("rfsock %p", rfsock);
+
+       if (bt_sock < 0)
+               return rfsock;
+
+       if (rfsock_set_buffer(rfsock) < 0) {
+               cleanup_rfsock(rfsock);
+               return NULL;
+       }
+
+       return rfsock;
+}
+
+static sdp_record_t *create_rfcomm_record(uint8_t chan, uuid_t *uuid,
+                                               const char *svc_name,
+                                               bool has_obex)
+{
+       sdp_list_t *svclass_id;
+       sdp_list_t *seq, *proto_seq, *pbg_seq;
+       sdp_list_t *proto[3];
+       uuid_t l2cap_uuid, rfcomm_uuid, obex_uuid, pbg_uuid;
        sdp_data_t *channel;
        sdp_record_t *record;
 
@@ -145,196 +205,165 @@ static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name)
 
        record->handle =  sdp_next_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(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
-       svclass_id = sdp_list_append(NULL, &opush_uuid);
+       svclass_id = sdp_list_append(NULL, 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(NULL, profile);
-       sdp_set_profile_descs(record, pfseq);
-
        sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
        proto[0] = sdp_list_append(NULL, &l2cap_uuid);
-       apseq = sdp_list_append(NULL, proto[0]);
+       seq = sdp_list_append(NULL, proto[0]);
 
        sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
        proto[1] = sdp_list_append(NULL, &rfcomm_uuid);
        channel = sdp_data_alloc(SDP_UINT8, &chan);
        proto[1] = sdp_list_append(proto[1], channel);
-       apseq = sdp_list_append(apseq, proto[1]);
+       seq = sdp_list_append(seq, proto[1]);
 
-       sdp_uuid16_create(&obex_uuid, OBEX_UUID);
-       proto[2] = sdp_list_append(NULL, &obex_uuid);
-       apseq = sdp_list_append(apseq, proto[2]);
+       if (has_obex) {
+               sdp_uuid16_create(&obex_uuid, OBEX_UUID);
+               proto[2] = sdp_list_append(NULL, &obex_uuid);
+               seq = sdp_list_append(seq, proto[2]);
+       }
 
-       aproto = sdp_list_append(NULL, apseq);
-       sdp_set_access_protos(record, aproto);
+       proto_seq = sdp_list_append(NULL, seq);
+       sdp_set_access_protos(record, proto_seq);
 
-       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_uuid16_create(&pbg_uuid, PUBLIC_BROWSE_GROUP);
+       pbg_seq = sdp_list_append(NULL, &pbg_uuid);
+       sdp_set_browse_groups(record, pbg_seq);
 
        if (svc_name)
-               service_name = svc_name;
-
-       sdp_set_info_attr(record, service_name, NULL, NULL);
+               sdp_set_info_attr(record, svc_name, NULL, NULL);
 
        sdp_data_free(channel);
        sdp_list_free(proto[0], NULL);
        sdp_list_free(proto[1], NULL);
-       sdp_list_free(proto[2], NULL);
-       sdp_list_free(apseq, NULL);
-       sdp_list_free(pfseq, NULL);
-       sdp_list_free(aproto, NULL);
-       sdp_list_free(root, NULL);
+       if (has_obex)
+               sdp_list_free(proto[2], NULL);
+       sdp_list_free(seq, NULL);
+       sdp_list_free(proto_seq, NULL);
+       sdp_list_free(pbg_seq, NULL);
        sdp_list_free(svclass_id, NULL);
 
        return record;
 }
 
-static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name)
+static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name)
 {
-       const char *service_name = "OBEX Phonebook Access Server";
-       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_data_t *channel;
-       uint8_t formats[] = { 0x01 };
+       uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff };
        uint8_t dtd = SDP_UINT8;
-       sdp_data_t *sflist;
+       uuid_t uuid;
+       sdp_list_t *seq;
+       sdp_profile_desc_t profile[1];
+       void *dtds[sizeof(formats)], *values[sizeof(formats)];
+       sdp_data_t *formats_list;
        sdp_record_t *record;
+       size_t i;
 
-       record = sdp_record_alloc();
+       sdp_uuid16_create(&uuid, OBEX_OBJPUSH_SVCLASS_ID);
+
+       record = create_rfcomm_record(chan, &uuid, svc_name, true);
        if (!record)
                return NULL;
 
-       record->handle =  sdp_next_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(&pbap_uuid, PBAP_PSE_SVCLASS_ID);
-       svclass_id = sdp_list_append(NULL, &pbap_uuid);
-       sdp_set_service_classes(record, svclass_id);
-
-       sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID);
+       sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
        profile[0].version = 0x0100;
-       pfseq = sdp_list_append(NULL, profile);
-       sdp_set_profile_descs(record, pfseq);
+       seq = sdp_list_append(NULL, profile);
+       sdp_set_profile_descs(record, seq);
 
-       sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
-       proto[0] = sdp_list_append(NULL, &l2cap_uuid);
-       apseq = sdp_list_append(NULL, proto[0]);
+       for (i = 0; i < sizeof(formats); i++) {
+               dtds[i] = &dtd;
+               values[i] = &formats[i];
+       }
+       formats_list = sdp_seq_alloc(dtds, values, sizeof(formats));
+       sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, formats_list);
 
-       sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
-       proto[1] = sdp_list_append(NULL, &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_list_free(seq, NULL);
 
-       sdp_uuid16_create(&obex_uuid, OBEX_UUID);
-       proto[2] = sdp_list_append(NULL, &obex_uuid);
-       apseq = sdp_list_append(apseq, proto[2]);
+       return record;
+}
 
-       aproto = sdp_list_append(NULL, apseq);
-       sdp_set_access_protos(record, aproto);
+static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name)
+{
+       sdp_list_t *seq;
+       sdp_profile_desc_t profile[1];
+       uint8_t formats = 0x01;
+       sdp_record_t *record;
+       uuid_t uuid;
 
-       sflist = sdp_data_alloc(dtd, formats);
-       sdp_attr_add(record, SDP_ATTR_SUPPORTED_REPOSITORIES, sflist);
+       sdp_uuid16_create(&uuid, PBAP_PSE_SVCLASS_ID);
 
-       if (svc_name)
-               service_name = svc_name;
+       record = create_rfcomm_record(chan, &uuid, svc_name, true);
+       if (!record)
+               return NULL;
 
-       sdp_set_info_attr(record, service_name, NULL, NULL);
+       sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID);
+       profile[0].version = 0x0101;
+       seq = sdp_list_append(NULL, profile);
+       sdp_set_profile_descs(record, seq);
 
-       sdp_data_free(channel);
-       sdp_list_free(proto[0], NULL);
-       sdp_list_free(proto[1], NULL);
-       sdp_list_free(proto[2], NULL);
-       sdp_list_free(apseq, NULL);
-       sdp_list_free(pfseq, NULL);
-       sdp_list_free(aproto, NULL);
-       sdp_list_free(root, NULL);
-       sdp_list_free(svclass_id, NULL);
+       sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_REPOSITORIES, SDP_UINT8,
+                                                               &formats);
+
+       sdp_list_free(seq, NULL);
 
        return record;
 }
 
-static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name)
+static sdp_record_t *create_mas_record(uint8_t chan, const char *svc_name)
 {
-       const char *service_name = "Serial Port";
-       sdp_list_t *svclass_id, *apseq, *profiles, *root;
-       uuid_t root_uuid, sp_uuid, l2cap, rfcomm;
-       sdp_profile_desc_t profile;
-       sdp_list_t *aproto, *proto[2];
-       sdp_data_t *channel;
+       sdp_list_t *seq;
+       sdp_profile_desc_t profile[1];
+       uint8_t minst = DEFAULT_MAS_INSTANCE;
+       uint8_t mtype = DEFAULT_MAS_MSG_TYPE;
        sdp_record_t *record;
+       uuid_t uuid;
 
-       record = sdp_record_alloc();
+       sdp_uuid16_create(&uuid, MAP_MSE_SVCLASS_ID);
+
+       record = create_rfcomm_record(chan, &uuid, svc_name, true);
        if (!record)
                return NULL;
 
-       record->handle =  sdp_next_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(&profile[0].uuid, MAP_PROFILE_ID);
+       profile[0].version = 0x0101;
+       seq = sdp_list_append(NULL, profile);
+       sdp_set_profile_descs(record, seq);
 
-       sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
-       svclass_id = sdp_list_append(NULL, &sp_uuid);
-       sdp_set_service_classes(record, svclass_id);
+       sdp_attr_add_new(record, SDP_ATTR_MAS_INSTANCE_ID, SDP_UINT8, &minst);
+       sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_MESSAGE_TYPES, SDP_UINT8,
+                                                                       &mtype);
 
-       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(seq, 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);
-       channel = sdp_data_alloc(SDP_UINT8, &chan);
-       proto[1] = sdp_list_append(proto[1], channel);
-       apseq = sdp_list_append(apseq, proto[1]);
+       return record;
+}
 
-       aproto = sdp_list_append(NULL, apseq);
-       sdp_set_access_protos(record, aproto);
+static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name)
+{
+       sdp_record_t *record;
+       uuid_t uuid;
 
-       sdp_add_lang_attr(record);
+       sdp_uuid16_create(&uuid, SERIAL_PORT_SVCLASS_ID);
 
-       if (svc_name)
-               service_name = svc_name;
+       record = create_rfcomm_record(chan, &uuid, svc_name, false);
+       if (!record)
+               return NULL;
 
-       sdp_set_info_attr(record, service_name, "BlueZ", "COM Port");
+       return record;
+}
 
-       sdp_set_url_attr(record, "http://www.bluez.org/",
-                       "http://www.bluez.org/", "http://www.bluez.org/");
+static sdp_record_t *create_app_record(uint8_t chan,
+                                               const uint8_t *app_uuid,
+                                               const char *svc_name)
+{
+       sdp_record_t *record;
+       uuid_t uuid;
 
-       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);
+       sdp_uuid128_create(&uuid, app_uuid);
 
-       sdp_data_free(channel);
-       sdp_list_free(proto[0], NULL);
-       sdp_list_free(proto[1], NULL);
-       sdp_list_free(apseq, NULL);
-       sdp_list_free(aproto, NULL);
-       sdp_list_free(root, NULL);
-       sdp_list_free(svclass_id, NULL);
-       sdp_list_free(profiles, NULL);
+       record = create_rfcomm_record(chan, &uuid, svc_name, false);
+       if (!record)
+               return NULL;
 
        return record;
 }
@@ -348,6 +377,24 @@ static const struct profile_info {
 } profiles[] = {
        {
                .uuid = {
+                       0x00, 0x00, 0x11, 0x08, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+               },
+               .channel = HSP_AG_DEFAULT_CHANNEL,
+               .svc_hint = 0,
+               .sec_level = BT_IO_SEC_MEDIUM,
+               .create_record = NULL
+       }, {
+               .uuid = {
+                       0x00, 0x00, 0x11, 0x1F, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+               },
+               .channel = HFP_AG_DEFAULT_CHANNEL,
+               .svc_hint = 0,
+               .sec_level = BT_IO_SEC_MEDIUM,
+               .create_record = NULL
+       }, {
+               .uuid = {
                        0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00,
                        0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
                },
@@ -369,35 +416,40 @@ static const struct profile_info {
                        0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00,
                        0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
                },
-               .channel = MAS_DEFAULT_CHANNEL,
-               .svc_hint = 0,
+               .channel = MAP_MAS_DEFAULT_CHANNEL,
+               .svc_hint = SVC_HINT_OBEX,
                .sec_level = BT_IO_SEC_MEDIUM,
-               .create_record = NULL
+               .create_record = create_mas_record
        }, {
                .uuid = {
                        0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
                        0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
                },
-               .channel = SPP_DEFAULT_CHANNEL,
+               .channel = 0,
                .svc_hint = 0,
                .sec_level = BT_IO_SEC_MEDIUM,
                .create_record = create_spp_record
        },
 };
 
-static uint32_t sdp_service_register(const struct profile_info *profile,
-                                                       const void *svc_name)
+static uint32_t sdp_service_register(uint8_t channel, const uint8_t *uuid,
+                                       const struct profile_info *profile,
+                                       const void *svc_name)
 {
-       sdp_record_t *record;
-
-       if (!profile || !profile->create_record)
-               return 0;
+       sdp_record_t *record = NULL;
+       uint8_t svc_hint = 0;
+
+       if (profile && profile->create_record) {
+               record = profile->create_record(channel, svc_name);
+               svc_hint = profile->svc_hint;
+       } else if (uuid) {
+               record = create_app_record(channel, uuid, svc_name);
+       }
 
-       record = profile->create_record(profile->channel, svc_name);
        if (!record)
                return 0;
 
-       if (bt_adapter_add_record(record, profile->svc_hint) < 0) {
+       if (bt_adapter_add_record(record, svc_hint) < 0) {
                error("Failed to register on SDP record");
                sdp_record_free(record);
                return 0;
@@ -483,11 +535,10 @@ static int try_write_all(int fd, unsigned char *buf, int len)
        return sent;
 }
 
-static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond,
+static gboolean jv_sock_client_event_cb(GIOChannel *io, GIOCondition cond,
                                                                gpointer data)
 {
        struct rfcomm_sock *rfsock = data;
-       unsigned char buf[1024];
        int len, sent;
 
        if (cond & G_IO_HUP) {
@@ -496,19 +547,18 @@ static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond,
        }
 
        if (cond & (G_IO_ERR | G_IO_NVAL)) {
-               error("Socket error: sock %d cond %d",
-                                       g_io_channel_unix_get_fd(io), cond);
+               error("Socket %d error", g_io_channel_unix_get_fd(io));
                goto fail;
        }
 
-       len = read(rfsock->fd, buf, sizeof(buf));
+       len = read(rfsock->jv_sock, rfsock->buf, rfsock->buf_size);
        if (len <= 0) {
                error("read(): %s", strerror(errno));
                /* Read again */
                return TRUE;
        }
 
-       sent = try_write_all(rfsock->real_sock, buf, len);
+       sent = try_write_all(rfsock->bt_sock, rfsock->buf, len);
        if (sent < 0) {
                error("write(): %s", strerror(errno));
                goto fail;
@@ -516,17 +566,18 @@ static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond,
 
        return TRUE;
 fail:
+       DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond);
+
        connections = g_list_remove(connections, rfsock);
        cleanup_rfsock(rfsock);
 
        return FALSE;
 }
 
-static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond,
+static gboolean bt_sock_event_cb(GIOChannel *io, GIOCondition cond,
                                                                gpointer data)
 {
        struct rfcomm_sock *rfsock = data;
-       unsigned char buf[1024];
        int len, sent;
 
        if (cond & G_IO_HUP) {
@@ -535,19 +586,18 @@ static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond,
        }
 
        if (cond & (G_IO_ERR | G_IO_NVAL)) {
-               error("Socket error: sock %d cond %d",
-                                       g_io_channel_unix_get_fd(io), cond);
+               error("Socket %d error", g_io_channel_unix_get_fd(io));
                goto fail;
        }
 
-       len = read(rfsock->real_sock, buf, sizeof(buf));
+       len = read(rfsock->bt_sock, rfsock->buf, rfsock->buf_size);
        if (len <= 0) {
                error("read(): %s", strerror(errno));
                /* Read again */
                return TRUE;
        }
 
-       sent = try_write_all(rfsock->fd, buf, len);
+       sent = try_write_all(rfsock->jv_sock, rfsock->buf, len);
        if (sent < 0) {
                error("write(): %s", strerror(errno));
                goto fail;
@@ -555,6 +605,8 @@ static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond,
 
        return TRUE;
 fail:
+       DBG("rfsock %p bt_sock %d cond %d", rfsock, rfsock->bt_sock, cond);
+
        connections = g_list_remove(connections, rfsock);
        cleanup_rfsock(rfsock);
 
@@ -574,7 +626,7 @@ static bool sock_send_accept(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr,
        cmd.channel = rfsock->channel;
        cmd.status = 0;
 
-       len = bt_sock_send_fd(rfsock->fd, &cmd, sizeof(cmd), fd_accepted);
+       len = bt_sock_send_fd(rfsock->jv_sock, &cmd, sizeof(cmd), fd_accepted);
        if (len != sizeof(cmd)) {
                error("Error sending accept signal");
                return false;
@@ -583,16 +635,34 @@ static bool sock_send_accept(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr,
        return true;
 }
 
+static gboolean jv_sock_server_event_cb(GIOChannel *io, GIOCondition cond,
+                                                               gpointer data)
+{
+       struct rfcomm_sock *rfsock = data;
+
+       DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond);
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       if (cond & (G_IO_ERR | G_IO_HUP)) {
+               servers[rfsock->channel].rfsock = NULL;
+               cleanup_rfsock(rfsock);
+       }
+
+       return FALSE;
+}
+
 static void accept_cb(GIOChannel *io, GError *err, gpointer user_data)
 {
        struct rfcomm_sock *rfsock = user_data;
-       struct rfcomm_sock *rfsock_acc;
-       GIOChannel *io_stack;
+       struct rfcomm_sock *new_rfsock;
+       GIOChannel *jv_io;
        GError *gerr = NULL;
        bdaddr_t dst;
        char address[18];
-       int sock_acc;
-       int hal_fd;
+       int new_sock;
+       int hal_sock;
        guint id;
        GIOCondition cond;
 
@@ -612,76 +682,127 @@ static void accept_cb(GIOChannel *io, GError *err, gpointer user_data)
        }
 
        ba2str(&dst, address);
-       DBG("Incoming connection from %s rfsock %p", address, rfsock);
+       DBG("Incoming connection from %s on channel %d (rfsock %p)", address,
+                                               rfsock->channel, rfsock);
 
-       sock_acc = g_io_channel_unix_get_fd(io);
-       rfsock_acc = create_rfsock(sock_acc, &hal_fd);
-       if (!rfsock_acc) {
+       new_sock = g_io_channel_unix_get_fd(io);
+       new_rfsock = create_rfsock(new_sock, &hal_sock);
+       if (!new_rfsock) {
                g_io_channel_shutdown(io, TRUE, NULL);
                return;
        }
 
-       DBG("rfsock: fd %d real_sock %d chan %u sock %d",
-               rfsock->fd, rfsock->real_sock, rfsock->channel,
-               sock_acc);
+       DBG("new rfsock %p bt_sock %d jv_sock %d hal_sock %d", new_rfsock,
+                       new_rfsock->bt_sock, new_rfsock->jv_sock, hal_sock);
 
-       if (!sock_send_accept(rfsock, &dst, hal_fd)) {
-               cleanup_rfsock(rfsock_acc);
+       if (!sock_send_accept(rfsock, &dst, hal_sock)) {
+               cleanup_rfsock(new_rfsock);
                return;
        }
 
-       connections = g_list_append(connections, rfsock_acc);
+       connections = g_list_append(connections, new_rfsock);
 
        /* Handle events from Android */
        cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
-       io_stack = g_io_channel_unix_new(rfsock_acc->fd);
-       id = g_io_add_watch(io_stack, cond, sock_stack_event_cb, rfsock_acc);
-       g_io_channel_unref(io_stack);
+       jv_io = g_io_channel_unix_new(new_rfsock->jv_sock);
+       id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, new_rfsock);
+       g_io_channel_unref(jv_io);
 
-       rfsock_acc->stack_watch = id;
+       new_rfsock->jv_watch = id;
 
        /* Handle rfcomm events */
        cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
-       id = g_io_add_watch(io, cond, sock_rfcomm_event_cb, rfsock_acc);
+       id = g_io_add_watch(io, cond, bt_sock_event_cb, new_rfsock);
        g_io_channel_set_close_on_unref(io, FALSE);
 
-       rfsock_acc->rfcomm_watch = id;
+       new_rfsock->bt_watch = id;
+}
+
+static int find_free_channel(void)
+{
+       int ch;
+
+       /* channel 0 is reserver so we don't use it */
+       for (ch = 1; ch <= RFCOMM_CHANNEL_MAX; ch++) {
+               struct rfcomm_channel *srv = &servers[ch];
+
+               if (!srv->reserved && srv->rfsock == NULL)
+                       return ch;
+       }
 
-       DBG("rfsock %p rfsock_acc %p stack_watch %d rfcomm_watch %d",
-               rfsock, rfsock_acc, rfsock_acc->stack_watch,
-               rfsock_acc->rfcomm_watch);
+       return 0;
 }
 
-static void handle_listen(const void *buf, uint16_t len)
+static BtIOSecLevel get_sec_level(uint8_t flags)
+{
+       /*
+        * HAL_SOCK_FLAG_AUTH should require MITM but in our case setting
+        * security to BT_IO_SEC_HIGH would also require 16-digits PIN code
+        * for pre-2.1 devices which is not what Android expects. For this
+        * reason we ignore this flag to not break apps which use "secure"
+        * sockets (have both auth and encrypt flags set, there is no public
+        * API in Android which should provide proper high security socket).
+        */
+       return flags & HAL_SOCK_FLAG_ENCRYPT ? BT_IO_SEC_MEDIUM :
+                                                       BT_IO_SEC_LOW;
+}
+
+static uint8_t rfcomm_listen(int chan, const uint8_t *name, const uint8_t *uuid,
+                                               uint8_t flags, int *hal_sock)
 {
-       const struct hal_cmd_sock_listen *cmd = buf;
        const struct profile_info *profile;
        struct rfcomm_sock *rfsock = NULL;
        BtIOSecLevel sec_level;
-       GIOChannel *io;
+       GIOChannel *io, *jv_io;
+       GIOCondition cond;
        GError *err = NULL;
-       int hal_fd = -1;
-       int chan;
+       guint id;
+       uuid_t uu;
+       char uuid_str[32];
 
-       DBG("");
+       sdp_uuid128_create(&uu, uuid);
+       sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str));
 
-       profile = get_profile_by_uuid(cmd->uuid);
-       if (!profile) {
-               if (!cmd->channel)
-                       goto failed;
+       DBG("chan %d flags 0x%02x uuid %s name %s", chan, flags, uuid_str,
+                                                                       name);
 
-               chan = cmd->channel;
-               sec_level = BT_IO_SEC_MEDIUM;
+       if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) ||
+                       (chan > RFCOMM_CHANNEL_MAX)) {
+               error("Invalid rfcomm listen params");
+               return HAL_STATUS_INVALID;
+       }
+
+       profile = get_profile_by_uuid(uuid);
+       if (!profile) {
+               sec_level = get_sec_level(flags);
        } else {
+               if (!profile->create_record)
+                       return HAL_STATUS_INVALID;
+
                chan = profile->channel;
                sec_level = profile->sec_level;
        }
 
-       DBG("rfcomm channel %d svc_name %s", chan, cmd->name);
+       if (chan <= 0)
+               chan = find_free_channel();
+
+       if (!chan) {
+               error("No free channels");
+               return HAL_STATUS_BUSY;
+       }
+
+       if (servers[chan].rfsock != NULL) {
+               error("Channel already registered (%d)", chan);
+               return HAL_STATUS_BUSY;
+       }
 
-       rfsock = create_rfsock(-1, &hal_fd);
+       DBG("chan %d sec_level %d", chan, sec_level);
+
+       rfsock = create_rfsock(-1, hal_sock);
        if (!rfsock)
-               goto failed;
+               return HAL_STATUS_FAILED;
+
+       rfsock->channel = chan;
 
        io = bt_io_listen(accept_cb, NULL, rfsock, NULL, &err,
                                BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
@@ -694,36 +815,76 @@ static void handle_listen(const void *buf, uint16_t len)
                goto failed;
        }
 
-       rfsock->real_sock = g_io_channel_unix_get_fd(io);
+       rfsock->bt_sock = g_io_channel_unix_get_fd(io);
 
+       g_io_channel_set_close_on_unref(io, FALSE);
        g_io_channel_unref(io);
 
-       DBG("real_sock %d fd %d hal_fd %d", rfsock->real_sock, rfsock->fd,
-                                                               hal_fd);
+       /* Handle events from Android */
+       cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       jv_io = g_io_channel_unix_new(rfsock->jv_sock);
+       id = g_io_add_watch_full(jv_io, G_PRIORITY_HIGH, cond,
+                                       jv_sock_server_event_cb, rfsock,
+                                       NULL);
+       g_io_channel_unref(jv_io);
+
+       rfsock->jv_watch = id;
 
-       if (write(rfsock->fd, &chan, sizeof(chan)) != sizeof(chan)) {
+       DBG("rfsock %p bt_sock %d jv_sock %d hal_sock %d", rfsock,
+                                                               rfsock->bt_sock,
+                                                               rfsock->jv_sock,
+                                                               *hal_sock);
+
+       if (write(rfsock->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) {
                error("Error sending RFCOMM channel");
                goto failed;
        }
 
-       rfsock->service_handle = sdp_service_register(profile, cmd->name);
+       rfsock->service_handle = sdp_service_register(chan, uuid, profile,
+                                                                       name);
 
-       servers = g_list_append(servers, rfsock);
+       servers[chan].rfsock = rfsock;
 
-       ipc_send_rsp_full(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, 0, NULL,
-                                                                       hal_fd);
-       close(hal_fd);
-       return;
+       return HAL_STATUS_SUCCESS;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN,
-                                                       HAL_STATUS_FAILED);
 
-       if (rfsock)
-               cleanup_rfsock(rfsock);
+       cleanup_rfsock(rfsock);
+       close(*hal_sock);
+       return HAL_STATUS_FAILED;
+}
 
-       if (hal_fd >= 0)
-               close(hal_fd);
+static void handle_listen(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_socket_listen *cmd = buf;
+       uint8_t status;
+       int hal_sock;
+
+       switch (cmd->type) {
+       case HAL_SOCK_RFCOMM:
+               status = rfcomm_listen(cmd->channel, cmd->name, cmd->uuid,
+                                                       cmd->flags, &hal_sock);
+               break;
+       case HAL_SOCK_SCO:
+       case HAL_SOCK_L2CAP:
+               status = HAL_STATUS_UNSUPPORTED;
+               break;
+       default:
+               status = HAL_STATUS_INVALID;
+               break;
+       }
+
+       if (status != HAL_STATUS_SUCCESS)
+               goto failed;
+
+       ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN,
+                                                       0, NULL, hal_sock);
+       close(hal_sock);
+       return;
+
+failed:
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN,
+                                                                       status);
 }
 
 static bool sock_send_connect(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr)
@@ -739,7 +900,7 @@ static bool sock_send_connect(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr)
        cmd.channel = rfsock->channel;
        cmd.status = 0;
 
-       len = write(rfsock->fd, &cmd, sizeof(cmd));
+       len = write(rfsock->jv_sock, &cmd, sizeof(cmd));
        if (len < 0) {
                error("%s", strerror(errno));
                return false;
@@ -757,7 +918,7 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
 {
        struct rfcomm_sock *rfsock = user_data;
        bdaddr_t *dst = &rfsock->dst;
-       GIOChannel *io_stack;
+       GIOChannel *jv_io;
        char address[18];
        guint id;
        GIOCondition cond;
@@ -768,29 +929,26 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
        }
 
        ba2str(dst, address);
-       DBG("Connected to %s", address);
-
-       DBG("rfsock: fd %d real_sock %d chan %u sock %d",
-               rfsock->fd, rfsock->real_sock, rfsock->channel,
-               g_io_channel_unix_get_fd(io));
+       DBG("Connected to %s on channel %d (rfsock %p)", address,
+                                               rfsock->channel, rfsock);
 
        if (!sock_send_connect(rfsock, dst))
                goto fail;
 
        /* Handle events from Android */
        cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
-       io_stack = g_io_channel_unix_new(rfsock->fd);
-       id = g_io_add_watch(io_stack, cond, sock_stack_event_cb, rfsock);
-       g_io_channel_unref(io_stack);
+       jv_io = g_io_channel_unix_new(rfsock->jv_sock);
+       id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, rfsock);
+       g_io_channel_unref(jv_io);
 
-       rfsock->stack_watch = id;
+       rfsock->jv_watch = id;
 
        /* Handle rfcomm events */
        cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-       id = g_io_add_watch(io, cond, sock_rfcomm_event_cb, rfsock);
+       id = g_io_add_watch(io, cond, bt_sock_event_cb, rfsock);
        g_io_channel_set_close_on_unref(io, FALSE);
 
-       rfsock->rfcomm_watch = id;
+       rfsock->bt_watch = id;
 
        return;
 fail:
@@ -798,13 +956,45 @@ fail:
        cleanup_rfsock(rfsock);
 }
 
+static bool do_rfcomm_connect(struct rfcomm_sock *rfsock, int chan)
+{
+       GIOChannel *io;
+       GError *gerr = NULL;
+
+       DBG("rfsock %p sec_level %d chan %d", rfsock, rfsock->sec_level, chan);
+
+       io = bt_io_connect(connect_cb, rfsock, NULL, &gerr,
+                               BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                               BT_IO_OPT_DEST_BDADDR, &rfsock->dst,
+                               BT_IO_OPT_CHANNEL, chan,
+                               BT_IO_OPT_SEC_LEVEL, rfsock->sec_level,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("Failed connect: %s", gerr->message);
+               g_error_free(gerr);
+               return false;
+       }
+
+       g_io_channel_set_close_on_unref(io, FALSE);
+       g_io_channel_unref(io);
+
+       if (write(rfsock->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) {
+               error("Error sending RFCOMM channel");
+               return false;
+       }
+
+       rfsock->bt_sock = g_io_channel_unix_get_fd(io);
+       rfsock_set_buffer(rfsock);
+       rfsock->channel = chan;
+       connections = g_list_append(connections, rfsock);
+
+       return true;
+}
+
 static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
 {
        struct rfcomm_sock *rfsock = data;
-       BtIOSecLevel sec_level = BT_IO_SEC_MEDIUM;
-       GError *gerr = NULL;
        sdp_list_t *list;
-       GIOChannel *io;
        int chan;
 
        DBG("");
@@ -845,101 +1035,146 @@ static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data)
 
        DBG("Got RFCOMM channel %d", chan);
 
-       if (rfsock->profile)
-               sec_level = rfsock->profile->sec_level;
+       if (do_rfcomm_connect(rfsock, chan))
+               return;
+fail:
+       cleanup_rfsock(rfsock);
+}
 
-       io = bt_io_connect(connect_cb, rfsock, NULL, &gerr,
-                               BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
-                               BT_IO_OPT_DEST_BDADDR, &rfsock->dst,
-                               BT_IO_OPT_CHANNEL, chan,
-                               BT_IO_OPT_SEC_LEVEL, sec_level,
-                               BT_IO_OPT_INVALID);
-       if (!io) {
-               error("Failed connect: %s", gerr->message);
-               g_error_free(gerr);
-               goto fail;
-       }
+static uint8_t connect_rfcomm(const bdaddr_t *addr, int chan,
+                                       const uint8_t *uuid, uint8_t flags,
+                                       int *hal_sock)
+{
+       struct rfcomm_sock *rfsock;
+       char address[18];
+       uuid_t uu;
+       char uuid_str[32];
 
-       if (write(rfsock->fd, &chan, sizeof(chan)) != sizeof(chan)) {
-               error("Error sending RFCOMM channel");
-               goto fail;
+       sdp_uuid128_create(&uu, uuid);
+       sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str));
+       ba2str(addr, address);
+
+       DBG("addr %s chan %d flags 0x%02x uuid %s", address, chan, flags,
+                                                               uuid_str);
+
+       if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) ||
+                                               !bacmp(addr, BDADDR_ANY)) {
+               error("Invalid rfcomm connect params");
+               return HAL_STATUS_INVALID;
        }
 
-       rfsock->real_sock = g_io_channel_unix_get_fd(io);
-       rfsock->channel = chan;
-       connections = g_list_append(connections, rfsock);
+       rfsock = create_rfsock(-1, hal_sock);
+       if (!rfsock)
+               return HAL_STATUS_FAILED;
 
-       g_io_channel_unref(io);
+       DBG("rfsock %p jv_sock %d hal_sock %d", rfsock, rfsock->jv_sock,
+                                                       *hal_sock);
 
-       return;
-fail:
-       connections = g_list_remove(connections, rfsock);
+       rfsock->sec_level = get_sec_level(flags);
+
+       bacpy(&rfsock->dst, addr);
+
+       if (!memcmp(uuid, zero_uuid, sizeof(zero_uuid))) {
+               if (!do_rfcomm_connect(rfsock, chan))
+                       goto failed;
+       } else {
+
+               if (bt_search_service(&adapter_addr, &rfsock->dst, &uu,
+                                       sdp_search_cb, rfsock, NULL, 0) < 0) {
+                       error("Failed to search SDP records");
+                       goto failed;
+               }
+       }
+
+       return HAL_STATUS_SUCCESS;
+
+failed:
        cleanup_rfsock(rfsock);
+       close(*hal_sock);
+       return HAL_STATUS_FAILED;
 }
 
 static void handle_connect(const void *buf, uint16_t len)
 {
-       const struct hal_cmd_sock_connect *cmd = buf;
-       struct rfcomm_sock *rfsock;
-       uuid_t uuid;
-       int hal_fd = -1;
+       const struct hal_cmd_socket_connect *cmd = buf;
+       bdaddr_t bdaddr;
+       uint8_t status;
+       int hal_sock;
 
        DBG("");
 
-       rfsock = create_rfsock(-1, &hal_fd);
-       if (!rfsock)
-               goto failed;
-
-       android2bdaddr(cmd->bdaddr, &rfsock->dst);
-
-       memset(&uuid, 0, sizeof(uuid));
-       uuid.type = SDP_UUID128;
-       memcpy(&uuid.value.uuid128, cmd->uuid, sizeof(uint128_t));
-
-       rfsock->profile = get_profile_by_uuid(cmd->uuid);
+       android2bdaddr(cmd->bdaddr, &bdaddr);
+
+       switch (cmd->type) {
+       case HAL_SOCK_RFCOMM:
+               status = connect_rfcomm(&bdaddr, cmd->channel, cmd->uuid,
+                                                       cmd->flags, &hal_sock);
+               break;
+       case HAL_SOCK_SCO:
+       case HAL_SOCK_L2CAP:
+               status = HAL_STATUS_UNSUPPORTED;
+               break;
+       default:
+               status = HAL_STATUS_INVALID;
+               break;
+       }
 
-       if (bt_search_service(&adapter_addr, &rfsock->dst, &uuid,
-                                       sdp_search_cb, rfsock, NULL) < 0) {
-               error("Failed to search SDP records");
-               cleanup_rfsock(rfsock);
+       if (status != HAL_STATUS_SUCCESS)
                goto failed;
-       }
 
-       ipc_send_rsp_full(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT, 0, NULL,
-                                                                       hal_fd);
-       close(hal_fd);
+       ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT,
+                                                       0, NULL, hal_sock);
+       close(hal_sock);
        return;
 
 failed:
-       ipc_send_rsp(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT,
-                                                       HAL_STATUS_FAILED);
+       ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT,
+                                                                       status);
 
-       if (hal_fd >= 0)
-               close(hal_fd);
 }
 
 static const struct ipc_handler cmd_handlers[] = {
-       /* HAL_OP_SOCK_LISTEN */
-       { handle_listen, false, sizeof(struct hal_cmd_sock_listen) },
-       /* HAL_OP_SOCK_CONNECT */
-       { handle_connect, false, sizeof(struct hal_cmd_sock_connect) },
+       /* HAL_OP_SOCKET_LISTEN */
+       { handle_listen, false, sizeof(struct hal_cmd_socket_listen) },
+       /* HAL_OP_SOCKET_CONNECT */
+       { handle_connect, false, sizeof(struct hal_cmd_socket_connect) },
 };
 
-void bt_socket_register(const bdaddr_t *addr)
+void bt_socket_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
 {
+       size_t i;
+
        DBG("");
 
+       /*
+        * make sure channels assigned for profiles are reserved and not used
+        * for app services
+        */
+       for (i = 0; i < G_N_ELEMENTS(profiles); i++)
+               if (profiles[i].channel)
+                       servers[profiles[i].channel].reserved = true;
+
        bacpy(&adapter_addr, addr);
-       ipc_register(HAL_SERVICE_ID_SOCK, cmd_handlers,
+
+       hal_ipc = ipc;
+       ipc_register(hal_ipc, HAL_SERVICE_ID_SOCKET, cmd_handlers,
                                                G_N_ELEMENTS(cmd_handlers));
 }
 
 void bt_socket_unregister(void)
 {
+       int ch;
+
        DBG("");
 
        g_list_free_full(connections, cleanup_rfsock);
-       g_list_free_full(servers, cleanup_rfsock);
 
-       ipc_unregister(HAL_SERVICE_ID_SOCK);
+       for (ch = 0; ch <= RFCOMM_CHANNEL_MAX; ch++)
+               if (servers[ch].rfsock)
+                       cleanup_rfsock(servers[ch].rfsock);
+
+       memset(servers, 0, sizeof(servers));
+
+       ipc_unregister(hal_ipc, HAL_SERVICE_ID_SOCKET);
+       hal_ipc = NULL;
 }
index 5150b89..b0e78c6 100644 (file)
@@ -2,21 +2,21 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
@@ -28,7 +28,5 @@ struct hal_sock_connect_signal {
        int     status;
 } __attribute__((packed));
 
-void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len);
-
-void bt_socket_register(const bdaddr_t *addr);
+void bt_socket_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode);
 void bt_socket_unregister(void);
index 24f2741..c1b1b25 100644 (file)
@@ -2,21 +2,21 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 <sys/param.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <sys/types.h>
+#include <sys/stat.h>
 
 #include "monitor/mainloop.h"
 
 static char exec_dir[PATH_MAX + 1];
 
 static pid_t daemon_pid = -1;
+static pid_t snoop_pid = -1;
 
 static void ctl_start(void)
 {
        char prg_name[PATH_MAX + 1];
-       char *prg_argv[3];
+       char *prg_argv[6];
        char *prg_envp[3];
        pid_t pid;
 
        snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd");
 
        prg_argv[0] = "/usr/bin/valgrind";
-       prg_argv[1] = prg_name;
-       prg_argv[2] = NULL;
+       prg_argv[1] = "--leak-check=full";
+       prg_argv[2] = "--track-origins=yes";
+       prg_argv[3] = prg_name;
+       prg_argv[4] = "-d";
+       prg_argv[5] = NULL;
 
        prg_envp[0] = "G_SLICE=always-malloc";
        prg_envp[1] = "G_DEBUG=gc-friendly";
@@ -79,6 +85,47 @@ static void ctl_start(void)
        daemon_pid = pid;
 }
 
+static void snoop_start(void)
+{
+       char prg_name[PATH_MAX + 1];
+       char *prg_argv[3];
+       char *prg_envp[1];
+       pid_t pid;
+
+       snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir,
+                                                       "bluetoothd-snoop");
+
+       prg_argv[0] = prg_name;
+       prg_argv[1] = "/tmp/btsnoop_hci.log";
+       prg_argv[2] = NULL;
+
+       prg_envp[0] = NULL;
+
+       printf("Starting %s\n", prg_name);
+
+       pid = fork();
+       if (pid < 0) {
+               perror("Failed to fork new process");
+               return;
+       }
+
+       if (pid == 0) {
+               execve(prg_argv[0], prg_argv, prg_envp);
+               exit(0);
+       }
+
+       printf("New process %d created\n", pid);
+
+       snoop_pid = pid;
+}
+
+static void snoop_stop(void)
+{
+       printf("Stoping %s/%s\n", exec_dir, "bluetoothd-snoop");
+
+       kill(snoop_pid, SIGTERM);
+}
+
 static void system_socket_callback(int fd, uint32_t events, void *user_data)
 {
        char buf[4096];
@@ -95,13 +142,20 @@ static void system_socket_callback(int fd, uint32_t events, void *user_data)
 
        printf("Received %s\n", buf);
 
-       if (strcmp(buf, "ctl.start=bluetoothd"))
-               return;
+       if (!strcmp(buf, "bluetooth.start=daemon")) {
+               if (daemon_pid > 0)
+                       return;
 
-       if (daemon_pid > 0)
-               return;
+               ctl_start();
+       } else if (!strcmp(buf, "bluetooth.start=snoop")) {
+               if (snoop_pid > 0)
+                       return;
 
-       ctl_start();
+               snoop_start();
+       } else if (!strcmp(buf, "bluetooth.stop=snoop")) {
+               if (snoop_pid > 0)
+                       snoop_stop();
+       }
 }
 
 static void signal_callback(int signum, void *user_data)
@@ -125,6 +179,8 @@ static void signal_callback(int signum, void *user_data)
 
                        if (pid == daemon_pid)
                                daemon_pid = -1;
+                       else if (pid == snoop_pid)
+                               snoop_pid = -1;
                }
                break;
        }
@@ -132,8 +188,7 @@ static void signal_callback(int signum, void *user_data)
 
 int main(int argc, char *argv[])
 {
-       static const char SYSTEM_SOCKET_PATH[] = "\0android_system";
-
+       const char SYSTEM_SOCKET_PATH[] = "\0android_system";
        sigset_t mask;
        struct sockaddr_un addr;
        int fd;
@@ -169,5 +224,8 @@ int main(int argc, char *argv[])
 
        mainloop_add_fd(fd, EPOLLIN, system_socket_callback, NULL, NULL);
 
+       /* Make sure bluetoothd creates files with proper permissions */
+       umask(0177);
+
        return mainloop_run();
 }
diff --git a/android/system/audio.h b/android/system/audio.h
new file mode 100644 (file)
index 0000000..26a0a3b
--- /dev/null
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_AUDIO_CORE_H
+#define ANDROID_AUDIO_CORE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#define popcount __builtin_popcount
+
+__BEGIN_DECLS
+
+/* The enums were moved here mostly from
+ * frameworks/base/include/media/AudioSystem.h
+ */
+
+/* device address used to refer to the standard remote submix */
+#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0"
+
+typedef int audio_io_handle_t;
+
+/* Audio stream types */
+typedef enum {
+    AUDIO_STREAM_DEFAULT          = -1,
+    AUDIO_STREAM_VOICE_CALL       = 0,
+    AUDIO_STREAM_SYSTEM           = 1,
+    AUDIO_STREAM_RING             = 2,
+    AUDIO_STREAM_MUSIC            = 3,
+    AUDIO_STREAM_ALARM            = 4,
+    AUDIO_STREAM_NOTIFICATION     = 5,
+    AUDIO_STREAM_BLUETOOTH_SCO    = 6,
+    AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user and must be routed to speaker */
+    AUDIO_STREAM_DTMF             = 8,
+    AUDIO_STREAM_TTS              = 9,
+
+    AUDIO_STREAM_CNT,
+    AUDIO_STREAM_MAX              = AUDIO_STREAM_CNT - 1,
+} audio_stream_type_t;
+
+/* Do not change these values without updating their counterparts
+ * in media/java/android/media/MediaRecorder.java!
+ */
+typedef enum {
+    AUDIO_SOURCE_DEFAULT             = 0,
+    AUDIO_SOURCE_MIC                 = 1,
+    AUDIO_SOURCE_VOICE_UPLINK        = 2,
+    AUDIO_SOURCE_VOICE_DOWNLINK      = 3,
+    AUDIO_SOURCE_VOICE_CALL          = 4,
+    AUDIO_SOURCE_CAMCORDER           = 5,
+    AUDIO_SOURCE_VOICE_RECOGNITION   = 6,
+    AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
+    AUDIO_SOURCE_REMOTE_SUBMIX       = 8, /* Source for the mix to be presented remotely.      */
+                                          /* An example of remote presentation is Wifi Display */
+                                          /*  where a dongle attached to a TV can be used to   */
+                                          /*  play the mix captured by this audio source.      */
+    AUDIO_SOURCE_CNT,
+    AUDIO_SOURCE_MAX                 = AUDIO_SOURCE_CNT - 1,
+    AUDIO_SOURCE_HOTWORD             = 1999, /* A low-priority, preemptible audio source for
+                                                for background software hotword detection.
+                                                Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION.
+                                                Used only internally to the framework. Not exposed
+                                                at the audio HAL. */
+} audio_source_t;
+
+/* special audio session values
+ * (XXX: should this be living in the audio effects land?)
+ */
+typedef enum {
+    /* session for effects attached to a particular output stream
+     * (value must be less than 0)
+     */
+    AUDIO_SESSION_OUTPUT_STAGE = -1,
+
+    /* session for effects applied to output mix. These effects can
+     * be moved by audio policy manager to another output stream
+     * (value must be 0)
+     */
+    AUDIO_SESSION_OUTPUT_MIX = 0,
+} audio_session_t;
+
+/* Audio sub formats (see enum audio_format). */
+
+/* PCM sub formats */
+typedef enum {
+    AUDIO_FORMAT_PCM_SUB_16_BIT          = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */
+    AUDIO_FORMAT_PCM_SUB_8_BIT           = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */
+    AUDIO_FORMAT_PCM_SUB_32_BIT          = 0x3, /* PCM signed .31 fixed point */
+    AUDIO_FORMAT_PCM_SUB_8_24_BIT        = 0x4, /* PCM signed 7.24 fixed point */
+} audio_format_pcm_sub_fmt_t;
+
+/* MP3 sub format field definition : can use 11 LSBs in the same way as MP3
+ * frame header to specify bit rate, stereo mode, version...
+ */
+typedef enum {
+    AUDIO_FORMAT_MP3_SUB_NONE            = 0x0,
+} audio_format_mp3_sub_fmt_t;
+
+/* AMR NB/WB sub format field definition: specify frame block interleaving,
+ * bandwidth efficient or octet aligned, encoding mode for recording...
+ */
+typedef enum {
+    AUDIO_FORMAT_AMR_SUB_NONE            = 0x0,
+} audio_format_amr_sub_fmt_t;
+
+/* AAC sub format field definition: specify profile or bitrate for recording... */
+typedef enum {
+    AUDIO_FORMAT_AAC_SUB_NONE            = 0x0,
+} audio_format_aac_sub_fmt_t;
+
+/* VORBIS sub format field definition: specify quality for recording... */
+typedef enum {
+    AUDIO_FORMAT_VORBIS_SUB_NONE         = 0x0,
+} audio_format_vorbis_sub_fmt_t;
+
+/* Audio format consists in a main format field (upper 8 bits) and a sub format
+ * field (lower 24 bits).
+ *
+ * The main format indicates the main codec type. The sub format field
+ * indicates options and parameters for each format. The sub format is mainly
+ * used for record to indicate for instance the requested bitrate or profile.
+ * It can also be used for certain formats to give informations not present in
+ * the encoded audio stream (e.g. octet alignement for AMR).
+ */
+typedef enum {
+    AUDIO_FORMAT_INVALID             = 0xFFFFFFFFUL,
+    AUDIO_FORMAT_DEFAULT             = 0,
+    AUDIO_FORMAT_PCM                 = 0x00000000UL, /* DO NOT CHANGE */
+    AUDIO_FORMAT_MP3                 = 0x01000000UL,
+    AUDIO_FORMAT_AMR_NB              = 0x02000000UL,
+    AUDIO_FORMAT_AMR_WB              = 0x03000000UL,
+    AUDIO_FORMAT_AAC                 = 0x04000000UL,
+    AUDIO_FORMAT_HE_AAC_V1           = 0x05000000UL,
+    AUDIO_FORMAT_HE_AAC_V2           = 0x06000000UL,
+    AUDIO_FORMAT_VORBIS              = 0x07000000UL,
+    AUDIO_FORMAT_MAIN_MASK           = 0xFF000000UL,
+    AUDIO_FORMAT_SUB_MASK            = 0x00FFFFFFUL,
+
+    /* Aliases */
+    AUDIO_FORMAT_PCM_16_BIT          = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_16_BIT),
+    AUDIO_FORMAT_PCM_8_BIT           = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_8_BIT),
+    AUDIO_FORMAT_PCM_32_BIT          = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_32_BIT),
+    AUDIO_FORMAT_PCM_8_24_BIT        = (AUDIO_FORMAT_PCM |
+                                        AUDIO_FORMAT_PCM_SUB_8_24_BIT),
+} audio_format_t;
+
+enum {
+    /* output channels */
+    AUDIO_CHANNEL_OUT_FRONT_LEFT            = 0x1,
+    AUDIO_CHANNEL_OUT_FRONT_RIGHT           = 0x2,
+    AUDIO_CHANNEL_OUT_FRONT_CENTER          = 0x4,
+    AUDIO_CHANNEL_OUT_LOW_FREQUENCY         = 0x8,
+    AUDIO_CHANNEL_OUT_BACK_LEFT             = 0x10,
+    AUDIO_CHANNEL_OUT_BACK_RIGHT            = 0x20,
+    AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER  = 0x40,
+    AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80,
+    AUDIO_CHANNEL_OUT_BACK_CENTER           = 0x100,
+    AUDIO_CHANNEL_OUT_SIDE_LEFT             = 0x200,
+    AUDIO_CHANNEL_OUT_SIDE_RIGHT            = 0x400,
+    AUDIO_CHANNEL_OUT_TOP_CENTER            = 0x800,
+    AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT        = 0x1000,
+    AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER      = 0x2000,
+    AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT       = 0x4000,
+    AUDIO_CHANNEL_OUT_TOP_BACK_LEFT         = 0x8000,
+    AUDIO_CHANNEL_OUT_TOP_BACK_CENTER       = 0x10000,
+    AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT        = 0x20000,
+
+    AUDIO_CHANNEL_OUT_MONO     = AUDIO_CHANNEL_OUT_FRONT_LEFT,
+    AUDIO_CHANNEL_OUT_STEREO   = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT),
+    AUDIO_CHANNEL_OUT_QUAD     = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_BACK_LEFT |
+                                  AUDIO_CHANNEL_OUT_BACK_RIGHT),
+    AUDIO_CHANNEL_OUT_SURROUND = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_CENTER |
+                                  AUDIO_CHANNEL_OUT_BACK_CENTER),
+    AUDIO_CHANNEL_OUT_5POINT1  = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_CENTER |
+                                  AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+                                  AUDIO_CHANNEL_OUT_BACK_LEFT |
+                                  AUDIO_CHANNEL_OUT_BACK_RIGHT),
+    // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1
+    AUDIO_CHANNEL_OUT_7POINT1  = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_CENTER |
+                                  AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+                                  AUDIO_CHANNEL_OUT_BACK_LEFT |
+                                  AUDIO_CHANNEL_OUT_BACK_RIGHT |
+                                  AUDIO_CHANNEL_OUT_SIDE_LEFT |
+                                  AUDIO_CHANNEL_OUT_SIDE_RIGHT),
+    AUDIO_CHANNEL_OUT_ALL      = (AUDIO_CHANNEL_OUT_FRONT_LEFT |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_CENTER |
+                                  AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
+                                  AUDIO_CHANNEL_OUT_BACK_LEFT |
+                                  AUDIO_CHANNEL_OUT_BACK_RIGHT |
+                                  AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER |
+                                  AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER |
+                                  AUDIO_CHANNEL_OUT_BACK_CENTER|
+                                  AUDIO_CHANNEL_OUT_SIDE_LEFT|
+                                  AUDIO_CHANNEL_OUT_SIDE_RIGHT|
+                                  AUDIO_CHANNEL_OUT_TOP_CENTER|
+                                  AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT|
+                                  AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER|
+                                  AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT|
+                                  AUDIO_CHANNEL_OUT_TOP_BACK_LEFT|
+                                  AUDIO_CHANNEL_OUT_TOP_BACK_CENTER|
+                                  AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT),
+
+    /* input channels */
+    AUDIO_CHANNEL_IN_LEFT            = 0x4,
+    AUDIO_CHANNEL_IN_RIGHT           = 0x8,
+    AUDIO_CHANNEL_IN_FRONT           = 0x10,
+    AUDIO_CHANNEL_IN_BACK            = 0x20,
+    AUDIO_CHANNEL_IN_LEFT_PROCESSED  = 0x40,
+    AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80,
+    AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100,
+    AUDIO_CHANNEL_IN_BACK_PROCESSED  = 0x200,
+    AUDIO_CHANNEL_IN_PRESSURE        = 0x400,
+    AUDIO_CHANNEL_IN_X_AXIS          = 0x800,
+    AUDIO_CHANNEL_IN_Y_AXIS          = 0x1000,
+    AUDIO_CHANNEL_IN_Z_AXIS          = 0x2000,
+    AUDIO_CHANNEL_IN_VOICE_UPLINK    = 0x4000,
+    AUDIO_CHANNEL_IN_VOICE_DNLINK    = 0x8000,
+
+    AUDIO_CHANNEL_IN_MONO   = AUDIO_CHANNEL_IN_FRONT,
+    AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT),
+    AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK),
+    AUDIO_CHANNEL_IN_ALL    = (AUDIO_CHANNEL_IN_LEFT |
+                               AUDIO_CHANNEL_IN_RIGHT |
+                               AUDIO_CHANNEL_IN_FRONT |
+                               AUDIO_CHANNEL_IN_BACK|
+                               AUDIO_CHANNEL_IN_LEFT_PROCESSED |
+                               AUDIO_CHANNEL_IN_RIGHT_PROCESSED |
+                               AUDIO_CHANNEL_IN_FRONT_PROCESSED |
+                               AUDIO_CHANNEL_IN_BACK_PROCESSED|
+                               AUDIO_CHANNEL_IN_PRESSURE |
+                               AUDIO_CHANNEL_IN_X_AXIS |
+                               AUDIO_CHANNEL_IN_Y_AXIS |
+                               AUDIO_CHANNEL_IN_Z_AXIS |
+                               AUDIO_CHANNEL_IN_VOICE_UPLINK |
+                               AUDIO_CHANNEL_IN_VOICE_DNLINK),
+};
+
+typedef uint32_t audio_channel_mask_t;
+
+typedef enum {
+    AUDIO_MODE_INVALID          = -2,
+    AUDIO_MODE_CURRENT          = -1,
+    AUDIO_MODE_NORMAL           = 0,
+    AUDIO_MODE_RINGTONE         = 1,
+    AUDIO_MODE_IN_CALL          = 2,
+    AUDIO_MODE_IN_COMMUNICATION = 3,
+
+    AUDIO_MODE_CNT,
+    AUDIO_MODE_MAX              = AUDIO_MODE_CNT - 1,
+} audio_mode_t;
+
+typedef enum {
+    AUDIO_IN_ACOUSTICS_AGC_ENABLE    = 0x0001,
+    AUDIO_IN_ACOUSTICS_AGC_DISABLE   = 0,
+    AUDIO_IN_ACOUSTICS_NS_ENABLE     = 0x0002,
+    AUDIO_IN_ACOUSTICS_NS_DISABLE    = 0,
+    AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE = 0x0004,
+    AUDIO_IN_ACOUSTICS_TX_DISABLE    = 0,
+} audio_in_acoustics_t;
+
+enum {
+    AUDIO_DEVICE_NONE                          = 0x0,
+    /* reserved bits */
+    AUDIO_DEVICE_BIT_IN                        = 0x80000000,
+    AUDIO_DEVICE_BIT_DEFAULT                   = 0x40000000,
+    /* output devices */
+    AUDIO_DEVICE_OUT_EARPIECE                  = 0x1,
+    AUDIO_DEVICE_OUT_SPEAKER                   = 0x2,
+    AUDIO_DEVICE_OUT_WIRED_HEADSET             = 0x4,
+    AUDIO_DEVICE_OUT_WIRED_HEADPHONE           = 0x8,
+    AUDIO_DEVICE_OUT_BLUETOOTH_SCO             = 0x10,
+    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET     = 0x20,
+    AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT      = 0x40,
+    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP            = 0x80,
+    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
+    AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER    = 0x200,
+    AUDIO_DEVICE_OUT_AUX_DIGITAL               = 0x400,
+    AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET         = 0x800,
+    AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET         = 0x1000,
+    AUDIO_DEVICE_OUT_USB_ACCESSORY             = 0x2000,
+    AUDIO_DEVICE_OUT_USB_DEVICE                = 0x4000,
+    AUDIO_DEVICE_OUT_REMOTE_SUBMIX             = 0x8000,
+    AUDIO_DEVICE_OUT_DEFAULT                   = AUDIO_DEVICE_BIT_DEFAULT,
+    AUDIO_DEVICE_OUT_ALL      = (AUDIO_DEVICE_OUT_EARPIECE |
+                                 AUDIO_DEVICE_OUT_SPEAKER |
+                                 AUDIO_DEVICE_OUT_WIRED_HEADSET |
+                                 AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER |
+                                 AUDIO_DEVICE_OUT_AUX_DIGITAL |
+                                 AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET |
+                                 AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET |
+                                 AUDIO_DEVICE_OUT_USB_ACCESSORY |
+                                 AUDIO_DEVICE_OUT_USB_DEVICE |
+                                 AUDIO_DEVICE_OUT_REMOTE_SUBMIX |
+                                 AUDIO_DEVICE_OUT_DEFAULT),
+    AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER),
+    AUDIO_DEVICE_OUT_ALL_SCO  = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+                                 AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT),
+    AUDIO_DEVICE_OUT_ALL_USB  = (AUDIO_DEVICE_OUT_USB_ACCESSORY |
+                                 AUDIO_DEVICE_OUT_USB_DEVICE),
+
+    /* input devices */
+    AUDIO_DEVICE_IN_COMMUNICATION         = AUDIO_DEVICE_BIT_IN | 0x1,
+    AUDIO_DEVICE_IN_AMBIENT               = AUDIO_DEVICE_BIT_IN | 0x2,
+    AUDIO_DEVICE_IN_BUILTIN_MIC           = AUDIO_DEVICE_BIT_IN | 0x4,
+    AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8,
+    AUDIO_DEVICE_IN_WIRED_HEADSET         = AUDIO_DEVICE_BIT_IN | 0x10,
+    AUDIO_DEVICE_IN_AUX_DIGITAL           = AUDIO_DEVICE_BIT_IN | 0x20,
+    AUDIO_DEVICE_IN_VOICE_CALL            = AUDIO_DEVICE_BIT_IN | 0x40,
+    AUDIO_DEVICE_IN_BACK_MIC              = AUDIO_DEVICE_BIT_IN | 0x80,
+    AUDIO_DEVICE_IN_REMOTE_SUBMIX         = AUDIO_DEVICE_BIT_IN | 0x100,
+    AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET     = AUDIO_DEVICE_BIT_IN | 0x200,
+    AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET     = AUDIO_DEVICE_BIT_IN | 0x400,
+    AUDIO_DEVICE_IN_USB_ACCESSORY         = AUDIO_DEVICE_BIT_IN | 0x800,
+    AUDIO_DEVICE_IN_USB_DEVICE            = AUDIO_DEVICE_BIT_IN | 0x1000,
+    AUDIO_DEVICE_IN_DEFAULT               = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT,
+
+    AUDIO_DEVICE_IN_ALL     = (AUDIO_DEVICE_IN_COMMUNICATION |
+                               AUDIO_DEVICE_IN_AMBIENT |
+                               AUDIO_DEVICE_IN_BUILTIN_MIC |
+                               AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET |
+                               AUDIO_DEVICE_IN_WIRED_HEADSET |
+                               AUDIO_DEVICE_IN_AUX_DIGITAL |
+                               AUDIO_DEVICE_IN_VOICE_CALL |
+                               AUDIO_DEVICE_IN_BACK_MIC |
+                               AUDIO_DEVICE_IN_REMOTE_SUBMIX |
+                               AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET |
+                               AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET |
+                               AUDIO_DEVICE_IN_USB_ACCESSORY |
+                               AUDIO_DEVICE_IN_USB_DEVICE |
+                               AUDIO_DEVICE_IN_DEFAULT),
+    AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET,
+};
+
+typedef uint32_t audio_devices_t;
+
+/* the audio output flags serve two purposes:
+ * - when an AudioTrack is created they indicate a "wish" to be connected to an
+ * output stream with attributes corresponding to the specified flags
+ * - when present in an output profile descriptor listed for a particular audio
+ * hardware module, they indicate that an output stream can be opened that
+ * supports the attributes indicated by the flags.
+ * the audio policy manager will try to match the flags in the request
+ * (when getOuput() is called) to an available output stream.
+ */
+typedef enum {
+    AUDIO_OUTPUT_FLAG_NONE = 0x0,       // no attributes
+    AUDIO_OUTPUT_FLAG_DIRECT = 0x1,     // this output directly connects a track
+                                        // to one output stream: no software mixer
+    AUDIO_OUTPUT_FLAG_PRIMARY = 0x2,    // this output is the primary output of
+                                        // the device. It is unique and must be
+                                        // present. It is opened by default and
+                                        // receives routing, audio mode and volume
+                                        // controls related to voice calls.
+    AUDIO_OUTPUT_FLAG_FAST = 0x4,       // output supports "fast tracks",
+                                        // defined elsewhere
+    AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers
+    AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10,  // offload playback of compressed
+                                                // streams to hardware codec
+    AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20 // use non-blocking write
+} audio_output_flags_t;
+
+/* The audio input flags are analogous to audio output flags.
+ * Currently they are used only when an AudioRecord is created,
+ * to indicate a preference to be connected to an input stream with
+ * attributes corresponding to the specified flags.
+ */
+typedef enum {
+    AUDIO_INPUT_FLAG_NONE = 0x0,        // no attributes
+    AUDIO_INPUT_FLAG_FAST = 0x1,        // prefer an input that supports "fast tracks"
+} audio_input_flags_t;
+
+/* Additional information about compressed streams offloaded to
+ * hardware playback
+ * The version and size fields must be initialized by the caller by using
+ * one of the constants defined here.
+ */
+typedef struct {
+    uint16_t version;                   // version of the info structure
+    uint16_t size;                      // total size of the structure including version and size
+    uint32_t sample_rate;               // sample rate in Hz
+    audio_channel_mask_t channel_mask;  // channel mask
+    audio_format_t format;              // audio format
+    audio_stream_type_t stream_type;    // stream type
+    uint32_t bit_rate;                  // bit rate in bits per second
+    int64_t duration_us;                // duration in microseconds, -1 if unknown
+    bool has_video;                     // true if stream is tied to a video stream
+    bool is_streaming;                  // true if streaming, false if local playback
+} audio_offload_info_t;
+
+#define AUDIO_MAKE_OFFLOAD_INFO_VERSION(maj,min) \
+            ((((maj) & 0xff) << 8) | ((min) & 0xff))
+
+#define AUDIO_OFFLOAD_INFO_VERSION_0_1 AUDIO_MAKE_OFFLOAD_INFO_VERSION(0, 1)
+#define AUDIO_OFFLOAD_INFO_VERSION_CURRENT AUDIO_OFFLOAD_INFO_VERSION_0_1
+
+static const audio_offload_info_t AUDIO_INFO_INITIALIZER = {
+    version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT,
+    size: sizeof(audio_offload_info_t),
+};
+
+static inline bool audio_is_output_device(audio_devices_t device)
+{
+    if (((device & AUDIO_DEVICE_BIT_IN) == 0) &&
+            (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0))
+        return true;
+    else
+        return false;
+}
+
+static inline bool audio_is_input_device(audio_devices_t device)
+{
+    if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
+        device &= ~AUDIO_DEVICE_BIT_IN;
+        if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0))
+            return true;
+    }
+    return false;
+}
+
+static inline bool audio_is_output_devices(audio_devices_t device)
+{
+    return (device & AUDIO_DEVICE_BIT_IN) == 0;
+}
+
+
+static inline bool audio_is_a2dp_device(audio_devices_t device)
+{
+    if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP))
+        return true;
+    else
+        return false;
+}
+
+static inline bool audio_is_bluetooth_sco_device(audio_devices_t device)
+{
+    device &= ~AUDIO_DEVICE_BIT_IN;
+    if ((popcount(device) == 1) && (device & (AUDIO_DEVICE_OUT_ALL_SCO |
+                   AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET)))
+        return true;
+    else
+        return false;
+}
+
+static inline bool audio_is_usb_device(audio_devices_t device)
+{
+    if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB))
+        return true;
+    else
+        return false;
+}
+
+static inline bool audio_is_remote_submix_device(audio_devices_t device)
+{
+    if ((device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX
+            || (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX)
+        return true;
+    else
+        return false;
+}
+
+static inline bool audio_is_input_channel(audio_channel_mask_t channel)
+{
+    if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0)
+        return channel != 0;
+    else
+        return false;
+}
+
+static inline bool audio_is_output_channel(audio_channel_mask_t channel)
+{
+    if ((channel & ~AUDIO_CHANNEL_OUT_ALL) == 0)
+        return channel != 0;
+    else
+        return false;
+}
+
+/* Derive an output channel mask from a channel count.
+ * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel
+ * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad,
+ * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC
+ * for continuity with stereo.
+ * Returns the matching channel mask, or 0 if the number of channels exceeds that of the
+ * configurations for which a default channel mask is defined.
+ */
+static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count)
+{
+    switch(channel_count) {
+    case 1:
+        return AUDIO_CHANNEL_OUT_MONO;
+    case 2:
+        return AUDIO_CHANNEL_OUT_STEREO;
+    case 3:
+        return (AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER);
+    case 4: // 4.0
+        return AUDIO_CHANNEL_OUT_QUAD;
+    case 5: // 5.0
+        return (AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER);
+    case 6: // 5.1
+        return AUDIO_CHANNEL_OUT_5POINT1;
+    case 7: // 6.1
+        return (AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER);
+    case 8:
+        return AUDIO_CHANNEL_OUT_7POINT1;
+    default:
+        return 0;
+    }
+}
+
+/* Similar to above, but for input.  Currently handles only mono and stereo. */
+static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count)
+{
+    switch (channel_count) {
+    case 1:
+        return AUDIO_CHANNEL_IN_MONO;
+    case 2:
+        return AUDIO_CHANNEL_IN_STEREO;
+    default:
+        return 0;
+    }
+}
+
+static inline bool audio_is_valid_format(audio_format_t format)
+{
+    switch (format & AUDIO_FORMAT_MAIN_MASK) {
+    case AUDIO_FORMAT_PCM:
+        if (format != AUDIO_FORMAT_PCM_16_BIT &&
+                format != AUDIO_FORMAT_PCM_8_BIT) {
+            return false;
+        }
+    case AUDIO_FORMAT_MP3:
+    case AUDIO_FORMAT_AMR_NB:
+    case AUDIO_FORMAT_AMR_WB:
+    case AUDIO_FORMAT_AAC:
+    case AUDIO_FORMAT_HE_AAC_V1:
+    case AUDIO_FORMAT_HE_AAC_V2:
+    case AUDIO_FORMAT_VORBIS:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static inline bool audio_is_linear_pcm(audio_format_t format)
+{
+    return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM);
+}
+
+static inline size_t audio_bytes_per_sample(audio_format_t format)
+{
+    size_t size = 0;
+
+    switch (format) {
+    case AUDIO_FORMAT_PCM_32_BIT:
+    case AUDIO_FORMAT_PCM_8_24_BIT:
+        size = sizeof(int32_t);
+        break;
+    case AUDIO_FORMAT_PCM_16_BIT:
+        size = sizeof(int16_t);
+        break;
+    case AUDIO_FORMAT_PCM_8_BIT:
+        size = sizeof(uint8_t);
+        break;
+    default:
+        break;
+    }
+    return size;
+}
+
+__END_DECLS
+
+#endif  // ANDROID_AUDIO_CORE_H
diff --git a/android/test-ipc.c b/android/test-ipc.c
new file mode 100644 (file)
index 0000000..bb7d15f
--- /dev/null
@@ -0,0 +1,577 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <glib.h>
+#include "src/shared/util.h"
+#include "src/log.h"
+#include "android/ipc-common.h"
+#include "android/ipc.h"
+
+static const char HAL_SK_PATH[] = "\0test_hal_socket";
+
+#define SERVICE_ID_MAX 10
+
+struct test_data {
+       bool disconnect;
+       const void *cmd;
+       uint16_t cmd_size;
+       uint8_t service;
+       const struct ipc_handler *handlers;
+       uint8_t handlers_size;
+};
+
+struct context {
+       GMainLoop *main_loop;
+
+       int sk;
+
+       guint source;
+       guint cmd_source;
+       guint notif_source;
+
+       GIOChannel *cmd_io;
+       GIOChannel *notif_io;
+
+       const struct test_data *data;
+};
+
+
+static struct ipc *ipc = NULL;
+
+static void context_quit(struct context *context)
+{
+       g_main_loop_quit(context->main_loop);
+}
+
+static gboolean cmd_watch(GIOChannel *io, GIOCondition cond,
+                                               gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_data *test_data = context->data;
+       const struct ipc_hdr *sent_msg = test_data->cmd;
+       uint8_t buf[128];
+       int sk;
+
+       struct ipc_hdr success_resp = {
+               .service_id = sent_msg->service_id,
+               .opcode = sent_msg->opcode,
+               .len = 0,
+       };
+
+       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+               g_assert(test_data->disconnect);
+               return FALSE;
+       }
+
+       g_assert(!test_data->disconnect);
+
+       sk = g_io_channel_unix_get_fd(io);
+
+       g_assert(read(sk, buf, sizeof(buf)) == sizeof(struct ipc_hdr));
+       g_assert(!memcmp(&success_resp, buf, sizeof(struct ipc_hdr)));
+
+       context_quit(context);
+
+       return TRUE;
+}
+
+static gboolean notif_watch(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_data *test_data = context->data;
+
+       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+               g_assert(test_data->disconnect);
+               return FALSE;
+       }
+
+       g_assert(!test_data->disconnect);
+
+       return TRUE;
+}
+
+static gboolean connect_handler(GIOChannel *io, GIOCondition cond,
+                                               gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_data *test_data = context->data;
+       GIOChannel *new_io;
+       GIOCondition watch_cond;
+       int sk;
+
+       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+               g_assert(FALSE);
+               return FALSE;
+       }
+
+       g_assert(!context->cmd_source || !context->notif_source);
+
+       sk = accept(context->sk, NULL, NULL);
+       g_assert(sk >= 0);
+
+       new_io = g_io_channel_unix_new(sk);
+
+       watch_cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+
+       if (context->cmd_source && !context->notif_source) {
+               context->notif_source = g_io_add_watch(new_io, watch_cond,
+                                                       notif_watch, context);
+               g_assert(context->notif_source > 0);
+               context->notif_io = new_io;
+       }
+
+       if (!context->cmd_source) {
+               context->cmd_source = g_io_add_watch(new_io, watch_cond,
+                                                       cmd_watch, context);
+               context->cmd_io = new_io;
+       }
+
+       if (context->cmd_source && context->notif_source && !test_data->cmd)
+               context_quit(context);
+
+       return TRUE;
+}
+
+static struct context *create_context(gconstpointer data)
+{
+       struct context *context = g_new0(struct context, 1);
+       struct sockaddr_un addr;
+       GIOChannel *io;
+       int ret, sk;
+
+       context->main_loop = g_main_loop_new(NULL, FALSE);
+       g_assert(context->main_loop);
+
+       sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
+       g_assert(sk >= 0);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+
+       memcpy(addr.sun_path, HAL_SK_PATH, sizeof(HAL_SK_PATH));
+
+       ret = bind(sk, (struct sockaddr *) &addr, sizeof(addr));
+       g_assert(ret == 0);
+
+       ret = listen(sk, 5);
+       g_assert(ret == 0);
+
+       io = g_io_channel_unix_new(sk);
+
+       g_io_channel_set_close_on_unref(io, TRUE);
+
+       context->source = g_io_add_watch(io,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               connect_handler, context);
+       g_assert(context->source > 0);
+
+       g_io_channel_unref(io);
+
+       context->sk = sk;
+       context->data = data;
+
+       return context;
+}
+
+static void execute_context(struct context *context)
+{
+       g_main_loop_run(context->main_loop);
+
+       g_io_channel_shutdown(context->notif_io, TRUE, NULL);
+       g_io_channel_shutdown(context->cmd_io, TRUE, NULL);
+       g_io_channel_unref(context->cmd_io);
+       g_io_channel_unref(context->notif_io);
+
+       g_source_remove(context->notif_source);
+       g_source_remove(context->cmd_source);
+       g_source_remove(context->source);
+
+       g_main_loop_unref(context->main_loop);
+
+       g_free(context);
+}
+
+static void disconnected(void *data)
+{
+       struct context *context = data;
+
+       g_assert(context->data->disconnect);
+
+       context_quit(context);
+}
+
+static void test_init(gconstpointer data)
+{
+       struct context *context = create_context(data);
+
+       ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX,
+                                               true, NULL, NULL);
+
+       g_assert(ipc);
+
+       execute_context(context);
+
+       ipc_cleanup(ipc);
+       ipc = NULL;
+}
+
+static gboolean send_cmd(gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_data *test_data = context->data;
+       int sk;
+
+       sk = g_io_channel_unix_get_fd(context->cmd_io);
+       g_assert(sk >= 0);
+
+       g_assert(write(sk, test_data->cmd, test_data->cmd_size) ==
+                                               test_data->cmd_size);
+
+       return FALSE;
+}
+
+static gboolean register_service(gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_data *test_data = context->data;
+
+       ipc_register(ipc, test_data->service, test_data->handlers,
+                                               test_data->handlers_size);
+
+       return FALSE;
+}
+
+static gboolean unregister_service(gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_data *test_data = context->data;
+
+       ipc_unregister(ipc, test_data->service);
+
+       return FALSE;
+}
+
+static void test_cmd(gconstpointer data)
+{
+       struct context *context = create_context(data);
+
+       ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX,
+                                       true, disconnected, context);
+
+       g_assert(ipc);
+
+       g_idle_add(send_cmd, context);
+
+       execute_context(context);
+
+       ipc_cleanup(ipc);
+       ipc = NULL;
+}
+
+static void test_cmd_reg(gconstpointer data)
+{
+       struct context *context = create_context(data);
+       const struct test_data *test_data = context->data;
+
+       ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX,
+                                       true, disconnected, context);
+
+       g_assert(ipc);
+
+       g_idle_add(register_service, context);
+       g_idle_add(send_cmd, context);
+
+       execute_context(context);
+
+       ipc_unregister(ipc, test_data->service);
+
+       ipc_cleanup(ipc);
+       ipc = NULL;
+}
+
+static void test_cmd_reg_1(gconstpointer data)
+{
+       struct context *context = create_context(data);
+
+       ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX,
+                                       true, disconnected, context);
+
+       g_assert(ipc);
+
+       g_idle_add(register_service, context);
+       g_idle_add(unregister_service, context);
+       g_idle_add(send_cmd, context);
+
+       execute_context(context);
+
+       ipc_cleanup(ipc);
+       ipc = NULL;
+}
+
+static void test_cmd_handler_1(const void *buf, uint16_t len)
+{
+       ipc_send_rsp(ipc, 0, 1, 0);
+}
+
+static void test_cmd_handler_2(const void *buf, uint16_t len)
+{
+       ipc_send_rsp(ipc, 0, 2, 0);
+}
+
+static void test_cmd_handler_invalid(const void *buf, uint16_t len)
+{
+       g_assert(false);
+}
+
+static const struct test_data test_init_1 = {};
+
+static const struct ipc_hdr test_cmd_1_hdr = {
+       .service_id = 0,
+       .opcode = 1,
+       .len = 0
+};
+
+static const struct ipc_hdr test_cmd_2_hdr = {
+       .service_id = 0,
+       .opcode = 2,
+       .len = 0
+};
+
+static const struct test_data test_cmd_service_invalid_1 = {
+       .cmd = &test_cmd_1_hdr,
+       .cmd_size = sizeof(test_cmd_1_hdr),
+       .disconnect = true,
+};
+
+static const struct ipc_handler cmd_handlers[] = {
+       { test_cmd_handler_1, false, 0 }
+};
+
+static const struct test_data test_cmd_service_valid_1 = {
+       .cmd = &test_cmd_1_hdr,
+       .cmd_size = sizeof(test_cmd_1_hdr),
+       .service = 0,
+       .handlers = cmd_handlers,
+       .handlers_size = 1
+};
+
+static const struct test_data test_cmd_service_invalid_2 = {
+       .cmd = &test_cmd_1_hdr,
+       .cmd_size = sizeof(test_cmd_1_hdr),
+       .service = 0,
+       .handlers = cmd_handlers,
+       .handlers_size = 1,
+       .disconnect = true,
+};
+
+static const struct ipc_handler cmd_handlers_invalid_2[] = {
+       { test_cmd_handler_1, false, 0 },
+       { test_cmd_handler_invalid, false, 0 }
+};
+
+static const struct ipc_handler cmd_handlers_invalid_1[] = {
+       { test_cmd_handler_invalid, false, 0 },
+       { test_cmd_handler_2, false, 0 },
+};
+
+static const struct test_data test_cmd_opcode_valid_1 = {
+       .cmd = &test_cmd_1_hdr,
+       .cmd_size = sizeof(test_cmd_1_hdr),
+       .service = 0,
+       .handlers = cmd_handlers_invalid_2,
+       .handlers_size = 2,
+};
+
+static const struct test_data test_cmd_opcode_valid_2 = {
+       .cmd = &test_cmd_2_hdr,
+       .cmd_size = sizeof(test_cmd_2_hdr),
+       .service = 0,
+       .handlers = cmd_handlers_invalid_1,
+       .handlers_size = 2,
+};
+
+static const struct test_data test_cmd_opcode_invalid_1 = {
+       .cmd = &test_cmd_2_hdr,
+       .cmd_size = sizeof(test_cmd_2_hdr),
+       .service = 0,
+       .handlers = cmd_handlers,
+       .handlers_size = 1,
+       .disconnect = true,
+};
+
+static const struct test_data test_cmd_hdr_invalid = {
+       .cmd = &test_cmd_1_hdr,
+       .cmd_size = sizeof(test_cmd_1_hdr) - 1,
+       .service = 0,
+       .handlers = cmd_handlers,
+       .handlers_size = 1,
+       .disconnect = true,
+};
+
+#define VARDATA_EX1 "some data example"
+
+struct vardata {
+       struct ipc_hdr hdr;
+       uint8_t data[IPC_MTU - sizeof(struct ipc_hdr)];
+} __attribute__((packed));
+
+static const struct vardata test_cmd_vardata = {
+       .hdr.service_id = 0,
+       .hdr.opcode = 1,
+       .hdr.len = sizeof(VARDATA_EX1),
+       .data = VARDATA_EX1,
+};
+
+static const struct ipc_handler cmd_vardata_handlers[] = {
+       { test_cmd_handler_1, true, sizeof(VARDATA_EX1) }
+};
+
+static const struct test_data test_cmd_vardata_valid = {
+       .cmd = &test_cmd_vardata,
+       .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1),
+       .service = 0,
+       .handlers = cmd_vardata_handlers,
+       .handlers_size = 1,
+};
+
+static const struct ipc_handler cmd_vardata_handlers_valid2[] = {
+       { test_cmd_handler_1, true, sizeof(VARDATA_EX1) - 1 }
+};
+
+static const struct test_data test_cmd_vardata_valid_2 = {
+       .cmd = &test_cmd_vardata,
+       .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1),
+       .service = 0,
+       .handlers = cmd_vardata_handlers_valid2,
+       .handlers_size = 1,
+};
+
+static const struct test_data test_cmd_vardata_invalid_1 = {
+       .cmd = &test_cmd_vardata,
+       .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1) - 1,
+       .service = 0,
+       .handlers = cmd_vardata_handlers,
+       .handlers_size = 1,
+       .disconnect = true,
+};
+
+static const struct ipc_hdr test_cmd_service_offrange_hdr = {
+       .service_id = SERVICE_ID_MAX + 1,
+       .opcode = 1,
+       .len = 0
+};
+
+static const struct test_data test_cmd_service_offrange = {
+       .cmd = &test_cmd_service_offrange_hdr,
+       .cmd_size = sizeof(struct ipc_hdr),
+       .service = 0,
+       .handlers = cmd_handlers,
+       .handlers_size = 1,
+       .disconnect = true,
+};
+
+static const struct vardata test_cmd_invalid_data_1 = {
+       .hdr.service_id = 0,
+       .hdr.opcode = 1,
+       .hdr.len = sizeof(VARDATA_EX1),
+       .data = VARDATA_EX1,
+};
+
+static const struct test_data test_cmd_msg_invalid_1 = {
+       .cmd = &test_cmd_invalid_data_1,
+       .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1) - 1,
+       .service = 0,
+       .handlers = cmd_handlers,
+       .handlers_size = 1,
+       .disconnect = true,
+};
+
+static const struct vardata test_cmd_invalid_data_2 = {
+       .hdr.service_id = 0,
+       .hdr.opcode = 1,
+       .hdr.len = sizeof(VARDATA_EX1) - 1,
+       .data = VARDATA_EX1,
+};
+
+static const struct test_data test_cmd_msg_invalid_2 = {
+       .cmd = &test_cmd_invalid_data_2,
+       .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1),
+       .service = 0,
+       .handlers = cmd_handlers,
+       .handlers_size = 1,
+       .disconnect = true,
+};
+
+int main(int argc, char *argv[])
+{
+       g_test_init(&argc, &argv, NULL);
+
+       if (g_test_verbose())
+               __btd_log_init("*", 0);
+
+       g_test_add_data_func("/android_ipc/init", &test_init_1, test_init);
+       g_test_add_data_func("/android_ipc/service_invalid_1",
+                               &test_cmd_service_invalid_1, test_cmd);
+       g_test_add_data_func("/android_ipc/service_valid_1",
+                               &test_cmd_service_valid_1, test_cmd_reg);
+       g_test_add_data_func("/android_ipc/service_invalid_2",
+                               &test_cmd_service_invalid_2, test_cmd_reg_1);
+       g_test_add_data_func("/android_ipc/opcode_valid_1",
+                               &test_cmd_opcode_valid_1, test_cmd_reg);
+       g_test_add_data_func("/android_ipc/opcode_valid_2",
+                               &test_cmd_opcode_valid_2, test_cmd_reg);
+       g_test_add_data_func("/android_ipc/opcode_invalid_1",
+                               &test_cmd_opcode_invalid_1, test_cmd_reg);
+       g_test_add_data_func("/android_ipc/vardata_valid",
+                               &test_cmd_vardata_valid, test_cmd_reg);
+       g_test_add_data_func("/android_ipc/vardata_valid_2",
+                               &test_cmd_vardata_valid_2, test_cmd_reg);
+       g_test_add_data_func("/android_ipc/vardata_invalid_1",
+                               &test_cmd_vardata_invalid_1, test_cmd_reg);
+       g_test_add_data_func("/android_ipc/service_offrange",
+                               &test_cmd_service_offrange, test_cmd_reg);
+       g_test_add_data_func("/android_ipc/hdr_invalid",
+                               &test_cmd_hdr_invalid, test_cmd_reg);
+       g_test_add_data_func("/android_ipc/msg_invalid_1",
+                               &test_cmd_msg_invalid_1, test_cmd_reg);
+       g_test_add_data_func("/android_ipc/msg_invalid_2",
+                               &test_cmd_msg_invalid_2, test_cmd_reg);
+
+       return g_test_run();
+}
index 5b009bc..560e991 100644 (file)
@@ -2,26 +2,25 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2013  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2013-2014  Intel Corporation. All rights reserved.
  *
  *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
+ *  This 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
-
 static inline void android2bdaddr(const void *buf, bdaddr_t *dst)
 {
        baswap(dst, buf);
index 753c753..8e9c06d 100644 (file)
 
 #include <glib.h>
 
+#include "src/shared/util.h"
 #include "lib/uuid.h"
 #include "att.h"
 
+static inline void put_uuid_le(const bt_uuid_t *src, void *dst)
+{
+       if (src->type == BT_UUID16)
+               put_le16(src->value.u16, dst);
+       else
+               /* Convert from 128-bit BE to LE */
+               bswap_128(&src->value.u128, dst);
+}
+
 const char *att_ecode2str(uint8_t status)
 {
        switch (status)  {
@@ -120,38 +130,51 @@ struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len)
        return list;
 }
 
+static void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid)
+{
+       if (type == BT_UUID16)
+               bt_uuid16_create(uuid, get_le16(val));
+       else {
+               uint128_t u128;
+
+               /* Convert from 128-bit LE to BE */
+               bswap_128(val, &u128);
+               bt_uuid128_create(uuid, u128);
+       }
+}
+
 uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
                                                uint8_t *pdu, size_t len)
 {
-       uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
-       uint16_t length;
+       uint16_t uuid_len;
 
        if (!uuid)
                return 0;
 
        if (uuid->type == BT_UUID16)
-               length = 2;
+               uuid_len = 2;
        else if (uuid->type == BT_UUID128)
-               length = 16;
+               uuid_len = 16;
        else
                return 0;
 
-       if (len < min_len + length)
-               return 0;
-
+       /* Attribute Opcode (1 octet) */
        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;
+       /* Starting Handle (2 octets) */
+       put_le16(start, &pdu[1]);
+       /* Ending Handle (2 octets) */
+       put_le16(end, &pdu[3]);
+       /* Attribute Group Type (2 or 16 octet UUID) */
+       put_uuid_le(uuid, &pdu[5]);
+
+       return 5 + uuid_len;
 }
 
 uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start,
                                                uint16_t *end, bt_uuid_t *uuid)
 {
        const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+       uint8_t type;
 
        if (pdu == NULL)
                return 0;
@@ -162,15 +185,17 @@ uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start,
        if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ)
                return 0;
 
-       if (len < min_len + 2)
+       if (len == (min_len + 2))
+               type = BT_UUID16;
+       else if (len == (min_len + 16))
+               type = BT_UUID128;
+       else
                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]);
+       *start = get_le16(&pdu[1]);
+       *end = get_le16(&pdu[3]);
+
+       get_uuid(type, &pdu[5], uuid);
 
        return len;
 }
@@ -212,7 +237,25 @@ struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, size_t len)
        if (pdu[0] != ATT_OP_READ_BY_GROUP_RESP)
                return NULL;
 
+       /* PDU must contain at least:
+        * - Attribute Opcode (1 octet)
+        * - Length (1 octet)
+        * - Attribute Data List (at least one entry):
+        *   - Attribute Handle (2 octets)
+        *   - End Group Handle (2 octets)
+        *   - Attribute Value (at least 1 octet) */
+       if (len < 7)
+               return NULL;
+
        elen = pdu[1];
+       /* Minimum Attribute Data List size */
+       if (elen < 5)
+               return NULL;
+
+       /* Reject incomplete Attribute Data List */
+       if ((len - 2) % elen)
+               return NULL;
+
        num = (len - 2) / elen;
        list = att_data_list_alloc(num, elen);
        if (list == NULL)
@@ -244,16 +287,13 @@ uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
        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]);
+       put_le16(start, &pdu[1]);
+       put_le16(end, &pdu[3]);
+       put_le16(uuid->value.u16, &pdu[5]);
 
        if (vlen > 0) {
                memcpy(&pdu[7], value, vlen);
@@ -267,39 +307,27 @@ uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
                                                uint16_t *end, bt_uuid_t *uuid,
                                                uint8_t *value, size_t *vlen)
 {
-       size_t valuelen;
-       uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) +
-                                               sizeof(*end) + sizeof(uint16_t);
-
        if (pdu == NULL)
                return 0;
 
-       if (len < min_len)
+       if (len < 7)
                return 0;
 
+       /* Attribute Opcode (1 octet) */
        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;
+       /* First requested handle number (2 octets) */
+       *start = get_le16(&pdu[1]);
+       /* Last requested handle number (2 octets) */
+       *end = get_le16(&pdu[3]);
+       /* 16-bit UUID to find (2 octets) */
+       bt_uuid16_create(uuid, get_le16(&pdu[5]));
 
        /* Attribute value to find */
-       if (valuelen > 0 && value)
-               memcpy(value, pdu + min_len, valuelen);
-
-       if (vlen)
-               *vlen = valuelen;
+       *vlen = len - 7;
+       if (*vlen > 0)
+               memcpy(value, pdu + 7, *vlen);
 
        return len;
 }
@@ -309,7 +337,7 @@ uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, size_t len)
        GSList *l;
        uint16_t offset;
 
-       if (pdu == NULL || len < 5)
+       if (!pdu)
                return 0;
 
        pdu[0] = ATT_OP_FIND_BY_TYPE_RESP;
@@ -319,8 +347,8 @@ uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, size_t len)
                                l = l->next, offset += sizeof(uint16_t) * 2) {
                struct att_range *range = l->data;
 
-               att_put_u16(range->start, &pdu[offset]);
-               att_put_u16(range->end, &pdu[offset + 2]);
+               put_le16(range->start, &pdu[offset]);
+               put_le16(range->end, &pdu[offset + 2]);
        }
 
        return offset;
@@ -332,18 +360,27 @@ GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len)
        GSList *matches;
        off_t offset;
 
+       /* PDU should contain at least:
+        * - Attribute Opcode (1 octet)
+        * - Handles Information List (at least one entry):
+        *   - Found Attribute Handle (2 octets)
+        *   - Group End Handle (2 octets) */
        if (pdu == NULL || len < 5)
                return NULL;
 
        if (pdu[0] != ATT_OP_FIND_BY_TYPE_RESP)
                return NULL;
 
+       /* Reject incomplete Handles Information List */
+       if ((len - 1) % 4)
+               return NULL;
+
        for (offset = 1, matches = NULL;
                                len >= (offset + sizeof(uint16_t) * 2);
                                offset += sizeof(uint16_t) * 2) {
                range = g_new0(struct att_range, 1);
-               range->start = att_get_u16(&pdu[offset]);
-               range->end = att_get_u16(&pdu[offset + 2]);
+               range->start = get_le16(&pdu[offset]);
+               range->end = get_le16(&pdu[offset + 2]);
 
                matches = g_slist_append(matches, range);
        }
@@ -354,35 +391,35 @@ GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len)
 uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
                                                uint8_t *pdu, size_t len)
 {
-       uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
-       uint16_t length;
+       uint16_t uuid_len;
 
        if (!uuid)
                return 0;
 
        if (uuid->type == BT_UUID16)
-               length = 2;
+               uuid_len = 2;
        else if (uuid->type == BT_UUID128)
-               length = 16;
+               uuid_len = 16;
        else
                return 0;
 
-       if (len < min_len + length)
-               return 0;
-
+       /* Attribute Opcode (1 octet) */
        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;
+       /* Starting Handle (2 octets) */
+       put_le16(start, &pdu[1]);
+       /* Ending Handle (2 octets) */
+       put_le16(end, &pdu[3]);
+       /* Attribute Type (2 or 16 octet UUID) */
+       put_uuid_le(uuid, &pdu[5]);
+
+       return 5 + uuid_len;
 }
 
 uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
                                                uint16_t *end, bt_uuid_t *uuid)
 {
        const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end);
+       uint8_t type;
 
        if (pdu == NULL)
                return 0;
@@ -390,19 +427,20 @@ uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start,
        if (start == NULL || end == NULL || uuid == NULL)
                return 0;
 
-       if (len < min_len + 2)
+       if (len == (min_len + 2))
+               type = BT_UUID16;
+       else if (len == (min_len + 16))
+               type = BT_UUID128;
+       else
                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]);
+       *start = get_le16(&pdu[1]);
+       *end = get_le16(&pdu[3]);
 
-       if (len == min_len + 2)
-               *uuid = att_get_uuid16(&pdu[5]);
-       else
-               *uuid = att_get_uuid128(&pdu[5]);
+       get_uuid(type, &pdu[5], uuid);
 
        return len;
 }
@@ -444,7 +482,24 @@ struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len)
        if (pdu[0] != ATT_OP_READ_BY_TYPE_RESP)
                return NULL;
 
+       /* PDU must contain at least:
+        * - Attribute Opcode (1 octet)
+        * - Length (1 octet)
+        * - Attribute Data List (at least one entry):
+        *   - Attribute Handle (2 octets)
+        *   - Attribute Value (at least 1 octet) */
+       if (len < 5)
+               return NULL;
+
        elen = pdu[1];
+       /* Minimum Attribute Data List size */
+       if (elen < 3)
+               return NULL;
+
+       /* Reject incomplete Attribute Data List */
+       if ((len - 2) % elen)
+               return NULL;
+
        num = (len - 2) / elen;
        list = att_data_list_alloc(num, elen);
        if (list == NULL)
@@ -468,14 +523,11 @@ uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen,
        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]);
+       put_le16(handle, &pdu[1]);
 
        if (vlen > 0) {
                memcpy(&pdu[3], value, vlen);
@@ -502,7 +554,7 @@ uint16_t dec_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle,
        if (pdu[0] != ATT_OP_WRITE_CMD)
                return 0;
 
-       *handle = att_get_u16(&pdu[1]);
+       *handle = get_le16(&pdu[1]);
        memcpy(value, pdu + min_len, len - min_len);
        *vlen = len - min_len;
 
@@ -517,14 +569,11 @@ uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen,
        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]);
+       put_le16(handle, &pdu[1]);
 
        if (vlen > 0) {
                memcpy(&pdu[3], value, vlen);
@@ -551,7 +600,7 @@ uint16_t dec_write_req(const uint8_t *pdu, size_t len, uint16_t *handle,
        if (pdu[0] != ATT_OP_WRITE_REQ)
                return 0;
 
-       *handle = att_get_u16(&pdu[1]);
+       *handle = get_le16(&pdu[1]);
        *vlen = len - min_len;
        if (*vlen > 0)
                memcpy(value, pdu + min_len, *vlen);
@@ -582,37 +631,31 @@ uint16_t dec_write_resp(const uint8_t *pdu, size_t len)
 
 uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, size_t len)
 {
-       const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle);
-
        if (pdu == NULL)
                return 0;
 
-       if (len < min_len)
-               return 0;
-
+       /* Attribute Opcode (1 octet) */
        pdu[0] = ATT_OP_READ_REQ;
-       att_put_u16(handle, &pdu[1]);
+       /* Attribute Handle (2 octets) */
+       put_le16(handle, &pdu[1]);
 
-       return min_len;
+       return 3;
 }
 
 uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu,
                                                                size_t len)
 {
-       const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) +
-                                                       sizeof(offset);
-
        if (pdu == NULL)
                return 0;
 
-       if (len < min_len)
-               return 0;
-
+       /* Attribute Opcode (1 octet) */
        pdu[0] = ATT_OP_READ_BLOB_REQ;
-       att_put_u16(handle, &pdu[1]);
-       att_put_u16(offset, &pdu[3]);
+       /* Attribute Handle (2 octets) */
+       put_le16(handle, &pdu[1]);
+       /* Value Offset (2 octets) */
+       put_le16(offset, &pdu[3]);
 
-       return min_len;
+       return 5;
 }
 
 uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle)
@@ -631,7 +674,7 @@ uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle)
        if (pdu[0] != ATT_OP_READ_REQ)
                return 0;
 
-       *handle = att_get_u16(&pdu[1]);
+       *handle = get_le16(&pdu[1]);
 
        return min_len;
 }
@@ -657,8 +700,8 @@ uint16_t dec_read_blob_req(const uint8_t *pdu, size_t len, uint16_t *handle,
        if (pdu[0] != ATT_OP_READ_BLOB_REQ)
                return 0;
 
-       *handle = att_get_u16(&pdu[1]);
-       *offset = att_get_u16(&pdu[3]);
+       *handle = get_le16(&pdu[1]);
+       *offset = get_le16(&pdu[3]);
 
        return min_len;
 }
@@ -721,38 +764,32 @@ ssize_t dec_read_resp(const uint8_t *pdu, size_t len, uint8_t *value,
 uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status,
                                                uint8_t *pdu, size_t 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);
+       /* Attribute Opcode (1 octet) */
        pdu[0] = ATT_OP_ERROR;
+       /* Request Opcode In Error (1 octet) */
        pdu[1] = opcode;
-       memcpy(&pdu[2], &u16, sizeof(u16));
+       /* Attribute Handle In Error (2 octets) */
+       put_le16(handle, &pdu[2]);
+       /* Error Code (1 octet) */
        pdu[4] = status;
 
-       return min_len;
+       return 5;
 }
 
 uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu,
                                                                size_t len)
 {
-       const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end);
-
        if (pdu == NULL)
                return 0;
 
-       if (len < min_len)
-               return 0;
-
+       /* Attribute Opcode (1 octet) */
        pdu[0] = ATT_OP_FIND_INFO_REQ;
-       att_put_u16(start, &pdu[1]);
-       att_put_u16(end, &pdu[3]);
+       /* Starting Handle (2 octets) */
+       put_le16(start, &pdu[1]);
+       /* Ending Handle (2 octets) */
+       put_le16(end, &pdu[3]);
 
-       return min_len;
+       return 5;
 }
 
 uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start,
@@ -772,8 +809,8 @@ uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start,
        if (pdu[0] != ATT_OP_FIND_INFO_REQ)
                return 0;
 
-       *start = att_get_u16(&pdu[1]);
-       *end = att_get_u16(&pdu[3]);
+       *start = get_le16(&pdu[1]);
+       *end = get_le16(&pdu[3]);
 
        return min_len;
 }
@@ -858,7 +895,7 @@ uint16_t enc_notification(uint16_t handle, uint8_t *value, size_t vlen,
                return 0;
 
        pdu[0] = ATT_OP_HANDLE_NOTIFY;
-       att_put_u16(handle, &pdu[1]);
+       put_le16(handle, &pdu[1]);
        memcpy(&pdu[3], value, vlen);
 
        return vlen + min_len;
@@ -876,7 +913,7 @@ uint16_t enc_indication(uint16_t handle, uint8_t *value, size_t vlen,
                return 0;
 
        pdu[0] = ATT_OP_HANDLE_IND;
-       att_put_u16(handle, &pdu[1]);
+       put_le16(handle, &pdu[1]);
        memcpy(&pdu[3], value, vlen);
 
        return vlen + min_len;
@@ -900,7 +937,7 @@ uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle,
        dlen = MIN(len - min_len, vlen);
 
        if (handle)
-               *handle = att_get_u16(&pdu[1]);
+               *handle = get_le16(&pdu[1]);
 
        memcpy(value, &pdu[3], dlen);
 
@@ -909,33 +946,26 @@ uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle,
 
 uint16_t enc_confirmation(uint8_t *pdu, size_t len)
 {
-       const uint16_t min_len = sizeof(pdu[0]);
-
        if (pdu == NULL)
                return 0;
 
-       if (len < min_len)
-               return 0;
-
+       /* Attribute Opcode (1 octet) */
        pdu[0] = ATT_OP_HANDLE_CNF;
 
-       return min_len;
+       return 1;
 }
 
 uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, size_t len)
 {
-       const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
-
        if (pdu == NULL)
                return 0;
 
-       if (len < min_len)
-               return 0;
-
+       /* Attribute Opcode (1 octet) */
        pdu[0] = ATT_OP_MTU_REQ;
-       att_put_u16(mtu, &pdu[1]);
+       /* Client Rx MTU (2 octets) */
+       put_le16(mtu, &pdu[1]);
 
-       return min_len;
+       return 3;
 }
 
 uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu)
@@ -954,25 +984,22 @@ uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu)
        if (pdu[0] != ATT_OP_MTU_REQ)
                return 0;
 
-       *mtu = att_get_u16(&pdu[1]);
+       *mtu = get_le16(&pdu[1]);
 
        return min_len;
 }
 
 uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len)
 {
-       const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu);
-
        if (pdu == NULL)
                return 0;
 
-       if (len < min_len)
-               return 0;
-
+       /* Attribute Opcode (1 octet) */
        pdu[0] = ATT_OP_MTU_RESP;
-       att_put_u16(mtu, &pdu[1]);
+       /* Server Rx MTU (2 octets) */
+       put_le16(mtu, &pdu[1]);
 
-       return min_len;
+       return 3;
 }
 
 uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu)
@@ -991,7 +1018,7 @@ uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu)
        if (pdu[0] != ATT_OP_MTU_RESP)
                return 0;
 
-       *mtu = att_get_u16(&pdu[1]);
+       *mtu = get_le16(&pdu[1]);
 
        return min_len;
 }
@@ -1006,15 +1033,12 @@ uint16_t enc_prep_write_req(uint16_t handle, uint16_t offset,
        if (pdu == NULL)
                return 0;
 
-       if (len < min_len)
-               return 0;
-
        if (vlen > len - min_len)
                vlen = len - min_len;
 
        pdu[0] = ATT_OP_PREP_WRITE_REQ;
-       att_put_u16(handle, &pdu[1]);
-       att_put_u16(offset, &pdu[3]);
+       put_le16(handle, &pdu[1]);
+       put_le16(offset, &pdu[3]);
 
        if (vlen > 0) {
                memcpy(&pdu[5], value, vlen);
@@ -1042,8 +1066,8 @@ uint16_t dec_prep_write_req(const uint8_t *pdu, size_t len, uint16_t *handle,
        if (pdu[0] != ATT_OP_PREP_WRITE_REQ)
                return 0;
 
-       *handle = att_get_u16(&pdu[1]);
-       *offset = att_get_u16(&pdu[3]);
+       *handle = get_le16(&pdu[1]);
+       *offset = get_le16(&pdu[3]);
 
        *vlen = len - min_len;
        if (*vlen > 0)
@@ -1062,15 +1086,12 @@ uint16_t enc_prep_write_resp(uint16_t handle, uint16_t offset,
        if (pdu == NULL)
                return 0;
 
-       if (len < min_len)
-               return 0;
-
        if (vlen > len - min_len)
                vlen = len - min_len;
 
        pdu[0] = ATT_OP_PREP_WRITE_RESP;
-       att_put_u16(handle, &pdu[1]);
-       att_put_u16(offset, &pdu[3]);
+       put_le16(handle, &pdu[1]);
+       put_le16(offset, &pdu[3]);
 
        if (vlen > 0) {
                memcpy(&pdu[5], value, vlen);
@@ -1098,8 +1119,8 @@ uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle,
        if (pdu[0] != ATT_OP_PREP_WRITE_REQ)
                return 0;
 
-       *handle = att_get_u16(&pdu[1]);
-       *offset = att_get_u16(&pdu[3]);
+       *handle = get_le16(&pdu[1]);
+       *offset = get_le16(&pdu[3]);
        *vlen = len - min_len;
        if (*vlen > 0)
                memcpy(value, pdu + min_len, *vlen);
@@ -1109,21 +1130,18 @@ uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle,
 
 uint16_t enc_exec_write_req(uint8_t flags, uint8_t *pdu, size_t len)
 {
-       const uint16_t min_len = sizeof(pdu[0]) + sizeof(flags);
-
        if (pdu == NULL)
                return 0;
 
-       if (len < min_len)
-               return 0;
-
        if (flags > 1)
                return 0;
 
+       /* Attribute Opcode (1 octet) */
        pdu[0] = ATT_OP_EXEC_WRITE_REQ;
+       /* Flags (1 octet) */
        pdu[1] = flags;
 
-       return min_len;
+       return 2;
 }
 
 uint16_t dec_exec_write_req(const uint8_t *pdu, size_t len, uint8_t *flags)
@@ -1152,9 +1170,10 @@ uint16_t enc_exec_write_resp(uint8_t *pdu)
        if (pdu == NULL)
                return 0;
 
+       /* Attribute Opcode (1 octet) */
        pdu[0] = ATT_OP_EXEC_WRITE_RESP;
 
-       return sizeof(pdu[0]);
+       return 1;
 }
 
 uint16_t dec_exec_write_resp(const uint8_t *pdu, size_t len)
index 28bc944..c612d80 100644 (file)
 #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_VALUE_LEN                      512
 #define ATT_DEFAULT_L2CAP_MTU                  48
 #define ATT_DEFAULT_LE_MTU                     23
@@ -111,95 +101,6 @@ struct att_range {
        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);
 
index a8ab582..874552b 100644 (file)
 #endif
 
 #include <glib.h>
-#include <bluetooth/sdp.h>
-#include <adapter.h>
 
+#include "src/adapter.h"
+#include "src/shared/util.h"
 #include "lib/uuid.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 "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "src/attrib-server.h"
+#include "attrib/gatt-service.h"
+#include "src/log.h"
 
 struct gatt_info {
        bt_uuid_t uuid;
@@ -56,6 +56,15 @@ struct attrib_cb {
        void *user_data;
 };
 
+static inline void put_uuid_le(const bt_uuid_t *src, void *dst)
+{
+       if (src->type == BT_UUID16)
+               put_le16(src->value.u16, dst);
+       else
+               /* Convert from 128-bit BE to LE */
+               bswap_128(&src->value.u128, dst);
+}
+
 static GSList *parse_opts(gatt_option opt1, va_list args)
 {
        gatt_option opt = opt1;
@@ -82,8 +91,8 @@ static GSList *parse_opts(gatt_option opt1, va_list args)
                case GATT_OPT_CHR_PROPS:
                        info->props = va_arg(args, int);
 
-                       if (info->props & (ATT_CHAR_PROPER_NOTIFY |
-                                               ATT_CHAR_PROPER_INDICATE))
+                       if (info->props & (GATT_CHR_PROP_NOTIFY |
+                                               GATT_CHR_PROP_INDICATE))
                                /* client characteristic configuration */
                                info->num_attrs += 1;
 
@@ -130,14 +139,8 @@ static struct attribute *add_service_declaration(struct btd_adapter *adapter,
        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;
+       put_uuid_le(uuid, &atval[0]);
+       len = bt_uuid_len(uuid);
 
        bt_uuid16_create(&bt_uuid, svc);
 
@@ -153,7 +156,7 @@ static int att_read_req(int authorization, int authentication, uint8_t props)
        else if (authentication == GATT_CHR_VALUE_READ ||
                                authentication == GATT_CHR_VALUE_BOTH)
                return ATT_AUTHENTICATION;
-       else if (!(props & ATT_CHAR_PROPER_READ))
+       else if (!(props & GATT_CHR_PROP_READ))
                return ATT_NOT_PERMITTED;
 
        return ATT_NONE;
@@ -167,8 +170,8 @@ static int att_write_req(int authorization, int authentication, uint8_t props)
        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)))
+       else if (!(props & (GATT_CHR_PROP_WRITE |
+                                       GATT_CHR_PROP_WRITE_WITHOUT_RESP)))
                return ATT_NOT_PERMITTED;
 
        return ATT_NONE;
@@ -228,8 +231,8 @@ static gboolean add_characteristic(struct btd_adapter *adapter,
        /* characteristic declaration */
        bt_uuid16_create(&bt_uuid, GATT_CHARAC_UUID);
        atval[0] = info->props;
-       att_put_u16(h + 1, &atval[1]);
-       att_put_uuid(info->uuid, &atval[3]);
+       put_le16(h + 1, &atval[1]);
+       put_uuid_le(&info->uuid, &atval[3]);
        if (attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                atval, 3 + info->uuid.type / 8) == NULL)
                return FALSE;
@@ -259,7 +262,7 @@ static gboolean add_characteristic(struct btd_adapter *adapter,
                *info->value_handle = a->handle;
 
        /* client characteristic configuration descriptor */
-       if (info->props & (ATT_CHAR_PROPER_NOTIFY | ATT_CHAR_PROPER_INDICATE)) {
+       if (info->props & (GATT_CHR_PROP_NOTIFY | GATT_CHR_PROP_INDICATE)) {
                uint8_t cfg_val[2];
 
                bt_uuid16_create(&bt_uuid, GATT_CLIENT_CHARAC_CFG_UUID);
index bb2cae1..73eaf7a 100644 (file)
 #include <bluetooth/sdp.h>
 #include <bluetooth/sdp_lib.h>
 
+#include "src/shared/util.h"
 #include "lib/uuid.h"
 #include "att.h"
 #include "gattrib.h"
 #include "gatt.h"
 
 struct discover_primary {
+       int ref;
        GAttrib *attrib;
        bt_uuid_t uuid;
        GSList *primaries;
@@ -62,6 +64,7 @@ struct included_uuid_query {
 };
 
 struct discover_char {
+       int ref;
        GAttrib *attrib;
        bt_uuid_t *uuid;
        uint16_t end;
@@ -70,13 +73,38 @@ struct discover_char {
        void *user_data;
 };
 
-static void discover_primary_free(struct discover_primary *dp)
+struct discover_desc {
+       int ref;
+       GAttrib *attrib;
+       bt_uuid_t *uuid;
+       uint16_t end;
+       GSList *descriptors;
+       gatt_cb_t cb;
+       void *user_data;
+};
+
+static void discover_primary_unref(void *data)
 {
-       g_slist_free(dp->primaries);
+       struct discover_primary *dp = data;
+
+       dp->ref--;
+
+       if (dp->ref > 0)
+               return;
+
+       g_slist_free_full(dp->primaries, g_free);
        g_attrib_unref(dp->attrib);
        g_free(dp);
 }
 
+static struct discover_primary *discover_primary_ref(
+                                               struct discover_primary *dp)
+{
+       dp->ref++;
+
+       return dp;
+}
+
 static struct included_discovery *isd_ref(struct included_discovery *isd)
 {
        __sync_fetch_and_add(&isd->refs, 1);
@@ -90,23 +118,84 @@ static void isd_unref(struct included_discovery *isd)
                return;
 
        if (isd->err)
-               isd->cb(NULL, isd->err, isd->user_data);
+               isd->cb(isd->err, NULL, isd->user_data);
        else
-               isd->cb(isd->includes, isd->err, isd->user_data);
+               isd->cb(isd->err, isd->includes, isd->user_data);
 
        g_slist_free_full(isd->includes, g_free);
        g_attrib_unref(isd->attrib);
        g_free(isd);
 }
 
-static void discover_char_free(struct discover_char *dc)
+static void discover_char_unref(void *data)
 {
+       struct discover_char *dc = data;
+
+       dc->ref--;
+
+       if (dc->ref > 0)
+               return;
+
        g_slist_free_full(dc->characteristics, g_free);
        g_attrib_unref(dc->attrib);
        g_free(dc->uuid);
        g_free(dc);
 }
 
+static struct discover_char *discover_char_ref(struct discover_char *dc)
+{
+       dc->ref++;
+
+       return dc;
+}
+
+static void discover_desc_unref(void *data)
+{
+       struct discover_desc *dd = data;
+
+       dd->ref--;
+
+       if (dd->ref > 0)
+               return;
+
+       g_slist_free_full(dd->descriptors, g_free);
+       g_attrib_unref(dd->attrib);
+       g_free(dd->uuid);
+       g_free(dd);
+}
+
+static struct discover_desc *discover_desc_ref(struct discover_desc *dd)
+{
+       dd->ref++;
+
+       return dd;
+}
+
+static void put_uuid_le(const bt_uuid_t *uuid, void *dst)
+{
+       if (uuid->type == BT_UUID16)
+               put_le16(uuid->value.u16, dst);
+       else
+               /* Convert from 128-bit BE to LE */
+               bswap_128(&uuid->value.u128, dst);
+}
+
+static void get_uuid128(uint8_t type, const void *val, bt_uuid_t *uuid)
+{
+       if (type == BT_UUID16) {
+               bt_uuid_t uuid16;
+
+               bt_uuid16_create(&uuid16, get_le16(val));
+               bt_uuid_to_uuid128(&uuid16, uuid);
+       } else {
+               uint128_t u128;
+
+               /* Convert from 128-bit LE to BE */
+               bswap_128(val, &u128);
+               bt_uuid128_create(uuid, u128);
+       }
+}
+
 static guint16 encode_discover_primary(uint16_t start, uint16_t end,
                                bt_uuid_t *uuid, uint8_t *pdu, size_t len)
 {
@@ -119,22 +208,12 @@ static guint16 encode_discover_primary(uint16_t start, uint16_t end,
                /* 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;
+               uint8_t value[16];
                size_t 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);
-               }
+               put_uuid_le(uuid, value);
+               vlen = bt_uuid_len(uuid);
 
                plen = enc_find_by_type_req(start, end, &prim, value, vlen,
                                                                pdu, len);
@@ -179,12 +258,12 @@ static void primary_by_uuid_cb(guint8 status, const guint8 *ipdu,
        if (oplen == 0)
                goto done;
 
-       g_attrib_send(dp->attrib, 0, buf, oplen, primary_by_uuid_cb, dp, NULL);
+       g_attrib_send(dp->attrib, 0, buf, oplen, primary_by_uuid_cb,
+                       discover_primary_ref(dp), discover_primary_unref);
        return;
 
 done:
-       dp->cb(dp->primaries, err, dp->user_data);
-       discover_primary_free(dp);
+       dp->cb(err, dp->primaries, dp->user_data);
 }
 
 static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
@@ -194,6 +273,7 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
        struct att_data_list *list;
        unsigned int i, err;
        uint16_t start, end;
+       uint8_t type;
 
        if (status) {
                err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
@@ -206,23 +286,25 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
                goto done;
        }
 
+       if (list->len == 6)
+               type = BT_UUID16;
+       else if (list->len == 20)
+               type = BT_UUID128;
+       else {
+               att_data_list_free(list);
+               err = ATT_ECODE_INVALID_PDU;
+               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;
+               bt_uuid_t uuid128;
 
-               start = att_get_u16(&data[0]);
-               end = att_get_u16(&data[2]);
+               start = get_le16(&data[0]);
+               end = get_le16(&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;
-               }
+               get_uuid128(type, &data[4], &uuid128);
 
                primary = g_try_new0(struct gatt_primary, 1);
                if (!primary) {
@@ -232,7 +314,7 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
                }
                primary->range.start = start;
                primary->range.end = end;
-               bt_uuid_to_string(&uuid, primary->uuid, sizeof(primary->uuid));
+               bt_uuid_to_string(&uuid128, primary->uuid, sizeof(primary->uuid));
                dp->primaries = g_slist_append(dp->primaries, primary);
        }
 
@@ -246,14 +328,14 @@ static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
                                                                buf, buflen);
 
                g_attrib_send(dp->attrib, 0, buf, oplen, primary_all_cb,
-                                                               dp, NULL);
+                                               discover_primary_ref(dp),
+                                               discover_primary_unref);
 
                return;
        }
 
 done:
-       dp->cb(dp->primaries, err, dp->user_data);
-       discover_primary_free(dp);
+       dp->cb(err, dp->primaries, dp->user_data);
 }
 
 guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
@@ -283,7 +365,9 @@ guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
        } else
                cb = primary_all_cb;
 
-       return g_attrib_send(attrib, 0, buf, plen, cb, dp, NULL);
+       return g_attrib_send(attrib, 0, buf, plen, cb,
+                                       discover_primary_ref(dp),
+                                       discover_primary_unref);
 }
 
 static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu,
@@ -293,7 +377,7 @@ static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu,
        struct included_discovery *isd = query->isd;
        struct gatt_included *incl = query->included;
        unsigned int err = status;
-       bt_uuid_t uuid;
+       bt_uuid_t uuid128;
        size_t buflen;
        uint8_t *buf;
 
@@ -306,19 +390,23 @@ static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu,
                goto done;
        }
 
-       uuid = att_get_uuid128(buf);
-       bt_uuid_to_string(&uuid, incl->uuid, sizeof(incl->uuid));
+       get_uuid128(BT_UUID128, buf, &uuid128);
+
+       bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid));
        isd->includes = g_slist_append(isd->includes, incl);
+       query->included = NULL;
 
 done:
-       if (err)
-               g_free(incl);
-
        if (isd->err == 0)
                isd->err = err;
+}
 
-       isd_unref(isd);
+static void inc_query_free(void *data)
+{
+       struct included_uuid_query *query = data;
 
+       isd_unref(query->isd);
+       g_free(query->included);
        g_free(query);
 }
 
@@ -335,22 +423,22 @@ static guint resolve_included_uuid(struct included_discovery *isd,
        query->included = incl;
 
        return g_attrib_send(isd->attrib, 0, buf, oplen,
-                               resolve_included_uuid_cb, query, NULL);
+                               resolve_included_uuid_cb, query,
+                               inc_query_free);
 }
 
 static struct gatt_included *included_from_buf(const uint8_t *buf, gsize len)
 {
        struct gatt_included *incl = g_new0(struct gatt_included, 1);
 
-       incl->handle = att_get_u16(&buf[0]);
-       incl->range.start = att_get_u16(&buf[2]);
-       incl->range.end = att_get_u16(&buf[4]);
+       incl->handle = get_le16(&buf[0]);
+       incl->range.start = get_le16(&buf[2]);
+       incl->range.end = get_le16(&buf[4]);
 
        if (len == 8) {
                bt_uuid_t uuid128;
-               bt_uuid_t uuid16 = att_get_uuid16(&buf[6]);
 
-               bt_uuid_to_uuid128(&uuid16, &uuid128);
+               get_uuid128(BT_UUID16, &buf[6], &uuid128);
                bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid));
        }
 
@@ -448,6 +536,7 @@ static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
        struct att_data_list *list;
        unsigned int i, err = ATT_ECODE_ATTR_NOT_FOUND;
        uint16_t last = 0;
+       uint8_t type;
 
        if (status) {
                err = status;
@@ -460,32 +549,34 @@ static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
                goto done;
        }
 
+       if (list->len == 7)
+               type = BT_UUID16;
+       else
+               type = BT_UUID128;
+
        for (i = 0; i < list->num; i++) {
                uint8_t *value = list->data[i];
                struct gatt_char *chars;
-               bt_uuid_t uuid;
+               bt_uuid_t uuid128;
 
-               last = att_get_u16(value);
+               last = get_le16(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]);
+               get_uuid128(type, &value[5], &uuid128);
 
-               if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid))
+               if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid128))
                        continue;
 
                chars = g_try_new0(struct gatt_char, 1);
                if (!chars) {
+                       att_data_list_free(list);
                        err = ATT_ECODE_INSUFF_RESOURCES;
                        goto done;
                }
 
                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));
+               chars->value_handle = get_le16(&value[3]);
+               bt_uuid_to_string(&uuid128, chars->uuid, sizeof(chars->uuid));
                dc->characteristics = g_slist_append(dc->characteristics,
                                                                        chars);
        }
@@ -509,16 +600,14 @@ static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
                        return;
 
                g_attrib_send(dc->attrib, 0, buf, oplen, char_discovered_cb,
-                                                               dc, NULL);
+                               discover_char_ref(dc), discover_char_unref);
 
                return;
        }
 
 done:
        err = (dc->characteristics ? 0 : err);
-
-       dc->cb(dc->characteristics, err, dc->user_data);
-       discover_char_free(dc);
+       dc->cb(err, dc->characteristics, dc->user_data);
 }
 
 guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
@@ -548,7 +637,7 @@ guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
        dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
 
        return g_attrib_send(attrib, 0, buf, plen, char_discovered_cb,
-                                                               dc, NULL);
+                               discover_char_ref(dc), discover_char_unref);
 }
 
 guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
@@ -777,7 +866,7 @@ static guint prepare_write(struct write_long_data *long_write)
                                                                        NULL);
 }
 
-guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
+guint gatt_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value,
                        size_t vlen, GAttribResultFunc func, gpointer user_data)
 {
        uint8_t *buf;
@@ -814,6 +903,30 @@ guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
        return prepare_write(long_write);
 }
 
+guint gatt_execute_write(GAttrib *attrib, uint8_t flags,
+                               GAttribResultFunc func, gpointer user_data)
+{
+       return execute_write(attrib, flags, func, user_data);
+}
+
+guint gatt_reliable_write_char(GAttrib *attrib, uint16_t handle,
+                                       const uint8_t *value, size_t vlen,
+                                       GAttribResultFunc func,
+                                       gpointer user_data)
+{
+       uint8_t *buf;
+       guint16 plen;
+       size_t buflen;
+
+       buf = g_attrib_get_buffer(attrib, &buflen);
+
+       plen = enc_prep_write_req(handle, 0, value, vlen, buf, buflen);
+       if (!plen)
+               return 0;
+
+       return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
+}
+
 guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
                                                        gpointer user_data)
 {
@@ -826,23 +939,122 @@ guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
        return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL);
 }
 
-guint gatt_discover_char_desc(GAttrib *attrib, uint16_t start, uint16_t end,
-                               GAttribResultFunc func, gpointer user_data)
+
+static void desc_discovered_cb(guint8 status, const guint8 *ipdu,
+                                       guint16 iplen, gpointer user_data)
+{
+       struct discover_desc *dd = user_data;
+       struct att_data_list *list;
+       unsigned int i, err = ATT_ECODE_ATTR_NOT_FOUND;
+       guint8 format;
+       uint16_t last = 0xffff;
+       uint8_t type;
+       gboolean uuid_found = FALSE;
+
+       if (status) {
+               err = status;
+               goto done;
+       }
+
+       list = dec_find_info_resp(ipdu, iplen, &format);
+       if (!list) {
+               err = ATT_ECODE_IO;
+               goto done;
+       }
+
+       if (format == ATT_FIND_INFO_RESP_FMT_16BIT)
+               type = BT_UUID16;
+       else
+               type = BT_UUID128;
+
+       for (i = 0; i < list->num; i++) {
+               uint8_t *value = list->data[i];
+               struct gatt_desc *desc;
+               bt_uuid_t uuid128;
+
+               last = get_le16(value);
+
+               get_uuid128(type, &value[2], &uuid128);
+
+               if (dd->uuid) {
+                       if (bt_uuid_cmp(dd->uuid, &uuid128))
+                               continue;
+                       else
+                               uuid_found = TRUE;
+               }
+
+               desc = g_try_new0(struct gatt_desc, 1);
+               if (!desc) {
+                       att_data_list_free(list);
+                       err = ATT_ECODE_INSUFF_RESOURCES;
+                       goto done;
+               }
+
+               bt_uuid_to_string(&uuid128, desc->uuid, sizeof(desc->uuid));
+               desc->handle = last;
+
+               if (type == BT_UUID16)
+                       desc->uuid16 = get_le16(&value[2]);
+
+               dd->descriptors = g_slist_append(dd->descriptors, desc);
+
+               if (uuid_found)
+                       break;
+       }
+
+       att_data_list_free(list);
+
+       if (last + 1 < dd->end && !uuid_found) {
+               guint16 oplen;
+               size_t buflen;
+               uint8_t *buf;
+
+               buf = g_attrib_get_buffer(dd->attrib, &buflen);
+
+               oplen = enc_find_info_req(last + 1, dd->end, buf, buflen);
+               if (oplen == 0)
+                       return;
+
+               g_attrib_send(dd->attrib, 0, buf, oplen, desc_discovered_cb,
+                               discover_desc_ref(dd), discover_desc_unref);
+
+               return;
+       }
+
+done:
+       err = (dd->descriptors ? 0 : err);
+       dd->cb(err, dd->descriptors, dd->user_data);
+}
+
+guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end,
+                                               bt_uuid_t *uuid, gatt_cb_t func,
+                                               gpointer user_data)
 {
-       uint8_t *buf;
        size_t buflen;
+       uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
+       struct discover_desc *dd;
        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, buf, plen, func, user_data, NULL);
+       dd = g_try_new0(struct discover_desc, 1);
+       if (dd == NULL)
+               return 0;
+
+       dd->attrib = g_attrib_ref(attrib);
+       dd->cb = func;
+       dd->user_data = user_data;
+       dd->end = end;
+       dd->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
+
+       return g_attrib_send(attrib, 0, buf, plen, desc_discovered_cb,
+                               discover_desc_ref(dd), discover_desc_unref);
 }
 
-guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
-                               GDestroyNotify notify, gpointer user_data)
+guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value,
+                       int vlen, GDestroyNotify notify, gpointer user_data)
 {
        uint8_t *buf;
        size_t buflen;
index e5abd85..7d055f0 100644 (file)
 
 #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
-#define GATT_EXTERNAL_REPORT_REFERENCE 0x2907
-#define GATT_REPORT_REFERENCE          0x2908
+/*
+ * GATT Characteristic Property bit field
+ * Reference: Core SPEC 4.1 page 2183 (Table 3.5: Characteristic Properties
+ * bit field) defines how the Characteristic Value can be used, or how the
+ * characteristic descriptors (see Section 3.3.3 - page 2184) can be accessed.
+ * In the core spec, regular properties are included in the characteristic
+ * declaration, and the extended properties are defined as descriptor.
+ */
+
+#define GATT_CHR_PROP_BROADCAST                                0x01
+#define GATT_CHR_PROP_READ                             0x02
+#define GATT_CHR_PROP_WRITE_WITHOUT_RESP               0x04
+#define GATT_CHR_PROP_WRITE                            0x08
+#define GATT_CHR_PROP_NOTIFY                           0x10
+#define GATT_CHR_PROP_INDICATE                         0x20
+#define GATT_CHR_PROP_AUTH                             0x40
+#define GATT_CHR_PROP_EXT_PROP                         0x80
 
 /* 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);
+typedef void (*gatt_cb_t) (uint8_t status, GSList *l, void *user_data);
 
 struct gatt_primary {
        char uuid[MAX_LEN_UUID_STR + 1];
@@ -74,6 +67,12 @@ struct gatt_char {
        uint16_t value_handle;
 };
 
+struct gatt_desc {
+       char uuid[MAX_LEN_UUID_STR + 1];
+       uint16_t handle;
+       uint16_t uuid16;
+};
+
 guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
                                                        gpointer user_data);
 
@@ -87,15 +86,24 @@ guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
 guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
                                                        gpointer user_data);
 
-guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
+guint gatt_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value,
                                        size_t vlen, GAttribResultFunc func,
                                        gpointer user_data);
 
-guint gatt_discover_char_desc(GAttrib *attrib, uint16_t start, uint16_t end,
+guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end,
+                                               bt_uuid_t *uuid, gatt_cb_t func,
+                                               gpointer user_data);
+
+guint gatt_reliable_write_char(GAttrib *attrib, uint16_t handle,
+                                       const uint8_t *value, size_t vlen,
+                                       GAttribResultFunc func,
+                                       gpointer user_data);
+
+guint gatt_execute_write(GAttrib *attrib, uint8_t flags,
                                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_write_cmd(GAttrib *attrib, uint16_t handle, const 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,
index 609b908..912dffb 100644 (file)
 #include <stdio.h>
 
 #include <bluetooth/bluetooth.h>
-#include <btio/btio.h>
 
+#include "btio/btio.h"
 #include "lib/uuid.h"
-#include "log.h"
-#include "att.h"
-#include "gattrib.h"
+#include "src/shared/util.h"
+#include "src/log.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
 
 #define GATT_TIMEOUT 30
 
@@ -381,7 +382,7 @@ static bool match_event(struct event *evt, const uint8_t *pdu, gsize len)
        if (len < 3)
                return false;
 
-       handle = att_get_u16(&pdu[1]);
+       handle = get_le16(&pdu[1]);
 
        if (evt->expected == pdu[0] && evt->handle == handle)
                return true;
@@ -402,7 +403,16 @@ static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data)
                return FALSE;
 
        if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+               struct command *c;
+
+               while ((c = g_queue_pop_head(attrib->requests))) {
+                       if (c->func)
+                               c->func(ATT_ECODE_IO, NULL, 0, c->user_data);
+                       command_destroy(c);
+               }
+
                attrib->read_watch = 0;
+
                return FALSE;
        }
 
index f211dcd..bca7f69 100644 (file)
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 
+#include "src/shared/util.h"
 #include "lib/uuid.h"
 #include "att.h"
-#include <btio/btio.h>
+#include "btio/btio.h"
 #include "gattrib.h"
 #include "gatt.h"
 #include "gatttool.h"
@@ -78,7 +79,7 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
        uint16_t handle, i, olen = 0;
        size_t plen;
 
-       handle = att_get_u16(&pdu[1]);
+       handle = get_le16(&pdu[1]);
 
        switch (pdu[0]) {
        case ATT_OP_HANDLE_NOTIFY:
@@ -137,7 +138,7 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
        operation(attrib);
 }
 
-static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
+static void primary_all_cb(uint8_t status, GSList *services, void *user_data)
 {
        GSList *l;
 
@@ -157,8 +158,7 @@ done:
        g_main_loop_quit(event_loop);
 }
 
-static void primary_by_uuid_cb(GSList *ranges, guint8 status,
-                                                       gpointer user_data)
+static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data)
 {
        GSList *l;
 
@@ -191,8 +191,8 @@ static gboolean primary(gpointer user_data)
        return FALSE;
 }
 
-static void char_discovered_cb(GSList *characteristics, guint8 status,
-                                                       gpointer user_data)
+static void char_discovered_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
 {
        GSList *l;
 
@@ -272,7 +272,7 @@ static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
                uint8_t *value = list->data[i];
                int j;
 
-               g_print("handle: 0x%04x \t value: ", att_get_u16(value));
+               g_print("handle: 0x%04x \t value: ", get_le16(value));
                value += 2;
                for (j = 0; j < list->len - 2; j++, value++)
                        g_print("%02x ", *value);
@@ -400,44 +400,23 @@ error:
        return FALSE;
 }
 
-static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen,
-                                                       gpointer user_data)
+static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data)
 {
-       struct att_data_list *list;
-       guint8 format;
-       int i;
+       GSList *l;
 
-       if (status != 0) {
-               g_printerr("Discover all characteristic descriptors failed: "
-                                               "%s\n", att_ecode2str(status));
-               goto done;
+       if (status) {
+               g_printerr("Discover descriptors failed: %s\n",
+                                                       att_ecode2str(status));
+               return;
        }
 
-       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);
+       for (l = descriptors; l; l = l->next) {
+               struct gatt_desc *desc = l->data;
 
-               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);
+               g_print("handle = 0x%04x, uuid = %s\n", desc->handle,
+                                                               desc->uuid);
        }
 
-       att_data_list_free(list);
-
-done:
        if (!opt_listen)
                g_main_loop_quit(event_loop);
 }
@@ -446,7 +425,8 @@ static gboolean characteristics_desc(gpointer user_data)
 {
        GAttrib *attrib = user_data;
 
-       gatt_discover_char_desc(attrib, opt_start, opt_end, char_desc_cb, NULL);
+       gatt_discover_desc(attrib, opt_start, opt_end, NULL, char_desc_cb,
+                                                                       NULL);
 
        return FALSE;
 }
index 5bd27af..08f39f7 100644 (file)
@@ -38,8 +38,9 @@
 #include <readline/readline.h>
 #include <readline/history.h>
 
+#include "src/shared/util.h"
 #include "lib/uuid.h"
-#include <btio/btio.h>
+#include "btio/btio.h"
 #include "att.h"
 #include "gattrib.h"
 #include "gatt.h"
@@ -113,7 +114,7 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
        size_t plen;
        GString *s;
 
-       handle = att_get_u16(&pdu[1]);
+       handle = get_le16(&pdu[1]);
 
        switch (pdu[0]) {
        case ATT_OP_HANDLE_NOTIFY:
@@ -180,7 +181,7 @@ static void disconnect_io()
        set_state(STATE_DISCONNECTED);
 }
 
-static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
+static void primary_all_cb(uint8_t status, GSList *services, void *user_data)
 {
        GSList *l;
 
@@ -202,8 +203,7 @@ static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
        }
 }
 
-static void primary_by_uuid_cb(GSList *ranges, guint8 status,
-                                                       gpointer user_data)
+static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data)
 {
        GSList *l;
 
@@ -225,7 +225,7 @@ static void primary_by_uuid_cb(GSList *ranges, guint8 status,
        }
 }
 
-static void included_cb(GSList *includes, guint8 status, gpointer user_data)
+static void included_cb(uint8_t status, GSList *includes, void *user_data)
 {
        GSList *l;
 
@@ -249,7 +249,7 @@ static void included_cb(GSList *includes, guint8 status, gpointer user_data)
        }
 }
 
-static void char_cb(GSList *characteristics, guint8 status, gpointer user_data)
+static void char_cb(uint8_t status, GSList *characteristics, void *user_data)
 {
        GSList *l;
 
@@ -269,46 +269,22 @@ static void char_cb(GSList *characteristics, guint8 status, gpointer user_data)
        }
 }
 
-static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen,
-                                                       gpointer user_data)
+static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data)
 {
-       struct att_data_list *list;
-       guint8 format;
-       uint16_t handle = 0xffff;
-       int i;
+       GSList *l;
 
-       if (status != 0) {
-               rl_printf("Discover descriptors finished: %s\n",
-                                               att_ecode2str(status));
+       if (status) {
+               error("Discover descriptors failed: %s\n",
+                                                       att_ecode2str(status));
                return;
        }
 
-       list = dec_find_info_resp(pdu, plen, &format);
-       if (list == NULL)
-               return;
+       for (l = descriptors; l; l = l->next) {
+               struct gatt_desc *desc = l->data;
 
-       for (i = 0; i < list->num; i++) {
-               char uuidstr[MAX_LEN_UUID_STR];
-               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);
-               rl_printf("handle: 0x%04x, uuid: %s\n", handle, uuidstr);
+               rl_printf("handle: 0x%04x, uuid: %s\n", desc->handle,
+                                                               desc->uuid);
        }
-
-       att_data_list_free(list);
-
-       if (handle != 0xffff && handle < end)
-               gatt_discover_char_desc(attrib, handle + 1, end, char_desc_cb,
-                                                                       NULL);
 }
 
 static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
@@ -362,7 +338,7 @@ static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu,
                int j;
 
                g_string_printf(s, "handle: 0x%04x \t value: ",
-                                                       att_get_u16(value));
+                                                       get_le16(value));
                value += 2;
                for (j = 0; j < list->len - 2; j++, value++)
                        g_string_append_printf(s, "%02x ", *value);
@@ -559,7 +535,7 @@ static void cmd_char_desc(int argcp, char **argvp)
        } else
                end = 0xffff;
 
-       gatt_discover_char_desc(attrib, start, end, char_desc_cb, NULL);
+       gatt_discover_desc(attrib, start, end, NULL, char_desc_cb, NULL);
 }
 
 static void cmd_read_hnd(int argcp, char **argvp)
index 77bab27..8f9a4c0 100644 (file)
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
-#include <bluetooth/sdp.h>
 
 #include "lib/uuid.h"
-#include <btio/btio.h>
+#include "btio/btio.h"
 #include "att.h"
 #include "gattrib.h"
 #include "gatt.h"
index f62a533..5f91171 100644 (file)
@@ -591,34 +591,60 @@ static gboolean get_key_size(int sock, int *size, GError **err)
        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)
+static gboolean set_l2opts(int sock, uint16_t imtu, uint16_t omtu,
+                                               uint8_t mode, GError **err)
 {
-       if (imtu || omtu || mode) {
-               struct l2cap_options l2o;
-               socklen_t len;
+       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;
-               }
+       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 (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 (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) {
+               ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean set_le_imtu(int sock, uint16_t imtu, GError **err)
+{
+       if (setsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, &imtu,
+                                                       sizeof(imtu)) < 0) {
+               ERROR_FAILED(err, "setsockopt(BT_RCVMTU)", errno);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean l2cap_set(int sock, uint8_t src_type, 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) {
+               gboolean ret;
+
+               if (src_type == BDADDR_BREDR)
+                       ret = set_l2opts(sock, imtu, omtu, mode, err);
+               else
+                       ret = set_le_imtu(sock, imtu, err);
+
+               if (!ret)
+                       return ret;
        }
 
        if (master >= 0 && l2cap_set_master(sock, master) < 0) {
@@ -860,8 +886,7 @@ static gboolean parse_set_opts(struct set_opts *opts, GError **err,
        return TRUE;
 }
 
-static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
-                               socklen_t len, GError **err)
+static gboolean get_src(int sock, void *src, socklen_t len, GError **err)
 {
        socklen_t olen;
 
@@ -872,6 +897,13 @@ static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst,
                return FALSE;
        }
 
+       return TRUE;
+}
+
+static gboolean get_dst(int sock, void *dst, socklen_t len, GError **err)
+{
+       socklen_t olen;
+
        memset(dst, 0, len);
        olen = len;
        if (getpeername(sock, dst, &olen) < 0) {
@@ -939,20 +971,36 @@ static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
        uint8_t dev_class[3];
        uint16_t handle;
        socklen_t len;
-       gboolean flushable = FALSE;
+       gboolean flushable = FALSE, have_dst = FALSE;
        uint32_t priority;
 
+       if (!get_src(sock, &src, sizeof(src), err))
+               return FALSE;
+
+       memset(&l2o, 0, sizeof(l2o));
+
+       if (src.l2_bdaddr_type != BDADDR_BREDR) {
+               len = sizeof(l2o.imtu);
+               if (getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU,
+                                               &l2o.imtu, &len) == 0)
+                       goto parse_opts;
+
+               /* Non-LE CoC enabled kernels will return one of these
+                * in which case we need to fall back to L2CAP_OPTIONS.
+                */
+               if (errno != EPROTONOSUPPORT && errno != ENOPROTOOPT) {
+                       ERROR_FAILED(err, "getsockopt(BT_RCVMTU)", errno);
+                       return FALSE;
+               }
+       }
+
        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;
-
+parse_opts:
        while (opt != BT_IO_OPT_INVALID) {
                switch (opt) {
                case BT_IO_OPT_SOURCE:
@@ -962,14 +1010,29 @@ static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
                        bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr);
                        break;
                case BT_IO_OPT_DEST:
+                       if (!have_dst)
+                               have_dst = get_dst(sock, &dst, sizeof(dst),
+                                                                       err);
+                       if (!have_dst)
+                               return FALSE;
                        ba2str(&dst.l2_bdaddr, va_arg(args, char *));
                        break;
                case BT_IO_OPT_DEST_BDADDR:
+                       if (!have_dst)
+                               have_dst = get_dst(sock, &dst, sizeof(dst),
+                                                                       err);
+                       if (!have_dst)
+                               return FALSE;
                        bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
                        break;
                case BT_IO_OPT_DEST_TYPE:
-                       ERROR_FAILED(err, "Not implemented", EINVAL);
-                       return FALSE;
+                       if (!have_dst)
+                               have_dst = get_dst(sock, &dst, sizeof(dst),
+                                                                       err);
+                       if (!have_dst)
+                               return FALSE;
+                       *(va_arg(args, uint8_t *)) = dst.l2_bdaddr_type;
+                       break;
                case BT_IO_OPT_DEFER_TIMEOUT:
                        len = sizeof(int);
                        if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
@@ -989,14 +1052,46 @@ static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
                                return FALSE;
                        break;
                case BT_IO_OPT_PSM:
-                       *(va_arg(args, uint16_t *)) = src.l2_psm ?
-                                       btohs(src.l2_psm) : btohs(dst.l2_psm);
+                       if (src.l2_psm) {
+                               *(va_arg(args, uint16_t *)) = btohs(src.l2_psm);
+                               break;
+                       }
+
+                       if (!have_dst)
+                               have_dst = get_dst(sock, &dst, sizeof(dst),
+                                                                       err);
+                       if (!have_dst)
+                               return FALSE;
+
+                       *(va_arg(args, uint16_t *)) = 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);
+                       if (src.l2_cid) {
+                               *(va_arg(args, uint16_t *)) = btohs(src.l2_cid);
+                               break;
+                       }
+
+                       if (!have_dst)
+                               have_dst = get_dst(sock, &dst, sizeof(dst),
+                                                                       err);
+                       if (!have_dst)
+                               return FALSE;
+
+                       *(va_arg(args, uint16_t *)) = btohs(dst.l2_cid);
                        break;
                case BT_IO_OPT_OMTU:
+                       if (src.l2_bdaddr_type == BDADDR_BREDR) {
+                               *(va_arg(args, uint16_t *)) = l2o.omtu;
+                               break;
+                       }
+
+                       if (getsockopt(sock, SOL_BLUETOOTH, BT_SNDMTU,
+                                                       &l2o.omtu, &len) < 0) {
+                               ERROR_FAILED(err, "getsockopt(BT_RCVMTU)",
+                                                                       errno);
+                               return FALSE;
+                       }
+
                        *(va_arg(args, uint16_t *)) = l2o.omtu;
                        break;
                case BT_IO_OPT_IMTU:
@@ -1079,13 +1174,13 @@ static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
 {
        BtIOOption opt = opt1;
        struct sockaddr_rc src, dst;
+       gboolean have_dst = FALSE;
        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))
+       if (!get_src(sock, &src, sizeof(src), err))
                return FALSE;
 
        while (opt != BT_IO_OPT_INVALID) {
@@ -1097,9 +1192,19 @@ static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
                        bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr);
                        break;
                case BT_IO_OPT_DEST:
+                       if (!have_dst)
+                               have_dst = get_dst(sock, &dst, sizeof(dst),
+                                                                       err);
+                       if (!have_dst)
+                               return FALSE;
                        ba2str(&dst.rc_bdaddr, va_arg(args, char *));
                        break;
                case BT_IO_OPT_DEST_BDADDR:
+                       if (!have_dst)
+                               have_dst = get_dst(sock, &dst, sizeof(dst),
+                                                                       err);
+                       if (!have_dst)
+                               return FALSE;
                        bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr);
                        break;
                case BT_IO_OPT_DEFER_TIMEOUT:
@@ -1117,13 +1222,29 @@ static gboolean rfcomm_get(int sock, GError **err, BtIOOption opt1,
                                return FALSE;
                        break;
                case BT_IO_OPT_CHANNEL:
-                       *(va_arg(args, uint8_t *)) = src.rc_channel ?
-                                       src.rc_channel : dst.rc_channel;
+                       if (src.rc_channel) {
+                               *(va_arg(args, uint8_t *)) = src.rc_channel;
+                               break;
+                       }
+
+                       if (!have_dst)
+                               have_dst = get_dst(sock, &dst, sizeof(dst),
+                                                                       err);
+                       if (!have_dst)
+                               return FALSE;
+
+                       *(va_arg(args, uint8_t *)) = 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:
+                       if (!have_dst)
+                               have_dst = get_dst(sock, &dst, sizeof(dst),
+                                                                       err);
+                       if (!have_dst)
+                               return FALSE;
+
                        *(va_arg(args, uint8_t *)) = dst.rc_channel;
                        break;
                case BT_IO_OPT_MASTER:
@@ -1197,8 +1318,10 @@ static gboolean sco_get(int sock, GError **err, BtIOOption opt1, va_list args)
                return FALSE;
        }
 
-       if (!get_peers(sock, (struct sockaddr *) &src,
-                               (struct sockaddr *) &dst, sizeof(src), err))
+       if (!get_src(sock, &src, sizeof(src), err))
+               return FALSE;
+
+       if (!get_dst(sock, &dst, sizeof(dst), err))
                return FALSE;
 
        while (opt != BT_IO_OPT_INVALID) {
@@ -1320,9 +1443,9 @@ gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...)
 
        switch (type) {
        case BT_IO_L2CAP:
-               return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
-                               opts.mode, opts.master, opts.flushable,
-                               opts.priority, err);
+               return l2cap_set(sock, opts.src_type, 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:
@@ -1368,9 +1491,10 @@ static GIOChannel *create_io(gboolean server, struct set_opts *opts,
                if (l2cap_bind(sock, &opts->src, opts->src_type,
                                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))
+               if (!l2cap_set(sock, opts->src_type, opts->sec_level,
+                               opts->imtu, opts->omtu, opts->mode,
+                               opts->master, opts->flushable, opts->priority,
+                               err))
                        goto failed;
                break;
        case BT_IO_RFCOMM:
index 2d9dffd..eeabd5b 100644 (file)
@@ -154,14 +154,11 @@ dbus_bool_t agent_input(DBusConnection *conn, const char *input)
        return TRUE;
 }
 
-static DBusMessage *release_agent(DBusConnection *conn,
-                                       DBusMessage *msg, void *user_data)
+static void agent_release(DBusConnection *conn)
 {
        agent_registered = FALSE;
        agent_capability = NULL;
 
-       rl_printf("Agent released\n");
-
        if (pending_message) {
                dbus_message_unref(pending_message);
                pending_message = NULL;
@@ -170,6 +167,14 @@ static DBusMessage *release_agent(DBusConnection *conn,
        agent_release_prompt();
 
        g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE);
+}
+
+static DBusMessage *release_agent(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       rl_printf("Agent released\n");
+
+       agent_release(conn);
 
        return dbus_message_new_method_return(msg);
 }
@@ -418,13 +423,8 @@ static void unregister_agent_reply(DBusMessage *message, void *user_data)
        dbus_error_init(&error);
 
        if (dbus_set_error_from_message(&error, message) == FALSE) {
-               agent_registered = FALSE;
-               agent_capability = NULL;
                rl_printf("Agent unregistered\n");
-
-               if (g_dbus_unregister_interface(conn, AGENT_PATH,
-                                               AGENT_INTERFACE) == FALSE)
-                       rl_printf("Failed to unregister agent object\n");
+               agent_release(conn);
        } else {
                rl_printf("Failed to unregister agent: %s\n", error.name);
                dbus_error_free(&error);
@@ -438,6 +438,12 @@ void agent_unregister(DBusConnection *conn, GDBusProxy *manager)
                return;
        }
 
+       if (!manager) {
+               rl_printf("Agent unregistered\n");
+               agent_release(conn);
+               return;
+       }
+
        if (g_dbus_proxy_method_call(manager, "UnregisterAgent",
                                                unregister_agent_setup,
                                                unregister_agent_reply,
index ebc85c6..3244d42 100644 (file)
@@ -29,6 +29,7 @@
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <stdbool.h>
 #include <signal.h>
 #include <sys/signalfd.h>
 
@@ -153,6 +154,8 @@ static void print_iter(const char *label, const char *name,
        dbus_uint16_t valu16;
        dbus_int16_t vals16;
        const char *valstr;
+       DBusMessageIter subiter;
+       int type;
 
        if (iter == NULL) {
                rl_printf("%s%s is nil\n", label, name);
@@ -185,6 +188,24 @@ static void print_iter(const char *label, const char *name,
                dbus_message_iter_get_basic(iter, &vals16);
                rl_printf("%s%s: %d\n", label, name, vals16);
                break;
+       case DBUS_TYPE_ARRAY:
+               dbus_message_iter_recurse(iter, &subiter);
+               rl_printf("%s%s:\n", label, name);
+
+               do {
+                       type = dbus_message_iter_get_arg_type(&subiter);
+                       if (type == DBUS_TYPE_INVALID)
+                               break;
+
+                       if (type == DBUS_TYPE_STRING) {
+                               dbus_message_iter_get_basic(&subiter, &valstr);
+                               rl_printf("\t%s\n", valstr);
+                       }
+
+                       dbus_message_iter_next(&subiter);
+               } while(true);
+
+               break;
        default:
                rl_printf("%s%s has unsupported type\n", label, name);
                break;
@@ -315,8 +336,11 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
                        dev_list = NULL;
                }
        } else if (!strcmp(interface, "org.bluez.AgentManager1")) {
-               if (agent_manager == proxy)
+               if (agent_manager == proxy) {
                        agent_manager = NULL;
+                       if (auto_register_agent)
+                               agent_unregister(dbus_conn, NULL);
+               }
        }
 }
 
@@ -851,6 +875,93 @@ static void cmd_trust(const char *arg)
        g_free(str);
 }
 
+static void cmd_untrust(const char *arg)
+{
+       GDBusProxy *proxy;
+       dbus_bool_t trusted;
+       char *str;
+
+       if (!arg || !strlen(arg)) {
+               rl_printf("Missing device address argument\n");
+               return;
+       }
+
+       proxy = find_proxy_by_address(dev_list, arg);
+       if (!proxy) {
+               rl_printf("Device %s not available\n", arg);
+               return;
+       }
+
+       trusted = FALSE;
+
+       str = g_strdup_printf("%s untrust", arg);
+
+       if (g_dbus_proxy_set_property_basic(proxy, "Trusted",
+                                       DBUS_TYPE_BOOLEAN, &trusted,
+                                       generic_callback, str, g_free) == TRUE)
+               return;
+
+       g_free(str);
+}
+
+static void cmd_block(const char *arg)
+{
+       GDBusProxy *proxy;
+       dbus_bool_t blocked;
+       char *str;
+
+       if (!arg || !strlen(arg)) {
+               rl_printf("Missing device address argument\n");
+               return;
+       }
+
+       proxy = find_proxy_by_address(dev_list, arg);
+       if (!proxy) {
+               rl_printf("Device %s not available\n", arg);
+               return;
+       }
+
+       blocked = TRUE;
+
+       str = g_strdup_printf("%s block", arg);
+
+       if (g_dbus_proxy_set_property_basic(proxy, "Blocked",
+                                       DBUS_TYPE_BOOLEAN, &blocked,
+                                       generic_callback, str, g_free) == TRUE)
+               return;
+
+       g_free(str);
+}
+
+static void cmd_unblock(const char *arg)
+{
+       GDBusProxy *proxy;
+       dbus_bool_t blocked;
+       char *str;
+
+       if (!arg || !strlen(arg)) {
+               rl_printf("Missing device address argument\n");
+               return;
+       }
+
+       proxy = find_proxy_by_address(dev_list, arg);
+       if (!proxy) {
+               rl_printf("Device %s not available\n", arg);
+               return;
+       }
+
+       blocked = FALSE;
+
+       str = g_strdup_printf("%s unblock", arg);
+
+       if (g_dbus_proxy_set_property_basic(proxy, "Blocked",
+                                       DBUS_TYPE_BOOLEAN, &blocked,
+                                       generic_callback, str, g_free) == TRUE)
+               return;
+
+       g_free(str);
+}
+
 static void remove_device_reply(DBusMessage *message, void *user_data)
 {
        DBusError error;
@@ -1088,6 +1199,12 @@ static const struct {
                                                        dev_generator },
        { "trust",        "<dev>",    cmd_trust, "Trust device",
                                                        dev_generator },
+       { "untrust",      "<dev>",    cmd_untrust, "Untrust device",
+                                                       dev_generator },
+       { "block",        "<dev>",    cmd_block, "Block device",
+                                                               dev_generator },
+       { "unblock",      "<dev>",    cmd_unblock, "Unblock device",
+                                                               dev_generator },
        { "remove",       "<dev>",    cmd_remove, "Remove device",
                                                        dev_generator },
        { "connect",      "<dev>",    cmd_connect, "Connect device",
index 9f1f690..3a68c09 100644 (file)
@@ -1,5 +1,8 @@
 /* config.h.in.  Generated from configure.ac by autoheader.  */
 
+/* Directory for the Android daemon storage files */
+#undef ANDROID_STORAGEDIR
+
 /* Directory for the configuration files */
 #undef CONFIGDIR
 
index 7e58a43..b65515f 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for bluez 5.12.
+# Generated by GNU Autoconf 2.69 for bluez 5.19.
 #
 #
 # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -587,8 +587,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='bluez'
 PACKAGE_TARNAME='bluez'
-PACKAGE_VERSION='5.12'
-PACKAGE_STRING='bluez 5.12'
+PACKAGE_VERSION='5.19'
+PACKAGE_STRING='bluez 5.19'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -633,6 +633,10 @@ ac_subst_vars='am__EXEEXT_FALSE
 am__EXEEXT_TRUE
 LTLIBOBJS
 LIBOBJS
+SPEEXDSP_LIBS
+SPEEXDSP_CFLAGS
+SBC_LIBS
+SBC_CFLAGS
 ANDROID_FALSE
 ANDROID_TRUE
 CONFIGDIR
@@ -859,7 +863,11 @@ DBUS_LIBS
 UDEV_CFLAGS
 UDEV_LIBS
 ICAL_CFLAGS
-ICAL_LIBS'
+ICAL_LIBS
+SBC_CFLAGS
+SBC_LIBS
+SPEEXDSP_CFLAGS
+SPEEXDSP_LIBS'
 
 
 # Initialize some variables set by options.
@@ -1400,7 +1408,7 @@ 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 5.12 to adapt to many kinds of systems.
+\`configure' configures bluez 5.19 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1470,7 +1478,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of bluez 5.12:";;
+     short | recursive ) echo "Configuration of bluez 5.19:";;
    esac
   cat <<\_ACEOF
 
@@ -1552,6 +1560,12 @@ Some influential environment variables:
   UDEV_LIBS   linker flags for UDEV, overriding pkg-config
   ICAL_CFLAGS C compiler flags for ICAL, overriding pkg-config
   ICAL_LIBS   linker flags for ICAL, overriding pkg-config
+  SBC_CFLAGS  C compiler flags for SBC, overriding pkg-config
+  SBC_LIBS    linker flags for SBC, overriding pkg-config
+  SPEEXDSP_CFLAGS
+              C compiler flags for SPEEXDSP, overriding pkg-config
+  SPEEXDSP_LIBS
+              linker flags for SPEEXDSP, 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.
@@ -1619,7 +1633,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-bluez configure 5.12
+bluez configure 5.19
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1984,7 +1998,7 @@ 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 5.12, which was
+It was created by bluez $as_me 5.19, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2839,7 +2853,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='bluez'
- VERSION='5.12'
+ VERSION='5.19'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -13006,12 +13020,12 @@ 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
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.6\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.6") 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`
+  pkg_cv_DBUS_CFLAGS=`$PKG_CONFIG --cflags "dbus-1 >= 1.6" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -13023,12 +13037,12 @@ 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
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"dbus-1 >= 1.6\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "dbus-1 >= 1.6") 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`
+  pkg_cv_DBUS_LIBS=`$PKG_CONFIG --libs "dbus-1 >= 1.6" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -13049,18 +13063,18 @@ 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`
+               DBUS_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "dbus-1 >= 1.6" 2>&1`
         else
-               DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 >= 1.4" 2>&1`
+               DBUS_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "dbus-1 >= 1.6" 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
+       as_fn_error $? "D-Bus >= 1.6 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
+       as_fn_error $? "D-Bus >= 1.6 is required" "$LINENO" 5
 else
        DBUS_CFLAGS=$pkg_cv_DBUS_CFLAGS
        DBUS_LIBS=$pkg_cv_DBUS_LIBS
@@ -13628,6 +13642,164 @@ else
 fi
 
 
+if (test "${enable_android}" = "yes"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SBC" >&5
+$as_echo_n "checking for SBC... " >&6; }
+
+if test -n "$SBC_CFLAGS"; then
+    pkg_cv_SBC_CFLAGS="$SBC_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sbc >= 1.2\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "sbc >= 1.2") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_SBC_CFLAGS=`$PKG_CONFIG --cflags "sbc >= 1.2" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$SBC_LIBS"; then
+    pkg_cv_SBC_LIBS="$SBC_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sbc >= 1.2\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "sbc >= 1.2") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_SBC_LIBS=`$PKG_CONFIG --libs "sbc >= 1.2" 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
+               SBC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sbc >= 1.2" 2>&1`
+        else
+               SBC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sbc >= 1.2" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$SBC_PKG_ERRORS" >&5
+
+       as_fn_error $? "SBC library >= 1.2 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 $? "SBC library >= 1.2 is required" "$LINENO" 5
+else
+       SBC_CFLAGS=$pkg_cv_SBC_CFLAGS
+       SBC_LIBS=$pkg_cv_SBC_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       dummy=yes
+fi
+
+
+fi
+
+if (test "${enable_android}" = "yes"); then
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SPEEXDSP" >&5
+$as_echo_n "checking for SPEEXDSP... " >&6; }
+
+if test -n "$SPEEXDSP_CFLAGS"; then
+    pkg_cv_SPEEXDSP_CFLAGS="$SPEEXDSP_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"speexdsp >= 1.2\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "speexdsp >= 1.2") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_SPEEXDSP_CFLAGS=`$PKG_CONFIG --cflags "speexdsp >= 1.2" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$SPEEXDSP_LIBS"; then
+    pkg_cv_SPEEXDSP_LIBS="$SPEEXDSP_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"speexdsp >= 1.2\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "speexdsp >= 1.2") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_SPEEXDSP_LIBS=`$PKG_CONFIG --libs "speexdsp >= 1.2" 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
+               SPEEXDSP_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "speexdsp >= 1.2" 2>&1`
+        else
+               SPEEXDSP_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "speexdsp >= 1.2" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$SPEEXDSP_PKG_ERRORS" >&5
+
+       as_fn_error $? "SPEEXDSP library >= 1.2 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 $? "SPEEXDSP library >= 1.2 is required" "$LINENO" 5
+else
+       SPEEXDSP_CFLAGS=$pkg_cv_SPEEXDSP_CFLAGS
+       SPEEXDSP_LIBS=$pkg_cv_SPEEXDSP_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+       dummy=yes
+fi
+
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define ANDROID_STORAGEDIR "${storagedir}/android"
+_ACEOF
+
+
 ac_config_files="$ac_config_files Makefile src/bluetoothd.8 lib/bluez.pc"
 
 cat >confcache <<\_ACEOF
@@ -14220,7 +14392,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # 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 5.12, which was
+This file was extended by bluez $as_me 5.19, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -14286,7 +14458,7 @@ _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 5.12
+bluez config.status 5.19
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
index 18d0b55..d858ff6 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(bluez, 5.12)
+AC_INIT(bluez, 5.19)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
                                        tar-pax no-dist-gzip dist-xz])
@@ -62,8 +62,8 @@ if (test "${enable_threads}" = "yes"); then
        GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS"
 fi
 
-PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes,
-                               AC_MSG_ERROR(D-Bus >= 1.4 is required))
+PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.6, dummy=yes,
+                               AC_MSG_ERROR(D-Bus >= 1.6 is required))
 AC_SUBST(DBUS_CFLAGS)
 AC_SUBST(DBUS_LIBS)
 
@@ -252,4 +252,21 @@ AC_ARG_ENABLE(android, AC_HELP_STRING([--enable-android],
                                        [enable_android=${enableval}])
 AM_CONDITIONAL(ANDROID, test "${enable_android}" = "yes")
 
+if (test "${enable_android}" = "yes"); then
+       PKG_CHECK_MODULES(SBC, sbc >= 1.2, dummy=yes,
+                                       AC_MSG_ERROR(SBC library >= 1.2 is required))
+       AC_SUBST(SBC_CFLAGS)
+       AC_SUBST(SBC_LIBS)
+fi
+
+if (test "${enable_android}" = "yes"); then
+       PKG_CHECK_MODULES(SPEEXDSP, speexdsp >= 1.2, dummy=yes,
+                                       AC_MSG_ERROR(SPEEXDSP library >= 1.2 is required))
+       AC_SUBST(SPEEXDSP_CFLAGS)
+       AC_SUBST(SPEEXDSP_LIBS)
+fi
+
+AC_DEFINE_UNQUOTED(ANDROID_STORAGEDIR, "${storagedir}/android",
+                       [Directory for the Android daemon storage files])
+
 AC_OUTPUT(Makefile src/bluetoothd.8 lib/bluez.pc)
index 05f90e3..07ea991 100644 (file)
@@ -128,32 +128,53 @@ Methods           void Play()
 
                        Resume playback.
 
+                       Possible Errors: org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
                void Pause()
 
                        Pause playback.
 
+                       Possible Errors: org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
                void Stop()
 
                        Stop playback.
 
+                       Possible Errors: org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
                void Next()
 
                        Next item.
 
+                       Possible Errors: org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
                void Previous()
 
                        Previous item.
 
+                       Possible Errors: org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
                void FastForward()
 
                        Fast forward playback, this action is only stopped
                        when another method in this interface is called.
 
+                       Possible Errors: org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
                void Rewind()
 
                        Rewind playback, this action is only stopped
                        when another method in this interface is called.
 
+                       Possible Errors: org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
 Properties     string Equalizer [readwrite]
 
                        Possible values: "off" or "on"
@@ -300,10 +321,17 @@ Methods           object Search(string value, dict filter)
                        To list the items found use the folder object returned
                        and pass to ChangeFolder.
 
+                       Possible Errors: org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
                array{objects, properties} ListItems(dict filter)
 
                        Return a list of items found
 
+                       Possible Errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
                void ChangeFolder(object folder)
 
                        Change current folder.
@@ -313,6 +341,10 @@ Methods            object Search(string value, dict filter)
                        exception is NowPlaying folder which should be always
                        present while the player is active.
 
+                       Possible Errors: org.bluez.Error.InvalidArguments
+                                        org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
 Properties     uint32 NumberOfItems [readonly]
 
                        Number of items in the folder
@@ -368,10 +400,16 @@ Methods           void Play()
 
                        Play item
 
+                       Possible Errors: org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
                void AddtoNowPlaying()
 
                        Add item to now playing list
 
+                       Possible Errors: org.bluez.Error.NotSupported
+                                        org.bluez.Error.Failed
+
 Properties     object Player [readonly]
 
                        Player object path the item belongs to
index 371f252..b691ae0 100644 (file)
@@ -4,12 +4,43 @@ Bluetooth Management API
 Copyright (C) 2008-2009  Marcel Holtmann <marcel@holtmann.org>
 
 
+Overview
+========
+
 This document describes the format of data used for communicating with
 the kernel using a so-called Bluetooth Management sockets. These sockets
-are available starting with Linux kernel version 3.4, and can be created
-by setting the hci_channel member of struct sockaddr_hci to
-HCI_CHANNEL_CONTROL (3) when creating a raw HCI socket. In C the needed
-code would look something like the following:
+are available starting with Linux kernel version 3.4
+
+The following kernel versions introduced new commands, new events or
+important fixes to the Bluetooth Management API:
+
+Linux kernel v3.4      Version 1.0
+Linux kernel v3.5      Version 1.1
+Linux kernel v3.7      Version 1.2
+Linux kernel v3.9      Version 1.3
+Linux kernel v3.13     Version 1.4
+Linux kernel v3.15     Version 1.5     (not yet released)
+
+Version 1.1 introduces Set Device ID command.
+
+Version 1.2 introduces Passkey Notify event.
+
+Version 1.3 does not introduce any new command or event.
+
+Version 1.4 introduces Set Advertising, Set BR/EDR, Set Static Address
+and Set Scan Parameters commands.
+
+Version 1.5 introduces Set Secure Connections, Set Debug Keys, Set Privacy
+and Load Identity Resolving Keys commands. It also introduces New Identity
+Resolving Key and New Signature Resolving Key events.
+
+
+Example
+=======
+
+The Bluetooth management sockets can be created by setting the hci_channel
+member of struct sockaddr_hci to HCI_CHANNEL_CONTROL (3) when creating a
+raw HCI socket. In C the needed code would look something like the following:
 
 int mgmt_create(void)
 {
@@ -194,6 +225,9 @@ Read Controller Information Command
                9       High Speed
                10      Low Energy
                11      Advertising
+               12      Secure Connections
+               13      Debug Keys
+               14      Privacy
 
        This command generates a Command Complete event on success or
        a Command Status event on failure.
@@ -650,7 +684,7 @@ Load Link Keys Command
 
        This command is used to feed the kernel with currently known
        link keys. The command does not need to be called again upon the
-       receiption of new link key events since the kernel updates its
+       receiption of New Link Key events since the kernel updates its
        list automatically.
 
        The Debug_Keys parameter is used to tell the kernel whether to
@@ -658,6 +692,31 @@ Load Link Keys Command
        this parameter are 0x00 and 0x01. All other values will return
        an Invalid Parameters response.
 
+       Usage of the Debug_Keys parameter is deprecated and has been
+       replaced with the Set Debug Keys command. When setting the
+       Debug_Keys option via Load Link Keys command it has the same
+       affect as setting it via Set Debug Keys and applies to all
+       keys in the system.
+
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       Reserved (not in use)
+               2       Reserved (not in use)
+
+       Public and random LE addresses are not valid and will be rejected.
+
+       Currently defined Key_Type values are:
+
+               0x00    Combination key
+               0x01    Local Unit key
+               0x02    Remote Unit key
+               0x03    Debug Combination key
+               0x04    Unauthenticated Combination key from P-192
+               0x05    Authenticated Combination key from P-192
+               0x06    Changed Combination key
+               0x07    Unauthenticated Combination key from P-256
+               0x08    Authenticated Combination key from P-256
+
        This command can be used when the controller is not powered.
 
        This command generates a Command Complete event on success or
@@ -676,7 +735,7 @@ Load Long Term Keys Command
                                Key1 {
                                        Address (6 Octets)
                                        Address_Type (1 Octet)
-                                       Authenticated (1 Octet)
+                                       Key_Type (1 Octet)
                                        Master (1 Octet)
                                        Encryption_Size (1 Octet)
                                        Encryption_Diversifier (2 Octets)
@@ -689,9 +748,25 @@ Load Long Term Keys Command
 
        This command is used to feed the kernel with currently known
        (SMP) Long Term Keys. The command does not need to be called
-       again upon the receiption of new Long Term Key events since the
+       again upon the receiption of New Long Term Key events since the
        kernel updates its list automatically.
 
+       Possible values for the Address_Type parameter:
+               0       Reserved (not in use)
+               1       LE Public
+               2       LE Random
+
+       The provided Address and Address_Type are the identity of
+       a device. So either its public address or static random address.
+
+       Unresolvable random addresses and resolvable random addresses are
+       not valid and will be rejected.
+
+       Currently defined Key_Type values are:
+
+               0x00    Unauthenticated key
+               0x01    Authenticated key
+
        This command can be used when the controller is not powered.
 
        This command generates a Command Complete event on success or
@@ -714,6 +789,11 @@ Disconnect Command
        This command is used to force the disconnection of a currently
        connected device.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -748,6 +828,10 @@ Get Connections Command
                1       LE Public
                2       LE Random
 
+       For devices using resolvable random addresses with a known
+       identity resolving key, the Address and Address_Type will
+       contain the identity information.
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success or
@@ -772,6 +856,11 @@ PIN Code Reply Command
 
        This command is used to respond to a PIN Code request event.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -796,6 +885,11 @@ PIN Code Negative Reply Command
        This command is used to return a negative response to a PIN Code
        Request event.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -850,6 +944,17 @@ Pair Device Command
                1       LE Public
                2       LE Random
 
+       The Address and Address_Type of the return parameters will
+       return the identity address if know. In case of resolvable
+       random address given as command parameters and the remote
+       provides an identity resolving key, the return parameters
+       will provide the resolved address.
+
+       To allow tracking of which resolvable random address changed
+       into which identity address, the New Identity Resolving Key
+       event will be send before receiving Command Complete event
+       for this command.
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -875,6 +980,11 @@ Cancel Pair Device
        The Address and Address_Type parameters should match what was
        given to a preceding Pair Device command.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -898,11 +1008,24 @@ Unpair Device Command
 
        Removes all keys associated with the remote device.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        The Disconnect parameter tells the kernel whether to forcefully
        disconnect any existing connections to the device. It should in
        practice always be 1 except for some special GAP qualification
        test-cases where a key removal without disconnecting is needed.
 
+       When unpairing a device its link key, long term key and if
+       provided identity resolving key will be purged.
+
+       For devices using resolvable random addresses where the identity
+       resolving key was available, after this command they will now no
+       longer be resolved. The device will essentially become private
+       again.
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -927,6 +1050,11 @@ User Confirmation Reply Command
        This command is used to respond to a User Confirmation Request
        event.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -951,6 +1079,11 @@ User Confirmation Negative Reply Command
        This command is used to return a negative response to a User
        Confirmation Request event.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -976,6 +1109,11 @@ User Passkey Reply Command
        This command is used to respond to a User Confirmation Passkey
        Request event.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -1000,6 +1138,11 @@ User Passkey Negative Reply Command
        This command is used to return a negative response to a User
        Passkey Request event.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -1017,13 +1160,19 @@ Read Local Out Of Band Data Command
        Command Code:           0x0020
        Controller Index:       <controller id>
        Command Parameters:
-       Return Parameters:      Hash (16 Octets)
-                               Randomizer (16 Octets)
+       Return Parameters:      Hash_192 (16 Octets)
+                               Randomizer_192 (16 Octets)
+                               Hash_256 (16 Octets, Optional)
+                               Randomizer_256 (16 Octets, Optional)
 
        This command is used to read the local Out of Band data.
 
        This command can only be used when the controller is powered.
 
+       If Secure Connections support is enabled, then this command
+       will return P-192 versions of hash and randomizer as well as
+       P-256 versions of both.
+
        This command generates a Command Complete event on success or
        a Command Status event on failure.
 
@@ -1041,16 +1190,36 @@ Add Remote Out Of Band Data Command
        Controller Index:       <controller id>
        Command Parameters:     Address (6 Octets)
                                Address_Type (1 Octet)
-                               Hash (16 Octets)
-                               Randomizer (16 Octets)
+                               Hash_192 (16 Octets)
+                               Randomizer_192 (16 Octets)
+                               Hash_256 (16 Octets, Optional)
+                               Randomizer_256 (16 Octets, Optional)
        Return Parameters:      Address (6 Octets)
                                Address_Type (1 Octet)
 
        This command is used to provide Out of Band data for a remote
        device.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        Provided Out Of Band data is persistent over power down/up toggles.
 
+       This command also accept optional P-256 versions of hash and
+       randomizer. If they are not provided, then they are set to
+       zero value.
+
+       The P-256 versions of both can also be provided when the
+       support for Secure Connections is not enabled. However in
+       that case they will never be used.
+
+       To only provide the P-256 versions of hash and randomizer,
+       it is valid to leave both P-192 fields as zero values. If
+       Secure Connections is disabled, then of course this is the
+       same as not providing any data at all.
+
        This command generates a Command Complete event on success
        or failure.
 
@@ -1073,6 +1242,11 @@ Remove Remote Out Of Band Data Command
        This command is used to remove data added using the Add Remote
        Out Of Band Data command.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command generates a Command Complete event on success
        or failure.
 
@@ -1154,6 +1328,16 @@ Confirm Name Command
        expected for each Device Found event with the Confirm Name
        flag set.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
+       The Name_Known parameter should be set to 0x01 if user space
+       knows the name for the device and 0x00 if it doesn't. If set to
+       0x00 the kernel will perform a name resolving procedure for the
+       device in question.
+
        This command can only be used when the controller is powered.
 
        This command generates a Command Complete event on success
@@ -1178,6 +1362,11 @@ Block Device Command
        which should be blocked from being connect to the local
        controller.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command can be used when the controller is not powered.
 
        This command generates a Command Complete event on success
@@ -1201,6 +1390,11 @@ Unblock Device Command
        This command is used to remove a device from the list of blocked
        devices (where it was added to using the Block Device command).
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        This command can be used when the controller is not powered.
 
        This command generates a Command Complete event on success
@@ -1317,6 +1511,15 @@ Set Static Address Command
        The special BDADDR_ANY address (00:00:00:00:00:00) can be used
        to disable the static address.
 
+       When a controller has a public address (which is required for
+       all dual-mode controllers), this address is not used. Only when
+       the controller information reports BDADDR_ANY (00:00:00:00:00:00),
+       it is required to configure a static address first.
+
+       If privacy mode is enabled and the controller is single mode
+       LE only without a public address, the static random address is
+       used as identity address.
+
        This command generates a Command Complete event on success or a
        Command Status event on failure.
 
@@ -1348,14 +1551,171 @@ Set Scan Parameters Command
                                Invalid Index
 
 
+Set Secure Connections Command
+==============================
+
+       Command Code:           0x002D
+       Controller Index:       <controller id>
+       Command Parameters:     Secure_Connections (1 Octet)
+       Return Parameters:      Current_Settings (4 Octets)
+
+       This command is used to enable/disable Secure Connections
+       support for a controller. The allowed values for the
+       Secure_Connections command parameter are 0x00, 0x01 and 0x02.
+       All other values will return Invalid Parameters.
+
+       The value 0x00 disables Secure Connections, the value 0x01
+       enables Secure Connections and the value 0x02 enables Secure
+       Connections Only mode.
+
+       This command is only available for BR/EDR capable controllers
+       supporting the core specification version 4.1 or greater
+       (e.g. not for single-mode LE controllers or pre-4.1 ones).
+
+       This command can be used when the controller is not powered and
+       all settings will be programmed once powered.
+
+       In case the controller does not support Secure Connections
+       the command will fail regardless with Not Supported error.
+
+       This command generates a Command Complete event on success or
+       a Command Status event on failure.
+
+       Possible errors:        Busy
+                               Not Supported
+                               Invalid Parameters
+                               Invalid Index
+
+
+Set Debug Keys Command
+======================
+
+       Command Code:           0x002E
+       Controller Index:       <controller id>
+       Command Parameters:     Debug_Keys (1 Octet)
+       Return Parameters:      Current_Settings (4 Octets)
+
+       This command is used to tell the kernel whether to accept the
+       usage of debug keys or not. The allowed values for this parameter
+       are 0x00 and 0x01. All other values will return an Invalid Parameters
+       response.
+
+       This command generates a Command Complete event on success or
+       a Command Status event on failure.
+
+       Possible errors:        Busy
+                               Not Supported
+                               Invalid Parameters
+                               Invalid Index
+
+
+Set Privacy Command
+===================
+
+       Command Code:           0x002F
+       Controller Index:       <controller id>
+       Command Parameters:     Privacy (1 Octet)
+                               Identity_Resolving_Key (16 Octets)
+       Return Parameters:      Current_Settings (4 Octets)
+
+       This command is used to enable Low Energy Privacy feature using
+       resolvable private addresses.
+
+       The value 0x00 disables privacy mode, the value 0x01 enables
+       privacy mode.
+
+       When the controller has a public address (mandatory for dual-mode
+       controllers) it is used as identity address. In case the controller
+       is single mode LE only without a public address, it is required
+       to configure a static random andress first. The privacy mode can
+       only be enabled when an identity address is available.
+
+       The Identity_Resolving_Key is the local key assigned for the local
+       resolvable private address.
+
+       Possible errors:        Busy
+                               Not Supported
+                               Invalid Parameters
+                               Invalid Index
+
+
+Load Identity Resolving Keys Command
+====================================
+
+       Command Code:           0x0030
+       Controller Index:       <controller id>
+       Command Parameters:     Key_Count (2 Octets)
+                               Key1 {
+                                       Address (6 Octets)
+                                       Address_Type (1 Octet)
+                                       Value (16 Octets)
+                               }
+                               Key2 {  }
+                               ...
+       Return Parameters:
+
+       This command is used to feed the kernel with currently known
+       identity resolving keys. The command does not need to be called
+       again upon the receiption of New Identity Resolving Key events
+       since the kernel updates its list automatically.
+
+       Possible values for the Address_Type parameter:
+               0       Reserved (not in use)
+               1       LE Public
+               2       LE Random
+
+       The provided Address and Address_Type are the identity of
+       a device. So either its public address or static random address.
+
+       Unresolvable random addresses and resolvable random addresses are
+       not valid and will be rejected.
+
+       This command can be used when the controller is not powered.
+
+       This command generates a Command Complete event on success or
+       a Command Status event on failure.
+
+       Possible errors:        Invalid Parameters
+                               Invalid Index
+
+
+Get Connection Information Command
+==================================
+
+       Command Code:           0x0031
+       Controller Index:       <controller id>
+       Command Parameters:     Address (6 Octets)
+                               Address_Type (1 Octet)
+       Return Parameters:      Address (6 Octets)
+                               Address_Type (1 Octet)
+                               RSSI (1 Octet)
+                               TX_Power (1 Octet)
+                               Max_TX_Power (1 Octet)
+
+       This command is used to get connection information.
+
+       TX_Power and Max_TX_Power can be set to 127 if values are invalid or
+       unknown.
+
+       This command generates a Command Complete event on success and
+       on failure. In case of failure only Address and Address_Type fields
+       are valid and values of remaining parameters are considered invalid
+       and shall be ignored.
+
+       Possible errors:        Not Connected
+                               Not Powered
+                               Invalid Parameters
+                               Invalid Index
+
+
 Command Complete Event
 ======================
 
-Event Code             0x0001
-Controller Index:      <controller id> or <non-controller>
-Event Parameters       Command_Opcode (2 Octets)
-                       Status (1 Octet)
-                       Return_Parameters
+       Event Code:             0x0001
+       Controller Index:       <controller id> or <non-controller>
+       Event Parameters:       Command_Opcode (2 Octets)
+                               Status (1 Octet)
+                               Return_Parameters
 
        This event is an indication that a command has completed. The
        fixed set of parameters includes the opcode to identify the
@@ -1368,10 +1728,10 @@ Event Parameters        Command_Opcode (2 Octets)
 Command Status Event
 ====================
 
-Event Code             0x0002
-Controller Index:      <controller id> or <non-controller>
-Event Parameters       Command_Opcode (2 Octets)
-                       Status (1 Octet)
+       Event Code:             0x0002
+       Controller Index:       <controller id> or <non-controller>
+       Event Parameters:       Command_Opcode (2 Octets)
+                               Status (1 Octet)
 
        The command status event is used to indicate an early status for
        a pending command. In the case that the status indicates failure
@@ -1382,9 +1742,9 @@ Event Parameters  Command_Opcode (2 Octets)
 Controller Error Event
 ======================
 
-Event Code             0x0003
-Controller Index:      <controller id>
-Event Parameters       Error_Code (1 Octet)
+       Event Code:             0x0003
+       Controller Index:       <controller id>
+       Event Parameters:       Error_Code (1 Octet)
 
        This event maps straight to the HCI Hardware Error event and is
        used to indicate something wrong with the controller hardware.
@@ -1393,9 +1753,9 @@ Event Parameters  Error_Code (1 Octet)
 Index Added Event
 =================
 
-Event Code             0x0004
-Controller Index:      <controller id>
-Event Parameters
+       Event Code:             0x0004
+       Controller Index:       <controller id>
+       Event Parameters:
 
        This event indicates that a new controller has been added to the
        system. It is usually followed by a Read Controller Information
@@ -1405,9 +1765,9 @@ Event Parameters
 Index Removed Event
 ===================
 
-Event Code             0x0005
-Controller Index:      <controller id>
-Event Parameters
+       Event Code:             0x0005
+       Controller Index:       <controller id>
+       Event Parameters:
 
        This event indicates that a controller has been removed from the
        system.
@@ -1416,9 +1776,9 @@ Event Parameters
 New Settings Event
 ==================
 
-Event Code             0x0006
-Controller Index:      <controller id>
-Event Parameters:      Current_Settings (4 Octets)
+       Event Code:             0x0006
+       Controller Index:       <controller id>
+       Event Parameters:       Current_Settings (4 Octets)
 
        This event indicates that one or more of the settings for a
        controller has changed.
@@ -1427,9 +1787,9 @@ Event Parameters: Current_Settings (4 Octets)
 Class Of Device Changed Event
 =============================
 
-Event Code             0x0007
-Controller Index:      <controller id>
-Event Parameters:      Class_Of_Device (3 Octets)
+       Event Code:             0x0007
+       Controller Index:       <controller id>
+       Event Parameters:       Class_Of_Device (3 Octets)
 
        This event indicates that the Class of Device value for the
        controller has changed. When the controller is powered off the
@@ -1439,10 +1799,10 @@ Event Parameters:       Class_Of_Device (3 Octets)
 Local Name Changed Event
 ========================
 
-Event Code             0x0008
-Controller Index       <controller id>
-Event Parameters       Name (249 Octets)
-                       Short_Name (11 Octets)
+       Event Code:             0x0008
+       Controller Index:       <controller id>
+       Event Parameters:       Name (249 Octets)
+                               Short_Name (11 Octets)
 
        This event indicates that the local name of the controller has
        changed.
@@ -1451,80 +1811,132 @@ Event Parameters       Name (249 Octets)
 New Link Key Event
 ==================
 
-Event Code             0x0009
-Controller Index:      <controller id>
-Event Parametersa      Store_Hint (1 Octet)
-                       Key {
-                               Address (6 Octets)
-                               Address_Type (1 Octet)
-                               Key_Type (1 Octet)
-                               Value (16 Octets)
-                               PIN_Length (1 Octet)
-                       }
+       Event Code:             0x0009
+       Controller Index:       <controller id>
+       Event Parameters:       Store_Hint (1 Octet)
+                               Key {
+                                       Address (6 Octets)
+                                       Address_Type (1 Octet)
+                                       Key_Type (1 Octet)
+                                       Value (16 Octets)
+                                       PIN_Length (1 Octet)
+                               }
 
        This event indicates that a new link key has bee generated for a
-       remote device. The Store_Hint parameter indicates whether the
-       host is expected to store the key persistently or not (e.g. this
-       would not be set if the authentication requirement was "No
-       Bonding").
+       remote device.
+
+       The Store_Hint parameter indicates whether the host is expected
+       to store the key persistently or not (e.g. this would not be set
+       if the authentication requirement was "No Bonding").
+
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       Reserved (not in use)
+               2       Reserved (not in use)
+
+       Public and random LE addresses are not valid and will be rejected.
+
+       Currently defined Key_Type values are:
+
+               0x00    Combination key
+               0x01    Local Unit key
+               0x02    Remote Unit key
+               0x03    Debug Combination key
+               0x04    Unauthenticated Combination key from P-192
+               0x05    Authenticated Combination key from P-192
+               0x06    Changed Combination key
+               0x07    Unauthenticated Combination key from P-256
+               0x08    Authenticated Combination key from P-256
+
+       Receiving this event indicates that a pairing procecure has
+       been completed.
 
 
 New Long Term Key Event
 =======================
 
-Event Code             0x000A
-Controller Index       <controller id>
-Event Parameters       Store_Hint (1 Octet)
-                       Key {
-                               Address (6 Octets)
-                               Address_Type (1 Octet)
-                               Authenticated (1 Octet)
-                               Master (1 Octet)
-                               Encryption Size (1 Octet)
-                               Enc. Diversifier (2 Octets)
-                               Random Number (8 Octets)
-                               Value (16 Octets)
-                       }
+       Event Code:             0x000A
+       Controller Index:       <controller id>
+       Event Parameters:       Store_Hint (1 Octet)
+                               Key {
+                                       Address (6 Octets)
+                                       Address_Type (1 Octet)
+                                       Key_Type (1 Octet)
+                                       Master (1 Octet)
+                                       Encryption Size (1 Octet)
+                                       Enc. Diversifier (2 Octets)
+                                       Random Number (8 Octets)
+                                       Value (16 Octets)
+                               }
+
+       This event indicates that a new long term key has been generated
+       for a remote device.
+
+       The Store_Hint parameter indicates whether the host is expected
+       to store the key persistently or not (e.g. this would not be set
+       if the authentication requirement was "No Bonding").
+
+       Possible values for the Address_Type parameter:
+               0       Reserved (not in use)
+               1       LE Public
+               2       LE Random
+
+       The provided Address and Address_Type are the identity of
+       a device. So either its public address or static random address.
+
+       For unresolvable random addresses and resolvable random addresses
+       without identity information and identity resolving key, the
+       Store_Hint will be set to not store the long term key.
 
-       This event indicates that a new long term key has bee generated
-       for a remote device. The Store_Hint parameter indicates whether
-       the host is expected to store the key persistently or not (e.g.
-       this would not be set if the authentication requirement was "No
-       Bonding").
+       Currently defined Key_Type values are:
+
+               0x00    Unauthenticated key
+               0x01    Authenticated key
+
+       Receiving this event indicates that a pairing procecure has
+       been completed.
 
 
 Device Connected Event
 ======================
 
-Event Code             0x000B
-Controller Index:      <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
-                       Flags (4 Octets)
-                       EIR_Data_Length (2 Octets)
-                       EIR_Data (0-65535 Octets)
+       Event Code:             0x000B
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
+                               Flags (4 Octets)
+                               EIR_Data_Length (2 Octets)
+                               EIR_Data (0-65535 Octets)
+
+       This event indicates that a successful baseband connection has
+       been created to the remote device.
 
        Possible values for the Address_Type parameter:
                0       BR/EDR
                1       LE Public
                2       LE Random
 
+       For devices using resolvable random addresses with a known
+       identity resolving key, the Address and Address_Type will
+       contain the identity information.
+
+       It is possible that devices get connected via its resolvable
+       random address and after New Identity Resolving Key event
+       start using its identity.
+
        The following bits are defined for the Flags parameter:
                0       Reserved (not in use)
                1       Legacy Pairing
 
-       This event indicates that a successful baseband connection has
-       been created to the remote device.
-
 
 Device Disconnected Event
 =========================
 
-Event Code             0x000C
-Controller Index:      <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
-                       Reason (1 Octet)
+       Event Code:             0x000C
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
+                               Reason (1 Octet)
 
        This event indicates that the baseband connection was lost to a
        remote device.
@@ -1534,6 +1946,10 @@ Event Parameters Address (6 Octets)
                1       LE Public
                2       LE Random
 
+       For devices using resolvable random addresses with a known
+       identity resolving key, the Address and Address_Type will
+       contain the identity information.
+
        Possible values for the Reason parameter:
                0       Unspecified
                1       Connection timeout
@@ -1553,11 +1969,11 @@ Event Parameters        Address (6 Octets)
 Connect Failed Event
 ====================
 
-Event Code             0x000D
-Controller Index:      <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
-                       Status (1 Octet)
+       Event Code:             0x000D
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
+                               Status (1 Octet)
 
        This event indicates that a connection attempt failed to a
        remote device.
@@ -1567,20 +1983,29 @@ Event Parameters        Address (6 Octets)
                1       LE Public
                2       LE Random
 
+       For devices using resolvable random addresses with a known
+       identity resolving key, the Address and Address_Type will
+       contain the identity information.
+
 
 PIN Code Request Event
 ======================
 
-Event Code             0x000E
-Controller Index:      <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
-                       Secure (1 Octet)
+       Event Code:             0x000E
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
+                               Secure (1 Octet)
 
        This event is used to request a PIN Code reply from user space.
        The reply should either be returned using the PIN Code Reply or
        the PIN Code Negative Reply command.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        Secure: 0x01  secure PIN code required
                0x00  secure PIN code not required
 
@@ -1588,18 +2013,25 @@ Event Parameters        Address (6 Octets)
 User Confirmation Request Event
 ===============================
 
-Event Code             0x000F
-Controller Index:      <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
-                       Confirm_Hint (1 Octet)
-                       Value (4 Octets)
+       Event Code:             0x000F
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
+                               Confirm_Hint (1 Octet)
+                               Value (4 Octets)
 
        This event is used to request a user confirmation request from
-       user space. If the Confirm_Hint parameter value is 0x01 this
-       means that a simple "Yes/No" confirmation should be presented to
-       the user instead of a full numerical confirmation (in which case
-       the parameter value will be 0x00).
+       user space.
+
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
+       If the Confirm_Hint parameter value is 0x01 this means that
+       a simple "Yes/No" confirmation should be presented to the user
+       instead of a full numerical confirmation (in which case the
+       parameter value will be 0x00).
 
        User space should respond to this command either using the User
        Confirmation Reply or the User Confirmation Negative Reply
@@ -1609,40 +2041,50 @@ Event Parameters        Address (6 Octets)
 User Passkey Request Event
 ==========================
 
-Event Code             0x0010
-Controller Index:      <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
+       Event Code:             0x0010
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
 
        This event is used to request a passkey from user space. The
        response to this event should either be the User Passkey Reply
        command or the User Passkey Negative Reply command.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
 
 Authentication Failed Event
 ===========================
 
-Event Code             0x0011
-Controller Index:      <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
-                       Status (1 Octet)
+       Event Code:             0x0011
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
+                               Status (1 Octet)
 
        This event indicates that there was an authentication failure
        with a remote device.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
 
 Device Found Event
 ==================
 
-Event Code             0x0012
-Controller Index       <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
-                       RSSI (1 Octet)
-                       Flags (4 Octets)
-                       EIR_Data_Length (2 Octets)
-                       EIR_Data (0-65535 Octets)
+       Event Code:             0x0012
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
+                               RSSI (1 Octet)
+                               Flags (4 Octets)
+                               EIR_Data_Length (2 Octets)
+                               EIR_Data (0-65535 Octets)
 
        This event indicates that a device was found during device
        discovery.
@@ -1673,10 +2115,10 @@ Event Parameters        Address (6 Octets)
 Discovering Event
 =================
 
-Event Code             0x0013
-Controller Index       <controller id>
-Event Parameters       Address_Type (1 Octet)
-                       Discovering (1 Octet)
+       Event Code:             0x0013
+       Controller Index:       <controller id>
+       Event Parameters:       Address_Type (1 Octet)
+                               Discovering (1 Octet)
 
        This event indicates that the controller has started discovering
        devices. This discovering state can come and go multiple times
@@ -1689,59 +2131,169 @@ Event Parameters       Address_Type (1 Octet)
 Device Blocked Event
 ====================
 
-Event Code             0x0014
-Controller Index       <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
+       Event Code:             0x0014
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
 
        This event indicates that a device has been blocked using the
-       Block Device command. The event will only be sent to Management
-       sockets other than the one through which the command was sent.
+       Block Device command.
+
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
+       The event will only be sent to Management sockets other than the
+       one through which the command was sent.
 
 
 Device Unblocked Event
 ======================
 
-Event Code             0x0015
-Controller Index       <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
+       Event Code:             0x0015
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
 
        This event indicates that a device has been unblocked using the
-       Unblock Device command. The event will only be sent to
-       Management sockets other than the one through which the command
-       was sent.
+       Unblock Device command.
+
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
+       The event will only be sent to Management sockets other than the
+       one through which the command was sent.
 
 
 Device Unpaired Event
 =====================
 
-Event Code             0x0016
-Controller Index       <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
+       Event Code:             0x0016
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
 
        This event indicates that a device has been unpaired (i.e. all
        its keys have been removed from the kernel) using the Unpair
-       Device command. The event will only be sent to Management
-       sockets other than the one through which the Unpair Device
-       command was sent.
+       Device command.
+
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
+       For devices using resolvable random addresses with a known
+       identity resolving key, the event paramters will contain
+       the identity. After receiving this event, the device will
+       become essentially private again.
+
+       The event will only be sent to Management sockets other than the
+       one through which the Unpair Device command was sent.
 
 
 Passkey Notify Event
 ====================
 
-Event Code             0x0017
-Controller Index       <controller id>
-Event Parameters       Address (6 Octets)
-                       Address_Type (1 Octet)
-                       Passkey (4 Octets)
-                       Entered (1 Octet)
+       Event Code:             0x0017
+       Controller Index:       <controller id>
+       Event Parameters:       Address (6 Octets)
+                               Address_Type (1 Octet)
+                               Passkey (4 Octets)
+                               Entered (1 Octet)
 
        This event is used to request passkey notification to the user.
        Unlike the other authentication events it does not need
        responding to using any Management command.
 
+       Possible values for the Address_Type parameter:
+               0       BR/EDR
+               1       LE Public
+               2       LE Random
+
        The Passkey parameter indicates the passkey to be shown to the
        user whereas the Entered parameter indicates how many characters
        the user has entered on the remote side.
+
+
+New Identity Resolving Key Event
+================================
+
+       Event Code:             0x0018
+       Controller Index:       <controller id>
+       Event Parameters:       Store_Hint (1 Octet)
+                               Random_Address (6 Octets)
+                               Key {
+                                       Address (6 Octets)
+                                       Address_Type (1 Octet)
+                                       Value (16 Octets)
+                               }
+
+       This event indicates that a new identity resolving key has been
+       generated for a remote device.
+
+       The Store_Hint parameter indicates whether the host is expected
+       to store the key persistently or not.
+
+       The Random_Address provides the resolvable random address that
+       was resolved into an identity. A value of 00:00:00:00:00:00
+       indicates that the identity resolving key was provided for
+       a public address or static random address.
+
+       Once this event has been send for a resolvable random address,
+       all further events mapping this device will send out using the
+       identity address information.
+
+       This event also indicates that now the identity address should
+       be used for commands instead of the resolvable random address.
+
+       Possible values for the Address_Type parameter:
+               0       Reserved (not in use)
+               1       LE Public
+               2       LE Random
+
+       The provided Address and Address_Type are the identity of
+       a device. So either its public address or static random address.
+
+
+New Signature Resolving Key Event
+=================================
+
+       Event Code:             0x0019
+       Controller Index:       <controller id>
+       Event Parameters:       Store_Hint (1 Octet)
+                               Key {
+                                       Address (6 Octets)
+                                       Address_Type (1 Octet)
+                                       Master (1 Octet)
+                                       Value (16 Octets)
+                               }
+
+       This event indicates that a new signature resolving key has been
+       generated for either the master or slave device.
+
+       The Store_Hint parameter indicates whether the host is expected
+       to store the key persistently or not.
+
+       When the Master parameter is set to 0x01, then the signature
+       resolving key from the remote peer device is provided. It is
+       the key that is used for signature verification.
+
+       When the Master parameter is set to 0x00, then it is the local
+       signature resolving key that is used to sign data. The remote
+       peer device will be using it for signature verification.
+
+       The local signature resolving key will be generated with each
+       pairing request. Only after receiving this event with Master
+       parameter set to 0x00 it is possible to use ATT Signed Write
+       procedures.
+
+       Possible values for the Address_Type parameter:
+               0       Reserved (not in use)
+               1       LE Public
+               2       LE Random
+
+       The provided Address and Address_Type are the identity of
+       a device. So either its public address or static random address.
index 1f22fea..9542a30 100644 (file)
@@ -90,12 +90,34 @@ Methods             void Cancel()
                                         org.bluez.obex.Error.InProgress
                                         org.bluez.obex.Error.Failed
 
+               void Suspend()
+
+                       Suspend transference.
+
+                       Possible errors: org.bluez.obex.Error.NotAuthorized
+                                        org.bluez.obex.Error.NotInProgress
+
+                       Note that it is not possible to suspend transfers
+                       which are queued which is why NotInProgress is listed
+                       as possible error.
+
+               void Resume()
+
+                       Resume transference.
+
+                       Possible errors: org.bluez.obex.Error.NotAuthorized
+                                        org.bluez.obex.Error.NotInProgress
+
+                       Note that it is not possible to resume transfers
+                       which are queued which is why NotInProgress is listed
+                       as possible error.
+
 Properties     string Status [readonly]
 
                        Inform the current status of the transfer.
 
-                       Possible values: "queued", "active", "complete" or
-                                       "error"
+                       Possible values: "queued", "active", "suspended",
+                                       "complete" or "error"
 
                object Session [readonly]
 
diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt
new file mode 100644 (file)
index 0000000..7cdecd5
--- /dev/null
@@ -0,0 +1,228 @@
+BlueZ settings storage
+**********************
+
+Purpose
+=======
+
+The purpose of this document is to describe the directory structure of
+BlueZ settings storage. In effect, this document will serve as the primary,
+up to date source of BlueZ storage information.
+
+It is intended as reference for developers. Direct access to the storage
+outside from bluetoothd is highly discouraged.
+
+Adapter and remote device info are read form the storage during object
+initialization. Write to storage is performed immediately on every value
+change.
+
+Default storage directory is /var/lib/bluetooth. This can be adjusted
+by the --localstatedir configure switch. Default is --localstatedir=/var.
+
+All files are in ini-file format.
+
+
+Storage directory structure
+===========================
+
+There is one directory per adapter, named by its Bluetooth address, which
+contains:
+ - a settings file for the local adapter
+ - an attributes file containing attributes of supported LE services
+ - a cache directory containing:
+    - one file per device, named by remote device address, which contains
+    device name
+ - one directory per remote device, named by remote device address, which
+   contains:
+    - an info file
+    - an attributes file containing attributes of remote LE services
+    - a ccc file containing persistent Client Characteristic Configuration
+      (CCC) descriptor information for GATT characteristics
+
+So the directory structure is:
+    /var/lib/bluetooth/<adapter address>/
+        ./settings
+        ./attributes
+        ./cache/
+            ./<remote device address>
+            ./<remote device address>
+            ...
+        ./<remote device address>/
+            ./info
+            ./attributes
+            ./ccc
+        ./<remote device address>/
+            ./info
+            ./attributes
+        ...
+
+
+Settings file format
+====================
+
+Settings file contains one [General] group with adapter info like:
+
+  Alias                        String          Friendly user provided name advertised
+                                       for this adapter
+
+                                       This value overwrites the system
+                                       name (pretty hostname)
+
+  Discoverable         Boolean         Discoverability of the adapter
+
+  PairableTimeout      Integer         How long to stay in pairable mode
+                                       before going back to non-pairable.
+                                       The value is in seconds.
+                                       0 = disable timer, i.e. stay
+                                       pairable forever
+
+  DiscoverableTimeout  Integer         How long to stay in discoverable mode
+                                       before going back to non-discoverable.
+                                       The value is in seconds.
+                                       0 = disable timer, i.e. stay
+                                       discoverable forever
+
+Sample:
+  [General]
+  Name=My PC
+  Discoverable=false
+  Pairable=true
+  DiscoverableTimeout=0
+
+
+Attributes file format
+======================
+
+The attributes file lists all attributes supported by the local adapter or
+remote device.
+
+Attributes are stored using their handle as group name (decimal format).
+
+Each group contains:
+
+  UUID                 String          128-bit UUID of the attribute
+
+  Value                        String          Value of the attribute as hexadecimal encoded
+                                       string
+
+  EndGroupHandle       Integer         End group handle in decimal format
+
+Sample:
+  [1]
+  UUID=00002800-0000-1000-8000-00805f9b34fb
+  Value=0018
+
+  [4]
+  UUID=00002803-0000-1000-8000-00805f9b34fb
+  Value=020600002A
+
+  [6]
+  UUID=00002a00-0000-1000-8000-00805f9b34fb
+  Value=4578616D706C6520446576696365
+
+
+CCC file format
+======================
+
+The ccc file stores the current CCC descriptor values for GATT characteristics
+which have notification/indication enabled by the remote device.
+
+Information is stored using CCC attribute handle as group name (in decimal
+format).
+
+Each group contains:
+
+  Value                        String          CCC descriptor value encoded in
+                                       hexadecimal
+
+
+Cache directory file format
+============================
+
+Each file, named by remote device address, may includes multiple groups
+(General and ServiceRecords).
+
+In ServiceRecords, SDP records are stored using their handle as key
+(hexadecimal format).
+
+[General] group contains:
+
+  Name         String          Remote device friendly name
+
+  ShortName    String          Remote device shortened name
+
+[ServiceRecords] group contains
+
+  <0x...>      String          SDP record as hexadecimal encoded
+                               string
+
+
+Info file format
+================
+
+Info file may includes multiple groups (General, Device ID, Link key and
+Long term key) related to a remote device.
+
+[General] group contains:
+
+  Name                 String          Remote device friendly name
+
+  Alias                        String          Alias name
+
+  Class                        String          Device class in hexadecimal,
+                                       i.e. 0x000000
+
+  Appearance           String          Device appearance in hexadecimal,
+                                       i.e. 0x0000
+
+  SupportedTechnologies        List of         List of technologies supported by
+                       strings         device, separated by ";"
+                                       Technologies can be BR/EDR or LE
+
+  AddressType          String          An address can be "static" or "public"
+
+  Trusted              Boolean         True if the remote device is trusted
+
+  Blocked              Boolean         True if the remote device is blocked
+
+  Services             List of         List of service UUIDs advertised by
+                       strings         remote in 128-bits UUID format,
+                                       separated by ";"
+
+
+[DeviceID] group contains:
+
+  Source               Integer         Assigner of Device ID
+
+  Vendor               Integer         Device vendor
+
+  Product              Integer         Device product
+
+  Version              Integer         Device version
+
+
+[LinkKey] group contains:
+
+  Key                  String          Key in hexadecimal format
+
+  Type                 Integer         Type of link key
+
+  PINLength            Integer         Length of PIN
+
+
+[LongTermKey] group contains:
+
+  Key                  String          Long term key in hexadecimal format
+
+  Authenticated                Boolean         True if remote device has been
+                                       authenticated
+
+  EncSize              Integer         Encrypted size
+
+  EDiv                 Integer         Encrypted diversifier
+
+  Rand                 Integer         Randomizer
+
+
+[SlaveLongTermKey] group contains:
+
+  Same as the [LongTermKey] group, except for slave keys.
diff --git a/doc/test-coverage.txt b/doc/test-coverage.txt
new file mode 100644 (file)
index 0000000..7b05ff6
--- /dev/null
@@ -0,0 +1,67 @@
+BlueZ test coverage
+*******************
+
+
+Automated unit testing
+======================
+
+Application            Count   Description
+-------------------------------------------
+test-crc                  9    Link Layer CRC-24 checksum
+test-eir                 14    EIR and AD parsing
+test-lib                 14    SDP library functions
+test-sdp                133    SDP qualification test cases
+test-uuid                30    UUID conversion handling
+test-mgmt                 2    Management interface handling
+test-textfile             4    Old textfile storage format
+test-ringbuf              3    Ring buffer functionality
+test-queue                1    Queue handling functionality
+test-hfp                 13    HFP Audio Gateway functionality
+test-avdtp               60    AVDTP qualification test cases
+test-avctp                9    AVCTP qualification test cases
+test-avrcp               90    AVRCP qualification test cases
+test-gobex               31    Generic OBEX functionality
+test-gobex-packet         9    OBEX packet handling
+test-gobex-header        28    OBEX header handling
+test-gobex-apparam       18    OBEX apparam handling
+test-gobex-transfer      36    OBEX transfer handling
+test-gdbus-client        12    D-Bus client handling
+                       -----
+                        506
+
+
+Automated end-to-end testing
+============================
+
+Application            Count   Description
+-------------------------------------------
+mgmt-tester             184    Kernel management interface testing
+l2cap-tester             26    Kernel L2CAP implementation testing
+rfcomm-tester             9    Kernel RFCOMM implementation testing
+smp-tester                5    Kernel SMP implementation testing
+sco-tester                8    Kernel SCO implementation testing
+gap-tester                1    Daemon D-Bus API testing
+hci-tester               14    Controller hardware testing
+                       -----
+                        247
+
+
+Android end-to-end testing
+==========================
+
+Application            Count   Description
+-------------------------------------------
+android-tester           86    Android HAL interface testing
+ipc-tester               94    Android IPC resistance testing
+                       -----
+                        180
+
+
+Android automated unit testing
+==============================
+
+Application            Count   Description
+-------------------------------------------
+test-ipc                 14    Android IPC library functions
+                       -----
+                         14
index 714854b..ab7f9de 100644 (file)
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/hci.h>
 
+#include "src/shared/util.h"
 #include "monitor/mainloop.h"
 #include "monitor/bt.h"
 
 #include "amp.h"
 
-#define le16_to_cpu(val) (val)
-#define le32_to_cpu(val) (val)
-#define cpu_to_le16(val) (val)
-#define cpu_to_le32(val) (val)
-
 #define PHY_MODE_IDLE          0x00
 #define PHY_MODE_INITIATOR     0x01
 #define PHY_MODE_ACCEPTOR      0x02
index d0dff74..e590efa 100644 (file)
 #include <string.h>
 #include <alloca.h>
 
+#include "src/shared/util.h"
+#include "src/shared/timeout.h"
 #include "monitor/bt.h"
 #include "btdev.h"
 
-#define le16_to_cpu(val) (val)
-#define le32_to_cpu(val) (val)
-#define cpu_to_le16(val) (val)
-#define cpu_to_le32(val) (val)
-
 #define has_bredr(btdev)       (!((btdev)->features[4] & 0x20))
 #define has_le(btdev)          (!!((btdev)->features[4] & 0x40))
 
@@ -57,12 +54,24 @@ struct btdev {
 
        struct btdev *conn;
 
+       bool auth_init;
+       uint8_t link_key[16];
+       uint16_t pin[16];
+       uint8_t pin_len;
+       uint8_t io_cap;
+       uint8_t auth_req;
+       bool ssp_auth_complete;
+       uint8_t ssp_status;
+
        btdev_command_func command_handler;
        void *command_data;
 
        btdev_send_func send_handler;
        void *send_data;
 
+       unsigned int inquiry_id;
+       unsigned int inquiry_timeout_id;
+
        struct hook *hook_list[MAX_HOOK_ENTRIES];
 
         uint16_t manufacturer;
@@ -76,6 +85,7 @@ struct btdev {
        uint16_t acl_max_pkt;
        uint8_t  country_code;
        uint8_t  bdaddr[6];
+       uint8_t  random_addr[6];
        uint8_t  le_features[8];
        uint8_t  le_states[8];
 
@@ -100,12 +110,22 @@ struct btdev {
        uint8_t  ext_inquiry_fec;
        uint8_t  ext_inquiry_rsp[240];
        uint8_t  simple_pairing_mode;
+       uint8_t  ssp_debug_mode;
+       uint8_t  secure_conn_support;
        uint8_t  le_supported;
        uint8_t  le_simultaneous;
        uint8_t  le_event_mask[8];
        uint8_t  le_adv_data[31];
        uint8_t  le_adv_data_len;
+       uint8_t  le_adv_type;
+       uint8_t  le_adv_own_addr;
+       uint8_t  le_adv_direct_addr_type;
+       uint8_t  le_adv_direct_addr[6];
+       uint8_t  le_scan_data[31];
+       uint8_t  le_scan_data_len;
        uint8_t  le_scan_enable;
+       uint8_t  le_scan_type;
+       uint8_t  le_scan_own_addr_type;
        uint8_t  le_filter_dup;
        uint8_t  le_adv_enable;
        uint8_t  le_ltk[16];
@@ -115,8 +135,22 @@ struct btdev {
        uint8_t  sync_train_service_data;
 };
 
+struct inquiry_data {
+       struct btdev *btdev;
+       int num_resp;
+
+       int sent_count;
+       int iter;
+};
+
+#define DEFAULT_INQUIRY_INTERVAL 100 /* 100 miliseconds */
+
 #define MAX_BTDEV_ENTRIES 16
 
+static const uint8_t LINK_KEY_NONE[16] = { 0 };
+static const uint8_t LINK_KEY_DUMMY[16] = {    0, 1, 2, 3, 4, 5, 6, 7,
+                                               8, 9, 0, 1, 2, 3, 4, 5 };
+
 static struct btdev *btdev_list[MAX_BTDEV_ENTRIES] = { };
 
 static int get_hook_index(struct btdev *btdev, enum btdev_hook_type type,
@@ -268,6 +302,12 @@ static void set_bredr_commands(struct btdev *btdev)
        btdev->commands[0]  |= 0x80;    /* Cancel Create Connection */
        btdev->commands[1]  |= 0x01;    /* Accept Connection Request */
        btdev->commands[1]  |= 0x02;    /* Reject Connection Request */
+       btdev->commands[1]  |= 0x04;    /* Link Key Request Reply */
+       btdev->commands[1]  |= 0x08;    /* Link Key Request Negative Reply */
+       btdev->commands[1]  |= 0x10;    /* PIN Code Request Reply */
+       btdev->commands[1]  |= 0x20;    /* PIN Code Request Negative Reply */
+       btdev->commands[1]  |= 0x80;    /* Authentication Requested */
+       btdev->commands[2]  |= 0x01;    /* Set Connection Encryption */
        btdev->commands[2]  |= 0x08;    /* Remote Name Request */
        btdev->commands[2]  |= 0x10;    /* Cancel Remote Name Request */
        btdev->commands[2]  |= 0x20;    /* Read Remote Supported Features */
@@ -314,6 +354,7 @@ static void set_bredr_commands(struct btdev *btdev)
        btdev->commands[17] |= 0x80;    /* Read Local OOB Data */
        btdev->commands[18] |= 0x01;    /* Read Inquiry Response TX Power */
        btdev->commands[18] |= 0x02;    /* Write Inquiry Response TX Power */
+       btdev->commands[18] |= 0x80;    /* IO Capability Request Reply */
        btdev->commands[23] |= 0x04;    /* Read Data Block Size */
 }
 
@@ -351,6 +392,11 @@ static void set_bredrle_commands(struct btdev *btdev)
         */
        btdev->commands[22] |= 0x04;    /* Set Event Mask Page 2 */
        btdev->commands[31] |= 0x80;    /* Read Sync Train Parameters */
+       btdev->commands[32] |= 0x04;    /* Read Secure Connections Support */
+       btdev->commands[32] |= 0x08;    /* Write Secure Connections Support */
+       btdev->commands[32] |= 0x10;    /* Read Auth Payload Timeout */
+       btdev->commands[32] |= 0x20;    /* Write Auth Payload Timeout */
+       btdev->commands[32] |= 0x40;    /* Read Local OOB Extended Data */
 }
 
 static void set_amp_commands(struct btdev *btdev)
@@ -366,6 +412,7 @@ static void set_bredrle_features(struct btdev *btdev)
        btdev->features[0] |= 0x20;     /* Role switch */
        btdev->features[0] |= 0x80;     /* Sniff mode */
        btdev->features[1] |= 0x08;     /* SCO link */
+       btdev->features[2] |= 0x08;     /* Transparent SCO */
        btdev->features[3] |= 0x40;     /* RSSI with inquiry results */
        btdev->features[3] |= 0x80;     /* Extended SCO link */
        btdev->features[4] |= 0x08;     /* AFH capable slave */
@@ -390,6 +437,8 @@ static void set_bredrle_features(struct btdev *btdev)
        btdev->feat_page_2[0] |= 0x04;  /* Synchronization Train */
        btdev->feat_page_2[0] |= 0x08;  /* Synchronization Scan */
        btdev->feat_page_2[0] |= 0x10;  /* Inquiry Response Notification */
+       btdev->feat_page_2[1] |= 0x01;  /* Secure Connections */
+       btdev->feat_page_2[1] |= 0x02;  /* Ping */
 
        btdev->max_page = 2;
 }
@@ -501,6 +550,9 @@ void btdev_destroy(struct btdev *btdev)
        if (!btdev)
                return;
 
+       if (btdev->inquiry_id > 0)
+               timeout_remove(btdev->inquiry_id);
+
        del_btdev(btdev);
 
        free(btdev);
@@ -516,6 +568,14 @@ uint8_t *btdev_get_features(struct btdev *btdev)
        return btdev->features;
 }
 
+static bool use_ssp(struct btdev *btdev1, struct btdev *btdev2)
+{
+       if (btdev1->auth_enable || btdev2->auth_enable)
+               return false;
+
+       return (btdev1->simple_pairing_mode && btdev2->simple_pairing_mode);
+}
+
 void btdev_set_command_handler(struct btdev *btdev, btdev_command_func handler,
                                                        void *user_data)
 {
@@ -649,12 +709,23 @@ static void num_completed_packets(struct btdev *btdev)
        }
 }
 
-static void inquiry_complete(struct btdev *btdev, uint8_t status)
+static bool inquiry_callback(void *user_data)
 {
+       struct inquiry_data *data = user_data;
+       struct btdev *btdev = data->btdev;
        struct bt_hci_evt_inquiry_complete ic;
+       int sent = data->sent_count;
        int i;
 
-       for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+       /*Report devices only once and wait for inquiry timeout*/
+       if (data->iter == MAX_BTDEV_ENTRIES)
+               return true;
+
+       for (i = data->iter; i < MAX_BTDEV_ENTRIES; i++) {
+               /*Lets sent 10 inquiry results at once */
+               if (sent + 10 == data->sent_count)
+                       break;
+
                if (!btdev_list[i] || btdev_list[i] == btdev)
                        continue;
 
@@ -676,6 +747,7 @@ static void inquiry_complete(struct btdev *btdev, uint8_t status)
 
                        send_event(btdev, BT_HCI_EVT_EXT_INQUIRY_RESULT,
                                                        &ir, sizeof(ir));
+                       data->sent_count++;
                        continue;
                }
 
@@ -692,6 +764,7 @@ static void inquiry_complete(struct btdev *btdev, uint8_t status)
 
                        send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI,
                                                        &ir, sizeof(ir));
+                       data->sent_count++;
                } else {
                        struct bt_hci_evt_inquiry_result ir;
 
@@ -705,12 +778,119 @@ static void inquiry_complete(struct btdev *btdev, uint8_t status)
 
                        send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT,
                                                        &ir, sizeof(ir));
+                       data->sent_count++;
                }
-        }
+       }
+       data->iter = i;
 
-       ic.status = status;
+       /* Check if we sent already required amount of responses*/
+       if (data->num_resp && data->sent_count == data->num_resp)
+               goto finish;
+
+       return true;
+
+finish:
+       /* Note that destroy will be called */
+       ic.status = BT_HCI_ERR_SUCCESS;
+       send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
+
+       return false;
+}
+
+static void inquiry_destroy(void *user_data)
+{
+       struct inquiry_data *data = user_data;
+       struct btdev *btdev = data->btdev;
+
+       if (!btdev)
+               goto finish;
+
+       btdev->inquiry_id = 0;
+
+       if (btdev->inquiry_timeout_id > 0) {
+               timeout_remove(btdev->inquiry_timeout_id);
+               btdev->inquiry_timeout_id = 0;
+       }
+
+finish:
+       free(data);
+}
+
+static bool inquiry_timeout(void *user_data)
+{
+       struct inquiry_data *data = user_data;
+       struct btdev *btdev = data->btdev;
+       struct bt_hci_evt_inquiry_complete ic;
+
+       timeout_remove(btdev->inquiry_id);
+       btdev->inquiry_timeout_id = 0;
 
+       /* Inquiry is stopped, send Inquiry complete event. */
+       ic.status = BT_HCI_ERR_SUCCESS;
        send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
+
+       return false;
+}
+
+static void inquiry_cmd(struct btdev *btdev, const void *cmd)
+{
+       const struct bt_hci_cmd_inquiry *inq_cmd = cmd;
+       struct inquiry_data *data;
+       struct bt_hci_evt_inquiry_complete ic;
+       int status = BT_HCI_ERR_HARDWARE_FAILURE;
+       unsigned int inquiry_len_ms;
+
+       if (btdev->inquiry_id > 0) {
+               status = BT_HCI_ERR_COMMAND_DISALLOWED;
+               goto failed;
+       }
+
+       data = malloc(sizeof(*data));
+       if (!data)
+               goto failed;
+
+       memset(data, 0, sizeof(*data));
+       data->btdev = btdev;
+       data->num_resp = inq_cmd->num_resp;
+
+       /* Add timeout to cancel inquiry */
+       inquiry_len_ms = 1280 * inq_cmd->length;
+       if (inquiry_len_ms)
+               btdev->inquiry_timeout_id = timeout_add(inquiry_len_ms,
+                                                       inquiry_timeout,
+                                                       data, NULL);
+
+       btdev->inquiry_id = timeout_add(DEFAULT_INQUIRY_INTERVAL,
+                                                       inquiry_callback, data,
+                                                       inquiry_destroy);
+       /* Return if success */
+       if (btdev->inquiry_id > 0)
+               return;
+
+failed:
+       ic.status = status;
+       send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
+}
+
+static void inquiry_cancel(struct btdev *btdev)
+{
+       uint8_t status;
+
+       if (!btdev->inquiry_id) {
+               status = BT_HCI_ERR_COMMAND_DISALLOWED;
+               cmd_complete(btdev, BT_HCI_CMD_INQUIRY_CANCEL, &status,
+                                                       sizeof(status));
+               return;
+       }
+
+       timeout_remove(btdev->inquiry_timeout_id);
+       btdev->inquiry_timeout_id = 0;
+       timeout_remove(btdev->inquiry_id);
+       btdev->inquiry_id = 0;
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(btdev, BT_HCI_CMD_INQUIRY_CANCEL, &status,
+                                                       sizeof(status));
 }
 
 static void conn_complete(struct btdev *btdev,
@@ -747,6 +927,21 @@ static void conn_complete(struct btdev *btdev,
        send_event(btdev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
 }
 
+static void accept_conn_request_complete(struct btdev *btdev,
+                                                       const uint8_t *bdaddr)
+{
+       struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+       if (!remote)
+               return;
+
+       if (btdev->auth_enable || remote->auth_enable)
+               send_event(remote, BT_HCI_EVT_LINK_KEY_REQUEST,
+                                                       btdev->bdaddr, 6);
+       else
+               conn_complete(btdev, bdaddr, BT_HCI_ERR_SUCCESS);
+}
+
 static void sync_conn_complete(struct btdev *btdev, uint16_t voice_setting,
                                                                uint8_t status)
 {
@@ -819,11 +1014,47 @@ static void le_conn_complete(struct btdev *btdev,
        send_event(btdev, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
 }
 
+static const uint8_t *scan_addr(const struct btdev *btdev)
+{
+       if (btdev->le_scan_own_addr_type == 0x01)
+               return btdev->random_addr;
+
+       return btdev->bdaddr;
+}
+
+static const uint8_t *adv_addr(const struct btdev *btdev)
+{
+       if (btdev->le_adv_own_addr == 0x01)
+               return btdev->random_addr;
+
+       return btdev->bdaddr;
+}
+
+static bool adv_match(struct btdev *scan, struct btdev *adv)
+{
+       /* Match everything if this is not directed advertising */
+       if (adv->le_adv_type != 0x01 && adv->le_adv_type != 0x04)
+               return true;
+
+       if (scan->le_scan_own_addr_type != adv->le_adv_direct_addr_type)
+               return false;
+
+       return !memcmp(scan_addr(scan), adv->le_adv_direct_addr, 6);
+}
+
+static bool adv_connectable(struct btdev *btdev)
+{
+       if (!btdev->le_adv_enable)
+               return false;
+
+       return btdev->le_adv_type != 0x03;
+}
+
 static void le_conn_request(struct btdev *btdev, const uint8_t *bdaddr)
 {
        struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
 
-       if (remote && remote->le_adv_enable)
+       if (remote && adv_connectable(remote) && adv_match(btdev, remote))
                le_conn_complete(btdev, bdaddr, 0);
        else
                le_conn_complete(btdev, bdaddr,
@@ -874,6 +1105,223 @@ static void disconnect_complete(struct btdev *btdev, uint16_t handle,
        send_event(remote, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
 }
 
+static void link_key_req_reply_complete(struct btdev *btdev,
+                                       const uint8_t *bdaddr,
+                                       const uint8_t *link_key)
+{
+       struct btdev *remote = btdev->conn;
+       struct bt_hci_evt_auth_complete ev;
+
+       memcpy(btdev->link_key, link_key, 16);
+
+       if (!remote) {
+               remote = find_btdev_by_bdaddr(bdaddr);
+               if (!remote)
+                       return;
+       }
+
+       if (!memcmp(remote->link_key, LINK_KEY_NONE, 16)) {
+               send_event(remote, BT_HCI_EVT_LINK_KEY_REQUEST,
+                                                       btdev->bdaddr, 6);
+               return;
+       }
+
+       ev.handle = cpu_to_le16(42);
+
+       if (!memcmp(btdev->link_key, remote->link_key, 16))
+               ev.status = BT_HCI_ERR_SUCCESS;
+       else
+               ev.status = BT_HCI_ERR_AUTH_FAILURE;
+
+       send_event(btdev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+       send_event(remote, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+}
+
+static void link_key_req_neg_reply_complete(struct btdev *btdev,
+                                                       const uint8_t *bdaddr)
+{
+       struct btdev *remote = btdev->conn;
+
+       if (!remote) {
+               remote = find_btdev_by_bdaddr(bdaddr);
+               if (!remote)
+                       return;
+       }
+
+       if (use_ssp(btdev, remote)) {
+               struct bt_hci_evt_io_capability_request io_req;
+
+               memcpy(io_req.bdaddr, bdaddr, 6);
+               send_event(btdev, BT_HCI_EVT_IO_CAPABILITY_REQUEST, &io_req,
+                                                       sizeof(io_req));
+       } else {
+               struct bt_hci_evt_pin_code_request pin_req;
+
+               memcpy(pin_req.bdaddr, bdaddr, 6);
+               send_event(btdev, BT_HCI_EVT_PIN_CODE_REQUEST, &pin_req,
+                                                       sizeof(pin_req));
+       }
+}
+
+static uint8_t get_link_key_type(struct btdev *btdev)
+{
+       struct btdev *remote = btdev->conn;
+       uint8_t auth, unauth;
+
+       if (!remote)
+               return 0x00;
+
+       if (!btdev->simple_pairing_mode)
+               return 0x00;
+
+       if (btdev->ssp_debug_mode || remote->ssp_debug_mode)
+               return 0x03;
+
+       if (btdev->secure_conn_support && remote->secure_conn_support) {
+               unauth = 0x04;
+               auth = 0x05;
+       } else {
+               unauth = 0x07;
+               auth = 0x08;
+       }
+
+       if (btdev->io_cap == 0x03 || remote->io_cap == 0x03)
+               return unauth;
+
+       if (!(btdev->auth_req & 0x01) && !(remote->auth_req & 0x01))
+               return unauth;
+
+       /* DisplayOnly only produces authenticated with KeyboardOnly */
+       if (btdev->io_cap == 0x00 && remote->io_cap != 0x02)
+               return unauth;
+
+       /* DisplayOnly only produces authenticated with KeyboardOnly */
+       if (remote->io_cap == 0x00 && btdev->io_cap != 0x02)
+               return unauth;
+
+       return auth;
+}
+
+static void link_key_notify(struct btdev *btdev, const uint8_t *bdaddr,
+                                                       const uint8_t *key)
+{
+       struct bt_hci_evt_link_key_notify ev;
+
+       memcpy(btdev->link_key, key, 16);
+
+       memcpy(ev.bdaddr, bdaddr, 6);
+       memcpy(ev.link_key, key, 16);
+       ev.key_type = get_link_key_type(btdev);
+
+       send_event(btdev, BT_HCI_EVT_LINK_KEY_NOTIFY, &ev, sizeof(ev));
+}
+
+static void encrypt_change(struct btdev *btdev, uint8_t mode, uint8_t status)
+{
+       struct bt_hci_evt_encrypt_change ev;
+
+       ev.status = status;
+       ev.handle = cpu_to_le16(42);
+       ev.encr_mode = mode;
+
+       send_event(btdev, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
+}
+
+static void pin_code_req_reply_complete(struct btdev *btdev,
+                                       const uint8_t *bdaddr, uint8_t pin_len,
+                                       const uint8_t *pin_code)
+{
+       struct bt_hci_evt_auth_complete ev;
+       struct btdev *remote = btdev->conn;
+
+       if (!remote) {
+               remote = find_btdev_by_bdaddr(bdaddr);
+               if (!remote)
+                       return;
+       }
+
+       memcpy(btdev->pin, pin_code, pin_len);
+       btdev->pin_len = pin_len;
+
+       if (!remote->pin_len) {
+               struct bt_hci_evt_pin_code_request pin_req;
+
+               memcpy(pin_req.bdaddr, btdev->bdaddr, 6);
+               send_event(remote, BT_HCI_EVT_PIN_CODE_REQUEST, &pin_req,
+                                                       sizeof(pin_req));
+               return;
+       }
+
+       if (btdev->pin_len == remote->pin_len &&
+                       !memcmp(btdev->pin, remote->pin, btdev->pin_len)) {
+               link_key_notify(btdev, remote->bdaddr, LINK_KEY_DUMMY);
+               link_key_notify(remote, btdev->bdaddr, LINK_KEY_DUMMY);
+               ev.status = BT_HCI_ERR_SUCCESS;
+       } else {
+               ev.status = BT_HCI_ERR_AUTH_FAILURE;
+       }
+
+       if (remote->conn) {
+               ev.handle = cpu_to_le16(42);
+               send_event(remote, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+       } else {
+               conn_complete(remote, btdev->bdaddr, ev.status);
+       }
+
+       btdev->pin_len = 0;
+       remote->pin_len = 0;
+}
+
+static void pin_code_req_neg_reply_complete(struct btdev *btdev,
+                                                       const uint8_t *bdaddr)
+{
+       struct bt_hci_evt_auth_complete ev;
+       struct btdev *remote = btdev->conn;
+
+       if (!remote) {
+               remote = find_btdev_by_bdaddr(bdaddr);
+               if (!remote)
+                       return;
+       }
+
+       ev.status = BT_HCI_ERR_PIN_OR_KEY_MISSING;
+       ev.handle = cpu_to_le16(42);
+
+       if (btdev->conn)
+               send_event(btdev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+       else
+               conn_complete(btdev, bdaddr, BT_HCI_ERR_PIN_OR_KEY_MISSING);
+
+       if (remote->conn) {
+               if (remote->pin_len)
+                       send_event(remote, BT_HCI_EVT_AUTH_COMPLETE, &ev,
+                                                               sizeof(ev));
+       } else {
+               conn_complete(remote, btdev->bdaddr,
+                                       BT_HCI_ERR_PIN_OR_KEY_MISSING);
+       }
+}
+
+static void auth_request_complete(struct btdev *btdev, uint16_t handle)
+{
+       struct btdev *remote = btdev->conn;
+
+       if (!remote) {
+               struct bt_hci_evt_auth_complete ev;
+
+               ev.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+               ev.handle = cpu_to_le16(handle);
+
+               send_event(btdev, BT_HCI_EVT_AUTH_COMPLETE, &ev, sizeof(ev));
+
+               return;
+       }
+
+       btdev->auth_init = true;
+
+       send_event(btdev, BT_HCI_EVT_LINK_KEY_REQUEST, remote->bdaddr, 6);
+}
+
 static void name_request_complete(struct btdev *btdev,
                                        const uint8_t *bdaddr, uint8_t status)
 {
@@ -914,6 +1362,19 @@ static void remote_features_complete(struct btdev *btdev, uint16_t handle)
                                                        &rfc, sizeof(rfc));
 }
 
+static void btdev_get_host_features(struct btdev *btdev, uint8_t features[8])
+{
+       memset(features, 0, 8);
+       if (btdev->simple_pairing_mode)
+               features[0] |= 0x01;
+       if (btdev->le_supported)
+               features[0] |= 0x02;
+       if (btdev->le_simultaneous)
+               features[0] |= 0x04;
+       if (btdev->secure_conn_support)
+               features[0] |= 0x08;
+}
+
 static void remote_ext_features_complete(struct btdev *btdev, uint16_t handle,
                                                                uint8_t page)
 {
@@ -931,7 +1392,7 @@ static void remote_ext_features_complete(struct btdev *btdev, uint16_t handle,
                        break;
                case 0x01:
                        refc.status = BT_HCI_ERR_SUCCESS;
-                       memset(refc.features, 0, 8);
+                       btdev_get_host_features(btdev, refc.features);
                        break;
                default:
                        refc.status = BT_HCI_ERR_INVALID_PARAMETERS;
@@ -972,7 +1433,121 @@ static void remote_version_complete(struct btdev *btdev, uint16_t handle)
                                                        &rvc, sizeof(rvc));
 }
 
-static void le_send_adv_report(struct btdev *btdev, const struct btdev *remote)
+static void io_cap_req_reply_complete(struct btdev *btdev,
+                                       const uint8_t *bdaddr,
+                                       uint8_t capability, uint8_t oob_data,
+                                       uint8_t authentication)
+{
+       struct btdev *remote = btdev->conn;
+       struct bt_hci_evt_io_capability_response ev;
+       struct bt_hci_rsp_io_capability_request_reply rsp;
+       uint8_t status;
+
+       if (!remote) {
+               status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+               goto done;
+       }
+
+       status = BT_HCI_ERR_SUCCESS;
+
+       btdev->io_cap = capability;
+       btdev->auth_req = authentication;
+
+       memcpy(ev.bdaddr, btdev->bdaddr, 6);
+       ev.capability = capability;
+       ev.oob_data = oob_data;
+       ev.authentication = authentication;
+
+       send_event(remote, BT_HCI_EVT_IO_CAPABILITY_RESPONSE, &ev, sizeof(ev));
+
+       if (remote->io_cap) {
+               struct bt_hci_evt_user_confirm_request cfm;
+
+               memcpy(cfm.bdaddr, btdev->bdaddr, 6);
+               cfm.passkey = 0;
+
+               send_event(remote, BT_HCI_EVT_USER_CONFIRM_REQUEST,
+                                                       &cfm, sizeof(cfm));
+
+               memcpy(cfm.bdaddr, bdaddr, 6);
+               send_event(btdev, BT_HCI_EVT_USER_CONFIRM_REQUEST,
+                                                       &cfm, sizeof(cfm));
+       } else {
+               send_event(remote, BT_HCI_EVT_IO_CAPABILITY_REQUEST,
+                                                       btdev->bdaddr, 6);
+       }
+
+done:
+       rsp.status = status;
+       memcpy(rsp.bdaddr, bdaddr, 6);
+       cmd_complete(btdev, BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY,
+                                                       &rsp, sizeof(rsp));
+}
+
+static void io_cap_req_neg_reply_complete(struct btdev *btdev,
+                                                       const uint8_t *bdaddr)
+{
+       struct bt_hci_rsp_io_capability_request_neg_reply rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       memcpy(rsp.bdaddr, bdaddr, 6);
+       cmd_complete(btdev, BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY,
+                                                       &rsp, sizeof(rsp));
+}
+
+static void ssp_complete(struct btdev *btdev, const uint8_t *bdaddr,
+                                               uint8_t status, bool wait)
+{
+       struct bt_hci_evt_simple_pairing_complete iev, aev;
+       struct bt_hci_evt_auth_complete auth;
+       struct btdev *remote = btdev->conn;
+       struct btdev *init, *accp;
+
+       if (!remote)
+               return;
+
+       btdev->ssp_status = status;
+       btdev->ssp_auth_complete = true;
+
+       if (!remote->ssp_auth_complete && wait)
+               return;
+
+       if (status == BT_HCI_ERR_SUCCESS &&
+                               remote->ssp_status != BT_HCI_ERR_SUCCESS)
+               status = remote->ssp_status;
+
+       iev.status = status;
+       aev.status = status;
+
+       if (btdev->auth_init) {
+               init = btdev;
+               accp = remote;
+               memcpy(iev.bdaddr, bdaddr, 6);
+               memcpy(aev.bdaddr, btdev->bdaddr, 6);
+       } else {
+               init = remote;
+               accp = btdev;
+               memcpy(iev.bdaddr, btdev->bdaddr, 6);
+               memcpy(aev.bdaddr, bdaddr, 6);
+       }
+
+       send_event(init, BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE, &iev,
+                                                               sizeof(iev));
+       send_event(accp, BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE, &aev,
+                                                               sizeof(aev));
+
+       if (status == BT_HCI_ERR_SUCCESS) {
+               link_key_notify(init, iev.bdaddr, LINK_KEY_DUMMY);
+               link_key_notify(accp, aev.bdaddr, LINK_KEY_DUMMY);
+       }
+
+       auth.status = status;
+       auth.handle = cpu_to_le16(42);
+       send_event(init, BT_HCI_EVT_AUTH_COMPLETE, &auth, sizeof(auth));
+}
+
+static void le_send_adv_report(struct btdev *btdev, const struct btdev *remote,
+                                                               uint8_t type)
 {
        struct __packed {
                uint8_t subevent;
@@ -986,20 +1561,45 @@ static void le_send_adv_report(struct btdev *btdev, const struct btdev *remote)
 
        memset(&meta_event.lar, 0, sizeof(meta_event.lar));
        meta_event.lar.num_reports = 1;
-       memcpy(meta_event.lar.addr, remote->bdaddr, 6);
-       meta_event.lar.data_len = remote->le_adv_data_len;
-       memcpy(meta_event.lar.data, remote->le_adv_data,
+       meta_event.lar.event_type = type;
+       meta_event.lar.addr_type = remote->le_adv_own_addr;
+       memcpy(meta_event.lar.addr, adv_addr(remote), 6);
+
+       /* Scan or advertising response */
+       if (type == 0x04) {
+               meta_event.lar.data_len = remote->le_scan_data_len;
+               memcpy(meta_event.lar.data, remote->le_scan_data,
+                                               meta_event.lar.data_len);
+       } else {
+               meta_event.lar.data_len = remote->le_adv_data_len;
+               memcpy(meta_event.lar.data, remote->le_adv_data,
                                                meta_event.lar.data_len);
+       }
        /* Not available */
        meta_event.raw[10 + meta_event.lar.data_len] = 127;
        send_event(btdev, BT_HCI_EVT_LE_META_EVENT, &meta_event,
                                        1 + 10 + meta_event.lar.data_len + 1);
 }
 
+static uint8_t get_adv_report_type(uint8_t adv_type)
+{
+       /*
+        * Connectable low duty cycle directed advertising creates a
+        * connectable directed advertising report type.
+        */
+       if (adv_type == 0x04)
+               return 0x01;
+
+       return adv_type;
+}
+
 static void le_set_adv_enable_complete(struct btdev *btdev)
 {
+       uint8_t report_type;
        int i;
 
+       report_type = get_adv_report_type(btdev->le_adv_type);
+
        for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
                if (!btdev_list[i] || btdev_list[i] == btdev)
                        continue;
@@ -1007,7 +1607,17 @@ static void le_set_adv_enable_complete(struct btdev *btdev)
                if (!btdev_list[i]->le_scan_enable)
                        continue;
 
-               le_send_adv_report(btdev_list[i], btdev);
+               if (!adv_match(btdev_list[i], btdev))
+                       continue;
+
+               le_send_adv_report(btdev_list[i], btdev, report_type);
+
+               if (btdev_list[i]->le_scan_type != 0x01)
+                       continue;
+
+               /* ADV_IND & ADV_SCAN_IND generate a scan response */
+               if (btdev->le_adv_type == 0x00 || btdev->le_adv_type == 0x02)
+                       le_send_adv_report(btdev_list[i], btdev, 0x04);
        }
 }
 
@@ -1016,13 +1626,27 @@ static void le_set_scan_enable_complete(struct btdev *btdev)
        int i;
 
        for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+               uint8_t report_type;
+
                if (!btdev_list[i] || btdev_list[i] == btdev)
                        continue;
 
                if (!btdev_list[i]->le_adv_enable)
                        continue;
 
-               le_send_adv_report(btdev, btdev_list[i]);
+               if (!adv_match(btdev, btdev_list[i]))
+                       continue;
+
+               report_type = get_adv_report_type(btdev_list[i]->le_adv_type);
+               le_send_adv_report(btdev, btdev_list[i], report_type);
+
+               if (btdev->le_scan_type != 0x01)
+                       continue;
+
+               /* ADV_IND & ADV_SCAN_IND generate a scan response */
+               if (btdev_list[i]->le_adv_type == 0x00 ||
+                                       btdev_list[i]->le_adv_type == 0x02)
+                       le_send_adv_report(btdev, btdev_list[i], 0x04);
        }
 }
 
@@ -1074,6 +1698,32 @@ static void le_encrypt_complete(struct btdev *btdev)
        send_event(remote, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
 }
 
+static void ltk_neg_reply_complete(struct btdev *btdev)
+{
+       struct bt_hci_rsp_le_ltk_req_neg_reply rp;
+       struct bt_hci_evt_encrypt_change ev;
+       struct btdev *remote = btdev->conn;
+
+       memset(&rp, 0, sizeof(rp));
+       rp.handle = cpu_to_le16(42);
+
+       if (!remote) {
+               rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+               cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, &rp,
+                                                       sizeof(rp));
+               return;
+       }
+
+       rp.status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY, &rp, sizeof(rp));
+
+       memset(&ev, 0, sizeof(ev));
+       ev.status = BT_HCI_ERR_PIN_OR_KEY_MISSING;
+       ev.handle = cpu_to_le16(42);
+
+       send_event(remote, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
+}
+
 static void default_cmd(struct btdev *btdev, uint16_t opcode,
                                                const void *data, uint8_t len)
 {
@@ -1095,12 +1745,20 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
        const struct bt_hci_cmd_write_afh_assessment_mode *waam;
        const struct bt_hci_cmd_write_ext_inquiry_response *weir;
        const struct bt_hci_cmd_write_simple_pairing_mode *wspm;
+       const struct bt_hci_cmd_io_capability_request_reply *icrr;
+       const struct bt_hci_cmd_io_capability_request_reply *icrnr;
        const struct bt_hci_cmd_write_le_host_supported *wlhs;
+       const struct bt_hci_cmd_write_secure_conn_support *wscs;
        const struct bt_hci_cmd_set_event_mask_page2 *semp2;
        const struct bt_hci_cmd_le_set_event_mask *lsem;
+       const struct bt_hci_cmd_le_set_random_address *lsra;
+       const struct bt_hci_cmd_le_set_adv_parameters *lsap;
        const struct bt_hci_cmd_le_set_adv_data *lsad;
+       const struct bt_hci_cmd_le_set_scan_rsp_data *lssrd;
        const struct bt_hci_cmd_setup_sync_conn *ssc;
+       const struct bt_hci_cmd_write_ssp_debug_mode *wsdm;
        const struct bt_hci_cmd_le_set_adv_enable *lsae;
+       const struct bt_hci_cmd_le_set_scan_parameters *lssp;
        const struct bt_hci_cmd_le_set_scan_enable *lsse;
        const struct bt_hci_cmd_le_start_encrypt *lse;
        const struct bt_hci_cmd_le_ltk_req_reply *llrr;
@@ -1128,6 +1786,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
        struct bt_hci_rsp_read_local_oob_data rlod;
        struct bt_hci_rsp_read_inquiry_resp_tx_power rirtp;
        struct bt_hci_rsp_read_le_host_supported rlhs;
+       struct bt_hci_rsp_read_secure_conn_support rscs;
+       struct bt_hci_rsp_read_local_oob_ext_data rloed;
        struct bt_hci_rsp_read_sync_train_params rstp;
        struct bt_hci_rsp_read_local_version rlv;
        struct bt_hci_rsp_read_local_commands rlc;
@@ -1147,6 +1807,12 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
        struct bt_hci_rsp_le_rand lr;
        struct bt_hci_rsp_le_test_end lte;
        struct bt_hci_rsp_remote_name_request_cancel rnrc_rsp;
+       struct bt_hci_rsp_link_key_request_reply lkrr_rsp;
+       struct bt_hci_rsp_link_key_request_neg_reply lkrnr_rsp;
+       struct bt_hci_rsp_pin_code_request_neg_reply pcrr_rsp;
+       struct bt_hci_rsp_pin_code_request_neg_reply pcrnr_rsp;
+       struct bt_hci_rsp_user_confirm_request_reply ucrr_rsp;
+       struct bt_hci_rsp_user_confirm_request_neg_reply ucrnr_rsp;
        uint8_t status, page;
 
        switch (opcode) {
@@ -1159,8 +1825,7 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
        case BT_HCI_CMD_INQUIRY_CANCEL:
                if (btdev->type == BTDEV_TYPE_LE)
                        goto unsupported;
-               status = BT_HCI_ERR_SUCCESS;
-               cmd_complete(btdev, opcode, &status, sizeof(status));
+               inquiry_cancel(btdev);
                break;
 
        case BT_HCI_CMD_CREATE_CONN:
@@ -1191,6 +1856,50 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
                cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
                break;
 
+       case BT_HCI_CMD_LINK_KEY_REQUEST_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               lkrr_rsp.status = BT_HCI_ERR_SUCCESS;
+               memcpy(lkrr_rsp.bdaddr, data, 6);
+               cmd_complete(btdev, opcode, &lkrr_rsp, sizeof(lkrr_rsp));
+               break;
+
+       case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               lkrnr_rsp.status = BT_HCI_ERR_SUCCESS;
+               memcpy(lkrnr_rsp.bdaddr, data, 6);
+               cmd_complete(btdev, opcode, &lkrnr_rsp, sizeof(lkrnr_rsp));
+               break;
+
+       case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               pcrr_rsp.status = BT_HCI_ERR_SUCCESS;
+               memcpy(pcrr_rsp.bdaddr, data, 6);
+               cmd_complete(btdev, opcode, &pcrr_rsp, sizeof(pcrr_rsp));
+               break;
+
+       case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               pcrnr_rsp.status = BT_HCI_ERR_SUCCESS;
+               memcpy(pcrnr_rsp.bdaddr, data, 6);
+               cmd_complete(btdev, opcode, &pcrnr_rsp, sizeof(pcrnr_rsp));
+               break;
+
+       case BT_HCI_CMD_AUTH_REQUESTED:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               break;
+
+       case BT_HCI_CMD_SET_CONN_ENCRYPT:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+               break;
+
        case BT_HCI_CMD_REMOTE_NAME_REQUEST:
                if (btdev->type == BTDEV_TYPE_LE)
                        goto unsupported;
@@ -1561,6 +2270,43 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
                cmd_complete(btdev, opcode, &status, sizeof(status));
                break;
 
+       case BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               icrr = data;
+               io_cap_req_reply_complete(btdev, icrr->bdaddr,
+                                                       icrr->capability,
+                                                       icrr->oob_data,
+                                                       icrr->authentication);
+               break;
+
+       case BT_HCI_CMD_IO_CAPABILITY_REQUEST_NEG_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               icrnr = data;
+               io_cap_req_neg_reply_complete(btdev, icrnr->bdaddr);
+               ssp_complete(btdev, icrnr->bdaddr, BT_HCI_ERR_AUTH_FAILURE,
+                                                                       false);
+               break;
+
+       case BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               ucrr_rsp.status = BT_HCI_ERR_SUCCESS;
+               memcpy(ucrr_rsp.bdaddr, data, 6);
+               cmd_complete(btdev, opcode, &ucrr_rsp, sizeof(ucrr_rsp));
+               ssp_complete(btdev, data, BT_HCI_ERR_SUCCESS, true);
+               break;
+
+       case BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               ucrnr_rsp.status = BT_HCI_ERR_SUCCESS;
+               memcpy(ucrnr_rsp.bdaddr, data, 6);
+               cmd_complete(btdev, opcode, &ucrnr_rsp, sizeof(ucrnr_rsp));
+               ssp_complete(btdev, data, BT_HCI_ERR_AUTH_FAILURE, true);
+               break;
+
        case BT_HCI_CMD_READ_LOCAL_OOB_DATA:
                if (btdev->type == BTDEV_TYPE_LE)
                        goto unsupported;
@@ -1595,6 +2341,30 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
                cmd_complete(btdev, opcode, &status, sizeof(status));
                break;
 
+       case BT_HCI_CMD_READ_SECURE_CONN_SUPPORT:
+               if (btdev->type != BTDEV_TYPE_BREDRLE)
+                       goto unsupported;
+               rscs.status = BT_HCI_ERR_SUCCESS;
+               rscs.support = btdev->secure_conn_support;
+               cmd_complete(btdev, opcode, &rscs, sizeof(rscs));
+               break;
+
+       case BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT:
+               if (btdev->type != BTDEV_TYPE_BREDRLE)
+                       goto unsupported;
+               wscs = data;
+               btdev->secure_conn_support = wscs->support;
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
+       case BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA:
+               if (btdev->type != BTDEV_TYPE_BREDRLE)
+                       goto unsupported;
+               rloed.status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &rloed, sizeof(rloed));
+               break;
+
        case BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS:
                if (btdev->type != BTDEV_TYPE_BREDRLE)
                        goto unsupported;
@@ -1650,13 +2420,7 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
                        break;
                case 0x01:
                        rlef.status = BT_HCI_ERR_SUCCESS;
-                       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;
+                       btdev_get_host_features(btdev, rlef.features);
                        break;
                case 0x02:
                        rlef.status = BT_HCI_ERR_SUCCESS;
@@ -1766,13 +2530,32 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
                cmd_complete(btdev, opcode, &lrlf, sizeof(lrlf));
                break;
 
+       case BT_HCI_CMD_LE_SET_RANDOM_ADDRESS:
+               if (btdev->type == BTDEV_TYPE_BREDR)
+                       goto unsupported;
+               lsra = data;
+               memcpy(btdev->random_addr, lsra->addr, 6);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
        case BT_HCI_CMD_LE_SET_ADV_PARAMETERS:
                if (btdev->type == BTDEV_TYPE_BREDR)
                        goto unsupported;
-               if (btdev->le_adv_enable)
+
+               if (btdev->le_adv_enable) {
                        status = BT_HCI_ERR_COMMAND_DISALLOWED;
-               else
-                       status = BT_HCI_ERR_SUCCESS;
+                       cmd_complete(btdev, opcode, &status, sizeof(status));
+                       break;
+               }
+
+               lsap = data;
+               btdev->le_adv_type = lsap->type;
+               btdev->le_adv_own_addr = lsap->own_addr_type;
+               btdev->le_adv_direct_addr_type = lsap->direct_addr_type;
+               memcpy(btdev->le_adv_direct_addr, lsap->direct_addr, 6);
+
+               status = BT_HCI_ERR_SUCCESS;
                cmd_complete(btdev, opcode, &status, sizeof(status));
                break;
 
@@ -1802,10 +2585,17 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
        case BT_HCI_CMD_LE_SET_SCAN_PARAMETERS:
                if (btdev->type == BTDEV_TYPE_BREDR)
                        goto unsupported;
+
+               lssp = data;
+
                if (btdev->le_scan_enable)
                        status = BT_HCI_ERR_COMMAND_DISALLOWED;
-               else
+               else {
                        status = BT_HCI_ERR_SUCCESS;
+                       btdev->le_scan_type = lssp->type;
+                       btdev->le_scan_own_addr_type = lssp->own_addr_type;
+               }
+
                cmd_complete(btdev, opcode, &status, sizeof(status));
                break;
 
@@ -1839,6 +2629,13 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
                cmd_complete(btdev, opcode, &lrwls, sizeof(lrwls));
                break;
 
+       case BT_HCI_CMD_LE_CLEAR_WHITE_LIST:
+               if (btdev->type == BTDEV_TYPE_BREDR)
+                       goto unsupported;
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
        case BT_HCI_CMD_LE_READ_SUPPORTED_STATES:
                if (btdev->type == BTDEV_TYPE_BREDR)
                        goto unsupported;
@@ -1857,18 +2654,21 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
                cmd_complete(btdev, opcode, &status, sizeof(status));
                break;
 
+       case BT_HCI_CMD_LE_SET_SCAN_RSP_DATA:
+               if (btdev->type == BTDEV_TYPE_BREDR)
+                       goto unsupported;
+               lssrd = data;
+               btdev->le_scan_data_len = lssrd->len;
+               memcpy(btdev->le_scan_data, lssrd->data, 31);
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
        case BT_HCI_CMD_LE_RAND:
                if (btdev->type == BTDEV_TYPE_BREDR)
                        goto unsupported;
                lr.status = BT_HCI_ERR_SUCCESS;
-               lr.number[0] = rand();
-               lr.number[1] = rand();
-               lr.number[2] = rand();
-               lr.number[3] = rand();
-               lr.number[4] = rand();
-               lr.number[5] = rand();
-               lr.number[6] = rand();
-               lr.number[7] = rand();
+               lr.number = rand();
                cmd_complete(btdev, opcode, &lr, sizeof(lr));
                break;
 
@@ -1888,6 +2688,12 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
                le_encrypt_complete(btdev);
                break;
 
+       case BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY:
+               if (btdev->type == BTDEV_TYPE_BREDR)
+                       goto unsupported;
+               ltk_neg_reply_complete(btdev);
+               break;
+
        case BT_HCI_CMD_SETUP_SYNC_CONN:
                if (btdev->type == BTDEV_TYPE_LE)
                        goto unsupported;
@@ -1909,6 +2715,15 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
                cmd_complete(btdev, opcode, &status, sizeof(status));
                break;
 
+       case BT_HCI_CMD_WRITE_SSP_DEBUG_MODE:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       goto unsupported;
+               wsdm = data;
+               btdev->ssp_debug_mode = wsdm->mode;
+               status = BT_HCI_ERR_SUCCESS;
+               cmd_complete(btdev, opcode, &status, sizeof(status));
+               break;
+
        case BT_HCI_CMD_LE_RECEIVER_TEST:
                if (btdev->type == BTDEV_TYPE_BREDR)
                        goto unsupported;
@@ -1951,6 +2766,12 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
        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_auth_requested *ar;
+       const struct bt_hci_cmd_set_conn_encrypt *sce;
+       const struct bt_hci_cmd_link_key_request_reply *lkrr;
+       const struct bt_hci_cmd_link_key_request_neg_reply *lkrnr;
+       const struct bt_hci_cmd_pin_code_request_neg_reply *pcrnr;
+       const struct bt_hci_cmd_pin_code_request_reply *pcrr;
        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;
@@ -1962,7 +2783,7 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
        case BT_HCI_CMD_INQUIRY:
                if (btdev->type == BTDEV_TYPE_LE)
                        return;
-               inquiry_complete(btdev, BT_HCI_ERR_SUCCESS);
+               inquiry_cmd(btdev, data);
                break;
 
        case BT_HCI_CMD_CREATE_CONN:
@@ -1988,7 +2809,7 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
                if (btdev->type == BTDEV_TYPE_LE)
                        return;
                acr = data;
-               conn_complete(btdev, acr->bdaddr, BT_HCI_ERR_SUCCESS);
+               accept_conn_request_complete(btdev, acr->bdaddr);
                break;
 
        case BT_HCI_CMD_REJECT_CONN_REQUEST:
@@ -1998,6 +2819,54 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
                conn_complete(btdev, rcr->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
                break;
 
+       case BT_HCI_CMD_LINK_KEY_REQUEST_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       return;
+               lkrr = data;
+               link_key_req_reply_complete(btdev, lkrr->bdaddr, lkrr->link_key);
+               break;
+
+       case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       return;
+               lkrnr = data;
+               link_key_req_neg_reply_complete(btdev, lkrnr->bdaddr);
+               break;
+
+       case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       return;
+               pcrr = data;
+               pin_code_req_reply_complete(btdev, pcrr->bdaddr, pcrr->pin_len,
+                                                       pcrr->pin_code);
+               break;
+
+       case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       return;
+               pcrnr = data;
+               pin_code_req_neg_reply_complete(btdev, pcrnr->bdaddr);
+               break;
+
+       case BT_HCI_CMD_AUTH_REQUESTED:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       return;
+               ar = data;
+               auth_request_complete(btdev, le16_to_cpu(ar->handle));
+               break;
+
+       case BT_HCI_CMD_SET_CONN_ENCRYPT:
+               if (btdev->type == BTDEV_TYPE_LE)
+                       return;
+               sce = data;
+               if (btdev->conn) {
+                       encrypt_change(btdev, sce->encr_mode,
+                                                       BT_HCI_ERR_SUCCESS);
+                       encrypt_change(btdev->conn, sce->encr_mode,
+                                                       BT_HCI_ERR_SUCCESS);
+               }
+               break;
+
        case BT_HCI_CMD_REMOTE_NAME_REQUEST:
                if (btdev->type == BTDEV_TYPE_LE)
                        return;
@@ -2037,6 +2906,7 @@ static void default_cmd_completion(struct btdev *btdev, uint16_t opcode,
                if (btdev->type == BTDEV_TYPE_BREDR)
                        return;
                lecc = data;
+               btdev->le_scan_own_addr_type = lecc->own_addr_type;
                le_conn_request(btdev, lecc->peer_addr);
                break;
        }
index 10e7a05..298edcf 100644 (file)
@@ -35,7 +35,9 @@
 
 #include "bluetooth/bluetooth.h"
 
+#include "src/shared/util.h"
 #include "monitor/bt.h"
+#include "monitor/rfcomm.h"
 #include "bthost.h"
 
 /* ACL handle and flags pack/unpack */
 #define acl_handle(h)          (h & 0x0fff)
 #define acl_flags(h)           (h >> 12)
 
-#define le16_to_cpu(val) (val)
-#define le32_to_cpu(val) (val)
-#define cpu_to_le16(val) (val)
-#define cpu_to_le32(val) (val)
+/* RFCOMM setters */
+#define RFCOMM_ADDR(cr, dlci)  (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
+#define RFCOMM_CTRL(type, pf)  (((type & 0xef) | (pf << 4)))
+#define RFCOMM_LEN8(len)       (((len) << 1) | 1)
+#define RFCOMM_LEN16(len)      ((len) << 1)
+#define RFCOMM_MCC_TYPE(cr, type)      (((type << 2) | (cr << 1) | 0x01))
+
+/* RFCOMM FCS calculation */
+#define CRC(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
+
+static unsigned char rfcomm_crc_table[256] = {
+       0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
+       0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
+       0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
+       0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
+
+       0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
+       0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
+       0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
+       0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
+
+       0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
+       0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
+       0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
+       0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
+
+       0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
+       0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
+       0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
+       0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
+
+       0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
+       0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
+       0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
+       0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
+
+       0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
+       0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
+       0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
+       0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
+
+       0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
+       0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
+       0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
+       0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
+
+       0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
+       0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
+       0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
+       0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
+};
+
+static uint8_t rfcomm_fcs2(uint8_t *data)
+{
+       return 0xff - rfcomm_crc_table[CRC(data) ^ data[2]];
+}
+
+static uint8_t rfcomm_fcs(uint8_t *data)
+{
+       return 0xff - CRC(data);
+}
 
 struct cmd {
        struct cmd *next;
@@ -67,21 +126,40 @@ struct cid_hook {
        struct cid_hook *next;
 };
 
+struct rfcomm_chan_hook {
+       uint8_t channel;
+       bthost_rfcomm_chan_hook_func_t func;
+       void *user_data;
+       struct rfcomm_chan_hook *next;
+};
+
 struct btconn {
        uint16_t handle;
+       uint8_t bdaddr[6];
        uint8_t addr_type;
+       uint8_t encr_mode;
        uint16_t next_cid;
        struct l2conn *l2conns;
+       struct rcconn *rcconns;
        struct cid_hook *cid_hooks;
+       struct rfcomm_chan_hook *rfcomm_chan_hooks;
        struct btconn *next;
+       void *smp_data;
 };
 
 struct l2conn {
        uint16_t scid;
        uint16_t dcid;
+       uint16_t psm;
        struct l2conn *next;
 };
 
+struct rcconn {
+       uint8_t channel;
+       uint16_t scid;
+       struct rcconn *next;
+};
+
 struct l2cap_pending_req {
        uint8_t ident;
        bthost_l2cap_rsp_cb cb;
@@ -89,6 +167,27 @@ struct l2cap_pending_req {
        struct l2cap_pending_req *next;
 };
 
+struct l2cap_conn_cb_data {
+       uint16_t psm;
+       bthost_l2cap_connect_cb func;
+       void *user_data;
+       struct l2cap_conn_cb_data *next;
+};
+
+struct rfcomm_conn_cb_data {
+       uint8_t channel;
+       bthost_rfcomm_connect_cb func;
+       void *user_data;
+       struct rfcomm_conn_cb_data *next;
+};
+
+struct rfcomm_connection_data {
+       uint8_t channel;
+       struct btconn *conn;
+       bthost_rfcomm_connect_cb cb;
+       void *user_data;
+};
+
 struct bthost {
        uint8_t bdaddr[6];
        bthost_send_func send_handler;
@@ -100,8 +199,17 @@ struct bthost {
        void *cmd_complete_data;
        bthost_new_conn_cb new_conn_cb;
        void *new_conn_data;
-       uint16_t server_psm;
+       struct rfcomm_connection_data *rfcomm_conn_data;
+       struct l2cap_conn_cb_data *new_l2cap_conn_data;
+       struct rfcomm_conn_cb_data *new_rfcomm_conn_data;
        struct l2cap_pending_req *l2reqs;
+       uint8_t pin[16];
+       uint8_t pin_len;
+       uint8_t io_capability;
+       uint8_t auth_req;
+       bool reject_user_confirm;
+       void *smp_data;
+       bool conn_init;
 };
 
 struct bthost *bthost_create(void)
@@ -124,6 +232,9 @@ static void l2conn_free(struct l2conn *conn)
 
 static void btconn_free(struct btconn *conn)
 {
+       if (conn->smp_data)
+               smp_conn_del(conn->smp_data);
+
        while (conn->l2conns) {
                struct l2conn *l2conn = conn->l2conns;
 
@@ -138,6 +249,20 @@ static void btconn_free(struct btconn *conn)
                free(hook);
        }
 
+       while (conn->rcconns) {
+               struct rcconn *rcconn = conn->rcconns;
+
+               conn->rcconns = rcconn->next;
+               free(rcconn);
+       }
+
+       while (conn->rfcomm_chan_hooks) {
+               struct rfcomm_chan_hook *hook = conn->rfcomm_chan_hooks;
+
+               conn->rfcomm_chan_hooks = hook->next;
+               free(hook);
+       }
+
        free(conn);
 }
 
@@ -153,22 +278,75 @@ static struct btconn *bthost_find_conn(struct bthost *bthost, uint16_t handle)
        return NULL;
 }
 
-static void bthost_add_l2cap_conn(struct bthost *bthost, struct btconn *conn,
-                                               uint16_t scid, uint16_t dcid)
+static struct btconn *bthost_find_conn_by_bdaddr(struct bthost *bthost,
+                                                       const uint8_t *bdaddr)
+{
+       struct btconn *conn;
+
+       for (conn = bthost->conns; conn != NULL; conn = conn->next) {
+               if (!memcmp(conn->bdaddr, bdaddr, 6))
+                       return conn;
+       }
+
+       return NULL;
+}
+
+static struct l2conn *bthost_add_l2cap_conn(struct bthost *bthost,
+                                               struct btconn *conn,
+                                               uint16_t scid, uint16_t dcid,
+                                               uint16_t psm)
 {
        struct l2conn *l2conn;
 
        l2conn = malloc(sizeof(*l2conn));
        if (!l2conn)
-               return;
+               return NULL;
 
        memset(l2conn, 0, sizeof(*l2conn));
 
+       l2conn->psm = psm;
        l2conn->scid = scid;
        l2conn->dcid = dcid;
 
        l2conn->next = conn->l2conns;
        conn->l2conns = l2conn;
+
+       return l2conn;
+}
+
+static struct rcconn *bthost_add_rfcomm_conn(struct bthost *bthost,
+                                               struct btconn *conn,
+                                               struct l2conn *l2conn,
+                                               uint8_t channel)
+{
+       struct rcconn *rcconn;
+
+       rcconn = malloc(sizeof(*rcconn));
+       if (!rcconn)
+               return NULL;
+
+       memset(rcconn, 0, sizeof(*rcconn));
+
+       rcconn->channel = channel;
+       rcconn->scid = l2conn->scid;
+
+       rcconn->next = conn->rcconns;
+       conn->rcconns = rcconn;
+
+       return rcconn;
+}
+
+static struct rcconn *btconn_find_rfcomm_conn_by_channel(struct btconn *conn,
+                                                               uint8_t chan)
+{
+       struct rcconn *rcconn;
+
+       for (rcconn = conn->rcconns; rcconn != NULL; rcconn = rcconn->next) {
+               if (rcconn->channel == chan)
+                       return rcconn;
+       }
+
+       return NULL;
 }
 
 static struct l2conn *btconn_find_l2cap_conn_by_scid(struct btconn *conn,
@@ -184,15 +362,43 @@ static struct l2conn *btconn_find_l2cap_conn_by_scid(struct btconn *conn,
        return NULL;
 }
 
-void bthost_destroy(struct bthost *bthost)
+static struct l2cap_conn_cb_data *bthost_find_l2cap_cb_by_psm(
+                                       struct bthost *bthost, uint16_t psm)
 {
-       struct cmd *cmd;
+       struct l2cap_conn_cb_data *cb;
+
+       for (cb = bthost->new_l2cap_conn_data; cb != NULL; cb = cb->next) {
+               if (cb->psm == psm)
+                       return cb;
+       }
+
+       return NULL;
+}
+
+static struct rfcomm_conn_cb_data *bthost_find_rfcomm_cb_by_channel(
+                                       struct bthost *bthost, uint8_t channel)
+{
+       struct rfcomm_conn_cb_data *cb;
+
+       for (cb = bthost->new_rfcomm_conn_data; cb != NULL; cb = cb->next) {
+               if (cb->channel == channel)
+                       return cb;
+       }
 
+       return NULL;
+}
+
+void bthost_destroy(struct bthost *bthost)
+{
        if (!bthost)
                return;
 
-       for (cmd = bthost->cmd_q.tail; cmd != NULL; cmd = cmd->next)
+       while (bthost->cmd_q.tail) {
+               struct cmd *cmd = bthost->cmd_q.tail;
+
+               bthost->cmd_q.tail = cmd->prev;
                free(cmd);
+       }
 
        while (bthost->conns) {
                struct btconn *conn = bthost->conns;
@@ -209,6 +415,23 @@ void bthost_destroy(struct bthost *bthost)
                free(req);
        }
 
+       while (bthost->new_l2cap_conn_data) {
+               struct l2cap_conn_cb_data *cb = bthost->new_l2cap_conn_data;
+
+               bthost->new_l2cap_conn_data = cb->next;
+               free(cb);
+       }
+
+       while (bthost->new_rfcomm_conn_data) {
+               struct rfcomm_conn_cb_data *cb = bthost->new_rfcomm_conn_data;
+
+               bthost->new_rfcomm_conn_data = cb->next;
+               free(cb);
+       }
+
+       if (bthost->rfcomm_conn_data)
+               free(bthost->rfcomm_conn_data);
+
        free(bthost);
 }
 
@@ -239,6 +462,8 @@ static void queue_command(struct bthost *bthost, const void *data,
 
        if (cmd_q->tail)
                cmd_q->tail->next = cmd;
+       else
+               cmd_q->head = cmd;
 
        cmd->prev = cmd_q->tail;
        cmd_q->tail = cmd;
@@ -374,6 +599,15 @@ bool bthost_l2cap_req(struct bthost *bthost, uint16_t handle, uint8_t code,
        if (!conn)
                return false;
 
+       if (code == BT_L2CAP_PDU_CONN_REQ &&
+                       len == sizeof(struct bt_l2cap_pdu_conn_req)) {
+               const struct bt_l2cap_pdu_conn_req *req = data;
+
+               bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(req->scid),
+                                                       le16_to_cpu(req->scid),
+                                                       le16_to_cpu(req->psm));
+       }
+
        ident = l2cap_sig_send(bthost, conn, code, 0, data, len);
        if (!ident)
                return false;
@@ -431,7 +665,7 @@ static void send_command(struct bthost *bthost, uint16_t opcode,
 static void next_cmd(struct bthost *bthost)
 {
        struct cmd_queue *cmd_q = &bthost->cmd_q;
-       struct cmd *cmd = cmd_q->tail;
+       struct cmd *cmd = cmd_q->head;
        struct cmd *next;
 
        if (!cmd)
@@ -447,8 +681,10 @@ static void next_cmd(struct bthost *bthost)
 
        if (next)
                next->prev = NULL;
+       else
+               cmd_q->tail = NULL;
 
-       cmd_q->tail = next;
+       cmd_q->head = next;
 
        free(cmd);
 }
@@ -493,6 +729,24 @@ static void evt_cmd_complete(struct bthost *bthost, const void *data,
                break;
        case BT_HCI_CMD_LE_SET_ADV_ENABLE:
                break;
+       case BT_HCI_CMD_LE_SET_ADV_PARAMETERS:
+               break;
+       case BT_HCI_CMD_PIN_CODE_REQUEST_REPLY:
+               break;
+       case BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY:
+               break;
+       case BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY:
+               break;
+       case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+               break;
+       case BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY:
+               break;
+       case BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY:
+               break;
+       case BT_HCI_CMD_LE_LTK_REQ_REPLY:
+               break;
+       case BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY:
+               break;
        default:
                printf("Unhandled cmd_complete opcode 0x%04x\n", opcode);
                break;
@@ -541,9 +795,11 @@ static void evt_conn_request(struct bthost *bthost, const void *data,
                                                                sizeof(cmd));
 }
 
-static void init_conn(struct bthost *bthost, uint16_t handle, uint8_t addr_type)
+static void init_conn(struct bthost *bthost, uint16_t handle,
+                               const uint8_t *bdaddr, uint8_t addr_type)
 {
        struct btconn *conn;
+       const uint8_t *ia, *ra;
 
        conn = malloc(sizeof(*conn));
        if (!conn)
@@ -551,12 +807,24 @@ static void init_conn(struct bthost *bthost, uint16_t handle, uint8_t addr_type)
 
        memset(conn, 0, sizeof(*conn));
        conn->handle = handle;
+       memcpy(conn->bdaddr, bdaddr, 6);
        conn->addr_type = addr_type;
        conn->next_cid = 0x0040;
 
        conn->next = bthost->conns;
        bthost->conns = conn;
 
+       if (bthost->conn_init) {
+               ia = bthost->bdaddr;
+               ra = conn->bdaddr;
+       } else {
+               ia = conn->bdaddr;
+               ra = bthost->bdaddr;
+       }
+
+       conn->smp_data = smp_conn_add(bthost->smp_data, handle, ia, ra,
+                                                       bthost->conn_init);
+
        if (bthost->new_conn_cb)
                bthost->new_conn_cb(conn->handle, bthost->new_conn_data);
 }
@@ -572,7 +840,7 @@ static void evt_conn_complete(struct bthost *bthost, const void *data,
        if (ev->status)
                return;
 
-       init_conn(bthost, le16_to_cpu(ev->handle), BDADDR_BREDR);
+       init_conn(bthost, le16_to_cpu(ev->handle), ev->bdaddr, BDADDR_BREDR);
 }
 
 static void evt_disconn_complete(struct bthost *bthost, const void *data,
@@ -611,6 +879,163 @@ static void evt_num_completed_packets(struct bthost *bthost, const void *data,
                return;
 }
 
+static void evt_auth_complete(struct bthost *bthost, const void *data,
+                                                               uint8_t len)
+{
+       const struct bt_hci_evt_auth_complete *ev = data;
+       struct bt_hci_cmd_set_conn_encrypt cp;
+
+       if (len < sizeof(*ev))
+               return;
+
+       if (ev->status)
+               return;
+
+       cp.handle = ev->handle;
+       cp.encr_mode = 0x01;
+
+       send_command(bthost, BT_HCI_CMD_SET_CONN_ENCRYPT, &cp, sizeof(cp));
+}
+
+static void evt_pin_code_request(struct bthost *bthost, const void *data,
+                                                               uint8_t len)
+{
+       const struct bt_hci_evt_pin_code_request *ev = data;
+
+       if (len < sizeof(*ev))
+               return;
+
+       if (bthost->pin_len > 0) {
+               struct bt_hci_cmd_pin_code_request_reply cp;
+
+               memset(&cp, 0, sizeof(cp));
+               memcpy(cp.bdaddr, ev->bdaddr, 6);
+               cp.pin_len = bthost->pin_len;
+               memcpy(cp.pin_code, bthost->pin, bthost->pin_len);
+
+               send_command(bthost, BT_HCI_CMD_PIN_CODE_REQUEST_REPLY,
+                                                       &cp, sizeof(cp));
+       } else {
+               struct bt_hci_cmd_pin_code_request_neg_reply cp;
+
+               memcpy(cp.bdaddr, ev->bdaddr, 6);
+               send_command(bthost, BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY,
+                                                       &cp, sizeof(cp));
+       }
+}
+
+static void evt_link_key_request(struct bthost *bthost, const void *data,
+                                                               uint8_t len)
+{
+       const struct bt_hci_evt_link_key_request *ev = data;
+       struct bt_hci_cmd_link_key_request_neg_reply cp;
+
+       if (len < sizeof(*ev))
+               return;
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(cp.bdaddr, ev->bdaddr, 6);
+
+       send_command(bthost, BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY,
+                                                       &cp, sizeof(cp));
+}
+
+static void evt_link_key_notify(struct bthost *bthost, const void *data,
+                                                               uint8_t len)
+{
+       const struct bt_hci_evt_link_key_notify *ev = data;
+
+       if (len < sizeof(*ev))
+               return;
+}
+
+static void evt_encrypt_change(struct bthost *bthost, const void *data,
+                                                               uint8_t len)
+{
+       const struct bt_hci_evt_encrypt_change *ev = data;
+       struct btconn *conn;
+       uint16_t handle;
+
+       if (len < sizeof(*ev))
+               return;
+
+       handle = acl_handle(ev->handle);
+       conn = bthost_find_conn(bthost, handle);
+       if (!conn)
+               return;
+
+       conn->encr_mode = ev->encr_mode;
+}
+
+static void evt_io_cap_response(struct bthost *bthost, const void *data,
+                                                               uint8_t len)
+{
+       const struct bt_hci_evt_io_capability_response *ev = data;
+       struct btconn *conn;
+
+       if (len < sizeof(*ev))
+               return;
+
+       conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr);
+       if (!conn)
+               return;
+}
+
+static void evt_io_cap_request(struct bthost *bthost, const void *data,
+                                                               uint8_t len)
+{
+       const struct bt_hci_evt_io_capability_request *ev = data;
+       struct bt_hci_cmd_io_capability_request_reply cp;
+       struct btconn *conn;
+
+       if (len < sizeof(*ev))
+               return;
+
+       conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr);
+       if (!conn)
+               return;
+
+       memcpy(cp.bdaddr, ev->bdaddr, 6);
+       cp.capability = bthost->io_capability;
+       cp.oob_data = 0x00;
+       cp.authentication = bthost->auth_req;
+
+       send_command(bthost, BT_HCI_CMD_IO_CAPABILITY_REQUEST_REPLY,
+                                                       &cp, sizeof(cp));
+}
+
+static void evt_user_confirm_request(struct bthost *bthost, const void *data,
+                                                               uint8_t len)
+{
+       const struct bt_hci_evt_user_confirm_request *ev = data;
+       struct btconn *conn;
+
+       if (len < sizeof(*ev))
+               return;
+
+       conn = bthost_find_conn_by_bdaddr(bthost, ev->bdaddr);
+       if (!conn)
+               return;
+
+       if (bthost->reject_user_confirm) {
+               send_command(bthost, BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY,
+                                                               ev->bdaddr, 6);
+               return;
+       }
+
+       send_command(bthost, BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+                                                               ev->bdaddr, 6);
+}
+
+static void evt_simple_pairing_complete(struct bthost *bthost, const void *data,
+                                                               uint8_t len)
+{
+       const struct bt_hci_evt_simple_pairing_complete *ev = data;
+
+       if (len < sizeof(*ev))
+               return;
+}
+
 static void evt_le_conn_complete(struct bthost *bthost, const void *data,
                                                                uint8_t len)
 {
@@ -628,7 +1053,40 @@ static void evt_le_conn_complete(struct bthost *bthost, const void *data,
        else
                addr_type = BDADDR_LE_RANDOM;
 
-       init_conn(bthost, le16_to_cpu(ev->handle), addr_type);
+       init_conn(bthost, le16_to_cpu(ev->handle), ev->peer_addr, addr_type);
+}
+
+static void evt_le_ltk_request(struct bthost *bthost, const void *data,
+                                                               uint8_t len)
+{
+       const struct bt_hci_evt_le_long_term_key_request *ev = data;
+       struct bt_hci_cmd_le_ltk_req_reply cp;
+       struct bt_hci_cmd_le_ltk_req_neg_reply *neg_cp = (void *) &cp;
+       uint16_t handle, ediv;
+       uint64_t rand;
+       struct btconn *conn;
+       int err;
+
+       if (len < sizeof(*ev))
+               return;
+
+       handle = acl_handle(ev->handle);
+       conn = bthost_find_conn(bthost, handle);
+       if (!conn)
+               return;
+
+       rand = le64_to_cpu(ev->rand);
+       ediv = le16_to_cpu(ev->ediv);
+
+       cp.handle = ev->handle;
+
+       err = smp_get_ltk(conn->smp_data, rand, ediv, cp.ltk);
+       if (err < 0)
+               send_command(bthost, BT_HCI_CMD_LE_LTK_REQ_NEG_REPLY,
+                                               neg_cp, sizeof(*neg_cp));
+       else
+               send_command(bthost, BT_HCI_CMD_LE_LTK_REQ_REPLY, &cp,
+                                                               sizeof(cp));
 }
 
 static void evt_le_meta_event(struct bthost *bthost, const void *data,
@@ -644,7 +1102,11 @@ static void evt_le_meta_event(struct bthost *bthost, const void *data,
        case BT_HCI_EVT_LE_CONN_COMPLETE:
                evt_le_conn_complete(bthost, evt_data, len - 1);
                break;
+       case BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST:
+               evt_le_ltk_request(bthost, evt_data, len - 1);
+               break;
        default:
+               printf("Unsupported LE Meta event 0x%2.2x\n", *event);
                break;
        }
 }
@@ -687,6 +1149,42 @@ static void process_evt(struct bthost *bthost, const void *data, uint16_t len)
                evt_num_completed_packets(bthost, param, hdr->plen);
                break;
 
+       case BT_HCI_EVT_AUTH_COMPLETE:
+               evt_auth_complete(bthost, param, hdr->plen);
+               break;
+
+       case BT_HCI_EVT_PIN_CODE_REQUEST:
+               evt_pin_code_request(bthost, param, hdr->plen);
+               break;
+
+       case BT_HCI_EVT_LINK_KEY_REQUEST:
+               evt_link_key_request(bthost, param, hdr->plen);
+               break;
+
+       case BT_HCI_EVT_LINK_KEY_NOTIFY:
+               evt_link_key_notify(bthost, param, hdr->plen);
+               break;
+
+       case BT_HCI_EVT_ENCRYPT_CHANGE:
+               evt_encrypt_change(bthost, param, hdr->plen);
+               break;
+
+       case BT_HCI_EVT_IO_CAPABILITY_RESPONSE:
+               evt_io_cap_response(bthost, param, hdr->plen);
+               break;
+
+       case BT_HCI_EVT_IO_CAPABILITY_REQUEST:
+               evt_io_cap_request(bthost, param, hdr->plen);
+               break;
+
+       case BT_HCI_EVT_USER_CONFIRM_REQUEST:
+               evt_user_confirm_request(bthost, param, hdr->plen);
+               break;
+
+       case BT_HCI_EVT_SIMPLE_PAIRING_COMPLETE:
+               evt_simple_pairing_complete(bthost, param, hdr->plen);
+               break;
+
        case BT_HCI_EVT_LE_META_EVENT:
                evt_le_meta_event(bthost, param, hdr->plen);
                break;
@@ -712,6 +1210,7 @@ static bool l2cap_conn_req(struct bthost *bthost, struct btconn *conn,
                                uint8_t ident, const void *data, uint16_t len)
 {
        const struct bt_l2cap_pdu_conn_req *req = data;
+       struct l2cap_conn_cb_data *cb_data;
        struct bt_l2cap_pdu_conn_rsp rsp;
        uint16_t psm;
 
@@ -723,7 +1222,8 @@ static bool l2cap_conn_req(struct bthost *bthost, struct btconn *conn,
        memset(&rsp, 0, sizeof(rsp));
        rsp.scid = req->scid;
 
-       if (bthost->server_psm && bthost->server_psm == psm)
+       cb_data = bthost_find_l2cap_cb_by_psm(bthost, psm);
+       if (cb_data)
                rsp.dcid = cpu_to_le16(conn->next_cid++);
        else
                rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */
@@ -733,30 +1233,54 @@ static bool l2cap_conn_req(struct bthost *bthost, struct btconn *conn,
 
        if (!rsp.result) {
                struct bt_l2cap_pdu_config_req conf_req;
+               struct l2conn *l2conn;
 
-               bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(rsp.dcid),
-                                                       le16_to_cpu(rsp.scid));
+               l2conn = bthost_add_l2cap_conn(bthost, conn,
+                                                       le16_to_cpu(rsp.dcid),
+                                                       le16_to_cpu(rsp.scid),
+                                                       le16_to_cpu(psm));
 
                memset(&conf_req, 0, sizeof(conf_req));
-               conf_req.dcid = rsp.dcid;
+               conf_req.dcid = rsp.scid;
 
                l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0,
                                                &conf_req, sizeof(conf_req));
+
+               if (cb_data && l2conn->psm == cb_data->psm && cb_data->func)
+                       cb_data->func(conn->handle, l2conn->dcid,
+                                                       cb_data->user_data);
        }
 
        return true;
 }
 
+static void rfcomm_sabm_send(struct bthost *bthost, struct btconn *conn,
+                       struct l2conn *l2conn, uint8_t cr, uint8_t dlci)
+{
+       struct rfcomm_cmd cmd;
+
+       cmd.address = RFCOMM_ADDR(cr, dlci);
+       cmd.control = RFCOMM_CTRL(RFCOMM_SABM, 1);
+       cmd.length = RFCOMM_LEN8(0);
+       cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd);
+
+       send_acl(bthost, conn->handle, l2conn->dcid, &cmd, sizeof(cmd));
+}
+
 static bool l2cap_conn_rsp(struct bthost *bthost, struct btconn *conn,
                                uint8_t ident, const void *data, uint16_t len)
 {
        const struct bt_l2cap_pdu_conn_rsp *rsp = data;
+       struct l2conn *l2conn;
 
        if (len < sizeof(*rsp))
                return false;
 
-       bthost_add_l2cap_conn(bthost, conn, le16_to_cpu(rsp->scid),
-                                               le16_to_cpu(rsp->dcid));
+       l2conn = btconn_find_l2cap_conn_by_scid(conn, le16_to_cpu(rsp->scid));
+       if (l2conn)
+               l2conn->dcid = le16_to_cpu(rsp->dcid);
+       else
+               return false;
 
        if (le16_to_cpu(rsp->result) == 0x0001) {
                struct bt_l2cap_pdu_config_req req;
@@ -766,6 +1290,9 @@ static bool l2cap_conn_rsp(struct bthost *bthost, struct btconn *conn,
 
                l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0,
                                                        &req, sizeof(req));
+       } else if (l2conn->psm == 0x0003 && !rsp->result && !rsp->status &&
+                                               bthost->rfcomm_conn_data) {
+               rfcomm_sabm_send(bthost, conn, l2conn, 1, 0);
        }
 
        return true;
@@ -993,7 +1520,7 @@ static bool l2cap_le_conn_req(struct bthost *bthost, struct btconn *conn,
        rsp.mps = 23;
        rsp.credits = 1;
 
-       if (bthost->server_psm && bthost->server_psm == psm)
+       if (bthost_find_l2cap_cb_by_psm(bthost, psm))
                rsp.dcid = cpu_to_le16(conn->next_cid++);
        else
                rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */
@@ -1011,8 +1538,8 @@ static bool l2cap_le_conn_rsp(struct bthost *bthost, struct btconn *conn,
 
        if (len < sizeof(*rsp))
                return false;
-
-       bthost_add_l2cap_conn(bthost, conn, 0, le16_to_cpu(rsp->dcid));
+       /* TODO add L2CAP connection before with proper PSM */
+       bthost_add_l2cap_conn(bthost, conn, 0, le16_to_cpu(rsp->dcid), 0);
 
        return true;
 }
@@ -1093,6 +1620,329 @@ static struct cid_hook *find_cid_hook(struct btconn *conn, uint16_t cid)
        return NULL;
 }
 
+static struct rfcomm_chan_hook *find_rfcomm_chan_hook(struct btconn *conn,
+                                                       uint8_t channel)
+{
+       struct rfcomm_chan_hook *hook;
+
+       for (hook = conn->rfcomm_chan_hooks; hook != NULL; hook = hook->next)
+               if (hook->channel == channel)
+                       return hook;
+
+       return NULL;
+}
+
+static void rfcomm_ua_send(struct bthost *bthost, struct btconn *conn,
+                       struct l2conn *l2conn, uint8_t cr, uint8_t dlci)
+{
+       struct rfcomm_cmd cmd;
+
+       cmd.address = RFCOMM_ADDR(cr, dlci);
+       cmd.control = RFCOMM_CTRL(RFCOMM_UA, 1);
+       cmd.length = RFCOMM_LEN8(0);
+       cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd);
+
+       send_acl(bthost, conn->handle, l2conn->dcid, &cmd, sizeof(cmd));
+}
+
+static void rfcomm_dm_send(struct bthost *bthost, struct btconn *conn,
+                       struct l2conn *l2conn, uint8_t cr, uint8_t dlci)
+{
+       struct rfcomm_cmd cmd;
+
+       cmd.address = RFCOMM_ADDR(cr, dlci);
+       cmd.control = RFCOMM_CTRL(RFCOMM_DM, 1);
+       cmd.length = RFCOMM_LEN8(0);
+       cmd.fcs = rfcomm_fcs2((uint8_t *)&cmd);
+
+       send_acl(bthost, conn->handle, l2conn->dcid, &cmd, sizeof(cmd));
+}
+
+static void rfcomm_sabm_recv(struct bthost *bthost, struct btconn *conn,
+                               struct l2conn *l2conn, const void *data,
+                               uint16_t len)
+{
+       const struct rfcomm_cmd *hdr = data;
+       uint8_t dlci;
+       struct rfcomm_conn_cb_data *cb;
+       uint8_t chan;
+
+       if (len < sizeof(*hdr))
+               return;
+
+       chan = RFCOMM_GET_CHANNEL(hdr->address);
+       dlci = RFCOMM_GET_DLCI(hdr->address);
+
+       cb = bthost_find_rfcomm_cb_by_channel(bthost, chan);
+       if (!dlci || cb) {
+               bthost_add_rfcomm_conn(bthost, conn, l2conn, chan);
+               rfcomm_ua_send(bthost, conn, l2conn, 1, dlci);
+               if (cb && cb->func)
+                       cb->func(conn->handle, l2conn->scid, cb->user_data,
+                                                                       true);
+       } else {
+               rfcomm_dm_send(bthost, conn, l2conn, 1, dlci);
+       }
+}
+
+static void rfcomm_disc_recv(struct bthost *bthost, struct btconn *conn,
+                               struct l2conn *l2conn, const void *data,
+                               uint16_t len)
+{
+       const struct rfcomm_cmd *hdr = data;
+       uint8_t dlci;
+
+       if (len < sizeof(*hdr))
+               return;
+
+       dlci = RFCOMM_GET_DLCI(hdr->address);
+
+       rfcomm_ua_send(bthost, conn, l2conn, 0, dlci);
+}
+
+static void rfcomm_ua_recv(struct bthost *bthost, struct btconn *conn,
+                               struct l2conn *l2conn, const void *data,
+                               uint16_t len)
+{
+       const struct rfcomm_cmd *ua_hdr = data;
+       uint8_t channel;
+       struct rfcomm_connection_data *conn_data = bthost->rfcomm_conn_data;
+       uint8_t type;
+       uint8_t buf[14];
+       struct rfcomm_hdr *hdr;
+       struct rfcomm_mcc *mcc;
+       struct rfcomm_pn *pn_cmd;
+
+       if (len < sizeof(*ua_hdr))
+               return;
+
+       channel = RFCOMM_GET_CHANNEL(ua_hdr->address);
+       type = RFCOMM_GET_TYPE(ua_hdr->control);
+
+       if (channel && conn_data && conn_data->channel == channel) {
+               bthost_add_rfcomm_conn(bthost, conn, l2conn, channel);
+               if (conn_data->cb)
+                       conn_data->cb(conn->handle, l2conn->scid,
+                                               conn_data->user_data, true);
+               free(bthost->rfcomm_conn_data);
+               bthost->rfcomm_conn_data = NULL;
+               return;
+       }
+
+       if (!conn_data || !RFCOMM_TEST_CR(type))
+               return;
+
+       bthost_add_rfcomm_conn(bthost, conn, l2conn, channel);
+
+       memset(buf, 0, sizeof(buf));
+
+       hdr = (struct rfcomm_hdr *) buf;
+       mcc = (struct rfcomm_mcc *) (buf + sizeof(*hdr));
+       pn_cmd = (struct rfcomm_pn *) (buf + sizeof(*hdr) + sizeof(*mcc));
+
+       hdr->address = RFCOMM_ADDR(1, 0);
+       hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0);
+       hdr->length  = RFCOMM_LEN8(sizeof(*mcc) + sizeof(*pn_cmd));
+
+       mcc->type = RFCOMM_MCC_TYPE(1, RFCOMM_PN);
+       mcc->length = RFCOMM_LEN8(sizeof(*pn_cmd));
+
+       pn_cmd->dlci = conn_data->channel * 2;
+       pn_cmd->priority = 7;
+       pn_cmd->ack_timer = 0;
+       pn_cmd->max_retrans = 0;
+       pn_cmd->mtu = 667;
+       pn_cmd->credits = 7;
+
+       buf[sizeof(*hdr) + sizeof(*mcc) + sizeof(*pn_cmd)] = rfcomm_fcs(buf);
+
+       send_acl(bthost, conn->handle, l2conn->dcid, buf, sizeof(buf));
+}
+
+static void rfcomm_dm_recv(struct bthost *bthost, struct btconn *conn,
+                               struct l2conn *l2conn, const void *data,
+                               uint16_t len)
+{
+       const struct rfcomm_cmd *hdr = data;
+       uint8_t channel;
+       struct rfcomm_connection_data *conn_data = bthost->rfcomm_conn_data;
+
+       if (len < sizeof(*hdr))
+               return;
+
+       channel = RFCOMM_GET_CHANNEL(hdr->address);
+
+       if (conn_data && conn_data->channel == channel) {
+               if (conn_data->cb)
+                       conn_data->cb(conn->handle, l2conn->scid,
+                                               conn_data->user_data, false);
+               free(bthost->rfcomm_conn_data);
+               bthost->rfcomm_conn_data = NULL;
+       }
+}
+
+static void rfcomm_msc_recv(struct bthost *bthost, struct btconn *conn,
+                                       struct l2conn *l2conn, uint8_t cr,
+                                       const struct rfcomm_msc *msc)
+{
+       uint8_t buf[8];
+       struct rfcomm_hdr *hdr = (struct rfcomm_hdr *) buf;
+       struct rfcomm_mcc *mcc = (struct rfcomm_mcc *) (buf + sizeof(*hdr));
+       struct rfcomm_msc *msc_cmd = (struct rfcomm_msc *) (buf +
+                                                               sizeof(*hdr) +
+                                                               sizeof(*mcc));
+
+       hdr->address = RFCOMM_ADDR(0, 0);
+       hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0);
+       hdr->length  = RFCOMM_LEN8(sizeof(*mcc) + sizeof(*msc));
+       mcc->type = RFCOMM_MCC_TYPE(cr, RFCOMM_MSC);
+       mcc->length = RFCOMM_LEN8(sizeof(*msc));
+
+       msc_cmd->dlci = msc->dlci;
+       msc_cmd->v24_sig = msc->v24_sig;
+       buf[sizeof(*hdr) + sizeof(*mcc) + sizeof(*msc_cmd)] = rfcomm_fcs(buf);
+
+       send_acl(bthost, conn->handle, l2conn->dcid, buf, sizeof(buf));
+}
+
+static void rfcomm_pn_recv(struct bthost *bthost, struct btconn *conn,
+                                       struct l2conn *l2conn, uint8_t cr,
+                                       const struct rfcomm_pn *pn)
+{
+       uint8_t buf[14];
+       struct rfcomm_hdr *hdr;
+       struct rfcomm_mcc *mcc;
+       struct rfcomm_pn *pn_cmd;
+
+       if (!cr) {
+               rfcomm_sabm_send(bthost, conn, l2conn, 1,
+                                       bthost->rfcomm_conn_data->channel * 2);
+               return;
+       }
+
+       hdr = (struct rfcomm_hdr *) buf;
+       mcc = (struct rfcomm_mcc *) (buf + sizeof(*hdr));
+       pn_cmd = (struct rfcomm_pn *) (buf + sizeof(*hdr) + sizeof(*mcc));
+
+       memset(buf, 0, sizeof(buf));
+
+       hdr->address = RFCOMM_ADDR(1, 0);
+       hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0);
+       hdr->length  = RFCOMM_LEN8(sizeof(*mcc) + sizeof(*pn_cmd));
+
+       mcc->type = RFCOMM_MCC_TYPE(0, RFCOMM_PN);
+       mcc->length = RFCOMM_LEN8(sizeof(*pn_cmd));
+
+       pn_cmd->dlci = pn->dlci;
+       pn_cmd->priority = pn->priority;
+       pn_cmd->ack_timer = pn->ack_timer;
+       pn_cmd->max_retrans = pn->max_retrans;
+       pn_cmd->mtu = pn->mtu;
+       pn_cmd->credits = pn->credits;
+
+       buf[sizeof(*hdr) + sizeof(*mcc) + sizeof(*pn_cmd)] = rfcomm_fcs(buf);
+
+       send_acl(bthost, conn->handle, l2conn->dcid, buf, sizeof(buf));
+}
+
+static void rfcomm_mcc_recv(struct bthost *bthost, struct btconn *conn,
+                       struct l2conn *l2conn, const void *data, uint16_t len)
+{
+       const struct rfcomm_mcc *mcc = data;
+       const struct rfcomm_msc *msc;
+       const struct rfcomm_pn *pn;
+
+       if (len < sizeof(*mcc))
+               return;
+
+       switch (RFCOMM_GET_MCC_TYPE(mcc->type)) {
+       case RFCOMM_MSC:
+               if (len - sizeof(*mcc) < sizeof(*msc))
+                       break;
+
+               msc = data + sizeof(*mcc);
+
+               rfcomm_msc_recv(bthost, conn, l2conn,
+                               RFCOMM_TEST_CR(mcc->type) / 2, msc);
+               break;
+       case RFCOMM_PN:
+               if (len - sizeof(*mcc) < sizeof(*pn))
+                       break;
+
+               pn = data + sizeof(*mcc);
+
+               rfcomm_pn_recv(bthost, conn, l2conn,
+                               RFCOMM_TEST_CR(mcc->type) / 2, pn);
+               break;
+       default:
+               break;
+       }
+}
+
+static void rfcomm_uih_recv(struct bthost *bthost, struct btconn *conn,
+                               struct l2conn *l2conn, const void *data,
+                               uint16_t len)
+{
+       const struct rfcomm_hdr *hdr = data;
+       uint16_t hdr_len;
+       const void *p;
+
+       if (len < sizeof(*hdr))
+               return;
+
+       if (RFCOMM_TEST_EA(hdr->length))
+               hdr_len = sizeof(*hdr);
+       else
+               hdr_len = sizeof(*hdr) + sizeof(uint8_t);
+
+       if (len < hdr_len)
+               return;
+
+       p = data + hdr_len;
+
+       if (RFCOMM_GET_DLCI(hdr->address)) {
+               struct rfcomm_chan_hook *hook;
+
+               hook = find_rfcomm_chan_hook(conn,
+                                       RFCOMM_GET_CHANNEL(hdr->address));
+               if (!hook)
+                       return;
+
+               hook->func(p, len - hdr_len - sizeof(uint8_t),
+                                                       hook->user_data);
+       } else {
+               rfcomm_mcc_recv(bthost, conn, l2conn, p, len - hdr_len);
+       }
+}
+
+static void process_rfcomm(struct bthost *bthost, struct btconn *conn,
+                               struct l2conn *l2conn, const void *data,
+                               uint16_t len)
+{
+       const struct rfcomm_hdr *hdr = data;
+
+       switch (RFCOMM_GET_TYPE(hdr->control)) {
+       case RFCOMM_SABM:
+               rfcomm_sabm_recv(bthost, conn, l2conn, data, len);
+               break;
+       case RFCOMM_DISC:
+               rfcomm_disc_recv(bthost, conn, l2conn, data, len);
+               break;
+       case RFCOMM_UA:
+               rfcomm_ua_recv(bthost, conn, l2conn, data, len);
+               break;
+       case RFCOMM_DM:
+               rfcomm_dm_recv(bthost, conn, l2conn, data, len);
+               break;
+       case RFCOMM_UIH:
+               rfcomm_uih_recv(bthost, conn, l2conn, data, len);
+               break;
+       default:
+               printf("Unknown frame type\n");
+               break;
+       }
+}
+
 static void process_acl(struct bthost *bthost, const void *data, uint16_t len)
 {
        const struct bt_hci_acl_hdr *acl_hdr = data;
@@ -1100,6 +1950,7 @@ static void process_acl(struct bthost *bthost, const void *data, uint16_t len)
        uint16_t handle, cid, acl_len, l2_len;
        struct cid_hook *hook;
        struct btconn *conn;
+       struct l2conn *l2conn;
        const void *l2_data;
 
        if (len < sizeof(*acl_hdr) + sizeof(*l2_hdr))
@@ -1137,8 +1988,16 @@ static void process_acl(struct bthost *bthost, const void *data, uint16_t len)
        case 0x0005:
                l2cap_le_sig(bthost, conn, l2_data, l2_len);
                break;
+       case 0x0006:
+               smp_data(conn->smp_data, l2_data, l2_len);
+               break;
        default:
-               printf("Packet for unknown CID 0x%04x (%u)\n", cid, cid);
+               l2conn = btconn_find_l2cap_conn_by_scid(conn, cid);
+               if (l2conn && l2conn->psm == 0x0003)
+                       process_rfcomm(bthost, conn, l2conn, l2_data, l2_len);
+               else
+                       printf("Packet for unknown CID 0x%04x (%u)\n", cid,
+                                                                       cid);
                break;
        }
 }
@@ -1185,6 +2044,8 @@ void bthost_set_connect_cb(struct bthost *bthost, bthost_new_conn_cb cb,
 void bthost_hci_connect(struct bthost *bthost, const uint8_t *bdaddr,
                                                        uint8_t addr_type)
 {
+       bthost->conn_init = true;
+
        if (addr_type == BDADDR_BREDR) {
                struct bt_hci_cmd_create_conn cc;
 
@@ -1213,16 +2074,43 @@ void bthost_write_scan_enable(struct bthost *bthost, uint8_t scan)
 
 void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable)
 {
+       struct bt_hci_cmd_le_set_adv_parameters cp;
+
+       memset(&cp, 0, sizeof(cp));
+       send_command(bthost, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+                                                       &cp, sizeof(cp));
+
        send_command(bthost, BT_HCI_CMD_LE_SET_ADV_ENABLE, &enable, 1);
 }
 
+void bthost_write_ssp_mode(struct bthost *bthost, uint8_t mode)
+{
+       send_command(bthost, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &mode, 1);
+}
+
+void bthost_request_auth(struct bthost *bthost, uint16_t handle)
+{
+       struct btconn *conn;
+
+       conn = bthost_find_conn(bthost, handle);
+       if (!conn)
+               return;
+
+       if (conn->addr_type == BDADDR_BREDR) {
+               struct bt_hci_cmd_auth_requested cp;
+
+               cp.handle = cpu_to_le16(handle);
+               send_command(bthost, BT_HCI_CMD_AUTH_REQUESTED, &cp, sizeof(cp));
+       } else {
+               smp_pair(conn->smp_data);
+       }
+}
+
 void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle,
                                                        const uint8_t ltk[16])
 {
        struct bt_hci_cmd_le_start_encrypt cmd;
 
-       printf("bthost_le_start_encrypt(handle %u)\n", handle);
-
        memset(&cmd, 0, sizeof(cmd));
        cmd.handle = htobs(handle);
        memcpy(cmd.ltk, ltk, 16);
@@ -1230,9 +2118,60 @@ void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle,
        send_command(bthost, BT_HCI_CMD_LE_START_ENCRYPT, &cmd, sizeof(cmd));
 }
 
-void bthost_set_server_psm(struct bthost *bthost, uint16_t psm)
+void bthost_add_l2cap_server(struct bthost *bthost, uint16_t psm,
+                               bthost_l2cap_connect_cb func, void *user_data)
+{
+       struct l2cap_conn_cb_data *data;
+
+       data = malloc(sizeof(struct l2cap_conn_cb_data));
+       if (!data)
+               return;
+
+       data->psm = psm;
+       data->user_data = user_data;
+       data->func = func;
+       data->next = bthost->new_l2cap_conn_data;
+
+       bthost->new_l2cap_conn_data = data;
+}
+
+void bthost_set_pin_code(struct bthost *bthost, const uint8_t *pin,
+                                                       uint8_t pin_len)
+{
+       memcpy(bthost->pin, pin, pin_len);
+       bthost->pin_len = pin_len;
+}
+
+void bthost_set_io_capability(struct bthost *bthost, uint8_t io_capability)
+{
+       bthost->io_capability = io_capability;
+}
+
+void bthost_set_auth_req(struct bthost *bthost, uint8_t auth_req)
+{
+       bthost->auth_req = auth_req;
+}
+
+void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject)
+{
+       bthost->reject_user_confirm = reject;
+}
+
+void bthost_add_rfcomm_server(struct bthost *bthost, uint8_t channel,
+                               bthost_rfcomm_connect_cb func, void *user_data)
 {
-       bthost->server_psm = psm;
+       struct rfcomm_conn_cb_data *data;
+
+       data = malloc(sizeof(struct rfcomm_conn_cb_data));
+       if (!data)
+               return;
+
+       data->channel = channel;
+       data->user_data = user_data;
+       data->func = func;
+       data->next = bthost->new_rfcomm_conn_data;
+
+       bthost->new_rfcomm_conn_data = data;
 }
 
 void bthost_start(struct bthost *bthost)
@@ -1240,6 +2179,8 @@ void bthost_start(struct bthost *bthost)
        if (!bthost)
                return;
 
+       bthost->smp_data = smp_start(bthost);
+
        bthost->ncmd = 1;
 
        send_command(bthost, BT_HCI_CMD_RESET, NULL, 0);
@@ -1247,6 +2188,113 @@ void bthost_start(struct bthost *bthost)
        send_command(bthost, BT_HCI_CMD_READ_BD_ADDR, NULL, 0);
 }
 
+bool bthost_connect_rfcomm(struct bthost *bthost, uint16_t handle,
+                               uint8_t channel, bthost_rfcomm_connect_cb func,
+                               void *user_data)
+{
+       struct rfcomm_connection_data *data;
+       struct bt_l2cap_pdu_conn_req req;
+       struct btconn *conn;
+
+       if (bthost->rfcomm_conn_data)
+               return false;
+
+       conn = bthost_find_conn(bthost, handle);
+       if (!conn)
+               return false;
+
+       data = malloc(sizeof(struct rfcomm_connection_data));
+       if (!data)
+               return false;
+
+       data->channel = channel;
+       data->conn = conn;
+       data->cb = func;
+       data->user_data = user_data;
+
+       bthost->rfcomm_conn_data = data;
+
+       req.psm = cpu_to_le16(0x0003);
+       req.scid = cpu_to_le16(conn->next_cid++);
+
+       return bthost_l2cap_req(bthost, handle, BT_L2CAP_PDU_CONN_REQ,
+                                       &req, sizeof(req), NULL, NULL);
+}
+
+void bthost_add_rfcomm_chan_hook(struct bthost *bthost, uint16_t handle,
+                                       uint8_t channel,
+                                       bthost_rfcomm_chan_hook_func_t func,
+                                       void *user_data)
+{
+       struct rfcomm_chan_hook *hook;
+       struct btconn *conn;
+
+       conn = bthost_find_conn(bthost, handle);
+       if (!conn)
+               return;
+
+       hook = malloc(sizeof(*hook));
+       if (!hook)
+               return;
+
+       memset(hook, 0, sizeof(*hook));
+
+       hook->channel = channel;
+       hook->func = func;
+       hook->user_data = user_data;
+
+       hook->next = conn->rfcomm_chan_hooks;
+       conn->rfcomm_chan_hooks = hook;
+}
+
+void bthost_send_rfcomm_data(struct bthost *bthost, uint16_t handle,
+                                       uint8_t channel, const void *data,
+                                       uint16_t len)
+{
+       struct btconn *conn;
+       struct rcconn *rcconn;
+       struct rfcomm_hdr *hdr;
+       uint8_t *uih_frame;
+       uint16_t uih_len;
+
+       conn = bthost_find_conn(bthost, handle);
+       if (!conn)
+               return;
+
+       rcconn = btconn_find_rfcomm_conn_by_channel(conn, channel);
+       if (!rcconn)
+               return;
+
+       if (len > 127)
+               uih_len = len + sizeof(struct rfcomm_cmd) + sizeof(uint8_t);
+       else
+               uih_len = len + sizeof(struct rfcomm_cmd);
+
+       uih_frame = malloc(uih_len);
+       if (!uih_frame)
+               return;
+
+       hdr = (struct rfcomm_hdr *) uih_frame;
+       hdr->address = RFCOMM_ADDR(1, channel * 2);
+       hdr->control = RFCOMM_CTRL(RFCOMM_UIH, 0);
+       if (len > 127) {
+               hdr->length  = RFCOMM_LEN16(cpu_to_le16(sizeof(*hdr) + len));
+               memcpy(uih_frame + sizeof(*hdr) + 1, data, len);
+       } else {
+               hdr->length  = RFCOMM_LEN8(sizeof(*hdr) + len);
+               memcpy(uih_frame + sizeof(*hdr), data, len);
+       }
+
+       uih_frame[uih_len - 1] = rfcomm_fcs((void *)hdr);
+       send_acl(bthost, handle, rcconn->scid, uih_frame, uih_len);
+
+       free(uih_frame);
+}
+
 void bthost_stop(struct bthost *bthost)
 {
+       if (bthost->smp_data) {
+               smp_stop(bthost->smp_data);
+               bthost->smp_data = NULL;
+       }
 }
index 474ada9..b00bcd6 100644 (file)
@@ -72,10 +72,56 @@ void bthost_write_scan_enable(struct bthost *bthost, uint8_t scan);
 
 void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable);
 
+void bthost_write_ssp_mode(struct bthost *bthost, uint8_t mode);
+
+void bthost_request_auth(struct bthost *bthost, uint16_t handle);
+
 void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle,
                                                        const uint8_t ltk[16]);
+typedef void (*bthost_l2cap_connect_cb) (uint16_t handle, uint16_t cid,
+                                                       void *user_data);
+
+void bthost_add_l2cap_server(struct bthost *bthost, uint16_t psm,
+                               bthost_l2cap_connect_cb func, void *user_data);
+
+void bthost_set_pin_code(struct bthost *bthost, const uint8_t *pin,
+                                                       uint8_t pin_len);
+void bthost_set_io_capability(struct bthost *bthost, uint8_t io_capability);
+void bthost_set_auth_req(struct bthost *bthost, uint8_t auth_req);
+void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject);
+
+typedef void (*bthost_rfcomm_connect_cb) (uint16_t handle, uint16_t cid,
+                                               void *user_data, bool status);
+
+void bthost_add_rfcomm_server(struct bthost *bthost, uint8_t channel,
+                       bthost_rfcomm_connect_cb func, void *user_data);
 
-void bthost_set_server_psm(struct bthost *bthost, uint16_t psm);
+bool bthost_connect_rfcomm(struct bthost *bthost, uint16_t handle,
+                               uint8_t channel, bthost_rfcomm_connect_cb func,
+                               void *user_data);
+
+typedef void (*bthost_rfcomm_chan_hook_func_t) (const void *data, uint16_t len,
+                                                       void *user_data);
+
+void bthost_add_rfcomm_chan_hook(struct bthost *bthost, uint16_t handle,
+                                       uint8_t channel,
+                                       bthost_rfcomm_chan_hook_func_t func,
+                                       void *user_data);
+
+void bthost_send_rfcomm_data(struct bthost *bthost, uint16_t handle,
+                                       uint8_t channel, const void *data,
+                                       uint16_t len);
 
 void bthost_start(struct bthost *bthost);
 void bthost_stop(struct bthost *bthost);
+
+/* LE SMP support */
+
+void *smp_start(struct bthost *bthost);
+void smp_stop(void *smp_data);
+void *smp_conn_add(void *smp_data, uint16_t handle, const uint8_t *ia,
+                                       const uint8_t *ra, bool conn_init);
+void smp_conn_del(void *conn_data);
+void smp_data(void *conn_data, const void *data, uint16_t len);
+int smp_get_ltk(void *smp_data, uint64_t rand, uint16_t ediv, uint8_t *ltk);
+void smp_pair(void *conn_data);
diff --git a/emulator/hfp.c b/emulator/hfp.c
new file mode 100644 (file)
index 0000000..928a729
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ *
+ *  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 <sys/socket.h>
+#include <sys/un.h>
+
+#include "monitor/mainloop.h"
+#include "src/shared/hfp.h"
+
+static void hfp_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       printf("%s%s\n", prefix, str);
+}
+
+static void command_handler(const char *command, void *user_data)
+{
+       struct hfp_gw *hfp = user_data;
+
+       printf("Command: %s\n", command);
+
+       hfp_gw_send_result(hfp, HFP_RESULT_ERROR);
+}
+
+static bool open_connection(void)
+{
+       static const char SOCKET_PATH[] = "\0hfp-headset";
+       struct hfp_gw *hfp;
+       struct sockaddr_un addr;
+       int fd;
+
+       fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (fd < 0) {
+               perror("Failed to create Unix socket");
+               return false;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       memcpy(addr.sun_path, SOCKET_PATH, sizeof(SOCKET_PATH));
+
+       if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Failed to connect Unix socket");
+               close(fd);
+               return false;
+       }
+
+       hfp = hfp_gw_new(fd);
+       if (!hfp) {
+               close(fd);
+               return false;
+       }
+
+       hfp_gw_set_close_on_unref(hfp, true);
+
+       hfp_gw_set_debug(hfp, hfp_debug, "HFP: ", NULL);
+
+       hfp_gw_set_command_handler(hfp, command_handler, hfp, NULL);
+
+       return true;
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               mainloop_quit();
+               break;
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       sigset_t mask;
+
+       mainloop_init();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       if (!open_connection())
+               return EXIT_FAILURE;
+
+       return mainloop_run();
+}
diff --git a/emulator/le.c b/emulator/le.c
new file mode 100644 (file)
index 0000000..0a1b00d
--- /dev/null
@@ -0,0 +1,809 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel 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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#include "src/shared/util.h"
+#include "src/shared/crypto.h"
+#include "monitor/mainloop.h"
+#include "monitor/bt.h"
+
+#include "le.h"
+
+#define WHITE_LIST_SIZE  16
+
+struct bt_le {
+       volatile int ref_count;
+       int vhci_fd;
+       struct bt_crypto *crypto;
+
+       uint8_t  event_mask[16];
+       uint16_t manufacturer;
+       uint8_t  commands[64];
+       uint8_t  features[8];
+       uint8_t  bdaddr[6];
+
+       uint8_t  le_event_mask[8];
+       uint16_t le_mtu;
+       uint8_t  le_max_pkt;
+       uint8_t  le_features[8];
+       uint8_t  le_random_addr[6];
+       uint16_t le_adv_min_interval;
+       uint16_t le_adv_max_interval;
+       uint8_t  le_adv_type;
+       uint8_t  le_adv_own_addr_type;
+       uint8_t  le_adv_direct_addr_type;
+       uint8_t  le_adv_direct_addr[6];
+       uint8_t  le_adv_channel_map;
+       uint8_t  le_adv_filter_policy;
+       int8_t   le_adv_tx_power;
+       uint8_t  le_adv_data_len;
+       uint8_t  le_adv_data[31];
+       uint8_t  le_scan_rsp_data_len;
+       uint8_t  le_scan_rsp_data[31];
+       uint8_t  le_adv_enable;
+
+       uint8_t  le_white_list_size;
+       uint8_t  le_states[8];
+};
+
+static void reset_defaults(struct bt_le *hci)
+{
+       memset(hci->event_mask, 0, sizeof(hci->event_mask));
+       hci->event_mask[0] |= 0x10;     /* Disconnection Complete */
+       hci->event_mask[0] |= 0x80;     /* Encryption Change */
+       hci->event_mask[1] |= 0x08;     /* Read Remote Version Information Complete */
+       hci->event_mask[1] |= 0x20;     /* Command Complete */
+       hci->event_mask[1] |= 0x40;     /* Command Status */
+       hci->event_mask[1] |= 0x80;     /* Hardware Error */
+       hci->event_mask[2] |= 0x04;     /* Number of Completed Packets */
+       hci->event_mask[3] |= 0x02;     /* Data Buffer Overflow */
+       hci->event_mask[5] |= 0x80;     /* Encryption Key Refresh Complete */
+
+       hci->manufacturer = 0x003f;     /* Bluetooth SIG (63) */
+
+       memset(hci->commands, 0, sizeof(hci->commands));
+       //hci->commands[0]  |= 0x20;    /* Disconnect */
+       //hci->commands[2]  |= 0x80;    /* Read Remote Version Information */
+       hci->commands[5]  |= 0x40;      /* Set Event Mask */
+       hci->commands[5]  |= 0x80;      /* Reset */
+       //hci->commands[10] |= 0x04;    /* Read Transmit Power Level */
+       hci->commands[14] |= 0x08;      /* Read Local Version Information */
+       hci->commands[14] |= 0x10;      /* Read Local Supported Commands */
+       hci->commands[14] |= 0x20;      /* Read Local Supported Features */
+       hci->commands[14] |= 0x80;      /* Read Buffer Size */
+       hci->commands[15] |= 0x02;      /* Read BD ADDR */
+       //hci->commands[15] |= 0x20;    /* Read RSSI */
+       hci->commands[25] |= 0x01;      /* LE Set Event Mask */
+       hci->commands[25] |= 0x02;      /* LE Read Buffer Size */
+       hci->commands[25] |= 0x04;      /* LE Read Local Supported Features */
+       hci->commands[25] |= 0x10;      /* LE Set Random Address */
+       hci->commands[25] |= 0x20;      /* LE Set Advertising Parameters */
+       hci->commands[25] |= 0x40;      /* LE Read Advertising Channel TX Power */
+       hci->commands[25] |= 0x80;      /* LE Set Advertising Data */
+       hci->commands[26] |= 0x01;      /* LE Set Scan Response Data */
+       hci->commands[26] |= 0x02;      /* LE Set Advertise Enable */
+       //hci->commands[26] |= 0x04;    /* LE Set Scan Parameters */
+       //hci->commands[26] |= 0x08;    /* LE Set Scan Enable */
+       //hci->commands[26] |= 0x10;    /* LE Create Connection */
+       //hci->commands[26] |= 0x20;    /* LE Create Connection Cancel */
+       hci->commands[26] |= 0x40;      /* LE Read White List Size */
+       hci->commands[26] |= 0x80;      /* LE Clear White List */
+       //hci->commands[27] |= 0x01;    /* LE Add Device To White List */
+       //hci->commands[27] |= 0x02;    /* LE Remove Device From White List */
+       //hci->commands[27] |= 0x04;    /* LE Connection Update */
+       //hci->commands[27] |= 0x08;    /* LE Set Host Channel Classification */
+       //hci->commands[27] |= 0x10;    /* LE Read Channel Map */
+       //hci->commands[27] |= 0x20;    /* LE Read Remote Used Features */
+       hci->commands[27] |= 0x40;      /* LE Encrypt */
+       hci->commands[27] |= 0x80;      /* LE Rand */
+       //hci->commands[28] |= 0x01;    /* LE Start Encryption */
+       //hci->commands[28] |= 0x02;    /* LE Long Term Key Request Reply */
+       //hci->commands[28] |= 0x04;    /* LE Long Term Key Request Negative Reply */
+       hci->commands[28] |= 0x08;      /* LE Read Supported States */
+       //hci->commands[28] |= 0x10;    /* LE Receiver Test */
+       //hci->commands[28] |= 0x20;    /* LE Transmitter Test */
+       //hci->commands[28] |= 0x40;    /* LE Test End */
+       //hci->commands[33] |= 0x10;    /* LE Remote Connection Parameter Request Reply */
+       //hci->commands[33] |= 0x20;    /* LE Remote Connection Parameter Request Negative Reply */
+
+       memset(hci->features, 0, sizeof(hci->features));
+       hci->features[4] |= 0x20;       /* BR/EDR Not Supported */
+       hci->features[4] |= 0x40;       /* LE Supported */
+
+       memset(hci->bdaddr, 0, sizeof(hci->bdaddr));
+
+       memset(hci->le_event_mask, 0, sizeof(hci->le_event_mask));
+       hci->le_event_mask[0] |= 0x01;  /* LE Connection Complete */
+       hci->le_event_mask[0] |= 0x02;  /* LE Advertising Report */
+       hci->le_event_mask[0] |= 0x04;  /* LE Connection Update Complete */
+       hci->le_event_mask[0] |= 0x08;  /* LE Read Remote Used Features Complete */
+       hci->le_event_mask[0] |= 0x10;  /* LE Long Term Key Request */
+       //hci->le_event_mask[0] |= 0x20;        /* LE Remote Connection Parameter Request */
+
+       hci->le_mtu = 64;
+       hci->le_max_pkt = 1;
+
+       memset(hci->le_features, 0, sizeof(hci->le_features));
+       hci->le_features[0] |= 0x01;    /* LE Encryption */
+       //hci->le_features[0] |= 0x02;  /* Connection Parameter Request Procedure */
+       //hci->le_features[0] |= 0x04;  /* Extended Reject Indication */
+       //hci->le_features[0] |= 0x08;  /* Slave-initiated Features Exchange */
+       //hci->le_features[0] |= 0x10;  /* LE Ping */
+
+       memset(hci->le_random_addr, 0, sizeof(hci->le_random_addr));
+
+       hci->le_adv_min_interval = 0x0800;
+       hci->le_adv_max_interval = 0x0800;
+       hci->le_adv_type = 0x00;
+       hci->le_adv_own_addr_type = 0x00;
+       hci->le_adv_direct_addr_type = 0x00;
+       memset(hci->le_adv_direct_addr, 0, 6);
+       hci->le_adv_channel_map = 0x07;
+       hci->le_adv_filter_policy = 0x00;
+
+       hci->le_adv_tx_power = 0;
+
+       memset(hci->le_adv_data, 0, sizeof(hci->le_adv_data));
+       hci->le_adv_data_len = 0;
+
+       memset(hci->le_scan_rsp_data, 0, sizeof(hci->le_scan_rsp_data));
+       hci->le_scan_rsp_data_len = 0;
+
+       hci->le_adv_enable = 0x00;
+
+       hci->le_white_list_size = WHITE_LIST_SIZE;
+
+       memset(hci->le_states, 0, sizeof(hci->le_states));
+       hci->le_states[0] |= 0x01;      /* Non-connectable Advertising */
+       hci->le_states[0] |= 0x02;      /* Scannable Advertising */
+       hci->le_states[0] |= 0x04;      /* Connectable Advertising */
+       hci->le_states[0] |= 0x08;      /* Directed Advertising */
+       hci->le_states[0] |= 0x10;      /* Passive Scanning */
+       hci->le_states[0] |= 0x20;      /* Active Scanning */
+       hci->le_states[0] |= 0x40;      /* Initiating */
+       hci->le_states[0] |= 0x80;      /* Connection */
+}
+
+static void send_event(struct bt_le *hci, uint8_t event,
+                                               void *data, uint8_t size)
+{
+       uint8_t type = BT_H4_EVT_PKT;
+       struct bt_hci_evt_hdr hdr;
+       struct iovec iov[3];
+       int iovcnt;
+
+       hdr.evt  = event;
+       hdr.plen = size;
+
+       iov[0].iov_base = &type;
+       iov[0].iov_len  = 1;
+       iov[1].iov_base = &hdr;
+       iov[1].iov_len  = sizeof(hdr);
+
+       if (size > 0) {
+               iov[2].iov_base = data;
+               iov[2].iov_len  = size;
+               iovcnt = 3;
+       } else
+               iovcnt = 2;
+
+       if (writev(hci->vhci_fd, iov, iovcnt) < 0)
+               fprintf(stderr, "Write to /dev/vhci failed (%m)\n");
+}
+
+static void cmd_complete(struct bt_le *hci, uint16_t opcode,
+                                               const void *data, uint8_t len)
+{
+       struct bt_hci_evt_cmd_complete *cc;
+       void *pkt_data;
+
+       pkt_data = alloca(sizeof(*cc) + len);
+       if (!pkt_data)
+               return;
+
+       cc = pkt_data;
+       cc->ncmd = 0x01;
+       cc->opcode = cpu_to_le16(opcode);
+
+       if (len > 0)
+               memcpy(pkt_data + sizeof(*cc), data, len);
+
+       send_event(hci, BT_HCI_EVT_CMD_COMPLETE, pkt_data, sizeof(*cc) + len);
+}
+
+static void cmd_status(struct bt_le *hci, 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(hci, BT_HCI_EVT_CMD_STATUS, &cs, sizeof(cs));
+}
+
+static void cmd_set_event_mask(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_set_event_mask *cmd = data;
+       uint8_t status;
+
+       memcpy(hci->event_mask, cmd->mask, 8);
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(hci, BT_HCI_CMD_SET_EVENT_MASK, &status, sizeof(status));
+}
+
+static void cmd_reset(struct bt_le *hci, const void *data, uint8_t size)
+{
+       uint8_t status;
+
+       reset_defaults(hci);
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(hci, BT_HCI_CMD_RESET, &status, sizeof(status));
+}
+
+static void cmd_read_local_version(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_read_local_version rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       rsp.hci_ver = 0x06;
+       rsp.hci_rev = cpu_to_le16(0x0000);
+       rsp.lmp_ver = 0x06;
+       rsp.manufacturer = cpu_to_le16(hci->manufacturer);
+       rsp.lmp_subver = cpu_to_le16(0x0000);
+
+       cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_VERSION, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_local_commands(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_read_local_commands rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       memcpy(rsp.commands, hci->commands, 64);
+
+       cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_COMMANDS, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_local_features(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_read_local_features rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       memcpy(rsp.features, hci->features, 8);
+
+       cmd_complete(hci, BT_HCI_CMD_READ_LOCAL_FEATURES, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_buffer_size(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_read_buffer_size rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       rsp.acl_mtu = cpu_to_le16(0x0000);
+       rsp.sco_mtu = 0x00;
+       rsp.acl_max_pkt = cpu_to_le16(0x0000);
+       rsp.sco_max_pkt = cpu_to_le16(0x0000);
+
+       cmd_complete(hci, BT_HCI_CMD_READ_BUFFER_SIZE, &rsp, sizeof(rsp));
+}
+
+static void cmd_read_bd_addr(struct bt_le *hci, const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_read_bd_addr rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       memcpy(rsp.bdaddr, hci->bdaddr, 6);
+
+       cmd_complete(hci, BT_HCI_CMD_READ_BD_ADDR, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_set_event_mask(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_event_mask *cmd = data;
+       uint8_t status;
+
+       memcpy(hci->le_event_mask, cmd->mask, 8);
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(hci, BT_HCI_CMD_LE_SET_EVENT_MASK,
+                                               &status, sizeof(status));
+}
+
+static void cmd_le_read_buffer_size(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_le_read_buffer_size rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       rsp.le_mtu = cpu_to_le16(hci->le_mtu);
+       rsp.le_max_pkt = hci->le_max_pkt;
+
+       cmd_complete(hci, BT_HCI_CMD_LE_READ_BUFFER_SIZE, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_read_local_features(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_le_read_local_features rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       memcpy(rsp.features, hci->le_features, 8);
+
+       cmd_complete(hci, BT_HCI_CMD_LE_READ_LOCAL_FEATURES,
+                                                       &rsp, sizeof(rsp));
+}
+
+static void cmd_le_set_random_address(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_random_address *cmd = data;
+       uint8_t status;
+
+       memcpy(hci->le_random_addr, cmd->addr, 6);
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(hci, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
+                                               &status, sizeof(status));
+}
+
+static void cmd_le_set_adv_parameters(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_adv_parameters *cmd = data;
+       uint16_t min_interval, max_interval;
+       uint8_t status;
+
+       if (hci->le_adv_enable == 0x01) {
+               cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+               return;
+       }
+
+       min_interval = le16_to_cpu(cmd->min_interval);
+       max_interval = le16_to_cpu(cmd->max_interval);
+
+       /* Valid range for advertising type is 0x00 to 0x03 */
+       switch (cmd->type) {
+       case 0x00:      /* ADV_IND */
+               /* Range for advertising interval min is 0x0020 to 0x4000 */
+               if (min_interval < 0x0020 || min_interval > 0x4000) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+                       return;
+               }
+               /* Range for advertising interval max is 0x0020 to 0x4000 */
+               if (max_interval < 0x0020 || max_interval > 0x4000) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+                       return;
+               }
+               /* Advertising interval max shall be less or equal */
+               if (min_interval > max_interval) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+                       return;
+               }
+               break;
+
+       case 0x01:      /* ADV_DIRECT_IND */
+               /* Range for direct address type is 0x00 to 0x01 */
+               if (cmd->direct_addr_type > 0x01) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+                       return;
+               }
+               break;
+
+       case 0x02:      /* ADV_SCAN_IND */
+       case 0x03:      /* ADV_NONCONN_IND */
+               /* Range for advertising interval min is 0x00a0 to 0x4000 */
+               if (min_interval < 0x00a0 || min_interval > 0x4000) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+                       return;
+               }
+               /* Range for advertising interval max is 0x00a0 to 0x4000 */
+               if (max_interval < 0x00a0 || max_interval > 0x4000) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+                       return;
+               }
+               /* Advertising interval min shall be less or equal */
+               if (min_interval > max_interval) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+                       return;
+               }
+               break;
+
+       default:
+               cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+               return;
+       }
+
+       /* Valid range for own address type is 0x00 to 0x01 */
+       if (cmd->own_addr_type > 0x01) {
+               cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+               return;
+       }
+
+       /* Valid range for advertising channel map is 0x01 to 0x07 */
+       if (cmd->channel_map < 0x01 || cmd->channel_map > 0x07) {
+               cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+               return;
+       }
+
+       /* Valid range for advertising filter policy is 0x00 to 0x03 */
+       if (cmd->filter_policy > 0x03) {
+               cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_PARAMETERS);
+               return;
+       }
+
+       hci->le_adv_min_interval = min_interval;
+       hci->le_adv_max_interval = max_interval;
+       hci->le_adv_type = cmd->type;
+       hci->le_adv_own_addr_type = cmd->own_addr_type;
+       hci->le_adv_direct_addr_type = cmd->direct_addr_type;
+       memcpy(hci->le_adv_direct_addr, cmd->direct_addr, 6);
+       hci->le_adv_channel_map = cmd->channel_map;
+       hci->le_adv_filter_policy = cmd->filter_policy;
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+                                               &status, sizeof(status));
+}
+
+static void cmd_le_read_adv_tx_power(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_le_read_adv_tx_power rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       rsp.level = hci->le_adv_tx_power;
+
+       cmd_complete(hci, BT_HCI_CMD_LE_READ_ADV_TX_POWER, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_set_adv_data(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_adv_data *cmd = data;
+       uint8_t status;
+
+       /* Valid range for advertising data length is 0x00 to 0x1f */
+       if (cmd->len > 0x1f) {
+               cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_DATA);
+               return;
+       }
+
+       hci->le_adv_data_len = cmd->len;
+       memcpy(hci->le_adv_data, cmd->data, 31);
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_DATA, &status, sizeof(status));
+}
+
+static void cmd_le_set_scan_rsp_data(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_scan_rsp_data *cmd = data;
+       uint8_t status;
+
+       /* Valid range for scan response data length is 0x00 to 0x1f */
+       if (cmd->len > 0x1f) {
+               cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_SCAN_RSP_DATA);
+               return;
+       }
+
+       hci->le_scan_rsp_data_len = cmd->len;
+       memcpy(hci->le_scan_rsp_data, cmd->data, 31);
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(hci, BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+                                               &status, sizeof(status));
+}
+
+static void cmd_le_set_adv_enable(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_set_adv_enable *cmd = data;
+       uint8_t status;
+
+       /* Valid range for advertising enable is 0x00 to 0x01 */
+       if (cmd->enable > 0x01) {
+               cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS,
+                                       BT_HCI_CMD_LE_SET_ADV_ENABLE);
+               return;
+       }
+
+       if (cmd->enable == hci->le_adv_enable) {
+               cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+                                       BT_HCI_CMD_LE_SET_ADV_ENABLE);
+               return;
+       }
+
+       hci->le_adv_enable = cmd->enable;
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(hci, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+                                               &status, sizeof(status));
+}
+
+static void cmd_le_read_white_list_size(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_le_read_white_list_size rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       rsp.size = hci->le_white_list_size;
+
+       cmd_complete(hci, BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE,
+                                                       &rsp, sizeof(rsp));
+}
+
+static void cmd_le_clear_white_list(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       uint8_t status;
+
+       status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(hci, BT_HCI_CMD_LE_CLEAR_WHITE_LIST,
+                                               &status, sizeof(status));
+}
+
+static void cmd_le_encrypt(struct bt_le *hci, const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_le_encrypt *cmd = data;
+       struct bt_hci_rsp_le_encrypt rsp;
+
+       if (!bt_crypto_e(hci->crypto, cmd->key, cmd->plaintext, rsp.data)) {
+               cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+                                       BT_HCI_CMD_LE_ENCRYPT);
+               return;
+       }
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+
+       cmd_complete(hci, BT_HCI_CMD_LE_ENCRYPT, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_rand(struct bt_le *hci, const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_le_rand rsp;
+       uint8_t value[8];
+
+       if (!bt_crypto_random_bytes(hci->crypto, value, 8)) {
+               cmd_status(hci, BT_HCI_ERR_COMMAND_DISALLOWED,
+                                       BT_HCI_CMD_LE_RAND);
+               return;
+       }
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       memcpy(&rsp.number, value, 8);
+
+       cmd_complete(hci, BT_HCI_CMD_LE_RAND, &rsp, sizeof(rsp));
+}
+
+static void cmd_le_read_supported_states(struct bt_le *hci,
+                                               const void *data, uint8_t size)
+{
+       struct bt_hci_rsp_le_read_supported_states rsp;
+
+       rsp.status = BT_HCI_ERR_SUCCESS;
+       memcpy(rsp.states, hci->le_states, 8);
+
+       cmd_complete(hci, BT_HCI_CMD_LE_READ_SUPPORTED_STATES,
+                                                       &rsp, sizeof(rsp));
+}
+
+static const struct {
+       uint16_t opcode;
+       void (*func) (struct bt_le *hci, const void *data, uint8_t size);
+       uint8_t size;
+       bool fixed;
+} cmd_table[] = {
+       { BT_HCI_CMD_SET_EVENT_MASK,       cmd_set_event_mask,      8, true },
+       { BT_HCI_CMD_RESET,                cmd_reset,               0, true },
+       { BT_HCI_CMD_READ_LOCAL_VERSION,   cmd_read_local_version,  0, true },
+       { BT_HCI_CMD_READ_LOCAL_COMMANDS,  cmd_read_local_commands, 0, true },
+       { BT_HCI_CMD_READ_LOCAL_FEATURES,  cmd_read_local_features, 0, true },
+       { BT_HCI_CMD_READ_BUFFER_SIZE,     cmd_read_buffer_size,    0, true },
+       { BT_HCI_CMD_READ_BD_ADDR,         cmd_read_bd_addr,        0, true },
+
+       { BT_HCI_CMD_LE_SET_EVENT_MASK,
+                               cmd_le_set_event_mask, 8, true },
+       { BT_HCI_CMD_LE_READ_BUFFER_SIZE,
+                               cmd_le_read_buffer_size, 0, true },
+       { BT_HCI_CMD_LE_READ_LOCAL_FEATURES,
+                               cmd_le_read_local_features, 0, true },
+       { BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
+                               cmd_le_set_random_address, 6, true },
+       { BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+                               cmd_le_set_adv_parameters, 15, true },
+       { BT_HCI_CMD_LE_READ_ADV_TX_POWER,
+                               cmd_le_read_adv_tx_power, 0, true },
+       { BT_HCI_CMD_LE_SET_ADV_DATA,
+                               cmd_le_set_adv_data, 32, true },
+       { BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+                               cmd_le_set_scan_rsp_data, 32, true },
+       { BT_HCI_CMD_LE_SET_ADV_ENABLE,
+                               cmd_le_set_adv_enable, 1, true },
+
+       { BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE,
+                               cmd_le_read_white_list_size, 0, true },
+       { BT_HCI_CMD_LE_CLEAR_WHITE_LIST,
+                               cmd_le_clear_white_list, 0, true },
+
+       { BT_HCI_CMD_LE_ENCRYPT, cmd_le_encrypt, 32, true },
+       { BT_HCI_CMD_LE_RAND, cmd_le_rand, 0, true },
+
+       { BT_HCI_CMD_LE_READ_SUPPORTED_STATES,
+                               cmd_le_read_supported_states, 0, true },
+
+       { }
+};
+
+static void process_command(struct bt_le *hci, const void *data, size_t size)
+{
+       const struct bt_hci_cmd_hdr *hdr = data;
+       uint16_t opcode;
+       unsigned int i;
+
+       if (size < sizeof(*hdr))
+               return;
+
+       data += sizeof(*hdr);
+       size -= sizeof(*hdr);
+
+       opcode = le16_to_cpu(hdr->opcode);
+
+       if (hdr->plen != size) {
+               cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, opcode);
+               return;
+       }
+
+       for (i = 0; cmd_table[i].func; i++) {
+               if (cmd_table[i].opcode != opcode)
+                       continue;
+
+               if ((cmd_table[i].fixed && size != cmd_table[i].size) ||
+                                               size < cmd_table[i].size) {
+                       cmd_status(hci, BT_HCI_ERR_INVALID_PARAMETERS, opcode);
+                       return;
+               }
+
+               cmd_table[i].func(hci, data, size);
+               return;
+       }
+
+       cmd_status(hci, BT_HCI_ERR_UNKNOWN_COMMAND, opcode);
+}
+
+static void vhci_read_callback(int fd, uint32_t events, void *user_data)
+{
+       struct bt_le *hci = user_data;
+       unsigned char buf[4096];
+       ssize_t len;
+
+       if (events & (EPOLLERR | EPOLLHUP))
+               return;
+
+       len = read(hci->vhci_fd, buf, sizeof(buf));
+       if (len < 1)
+               return;
+
+       switch (buf[0]) {
+       case BT_H4_CMD_PKT:
+               process_command(hci, buf + 1, len - 1);
+               break;
+       }
+}
+
+struct bt_le *bt_le_new(void)
+{
+       unsigned char setup_cmd[2];
+       struct bt_le *hci;
+
+       hci = calloc(1, sizeof(*hci));
+       if (!hci)
+               return NULL;
+
+       reset_defaults(hci);
+
+       hci->vhci_fd = open("/dev/vhci", O_RDWR);
+       if (hci->vhci_fd < 0) {
+               free(hci);
+               return NULL;
+       }
+
+       setup_cmd[0] = HCI_VENDOR_PKT;
+       setup_cmd[1] = HCI_BREDR;
+
+       if (write(hci->vhci_fd, setup_cmd, sizeof(setup_cmd)) < 0) {
+               close(hci->vhci_fd);
+               free(hci);
+               return NULL;
+       }
+
+       mainloop_add_fd(hci->vhci_fd, EPOLLIN, vhci_read_callback, hci, NULL);
+
+       hci->crypto = bt_crypto_new();
+
+       return bt_le_ref(hci);
+}
+
+struct bt_le *bt_le_ref(struct bt_le *hci)
+{
+       if (!hci)
+               return NULL;
+
+       __sync_fetch_and_add(&hci->ref_count, 1);
+
+       return hci;
+}
+
+void bt_le_unref(struct bt_le *hci)
+{
+       if (!hci)
+               return;
+
+       if (__sync_sub_and_fetch(&hci->ref_count, 1))
+               return;
+
+       bt_crypto_unref(hci->crypto);
+
+       mainloop_remove_fd(hci->vhci_fd);
+
+       close(hci->vhci_fd);
+
+       free(hci);
+}
diff --git a/emulator/le.h b/emulator/le.h
new file mode 100644 (file)
index 0000000..5e832e8
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2012  Intel 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
+ *
+ */
+
+#include <stdbool.h>
+
+struct bt_le;
+
+struct bt_le *bt_le_new(void);
+
+struct bt_le *bt_le_ref(struct bt_le *le);
+void bt_le_unref(struct bt_le *le);
index 85b10f1..bd2a29a 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
@@ -35,6 +35,7 @@
 #include "server.h"
 #include "vhci.h"
 #include "amp.h"
+#include "le.h"
 
 static void signal_callback(int signum, void *user_data)
 {
@@ -66,6 +67,7 @@ static const struct option main_options[] = {
        { "le",      no_argument,       NULL, 'L' },
        { "bredr",   no_argument,       NULL, 'B' },
        { "amp",     no_argument,       NULL, 'A' },
+       { "letest",  optional_argument, NULL, 'U' },
        { "amptest", optional_argument, NULL, 'T' },
        { "version", no_argument,       NULL, 'v' },
        { "help",    no_argument,       NULL, 'h' },
@@ -80,6 +82,7 @@ int main(int argc, char *argv[])
        struct server *server4;
        struct server *server5;
        bool server_enabled = false;
+       int letest_count = 0;
        int amptest_count = 0;
        int vhci_count = 0;
        enum vhci_type vhci_type = VHCI_TYPE_BREDRLE;
@@ -91,7 +94,8 @@ int main(int argc, char *argv[])
        for (;;) {
                int opt;
 
-               opt = getopt_long(argc, argv, "sl::LBATvh", main_options, NULL);
+               opt = getopt_long(argc, argv, "sl::LBAUTvh",
+                                               main_options, NULL);
                if (opt < 0)
                        break;
 
@@ -114,6 +118,12 @@ int main(int argc, char *argv[])
                case 'A':
                        vhci_type = VHCI_TYPE_AMP;
                        break;
+               case 'U':
+                       if (optarg)
+                               letest_count = atoi(optarg);
+                       else
+                               letest_count = 1;
+                       break;
                case 'T':
                        if (optarg)
                                amptest_count = atoi(optarg);
@@ -131,7 +141,8 @@ int main(int argc, char *argv[])
                }
        }
 
-       if (amptest_count < 1 && vhci_count < 1 && !server_enabled) {
+       if (letest_count < 1 && amptest_count < 1 &&
+                                       vhci_count < 1 && !server_enabled) {
                fprintf(stderr, "No emulator specified\n");
                return EXIT_FAILURE;
        }
@@ -144,6 +155,16 @@ int main(int argc, char *argv[])
 
        printf("Bluetooth emulator ver %s\n", VERSION);
 
+       for (i = 0; i < letest_count; i++) {
+               struct bt_le *le;
+
+               le = bt_le_new();
+               if (!le) {
+                       fprintf(stderr, "Failed to create LE controller\n");
+                       return EXIT_FAILURE;
+               }
+       }
+
        for (i = 0; i < amptest_count; i++) {
                struct bt_amp *amp;
 
index b2a4b02..f3c82d3 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index f0b3727..bf725e7 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
diff --git a/emulator/smp.c b/emulator/smp.c
new file mode 100644 (file)
index 0000000..32c82e5
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013-2014  Intel Corporation
+ *
+ *
+ *  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 <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <endian.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/hci.h"
+
+#include "src/shared/crypto.h"
+#include "monitor/bt.h"
+#include "bthost.h"
+
+#define SMP_CID 0x0006
+
+struct smp {
+       struct bthost *bthost;
+       struct smp_conn *conn;
+       struct bt_crypto *crypto;
+};
+
+struct smp_conn {
+       struct smp *smp;
+       uint16_t handle;
+       bool out;
+       uint8_t ia[6];
+       uint8_t ia_type;
+       uint8_t ra[6];
+       uint8_t ra_type;
+       uint8_t tk[16];
+       uint8_t prnd[16];
+       uint8_t rrnd[16];
+       uint8_t pcnf[16];
+       uint8_t preq[7];
+       uint8_t prsp[7];
+       uint8_t ltk[16];
+};
+
+static bool verify_random(struct smp_conn *conn, const uint8_t rnd[16])
+{
+       uint8_t confirm[16];
+
+       if (!bt_crypto_c1(conn->smp->crypto, conn->tk, conn->rrnd, conn->prsp,
+                               conn->preq, conn->ia_type, conn->ia,
+                               conn->ra_type, conn->ra, confirm))
+               return false;
+
+       if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf) != 0)) {
+               printf("Confirmation values don't match\n");
+               return false;
+       }
+
+       if (conn->out) {
+               bt_crypto_s1(conn->smp->crypto, conn->tk, conn->rrnd,
+                                                       conn->prnd, conn->ltk);
+               bthost_le_start_encrypt(conn->smp->bthost, conn->handle,
+                                                               conn->ltk);
+       } else {
+               bt_crypto_s1(conn->smp->crypto, conn->tk, conn->prnd,
+                                                       conn->rrnd, conn->ltk);
+       }
+
+       return true;
+}
+
+static void pairing_req(struct smp_conn *conn, const void *data, uint16_t len)
+{
+       struct bthost *bthost = conn->smp->bthost;
+       static const uint8_t rsp[] = {  0x02,   /* Pairing Response */
+                                       0x03,   /* NoInputNoOutput */
+                                       0x00,   /* OOB Flag */
+                                       0x01,   /* Bonding - no MITM */
+                                       0x10,   /* Max key size */
+                                       0x00,   /* Init. key dist. */
+                                       0x01,   /* Rsp. key dist. */
+                               };
+
+       memcpy(conn->preq, data, sizeof(conn->preq));
+       memcpy(conn->prsp, rsp, sizeof(rsp));
+
+       bthost_send_cid(bthost, conn->handle, SMP_CID, rsp, sizeof(rsp));
+}
+
+static void pairing_rsp(struct smp_conn *conn, const void *data, uint16_t len)
+{
+       memcpy(conn->prsp, data, sizeof(conn->prsp));
+
+       /*bthost_send_cid(bthost, handle, SMP_CID, pdu, req->send_len);*/
+}
+
+static void pairing_cfm(struct smp_conn *conn, const void *data, uint16_t len)
+{
+       struct bthost *bthost = conn->smp->bthost;
+       const uint8_t *cfm = data;
+       uint8_t rsp[17];
+
+       memcpy(conn->pcnf, data + 1, 16);
+
+       rsp[0] = cfm[0];
+       bt_crypto_c1(conn->smp->crypto, conn->tk, conn->prnd, conn->prsp,
+                               conn->preq, conn->ia_type, conn->ia,
+                               conn->ra_type, conn->ra, &rsp[1]);
+
+       bthost_send_cid(bthost, conn->handle, SMP_CID, rsp, sizeof(rsp));
+}
+
+static void pairing_rnd(struct smp_conn *conn, const void *data, uint16_t len)
+{
+       struct bthost *bthost = conn->smp->bthost;
+       const uint8_t *rnd = data;
+       uint8_t rsp[17];
+
+       memcpy(conn->rrnd, data + 1, 16);
+
+       if (!verify_random(conn, data + 1))
+               return;
+
+       rsp[0] = rnd[0];
+       memcpy(&rsp[1], conn->prnd, 16);
+
+       bthost_send_cid(bthost, conn->handle, SMP_CID, rsp, sizeof(rsp));
+}
+
+void smp_pair(void *conn_data)
+{
+       struct smp_conn *conn = conn_data;
+       struct bthost *bthost = conn->smp->bthost;
+       const uint8_t smp_pair_req[] = {        0x01,   /* Pairing Request */
+                                               0x03,   /* NoInputNoOutput */
+                                               0x00,   /* OOB Flag */
+                                               0x01,   /* Bonding - no MITM */
+                                               0x10,   /* Max key size */
+                                               0x00,   /* Init. key dist. */
+                                               0x01,   /* Rsp. key dist. */
+                                       };
+
+       memcpy(conn->preq, smp_pair_req, sizeof(smp_pair_req));
+
+       bthost_send_cid(bthost, conn->handle, SMP_CID, smp_pair_req,
+                                                       sizeof(smp_pair_req));
+}
+
+void smp_data(void *conn_data, const void *data, uint16_t len)
+{
+       struct smp_conn *conn = conn_data;
+       uint8_t opcode;
+
+       if (len < 1) {
+               printf("Received too small SMP PDU\n");
+               return;
+       }
+
+       opcode = *((const uint8_t *) data);
+
+       switch (opcode) {
+       case 0x01: /* Pairing Request */
+               pairing_req(conn, data, len);
+               break;
+       case 0x02: /* Pairing Response */
+               pairing_rsp(conn, data, len);
+               break;
+       case 0x03: /* Pairing Confirm */
+               pairing_cfm(conn, data, len);
+               break;
+       case 0x04: /* Pairing Random */
+               pairing_rnd(conn, data, len);
+               break;
+       default:
+               break;
+       }
+}
+
+int smp_get_ltk(void *smp_data, uint64_t rand, uint16_t ediv, uint8_t *ltk)
+{
+       struct smp_conn *conn = smp_data;
+       static const uint8_t no_ltk[16] = { 0 };
+
+       if (!memcmp(conn->ltk, no_ltk, 16))
+               return -ENOENT;
+
+       memcpy(ltk, conn->ltk, 16);
+
+       return 0;
+}
+
+void *smp_conn_add(void *smp_data, uint16_t handle, const uint8_t *ia,
+                                       const uint8_t *ra, bool conn_init)
+{
+       struct smp *smp = smp_data;
+       struct smp_conn *conn;
+
+       conn = malloc(sizeof(struct smp_conn));
+       if (!conn)
+               return NULL;
+
+       memset(conn, 0, sizeof(*conn));
+
+       conn->smp = smp;
+       conn->handle = handle;
+       conn->out = conn_init;
+
+       conn->ia_type = LE_PUBLIC_ADDRESS;
+       conn->ra_type = LE_PUBLIC_ADDRESS;
+       memcpy(conn->ia, ia, 6);
+       memcpy(conn->ra, ra, 6);
+
+       return conn;
+}
+
+void smp_conn_del(void *conn_data)
+{
+       struct smp_conn *conn = conn_data;
+
+       free(conn);
+}
+
+void *smp_start(struct bthost *bthost)
+{
+       struct smp *smp;
+
+       smp = malloc(sizeof(struct smp));
+       if (!smp)
+               return NULL;
+
+       memset(smp, 0, sizeof(*smp));
+
+       smp->crypto = bt_crypto_new();
+       if (!smp->crypto) {
+               free(smp);
+               return NULL;
+       }
+
+       smp->bthost = bthost;
+
+       return smp;
+}
+
+void smp_stop(void *smp_data)
+{
+       struct smp *smp = smp_data;
+
+       bt_crypto_unref(smp->crypto);
+
+       free(smp);
+}
index d32d5e5..00c6118 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index b9ae63f..1ec7191 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index be8cc29..eb68a0f 100644 (file)
@@ -51,11 +51,14 @@ struct GDBusClient {
        GDBusWatchFunction connect_func;
        void *connect_data;
        GDBusWatchFunction disconn_func;
+       gboolean connected;
        void *disconn_data;
        GDBusMessageFunction signal_func;
        void *signal_data;
        GDBusProxyFunction proxy_added;
        GDBusProxyFunction proxy_removed;
+       GDBusClientFunction ready;
+       void *ready_data;
        GDBusPropertyFunction property_changed;
        void *user_data;
        GList *proxy_list;
@@ -725,6 +728,93 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
        return TRUE;
 }
 
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+                               const char *name, int type, const void *value,
+                               size_t size, GDBusResultFunction function,
+                               void *user_data, GDBusDestroyFunction destroy)
+{
+       struct set_property_data *data;
+       GDBusClient *client;
+       DBusMessage *msg;
+       DBusMessageIter iter, variant, array;
+       DBusPendingCall *call;
+       char array_sig[3];
+       char type_sig[2];
+
+       if (!proxy || !name || !value)
+               return FALSE;
+
+       if (!dbus_type_is_basic(type))
+               return FALSE;
+
+       client = proxy->client;
+       if (!client)
+               return FALSE;
+
+       data = g_try_new0(struct set_property_data, 1);
+       if (!data)
+               return FALSE;
+
+       data->function = function;
+       data->user_data = user_data;
+       data->destroy = destroy;
+
+       msg = dbus_message_new_method_call(client->service_name,
+                                               proxy->obj_path,
+                                               DBUS_INTERFACE_PROPERTIES,
+                                               "Set");
+       if (!msg) {
+               g_free(data);
+               return FALSE;
+       }
+
+       array_sig[0] = DBUS_TYPE_ARRAY;
+       array_sig[1] = (char) type;
+       array_sig[2] = '\0';
+
+       type_sig[0] = (char) type;
+       type_sig[1] = '\0';
+
+       dbus_message_iter_init_append(msg, &iter);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
+                                                       &proxy->interface);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+       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))
+               dbus_message_iter_append_fixed_array(&array, type, &value,
+                                                                       size);
+       else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
+               const char **str = (const char **) value;
+               size_t i;
+
+               for (i = 0; i < size; i++)
+                       dbus_message_iter_append_basic(&array, type, &str[i]);
+       }
+
+       dbus_message_iter_close_container(&variant, &array);
+       dbus_message_iter_close_container(&iter, &variant);
+
+       if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
+                                                       &call, -1) == FALSE) {
+               dbus_message_unref(msg);
+               g_free(data);
+               return FALSE;
+       }
+
+       dbus_pending_call_set_notify(call, set_property_reply, data, g_free);
+       dbus_pending_call_unref(call);
+
+       dbus_message_unref(msg);
+
+       return TRUE;
+}
+
 struct method_call_data {
        GDBusReturnFunction function;
        void *user_data;
@@ -982,6 +1072,9 @@ static void parse_managed_objects(GDBusClient *client, DBusMessage *msg)
 
                dbus_message_iter_next(&dict);
        }
+
+       if (client->ready)
+               client->ready(client, client->ready_data);
 }
 
 static void get_managed_objects_reply(DBusPendingCall *call, void *user_data)
@@ -1054,6 +1147,8 @@ static void service_connect(DBusConnection *conn, void *user_data)
 
        get_managed_objects(client);
 
+       client->connected = TRUE;
+
        g_dbus_client_unref(client);
 }
 
@@ -1064,8 +1159,10 @@ static void service_disconnect(DBusConnection *conn, void *user_data)
        g_list_free_full(client->proxy_list, proxy_free);
        client->proxy_list = NULL;
 
-       if (client->disconn_func)
+       if (client->disconn_func) {
                client->disconn_func(conn, client->disconn_data);
+               client->connected = FALSE;
+       }
 }
 
 static DBusHandlerResult message_filter(DBusConnection *connection,
@@ -1118,6 +1215,7 @@ GDBusClient *g_dbus_client_new(DBusConnection *connection,
        client->dbus_conn = dbus_connection_ref(connection);
        client->service_name = g_strdup(service);
        client->base_path = g_strdup(path);
+       client->connected = FALSE;
 
        client->match_rules = g_ptr_array_sized_new(1);
        g_ptr_array_set_free_func(client->match_rules, g_free);
@@ -1192,7 +1290,11 @@ void g_dbus_client_unref(GDBusClient *client)
 
        g_list_free_full(client->proxy_list, proxy_free);
 
-       if (client->disconn_func)
+       /*
+        * Don't call disconn_func twice if disconnection
+        * was previously reported.
+        */
+       if (client->disconn_func && client->connected)
                client->disconn_func(client->dbus_conn, client->disconn_data);
 
        g_dbus_remove_watch(client->dbus_conn, client->watch);
@@ -1243,6 +1345,18 @@ gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
        return TRUE;
 }
 
+gboolean g_dbus_client_set_ready_watch(GDBusClient *client,
+                               GDBusClientFunction ready, void *user_data)
+{
+       if (client == NULL)
+               return FALSE;
+
+       client->ready = ready;
+       client->ready_data = user_data;
+
+       return TRUE;
+}
+
 gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
                                        GDBusProxyFunction proxy_added,
                                        GDBusProxyFunction proxy_removed,
index 9542109..551c306 100644 (file)
@@ -329,6 +329,11 @@ gboolean g_dbus_proxy_set_property_basic(GDBusProxy *proxy,
                                GDBusResultFunction function, void *user_data,
                                GDBusDestroyFunction destroy);
 
+gboolean g_dbus_proxy_set_property_array(GDBusProxy *proxy,
+                               const char *name, int type, const void *value,
+                               size_t size, GDBusResultFunction function,
+                               void *user_data, GDBusDestroyFunction destroy);
+
 typedef void (* GDBusSetupFunction) (DBusMessageIter *iter, void *user_data);
 typedef void (* GDBusReturnFunction) (DBusMessage *message, void *user_data);
 
@@ -337,6 +342,7 @@ gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method,
                                GDBusReturnFunction function, void *user_data,
                                GDBusDestroyFunction destroy);
 
+typedef void (* GDBusClientFunction) (GDBusClient *client, void *user_data);
 typedef void (* GDBusProxyFunction) (GDBusProxy *proxy, void *user_data);
 typedef void (* GDBusPropertyFunction) (GDBusProxy *proxy, const char *name,
                                        DBusMessageIter *iter, void *user_data);
@@ -359,7 +365,8 @@ gboolean g_dbus_client_set_disconnect_watch(GDBusClient *client,
                                GDBusWatchFunction function, void *user_data);
 gboolean g_dbus_client_set_signal_watch(GDBusClient *client,
                                GDBusMessageFunction function, void *user_data);
-
+gboolean g_dbus_client_set_ready_watch(GDBusClient *client,
+                               GDBusClientFunction ready, void *user_data);
 gboolean g_dbus_client_set_proxy_handlers(GDBusClient *client,
                                        GDBusProxyFunction proxy_added,
                                        GDBusProxyFunction proxy_removed,
index 099b67f..435fb93 100644 (file)
@@ -30,8 +30,6 @@
 
 #include "gdbus.h"
 
-#define DISPATCH_TIMEOUT  0
-
 #define info(fmt...)
 #define error(fmt...)
 #define debug(fmt...)
@@ -70,8 +68,6 @@ static gboolean message_dispatch(void *data)
 {
        DBusConnection *conn = data;
 
-       dbus_connection_ref(conn);
-
        /* Dispatch messages */
        while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS);
 
@@ -84,7 +80,7 @@ static inline void queue_dispatch(DBusConnection *conn,
                                                DBusDispatchStatus status)
 {
        if (status == DBUS_DISPATCH_DATA_REMAINS)
-               g_timeout_add(DISPATCH_TIMEOUT, message_dispatch, conn);
+               g_idle_add(message_dispatch, dbus_connection_ref(conn));
 }
 
 static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
@@ -92,9 +88,6 @@ static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
        struct watch_info *info = data;
        unsigned int flags = 0;
        DBusDispatchStatus status;
-       DBusConnection *conn;
-
-       conn = dbus_connection_ref(info->conn);
 
        if (cond & G_IO_IN)  flags |= DBUS_WATCH_READABLE;
        if (cond & G_IO_OUT) flags |= DBUS_WATCH_WRITABLE;
@@ -103,10 +96,8 @@ static gboolean watch_func(GIOChannel *chan, GIOCondition cond, gpointer data)
 
        dbus_watch_handle(info->watch, flags);
 
-       status = dbus_connection_get_dispatch_status(conn);
-       queue_dispatch(conn, status);
-
-       dbus_connection_unref(conn);
+       status = dbus_connection_get_dispatch_status(info->conn);
+       queue_dispatch(info->conn, status);
 
        return TRUE;
 }
index b248cbb..13cf9a9 100644 (file)
@@ -1253,6 +1253,8 @@ static struct generic_data *object_path_ref(DBusConnection *connection,
 
        if (!dbus_connection_register_object_path(connection, path,
                                                &generic_table, data)) {
+               dbus_connection_unref(data->conn);
+               g_free(data->path);
                g_free(data->introspect);
                g_free(data);
                return NULL;
index 281f8ea..fe70c8b 100644 (file)
@@ -425,7 +425,7 @@ GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len)
        return header;
 }
 
-GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam)
+GObexHeader *g_obex_header_new_tag(guint8 id, GObexApparam *apparam)
 {
        guint8 buf[1024];
        gssize len;
@@ -434,7 +434,12 @@ GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam)
        if (len < 0)
                return NULL;
 
-       return g_obex_header_new_bytes(G_OBEX_HDR_APPARAM, buf, len);
+       return g_obex_header_new_bytes(id, buf, len);
+}
+
+GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam)
+{
+       return g_obex_header_new_tag(G_OBEX_HDR_APPARAM, apparam);
 }
 
 GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val)
index 42a2a0c..c2f975f 100644 (file)
@@ -85,6 +85,7 @@ GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str);
 GObexHeader *g_obex_header_new_bytes(guint8 id, const void *data, gsize len);
 GObexHeader *g_obex_header_new_uint8(guint8 id, guint8 val);
 GObexHeader *g_obex_header_new_uint32(guint8 id, guint32 val);
+GObexHeader *g_obex_header_new_tag(guint8 id, GObexApparam *apparam);
 GObexHeader *g_obex_header_new_apparam(GObexApparam *apparam);
 
 GSList *g_obex_header_create_list(guint8 first_hdr_id, va_list args,
index b815d60..8498177 100644 (file)
@@ -195,9 +195,11 @@ static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp,
        struct transfer *transfer = user_data;
        GObexPacket *req;
        gboolean rspcode, final;
+       guint id;
 
        g_obex_debug(G_OBEX_DEBUG_TRANSFER, "transfer %u", transfer->id);
 
+       id = transfer->req_id;
        transfer->req_id = 0;
 
        if (err != NULL) {
@@ -230,8 +232,11 @@ static void transfer_response(GObex *obex, GError *err, GObexPacket *rsp,
        } else if (!g_obex_srm_active(transfer->obex)) {
                req = g_obex_packet_new(transfer->opcode, TRUE,
                                                        G_OBEX_HDR_INVALID);
-       } else
+       } else {
+               /* Keep id since request still outstanting */
+               transfer->req_id = id;
                return;
+       }
 
        transfer->req_id = g_obex_send_req(obex, req, -1, transfer_response,
                                                        transfer, &err);
@@ -390,7 +395,7 @@ static void transfer_put_req(GObex *obex, GObexPacket *req, gpointer user_data)
 
        rspcode = put_get_bytes(transfer, req);
 
-       /* Don't send continue while in SRM */
+       /* Don't send continue while SRM is active */
        if (g_obex_srm_active(transfer->obex) &&
                                rspcode == G_OBEX_RSP_CONTINUE)
                goto done;
index 8c08b1e..3848884 100644 (file)
 
 #define CONNID_INVALID         0xffffffff
 
+/* Challenge request */
+#define NONCE_TAG              0x00
+#define NONCE_LEN              16
+
+/* Challenge response */
+#define DIGEST_TAG             0x00
+
 guint gobex_debug = 0;
 
 struct srm_config {
@@ -85,6 +92,7 @@ struct _GObex {
        guint16 tx_mtu;
 
        guint32 conn_id;
+       GObexApparam *authchal;
 
        GQueue *tx_queue;
 
@@ -105,6 +113,8 @@ struct pending_pkt {
        GObexResponseFunc rsp_func;
        gpointer rsp_data;
        gboolean cancelled;
+       gboolean suspended;
+       gboolean authenticating;
 };
 
 struct req_handler {
@@ -253,6 +263,8 @@ static gboolean req_timeout(gpointer user_data)
        g_error_free(err);
        pending_pkt_free(p);
 
+       p->timeout_id = 0;
+
        return FALSE;
 }
 
@@ -355,9 +367,20 @@ done:
        obex->srm = NULL;
 }
 
+static gboolean g_obex_srm_enabled(GObex *obex)
+{
+       if (!obex->use_srm)
+               return FALSE;
+
+       if (obex->srm == NULL)
+               return FALSE;
+
+       return obex->srm->enabled;
+}
+
 static void check_srm_final(GObex *obex, guint8 op)
 {
-       if (obex->srm == NULL || !obex->srm->enabled)
+       if (!g_obex_srm_enabled(obex))
                return;
 
        switch (obex->srm->op) {
@@ -388,7 +411,8 @@ static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing)
                g_obex_header_get_uint8(hdr, &srm);
                g_obex_debug(G_OBEX_DEBUG_COMMAND, "srm 0x%02x", srm);
                set_srm(obex, op, srm);
-       }
+       } else if (!g_obex_srm_enabled(obex))
+               set_srm(obex, op, G_OBEX_SRM_DISABLE);
 
        hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_SRMP);
        if (hdr != NULL) {
@@ -396,7 +420,9 @@ static void setup_srm(GObex *obex, GObexPacket *pkt, gboolean outgoing)
                g_obex_header_get_uint8(hdr, &srmp);
                g_obex_debug(G_OBEX_DEBUG_COMMAND, "srmp 0x%02x", srmp);
                set_srmp(obex, srmp, outgoing);
-       } else
+       } else if (obex->pending_req && obex->pending_req->suspended)
+               g_obex_packet_add_uint8(pkt, G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT);
+       else
                set_srmp(obex, -1, outgoing);
 
        if (final)
@@ -423,7 +449,7 @@ static gboolean write_data(GIOChannel *io, GIOCondition cond,
 
                setup_srm(obex, p->pkt, TRUE);
 
-               if (g_obex_srm_active(obex))
+               if (g_obex_srm_enabled(obex))
                        goto encode;
 
                /* Can't send a request while there's a pending one */
@@ -531,26 +557,85 @@ static void init_connect_data(GObex *obex, struct connect_data *data)
        memcpy(&data->mtu, &u16, sizeof(u16));
 }
 
+static guint8 *digest_response(const guint8 *nonce)
+{
+       GChecksum *md5;
+       guint8 *result;
+       gsize size;
+
+       result = g_new0(guint8, NONCE_LEN);
+
+       md5 = g_checksum_new(G_CHECKSUM_MD5);
+       if (md5 == NULL)
+               return result;
+
+       g_checksum_update(md5, nonce, NONCE_LEN);
+       g_checksum_update(md5, (guint8 *) ":BlueZ", 6);
+
+       size = NONCE_LEN;
+       g_checksum_get_digest(md5, result, &size);
+
+       g_checksum_free(md5);
+
+       return result;
+}
+
+static void prepare_auth_rsp(GObex *obex, GObexPacket *rsp)
+{
+       GObexHeader *hdr;
+       GObexApparam *authrsp;
+       const guint8 *nonce;
+       guint8 *result;
+       gsize len;
+
+       /* Check if client is already responding to authentication challenge */
+       hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_AUTHRESP);
+       if (hdr)
+               goto done;
+
+       if (!g_obex_apparam_get_bytes(obex->authchal, NONCE_TAG, &nonce, &len))
+               goto done;
+
+       if (len != NONCE_LEN)
+               goto done;
+
+       result = digest_response(nonce);
+       authrsp = g_obex_apparam_set_bytes(NULL, DIGEST_TAG, result, NONCE_LEN);
+
+       hdr = g_obex_header_new_tag(G_OBEX_HDR_AUTHRESP, authrsp);
+       g_obex_packet_add_header(rsp, hdr);
+
+       g_obex_apparam_free(authrsp);
+       g_free(result);
+
+done:
+       g_obex_apparam_free(obex->authchal);
+       obex->authchal = NULL;
+}
+
 static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp)
 {
-       GObexHeader *connid;
+       GObexHeader *hdr;
        struct connect_data data;
        static guint32 next_connid = 1;
 
        init_connect_data(obex, &data);
        g_obex_packet_set_data(rsp, &data, sizeof(data), G_OBEX_DATA_COPY);
 
-       connid = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION);
-       if (connid != NULL) {
-               g_obex_header_get_uint32(connid, &obex->conn_id);
-               return;
+       hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION);
+       if (hdr) {
+               g_obex_header_get_uint32(hdr, &obex->conn_id);
+               goto done;
        }
 
        obex->conn_id = next_connid++;
 
-       connid = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION,
-                                                       obex->conn_id);
-       g_obex_packet_prepend_header(rsp, connid);
+       hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id);
+       g_obex_packet_prepend_header(rsp, hdr);
+
+done:
+       if (obex->authchal)
+               prepare_auth_rsp(obex, rsp);
 }
 
 static void prepare_srm_rsp(GObex *obex, GObexPacket *pkt)
@@ -646,7 +731,7 @@ guint g_obex_send_req(GObex *obex, GObexPacket *req, int timeout,
        if (obex->rx_last_op == G_OBEX_RSP_CONTINUE)
                goto create_pending;
 
-       if (g_obex_srm_active(obex) && obex->pending_req != NULL)
+       if (g_obex_srm_enabled(obex) && obex->pending_req != NULL)
                goto create_pending;
 
        hdr = g_obex_packet_get_header(req, G_OBEX_HDR_CONNECTION);
@@ -695,7 +780,9 @@ static gboolean pending_req_abort(GObex *obex, GError **err)
 
        p->cancelled = TRUE;
 
-       g_source_remove(p->timeout_id);
+       if (p->timeout_id > 0)
+               g_source_remove(p->timeout_id);
+
        p->timeout = G_OBEX_ABORT_TIMEOUT;
        p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex);
 
@@ -834,24 +921,77 @@ gboolean g_obex_remove_request_function(GObex *obex, guint id)
        return TRUE;
 }
 
+static void g_obex_srm_suspend(GObex *obex)
+{
+       struct pending_pkt *p = obex->pending_req;
+       GObexPacket *req;
+
+       if (p->timeout_id > 0) {
+               g_source_remove(p->timeout_id);
+               p->timeout_id = 0;
+       }
+
+       p->suspended = TRUE;
+
+       req = g_obex_packet_new(G_OBEX_OP_GET, TRUE,
+                                       G_OBEX_HDR_SRMP, G_OBEX_SRMP_WAIT,
+                                       G_OBEX_HDR_INVALID);
+
+       g_obex_send(obex, req, NULL);
+}
+
 void g_obex_suspend(GObex *obex)
 {
+       struct pending_pkt *req = obex->pending_req;
+
        g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
 
+       if (!g_obex_srm_active(obex) || !req)
+               goto done;
+
+       /* Send SRMP wait in case of GET */
+       if (g_obex_packet_get_operation(req->pkt, NULL) == G_OBEX_OP_GET) {
+               g_obex_srm_suspend(obex);
+               return;
+       }
+
+done:
+       obex->suspended = TRUE;
+
        if (obex->write_source > 0) {
                g_source_remove(obex->write_source);
                obex->write_source = 0;
        }
+}
 
-       obex->suspended = TRUE;
+static void g_obex_srm_resume(GObex *obex)
+{
+       struct pending_pkt *p = obex->pending_req;
+       GObexPacket *req;
+
+       p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout, obex);
+       p->suspended = FALSE;
+
+       req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
+
+       g_obex_send(obex, req, NULL);
 }
 
 void g_obex_resume(GObex *obex)
 {
+       struct pending_pkt *req = obex->pending_req;
+
        g_obex_debug(G_OBEX_DEBUG_COMMAND, "conn %u", obex->conn_id);
 
        obex->suspended = FALSE;
 
+       if (g_obex_srm_active(obex) || !req)
+               goto done;
+
+       if (g_obex_packet_get_operation(req->pkt, NULL) == G_OBEX_OP_GET)
+               g_obex_srm_resume(obex);
+
+done:
        if (g_queue_get_length(obex->tx_queue) > 0 || obex->tx_data > 0)
                enable_tx(obex);
 }
@@ -860,10 +1000,7 @@ gboolean g_obex_srm_active(GObex *obex)
 {
        gboolean ret = FALSE;
 
-       if (!obex->use_srm)
-               return FALSE;
-
-       if (obex->srm == NULL || !obex->srm->enabled)
+       if (!g_obex_srm_enabled(obex))
                goto done;
 
        if (obex->srm->srmp <= G_OBEX_SRMP_NEXT_WAIT)
@@ -875,10 +1012,31 @@ done:
        return ret;
 }
 
+static void auth_challenge(GObex *obex)
+{
+       struct pending_pkt *p = obex->pending_req;
+
+       if (p->authenticating)
+               return;
+
+       p->authenticating = TRUE;
+
+       prepare_auth_rsp(obex, p->pkt);
+
+       /* Remove it as pending and add it back to the queue so it gets sent
+        * again */
+       if (p->timeout_id > 0) {
+               g_source_remove(p->timeout_id);
+               p->timeout_id = 0;
+       }
+       obex->pending_req = NULL;
+       g_obex_send_internal(obex, p, NULL);
+}
+
 static void parse_connect_data(GObex *obex, GObexPacket *pkt)
 {
        const struct connect_data *data;
-       GObexHeader *connid;
+       GObexHeader *hdr;
        guint16 u16;
        size_t data_len;
 
@@ -893,9 +1051,13 @@ static void parse_connect_data(GObex *obex, GObexPacket *pkt)
                obex->tx_mtu = obex->io_tx_mtu;
        obex->tx_buf = g_realloc(obex->tx_buf, obex->tx_mtu);
 
-       connid = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
-       if (connid != NULL)
-               g_obex_header_get_uint32(connid, &obex->conn_id);
+       hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
+       if (hdr)
+               g_obex_header_get_uint32(hdr, &obex->conn_id);
+
+       hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_AUTHCHAL);
+       if (hdr)
+               obex->authchal = g_obex_header_get_apparam(hdr);
 }
 
 static gboolean parse_response(GObex *obex, GObexPacket *rsp)
@@ -907,12 +1069,15 @@ static gboolean parse_response(GObex *obex, GObexPacket *rsp)
        rspcode = g_obex_packet_get_operation(rsp, &final);
 
        opcode = g_obex_packet_get_operation(p->pkt, NULL);
-       if (opcode == G_OBEX_OP_CONNECT)
+       if (opcode == G_OBEX_OP_CONNECT) {
                parse_connect_data(obex, rsp);
+               if (rspcode == G_OBEX_RSP_UNAUTHORIZED && obex->authchal)
+                       auth_challenge(obex);
+       }
 
        setup_srm(obex, rsp, FALSE);
 
-       if (!g_obex_srm_active(obex))
+       if (!g_obex_srm_enabled(obex))
                return final;
 
        /*
@@ -921,7 +1086,9 @@ static gboolean parse_response(GObex *obex, GObexPacket *rsp)
         * continue sending responses until the transfer is finished
         */
        if (opcode == G_OBEX_OP_GET && rspcode == G_OBEX_RSP_CONTINUE) {
-               g_source_remove(p->timeout_id);
+               if (p->timeout_id > 0)
+                       g_source_remove(p->timeout_id);
+
                p->timeout_id = g_timeout_add_seconds(p->timeout, req_timeout,
                                                                        obex);
                return FALSE;
@@ -932,12 +1099,21 @@ static gboolean parse_response(GObex *obex, GObexPacket *rsp)
 
 static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
 {
-       struct pending_pkt *p = obex->pending_req;
+       struct pending_pkt *p;
        gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE;
 
        if (rsp != NULL)
                final_rsp = parse_response(obex, rsp);
 
+       if (!obex->pending_req)
+               return;
+
+       p = obex->pending_req;
+
+       /* Reset if final so it can no longer be cancelled */
+       if (final_rsp)
+               obex->pending_req = NULL;
+
        if (p->cancelled)
                err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
                                        "The operation was cancelled");
@@ -949,17 +1125,15 @@ static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
                p->rsp_func(obex, err, rsp, p->rsp_data);
 
                /* Check if user callback removed the request */
-               if (p != obex->pending_req)
+               if (!final_rsp && p != obex->pending_req)
                        return;
        }
 
        if (p->cancelled)
                g_error_free(err);
 
-       if (final_rsp) {
+       if (final_rsp)
                pending_pkt_free(p);
-               obex->pending_req = NULL;
-       }
 
        if (!disconn && g_queue_get_length(obex->tx_queue) > 0)
                enable_tx(obex);
@@ -1359,6 +1533,9 @@ void g_obex_unref(GObex *obex)
        if (obex->pending_req)
                pending_pkt_free(obex->pending_req);
 
+       if (obex->authchal)
+               g_obex_apparam_free(obex->authchal);
+
        g_free(obex);
 }
 
@@ -1384,6 +1561,18 @@ guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
        return g_obex_send_req(obex, req, -1, func, user_data, err);
 }
 
+guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data,
+                                                               GError **err)
+{
+       GObexPacket *req;
+
+       g_obex_debug(G_OBEX_DEBUG_COMMAND, "");
+
+       req = g_obex_packet_new(G_OBEX_OP_DISCONNECT, TRUE, G_OBEX_HDR_INVALID);
+
+       return g_obex_send_req(obex, req, -1, func, user_data, err);
+}
+
 guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func,
                                        gpointer user_data, GError **err)
 {
index 76a224e..7c47590 100644 (file)
@@ -75,6 +75,9 @@ void g_obex_unref(GObex *obex);
 guint g_obex_connect(GObex *obex, GObexResponseFunc func, gpointer user_data,
                                GError **err, guint8 first_hdr_id, ...);
 
+guint g_obex_disconnect(GObex *obex, GObexResponseFunc func, gpointer user_data,
+                                                               GError **err);
+
 guint g_obex_setpath(GObex *obex, const char *path, GObexResponseFunc func,
                                        gpointer user_data, GError **err);
 
index 17ee2c1..406b508 100644 (file)
@@ -542,7 +542,7 @@ const char *bt_compidtostr(int compid)
        case 139:
                return "Topcorn Positioning Systems, LLC";
        case 140:
-               return "Qualcomm Labs, Inc.";
+               return "Qualcomm Retail Solutions, Inc. (formerly Qualcomm Labs, Inc.)";
        case 141:
                return "Zscan Software";
        case 142:
@@ -677,6 +677,8 @@ const char *bt_compidtostr(int compid)
                return "Elgato Systems GmbH";
        case 207:
                return "ARCHOS SA";
+       case 208:
+               return "Dexcom, Inc.";
        case 209:
                return "Polar Electro Europe B.V.";
        case 210:
@@ -744,7 +746,7 @@ const char *bt_compidtostr(int compid)
        case 241:
                return "Witron Technology Limited";
        case 242:
-               return "Morse Project Inc.";
+               return "Aether Things Inc. (formerly Morse Project Inc.)";
        case 243:
                return "Kent Displays Inc.";
        case 244:
@@ -817,6 +819,120 @@ const char *bt_compidtostr(int compid)
                return "e.solutions";
        case 278:
                return "1OAK Technologies";
+       case 279:
+               return "Wimoto Technologies Inc";
+       case 280:
+               return "Radius Networks, Inc.";
+       case 281:
+               return "Wize Technology Co., Ltd.";
+       case 282:
+               return "Qualcomm Labs, Inc.";
+       case 283:
+               return "Aruba Networks";
+       case 284:
+               return "Baidu";
+       case 285:
+               return "Arendi AG";
+       case 286:
+               return "Skoda Auto a.s.";
+       case 287:
+               return "Volkswagon AG";
+       case 288:
+               return "Porsche AG";
+       case 289:
+               return "Sino Wealth Electronic Ltd.";
+       case 290:
+               return "AirTurn, Inc.";
+       case 291:
+               return "Kinsa, Inc.";
+       case 292:
+               return "HID Global";
+       case 293:
+               return "SEAT es";
+       case 294:
+               return "Promethean Ltd.";
+       case 295:
+               return "Salutica Allied Solutions";
+       case 296:
+               return "GPSI Group Pty Ltd";
+       case 297:
+               return "Nimble Devices Oy";
+       case 298:
+               return "Changzhou Yongse Infotech Co., Ltd";
+       case 299:
+               return "SportIQ";
+       case 300:
+               return "TEMEC Instruments B.V.";
+       case 301:
+               return "Sony Corporation";
+       case 302:
+               return "ASSA ABLOY";
+       case 303:
+               return "Clarion Co., Ltd.";
+       case 304:
+               return "Warehouse Innovations";
+       case 305:
+               return "Cypress Semiconductor Corporation";
+       case 306:
+               return "MADS Inc";
+       case 307:
+               return "Blue Maestro Limited";
+       case 308:
+               return "Resolution Products, Inc.";
+       case 309:
+               return "Airewear LLC";
+       case 310:
+               return "ETC sp. z.o.o.";
+       case 311:
+               return "Prestigio Plaza Ltd.";
+       case 312:
+               return "NTEO Inc.";
+       case 313:
+               return "Focus Systems Corporation";
+       case 314:
+               return "Tencent Holdings Limited";
+       case 315:
+               return "Allegion";
+       case 316:
+               return "Murata Manufacuring Co., Ltd.";
+       case 317:
+               return "WirelessWERX";
+       case 318:
+               return "Nod, Inc.";
+       case 319:
+               return "B&B Manufacturing Company";
+       case 320:
+               return "Alpine Electronics (China) Co., Ltd";
+       case 321:
+               return "FedEx Services";
+       case 322:
+               return "Grape Systems Inc.";
+       case 323:
+               return "Bkon Connect";
+       case 324:
+               return "Lintech GmbH";
+       case 325:
+               return "Novatel Wireless";
+       case 326:
+               return "Ciright";
+       case 327:
+               return "Mighty Cast, Inc.";
+       case 328:
+               return "Ambimat Electronics";
+       case 329:
+               return "Perytons Ltd.";
+       case 330:
+               return "Tivoli Audio, LLC";
+       case 331:
+               return "Master Lock";
+       case 332:
+               return "Mesh-Net Ltd";
+       case 333:
+               return "HUIZHOU DESAY SV AUTOMOTIVE CO., LTD.";
+       case 334:
+               return "Tangerine, Inc.";
+       case 335:
+               return "B&W Group Ltd.";
        case 65535:
                return "internal use";
        default:
index a896e52..bab0f99 100644 (file)
@@ -94,6 +94,9 @@ struct mgmt_rp_read_index_list {
 #define MGMT_SETTING_HS                        0x00000100
 #define MGMT_SETTING_LE                        0x00000200
 #define MGMT_SETTING_ADVERTISING       0x00000400
+#define MGMT_SETTING_SECURE_CONN       0x00000800
+#define MGMT_SETTING_DEBUG_KEYS                0x00001000
+#define MGMT_SETTING_PRIVACY           0x00002000
 
 #define MGMT_OP_READ_INFO              0x0004
 struct mgmt_rp_read_info {
@@ -176,11 +179,11 @@ struct mgmt_cp_load_link_keys {
 
 struct mgmt_ltk_info {
        struct mgmt_addr_info addr;
-       uint8_t authenticated;
+       uint8_t type;
        uint8_t master;
        uint8_t enc_size;
        uint16_t ediv;
-       uint8_t rand[8];
+       uint64_t rand;
        uint8_t val[16];
 } __packed;
 
@@ -270,6 +273,12 @@ struct mgmt_rp_read_local_oob_data {
        uint8_t hash[16];
        uint8_t randomizer[16];
 } __packed;
+struct mgmt_rp_read_local_oob_ext_data {
+       uint8_t hash192[16];
+       uint8_t randomizer192[16];
+       uint8_t hash256[16];
+       uint8_t randomizer256[16];
+} __packed;
 
 #define MGMT_OP_ADD_REMOTE_OOB_DATA    0x0021
 struct mgmt_cp_add_remote_oob_data {
@@ -335,6 +344,38 @@ struct mgmt_cp_set_scan_params {
        uint16_t window;
 } __packed;
 
+#define MGMT_OP_SET_SECURE_CONN                0x002D
+
+#define MGMT_OP_SET_DEBUG_KEYS         0x002E
+
+struct mgmt_irk_info {
+       struct mgmt_addr_info addr;
+       uint8_t val[16];
+} __packed;
+
+#define MGMT_OP_SET_PRIVACY            0x002F
+struct mgmt_cp_set_privacy {
+       uint8_t privacy;
+       uint8_t irk[16];
+} __packed;
+
+#define MGMT_OP_LOAD_IRKS              0x0030
+struct mgmt_cp_load_irks {
+       uint16_t irk_count;
+       struct mgmt_irk_info irks[0];
+} __packed;
+
+#define MGMT_OP_GET_CONN_INFO          0x0031
+struct mgmt_cp_get_conn_info {
+       struct mgmt_addr_info addr;
+} __packed;
+struct mgmt_rp_get_conn_info {
+       struct mgmt_addr_info addr;
+       int8_t rssi;
+       int8_t tx_power;
+       int8_t max_tx_power;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        uint16_t opcode;
@@ -471,6 +512,25 @@ struct mgmt_ev_passkey_notify {
        uint8_t entered;
 } __packed;
 
+#define MGMT_EV_NEW_IRK                        0x0018
+struct mgmt_ev_new_irk {
+       uint8_t  store_hint;
+       bdaddr_t rpa;
+       struct mgmt_irk_info key;
+} __packed;
+
+struct mgmt_csrk_info {
+       struct mgmt_addr_info addr;
+       uint8_t master;
+       uint8_t val[16];
+} __packed;
+
+#define MGMT_EV_NEW_CSRK               0x0019
+struct mgmt_ev_new_csrk {
+       uint8_t store_hint;
+       struct mgmt_csrk_info key;
+} __packed;
+
 static const char *mgmt_op[] = {
        "<0x0000>",
        "Read Version",
@@ -517,6 +577,11 @@ static const char *mgmt_op[] = {
        "Set BR/EDR",
        "Set Static Address",
        "Set Scan Parameters",
+       "Set Secure Connections",
+       "Set Debug Keys",
+       "Set Privacy",
+       "Load Identity Resolving Keys",
+       "Get Connection Information",
 };
 
 static const char *mgmt_ev[] = {
@@ -544,6 +609,8 @@ static const char *mgmt_ev[] = {
        "Device Unblocked",
        "Device Unpaired",
        "Passkey Notify",
+       "New Identity Resolving Key",
+       "New Signature Resolving Key",
 };
 
 static const char *mgmt_status[] = {
index cbdf15e..e5e4622 100644 (file)
--- a/lib/sdp.c
+++ b/lib/sdp.c
@@ -67,6 +67,9 @@ static uint128_t bluetooth_base_uuid = {
 
 #define SDP_MAX_ATTR_LEN 65535
 
+/* match MTU used by RFCOMM */
+#define SDP_LARGE_L2CAP_MTU 1013
+
 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);
@@ -178,8 +181,9 @@ static struct tupla ServiceClass[] = {
        { HDP_SVCLASS_ID,                       "HDP"                           },
        { HDP_SOURCE_SVCLASS_ID,                "HDP Source"                    },
        { HDP_SINK_SVCLASS_ID,                  "HDP Sink"                      },
-       { APPLE_AGENT_SVCLASS_ID,               "Apple Agent"                   },
+       { GENERIC_ACCESS_SVCLASS_ID,            "Generic Access"                },
        { GENERIC_ATTRIB_SVCLASS_ID,            "Generic Attribute"             },
+       { APPLE_AGENT_SVCLASS_ID,               "Apple Agent"                   },
        { 0 }
 };
 
@@ -1601,13 +1605,13 @@ void sdp_record_print(const sdp_record_t *rec)
 {
        sdp_data_t *d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY);
        if (d && SDP_IS_TEXT_STR(d->dtd))
-               printf("Service Name: %.*s", d->unitSize, d->val.str);
+               printf("Service Name: %.*s\n", d->unitSize, d->val.str);
        d = sdp_data_get(rec, SDP_ATTR_SVCDESC_PRIMARY);
        if (d && SDP_IS_TEXT_STR(d->dtd))
-               printf("Service Description: %.*s", d->unitSize, d->val.str);
+               printf("Service Description: %.*s\n", d->unitSize, d->val.str);
        d = sdp_data_get(rec, SDP_ATTR_PROVNAME_PRIMARY);
        if (d && SDP_IS_TEXT_STR(d->dtd))
-               printf("Service Provider: %.*s", d->unitSize, d->val.str);
+               printf("Service Provider: %.*s\n", d->unitSize, d->val.str);
 }
 
 #ifdef SDP_DEBUG
@@ -4644,6 +4648,26 @@ static int sdp_connect_local(sdp_session_t *session)
        return connect(session->sock, (struct sockaddr *) &sa, sizeof(sa));
 }
 
+static int set_l2cap_mtu(int sk, uint16_t mtu)
+{
+       struct l2cap_options l2o;
+       socklen_t len;
+
+       memset(&l2o, 0, sizeof(l2o));
+       len = sizeof(l2o);
+
+       if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0)
+               return -1;
+
+       l2o.imtu = mtu;
+       l2o.omtu = mtu;
+
+       if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0)
+               return -1;
+
+       return 0;
+}
+
 static int sdp_connect_l2cap(const bdaddr_t *src,
                const bdaddr_t *dst, sdp_session_t *session)
 {
@@ -4678,6 +4702,10 @@ static int sdp_connect_l2cap(const bdaddr_t *src,
                setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
        }
 
+       if ((flags & SDP_LARGE_MTU) &&
+                               set_l2cap_mtu(sk, SDP_LARGE_L2CAP_MTU) < 0)
+               return -1;
+
        sa.l2_psm = htobs(SDP_PSM);
        sa.l2_bdaddr = *dst;
 
@@ -4773,7 +4801,7 @@ int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf)
                        free(dtds);
                        goto fail;
                }
-               lengths = malloc(plen * sizeof(int *));
+               lengths = malloc(plen * sizeof(int));
                if (!lengths) {
                        free(dtds);
                        free(vals);
index f2f2484..34adf9a 100644 (file)
--- a/lib/sdp.h
+++ b/lib/sdp.h
@@ -150,8 +150,9 @@ extern "C" {
 #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_ACCESS_SVCLASS_ID      0x1800
 #define GENERIC_ATTRIB_SVCLASS_ID      0x1801
+#define APPLE_AGENT_SVCLASS_ID         0x2112
 
 /*
  * Standard profile descriptor identifiers; note these
@@ -207,6 +208,7 @@ extern "C" {
 #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 MAP_PROFILE_ID                 MAP_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
@@ -223,9 +225,9 @@ extern "C" {
 #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_ACCESS_PROFILE_ID      GENERIC_ACCESS_SVCLASS_ID
 #define GENERIC_ATTRIB_PROFILE_ID      GENERIC_ATTRIB_SVCLASS_ID
+#define APPLE_AGENT_PROFILE_ID         APPLE_AGENT_SVCLASS_ID
 
 /*
  * Compatibility macros for the old MDP acronym
@@ -296,6 +298,8 @@ extern "C" {
 #define SDP_ATTR_SUPPORTED_REPOSITORIES                0x0314
 #define SDP_ATTR_MAS_INSTANCE_ID               0x0315
 #define SDP_ATTR_SUPPORTED_MESSAGE_TYPES       0x0316
+#define SDP_ATTR_PBAP_SUPPORTED_FEATURES       0x0317
+#define SDP_ATTR_MAP_SUPPORTED_FEATURES                0x0317
 
 #define SDP_ATTR_SPECIFICATION_ID              0x0200
 #define SDP_ATTR_VENDOR_ID                     0x0201
index 6e1eb91..3ded393 100644 (file)
@@ -81,6 +81,7 @@ static inline void sdp_list_foreach(sdp_list_t *list, sdp_list_func_t f, void *u
 #define SDP_RETRY_IF_BUSY      0x01
 #define SDP_WAIT_ON_CLOSE      0x02
 #define SDP_NON_BLOCKING       0x04
+#define SDP_LARGE_MTU          0x08
 
 /*
  * a session with an SDP server
index 4363aee..5c3f986 100644 (file)
@@ -32,7 +32,6 @@
 
 #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 }
@@ -41,33 +40,36 @@ static uint128_t bluetooth_base_uuid = {
 #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)
 {
+       uint16_t be16;
+
        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));
+       /*
+        * No matter the system: 128-bit UUIDs should be stored
+        * as big-endian. 16-bit UUIDs are stored on host order.
+        */
+
+       be16 = htons(src->value.u16);
+       memcpy(&dst->value.u128.data[BASE_UUID16_OFFSET], &be16, sizeof(be16));
 }
 
 static void bt_uuid32_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
 {
+       uint32_t be32;
+
        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));
+       /*
+        * No matter the system: 128-bit UUIDs should be stored
+        * as big-endian. 32-bit UUIDs are stored on host order.
+        */
+
+       be32 = htonl(src->value.u32);
+       memcpy(&dst->value.u128.data[BASE_UUID32_OFFSET], &be32, sizeof(be32));
 }
 
 void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst)
@@ -154,10 +156,7 @@ int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n)
                unsigned int   data4;
                unsigned short data5;
 
-               uint128_t nvalue;
-               const uint8_t *data = (uint8_t *) &nvalue;
-
-               hton128(&uuid->value.u128, &nvalue);
+               const uint8_t *data = (uint8_t *) &uuid->value.u128;
 
                memcpy(&data0, &data[0], 4);
                memcpy(&data1, &data[4], 2);
@@ -231,8 +230,8 @@ 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;
+       uint128_t u128;
+       uint8_t *val = (uint8_t *) &u128;
 
        if (sscanf(string, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
                                &data0, &data1, &data2,
@@ -253,8 +252,6 @@ static int bt_string_to_uuid128(bt_uuid_t *uuid, const char *string)
        memcpy(&val[10], &data4, 4);
        memcpy(&val[14], &data5, 2);
 
-       ntoh128(&n128, &u128);
-
        bt_uuid128_create(uuid, u128);
 
        return 0;
index 95e5a9a..814fd92 100644 (file)
@@ -105,6 +105,38 @@ extern "C" {
 #define OBEX_MNS_UUID          "00001133-0000-1000-8000-00805f9b34fb"
 #define OBEX_MAP_UUID          "00001134-0000-1000-8000-00805f9b34fb"
 
+/* GATT UUIDs section */
+#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
+#define GATT_CHARAC_SYSTEM_ID                          0x2A23
+#define GATT_CHARAC_MODEL_NUMBER_STRING                        0x2A24
+#define GATT_CHARAC_SERIAL_NUMBER_STRING               0x2A25
+#define GATT_CHARAC_FIRMWARE_REVISION_STRING           0x2A26
+#define GATT_CHARAC_HARDWARE_REVISION_STRING           0x2A27
+#define GATT_CHARAC_SOFTWARE_REVISION_STRING           0x2A28
+#define GATT_CHARAC_MANUFACTURER_NAME_STRING           0x2A29
+
+/* 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
+#define GATT_EXTERNAL_REPORT_REFERENCE                 0x2907
+#define GATT_REPORT_REFERENCE                          0x2908
+
 typedef struct {
        enum {
                BT_UUID_UNSPEC = 0,
@@ -133,6 +165,11 @@ void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst);
 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);
 
+static inline int bt_uuid_len(const bt_uuid_t *uuid)
+{
+       return uuid->type / 8;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/monitor/analyze.c b/monitor/analyze.c
new file mode 100644 (file)
index 0000000..a747938
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-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 <stdio.h>
+#include <string.h>
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/btsnoop.h"
+#include "monitor/bt.h"
+#include "analyze.h"
+
+#define MAX_PACKET_SIZE                (1486 + 4)
+
+struct hci_dev {
+       uint16_t index;
+       uint8_t type;
+       uint8_t bdaddr[6];
+       struct timeval time_added;
+       struct timeval time_removed;
+       unsigned long num_cmd;
+       unsigned long num_evt;
+       unsigned long num_acl;
+       unsigned long num_sco;
+};
+
+static struct queue *dev_list;
+
+static void dev_destroy(void *data)
+{
+       struct hci_dev *dev = data;
+       const char *str;
+
+       switch (dev->type) {
+       case 0x00:
+               str = "BR/EDR";
+               break;
+       case 0x01:
+               str = "AMP";
+               break;
+       default:
+               str = "unknown";
+               break;
+       }
+
+       printf("Found %s controller with index %u\n", str, dev->index);
+       printf("  BD_ADDR %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+                       dev->bdaddr[5], dev->bdaddr[4], dev->bdaddr[3],
+                       dev->bdaddr[2], dev->bdaddr[1], dev->bdaddr[0]);
+       printf("  %lu commands\n", dev->num_cmd);
+       printf("  %lu events\n", dev->num_evt);
+       printf("  %lu ACL packets\n", dev->num_acl);
+       printf("  %lu SCO packets\n", dev->num_sco);
+       printf("\n");
+
+       free(dev);
+}
+
+static struct hci_dev *dev_alloc(uint16_t index)
+{
+       struct hci_dev *dev;
+
+       dev = new0(struct hci_dev, 1);
+       if (!dev) {
+               fprintf(stderr, "Failed to allocate new device entry\n");
+               return NULL;
+       }
+
+       dev->index = index;
+
+       return dev;
+}
+
+static bool dev_match_index(const void *a, const void *b)
+{
+       const struct hci_dev *dev = a;
+       uint16_t index = PTR_TO_UINT(b);
+
+       return dev->index == index;
+}
+
+static struct hci_dev *dev_lookup(uint16_t index)
+{
+       struct hci_dev *dev;
+
+       dev = queue_find(dev_list, dev_match_index, UINT_TO_PTR(index));
+       if (!dev) {
+               fprintf(stderr, "Creating new device for unknown index\n");
+
+               dev = dev_alloc(index);
+               if (!dev)
+                       return NULL;
+
+               queue_push_tail(dev_list, dev);
+       }
+
+       return dev;
+}
+
+static void new_index(struct timeval *tv, uint16_t index,
+                                       const void *data, uint16_t size)
+{
+       const struct btsnoop_opcode_new_index *ni = data;
+       struct hci_dev *dev;
+
+       dev = dev_alloc(index);
+       if (!dev)
+               return;
+
+       dev->type = ni->type;
+       memcpy(dev->bdaddr, ni->bdaddr, 6);
+
+       queue_push_tail(dev_list, dev);
+}
+
+static void del_index(struct timeval *tv, uint16_t index,
+                                       const void *data, uint16_t size)
+{
+       struct hci_dev *dev;
+
+       dev = queue_remove_if(dev_list, dev_match_index, UINT_TO_PTR(index));
+       if (!dev) {
+               fprintf(stderr, "Remove for an unexisting device\n");
+               return;
+       }
+
+       dev_destroy(dev);
+}
+
+static void command_pkt(struct timeval *tv, uint16_t index,
+                                       const void *data, uint16_t size)
+{
+       const struct bt_hci_cmd_hdr *hdr = data;
+       struct hci_dev *dev;
+
+       data += sizeof(*hdr);
+       size -= sizeof(*hdr);
+
+       dev = dev_lookup(index);
+       if (!dev)
+               return;
+
+       dev->num_cmd++;
+}
+
+static void rsp_read_bd_addr(struct hci_dev *dev, struct timeval *tv,
+                                       const void *data, uint16_t size)
+{
+       const struct bt_hci_rsp_read_bd_addr *rsp = data;
+
+       printf("Read BD Addr event with status 0x%2.2x\n", rsp->status);
+
+       if (rsp->status)
+               return;
+
+       memcpy(dev->bdaddr, rsp->bdaddr, 6);
+}
+
+static void evt_cmd_complete(struct hci_dev *dev, struct timeval *tv,
+                                       const void *data, uint16_t size)
+{
+       const struct bt_hci_evt_cmd_complete *evt = data;
+       uint16_t opcode;
+
+       data += sizeof(*evt);
+       size -= sizeof(*evt);
+
+       opcode = le16_to_cpu(evt->opcode);
+
+       switch (opcode) {
+       case BT_HCI_CMD_READ_BD_ADDR:
+               rsp_read_bd_addr(dev, tv, data, size);
+               break;
+       }
+}
+
+static void event_pkt(struct timeval *tv, uint16_t index,
+                                       const void *data, uint16_t size)
+{
+       const struct bt_hci_evt_hdr *hdr = data;
+       struct hci_dev *dev;
+
+       data += sizeof(*hdr);
+       size -= sizeof(*hdr);
+
+       dev = dev_lookup(index);
+       if (!dev)
+               return;
+
+       dev->num_evt++;
+
+       switch (hdr->evt) {
+       case BT_HCI_EVT_CMD_COMPLETE:
+               evt_cmd_complete(dev, tv, data, size);
+               break;
+       }
+}
+
+static void acl_pkt(struct timeval *tv, uint16_t index,
+                                       const void *data, uint16_t size)
+{
+       const struct bt_hci_acl_hdr *hdr = data;
+       struct hci_dev *dev;
+
+       data += sizeof(*hdr);
+       size -= sizeof(*hdr);
+
+       dev = dev_lookup(index);
+       if (!dev)
+               return;
+
+       dev->num_acl++;
+}
+
+static void sco_pkt(struct timeval *tv, uint16_t index,
+                                       const void *data, uint16_t size)
+{
+       const struct bt_hci_sco_hdr *hdr = data;
+       struct hci_dev *dev;
+
+       data += sizeof(*hdr);
+       size -= sizeof(*hdr);
+
+       dev = dev_lookup(index);
+       if (!dev)
+               return;
+
+       dev->num_sco++;
+}
+
+void analyze_trace(const char *path)
+{
+       struct btsnoop *btsnoop_file;
+       unsigned long num_packets = 0;
+       uint32_t type;
+
+       btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT);
+       if (!btsnoop_file)
+               return;
+
+       type = btsnoop_get_type(btsnoop_file);
+
+       switch (type) {
+       case BTSNOOP_TYPE_HCI:
+       case BTSNOOP_TYPE_UART:
+       case BTSNOOP_TYPE_MONITOR:
+               break;
+       default:
+               fprintf(stderr, "Unsupported packet format\n");
+               return;
+       }
+
+       dev_list = queue_new();
+       if (!dev_list) {
+               fprintf(stderr, "Failed to allocate device list\n");
+               goto done;
+       }
+
+       while (1) {
+               unsigned char buf[MAX_PACKET_SIZE];
+               struct timeval tv;
+               uint16_t index, opcode, pktlen;
+
+               if (btsnoop_read_hci(btsnoop_file, &tv, &index, &opcode,
+                                                       buf, &pktlen) < 0)
+                       break;
+
+               switch (opcode) {
+               case BTSNOOP_OPCODE_NEW_INDEX:
+                       new_index(&tv, index, buf, pktlen);
+                       break;
+               case BTSNOOP_OPCODE_DEL_INDEX:
+                       del_index(&tv, index, buf, pktlen);
+                       break;
+               case BTSNOOP_OPCODE_COMMAND_PKT:
+                       command_pkt(&tv, index, buf, pktlen);
+                       break;
+               case BTSNOOP_OPCODE_EVENT_PKT:
+                       event_pkt(&tv, index, buf, pktlen);
+                       break;
+               case BTSNOOP_OPCODE_ACL_TX_PKT:
+               case BTSNOOP_OPCODE_ACL_RX_PKT:
+                       acl_pkt(&tv, index, buf, pktlen);
+                       break;
+               case BTSNOOP_OPCODE_SCO_TX_PKT:
+               case BTSNOOP_OPCODE_SCO_RX_PKT:
+                       sco_pkt(&tv, index, buf, pktlen);
+                       break;
+               }
+
+               num_packets++;
+       }
+
+       printf("Trace contains %lu packets\n\n", num_packets);
+
+       queue_destroy(dev_list, dev_destroy);
+
+done:
+       btsnoop_unref(btsnoop_file);
+}
diff --git a/monitor/analyze.h b/monitor/analyze.h
new file mode 100644 (file)
index 0000000..c643d35
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-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
+ *
+ */
+
+void analyze_trace(const char *path);
index 849ed86..1a41d9e 100644 (file)
@@ -2,8 +2,8 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
  *
  *
  *  This library is free software; you can redistribute it and/or
@@ -99,19 +99,255 @@ struct bt_ll_reject_ind {
        uint8_t  error;
 } __attribute__ ((packed));
 
+#define LMP_ESC4(x) ((127 << 8) | (x))
+
+#define BT_LMP_ACCEPTED                        3
+struct bt_lmp_accepted {
+       uint8_t  opcode;
+} __attribute__ ((packed));
+
+#define BT_LMP_NOT_ACCEPTED            4
+struct bt_lmp_not_accepted {
+       uint8_t  opcode;
+       uint8_t  error;
+} __attribute__ ((packed));
+
+#define BT_LMP_CLKOFFSET_REQ           5
+
+#define BT_LMP_DETACH                  7
+struct bt_lmp_detach {
+       uint8_t  error;
+} __attribute__ ((packed));
+
+#define BT_LMP_AU_RAND                 11
+struct bt_lmp_au_rand {
+       uint8_t  number[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_SRES                    12
+struct bt_lmp_sres {
+       uint8_t  response[4];
+} __attribute__ ((packed));
+
+#define BT_LMP_ENCRYPTION_MODE_REQ     15
+struct bt_lmp_encryption_mode_req {
+       uint8_t  mode;
+} __attribute__ ((packed));
+
+#define BT_LMP_ENCRYPTION_KEY_SIZE_REQ 16
+struct bt_lmp_encryption_key_size_req {
+       uint8_t  key_size;
+} __attribute__ ((packed));
+
+#define BT_LMP_START_ENCRYPTION_REQ    17
+struct bt_lmp_start_encryption_req {
+       uint8_t  number[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_STOP_ENCRYPTION_REQ     18
+
+#define BT_LMP_UNSNIFF_REQ             24
+
+#define BT_LMP_MAX_POWER               33
+
+#define BT_LMP_MIN_POWER               34
+
+#define BT_LMP_AUTO_RATE               35
+
+#define BT_LMP_VERSION_REQ             37
+struct bt_lmp_version_req {
+       uint8_t  version;
+       uint16_t company;
+       uint16_t subversion;
+} __attribute__ ((packed));
+
+#define BT_LMP_VERSION_RES             38
+struct bt_lmp_version_res {
+       uint8_t  version;
+       uint16_t company;
+       uint16_t subversion;
+} __attribute__ ((packed));
+
+#define BT_LMP_FEATURES_REQ            39
+struct bt_lmp_features_req {
+       uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LMP_FEATURES_RES            40
+struct bt_lmp_features_res {
+       uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LMP_MAX_SLOT                        45
+struct bt_lmp_max_slot {
+       uint8_t  slots;
+} __attribute__ ((packed));
+
+#define BT_LMP_MAX_SLOT_REQ            46
+struct bt_lmp_max_slot_req {
+       uint8_t  slots;
+} __attribute__ ((packed));
+
+#define BT_LMP_TIMING_ACCURACY_REQ     47
+
+#define BT_LMP_TIMING_ACCURACY_RES     48
+struct bt_lmp_timing_accuracy_res {
+       uint8_t  drift;
+       uint8_t  jitter;
+} __attribute__ ((packed));
+
+#define BT_LMP_SETUP_COMPLETE          49
+
+#define BT_LMP_USE_SEMI_PERMANENT_KEY  50
+
+#define BT_LMP_HOST_CONNECTION_REQ     51
+
+#define BT_LMP_PAGE_SCAN_MODE_REQ      54
+struct bt_lmp_page_scan_mode_req {
+       uint8_t  scheme;
+       uint8_t  settings;
+} __attribute__ ((packed));
+
+#define BT_LMP_TEST_ACTIVATE           56
+
+#define BT_LMP_ENCRYPTION_KEY_SIZE_MASK_REQ    58
+
+#define BT_LMP_SET_AFH                 60
+struct bt_lmp_set_afh {
+       uint32_t instant;
+       uint8_t  mode;
+       uint8_t  map[10];
+} __attribute__ ((packed));
+
+#define BT_LMP_ENCAPSULATED_HEADER     61
+struct bt_lmp_encapsulated_header {
+       uint8_t  major;
+       uint8_t  minor;
+       uint8_t  length;
+} __attribute__ ((packed));
+
+#define BT_LMP_ENCAPSULATED_PAYLOAD    62
+struct bt_lmp_encapsulated_payload {
+       uint8_t  data[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_SIMPLE_PAIRING_CONFIRM  63
+struct bt_lmp_simple_pairing_confirm {
+       uint8_t  value[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_SIMPLE_PAIRING_NUMBER   64
+struct bt_lmp_simple_pairing_number {
+       uint8_t  value[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_DHKEY_CHECK             65
+struct bt_lmp_dhkey_check {
+       uint8_t  value[16];
+} __attribute__ ((packed));
+
+#define BT_LMP_PAUSE_ENCRYPTION_AES_REQ        66
+
+#define BT_LMP_ACCEPTED_EXT            LMP_ESC4(1)
+struct bt_lmp_accepted_ext {
+       uint8_t  escape;
+       uint8_t  opcode;
+} __attribute__ ((packed));
+
+#define BT_LMP_NOT_ACCEPTED_EXT                LMP_ESC4(2)
+struct bt_lmp_not_accepted_ext {
+       uint8_t  escape;
+       uint8_t  opcode;
+       uint8_t  error;
+} __attribute__ ((packed));
+
+#define BT_LMP_FEATURES_REQ_EXT                LMP_ESC4(3)
+struct bt_lmp_features_req_ext {
+       uint8_t  page;
+       uint8_t  max_page;
+       uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LMP_FEATURES_RES_EXT                LMP_ESC4(4)
+struct bt_lmp_features_res_ext {
+       uint8_t  page;
+       uint8_t  max_page;
+       uint8_t  features[8];
+} __attribute__ ((packed));
+
+#define BT_LMP_PACKET_TYPE_TABLE_REQ   LMP_ESC4(11)
+struct bt_lmp_packet_type_table_req {
+       uint8_t  table;
+} __attribute__ ((packed));
+
+#define BT_LMP_CHANNEL_CLASSIFICATION_REQ      LMP_ESC4(16)
+struct bt_lmp_channel_classification_req {
+       uint8_t  mode;
+       uint16_t min_interval;
+       uint16_t max_interval;
+} __attribute__ ((packed));
+
+#define BT_LMP_CHANNEL_CLASSIFICATION  LMP_ESC4(17)
+struct bt_lmp_channel_classification {
+       uint8_t  classification[10];
+} __attribute__ ((packed));
+
+#define BT_LMP_PAUSE_ENCRYPTION_REQ    LMP_ESC4(23)
+
+#define BT_LMP_RESUME_ENCRYPTION_REQ   LMP_ESC4(24)
+
+#define BT_LMP_IO_CAPABILITY_REQ       LMP_ESC4(25)
+struct bt_lmp_io_capability_req {
+       uint8_t  capability;
+       uint8_t  oob_data;
+       uint8_t  authentication;
+} __attribute__ ((packed));
+
+#define BT_LMP_IO_CAPABILITY_RES       LMP_ESC4(26)
+struct bt_lmp_io_capability_res {
+       uint8_t  capability;
+       uint8_t  oob_data;
+       uint8_t  authentication;
+} __attribute__ ((packed));
+
+#define BT_LMP_NUMERIC_COMPARISON_FAILED       LMP_ESC(27)
+
+#define BT_LMP_PASSKEY_FAILED          LMP_ESC4(28)
+
+#define BT_LMP_OOB_FAILED              LMP_ESC(29)
+
+#define BT_LMP_POWER_CONTROL_REQ       LMP_ESC4(31)
+struct bt_lmp_power_control_req {
+       uint8_t  request;
+} __attribute__ ((packed));
+
+#define BT_LMP_POWER_CONTROL_RES       LMP_ESC4(32)
+struct bt_lmp_power_control_res {
+       uint8_t  response;
+} __attribute__ ((packed));
+
+#define BT_LMP_PING_REQ                        LMP_ESC4(33)
+
+#define BT_LMP_PING_RES                        LMP_ESC4(34)
+
 #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_acl_hdr {
        uint16_t handle;
        uint16_t dlen;
 } __attribute__ ((packed));
 
-struct bt_hci_cmd_hdr {
-       uint16_t opcode;
-       uint8_t  plen;
+struct bt_hci_sco_hdr {
+       uint16_t handle;
+       uint8_t  dlen;
 } __attribute__ ((packed));
 
 struct bt_hci_evt_hdr {
@@ -185,11 +421,19 @@ struct bt_hci_cmd_link_key_request_reply {
        uint8_t  bdaddr[6];
        uint8_t  link_key[16];
 } __attribute__ ((packed));
+struct bt_hci_rsp_link_key_request_reply {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
 
 #define BT_HCI_CMD_LINK_KEY_REQUEST_NEG_REPLY  0x040c
 struct bt_hci_cmd_link_key_request_neg_reply {
        uint8_t  bdaddr[6];
 } __attribute__ ((packed));
+struct bt_hci_rsp_link_key_request_neg_reply {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
 
 #define BT_HCI_CMD_PIN_CODE_REQUEST_REPLY      0x040d
 struct bt_hci_cmd_pin_code_request_reply {
@@ -202,6 +446,10 @@ struct bt_hci_cmd_pin_code_request_reply {
 struct bt_hci_cmd_pin_code_request_neg_reply {
        uint8_t  bdaddr[6];
 } __attribute__ ((packed));
+struct bt_hci_rsp_pin_code_request_neg_reply {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
 
 #define BT_HCI_CMD_CHANGE_CONN_PKT_TYPE                0x040f
 struct bt_hci_cmd_change_conn_pkt_type {
@@ -314,16 +562,28 @@ struct bt_hci_cmd_io_capability_request_reply {
        uint8_t  oob_data;
        uint8_t  authentication;
 } __attribute__ ((packed));
+struct bt_hci_rsp_io_capability_request_reply {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
 
 #define BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY          0x042c
 struct bt_hci_cmd_user_confirm_request_reply {
        uint8_t  bdaddr[6];
 } __attribute__ ((packed));
+struct bt_hci_rsp_user_confirm_request_reply {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
 
 #define BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY      0x042d
 struct bt_hci_cmd_user_confirm_request_neg_reply {
        uint8_t  bdaddr[6];
 } __attribute__ ((packed));
+struct bt_hci_rsp_user_confirm_request_neg_reply {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
 
 #define BT_HCI_CMD_USER_PASSKEY_REQUEST_REPLY          0x042e
 struct bt_hci_cmd_user_passkey_request_reply {
@@ -353,6 +613,10 @@ struct bt_hci_cmd_io_capability_request_neg_reply {
        uint8_t  bdaddr[6];
        uint8_t  reason;
 } __attribute__ ((packed));
+struct bt_hci_rsp_io_capability_request_neg_reply {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
 
 #define BT_HCI_CMD_CREATE_PHY_LINK             0x0435
 struct bt_hci_cmd_create_phy_link {
@@ -918,8 +1182,8 @@ struct bt_hci_cmd_set_afh_host_classification {
 
 #define BT_HCI_CMD_READ_INQUIRY_SCAN_TYPE      0x0c42
 struct bt_hci_rsp_read_inquiry_scan_type {
-       uint8_t status;
-       uint8_t type;
+       uint8_t  status;
+       uint8_t  type;
 } __attribute__ ((packed));
 
 #define BT_HCI_CMD_WRITE_INQUIRY_SCAN_TYPE     0x0c43
@@ -1220,6 +1484,8 @@ struct bt_hci_rsp_read_data_block_size {
        uint16_t num_blocks;
 } __attribute__ ((packed));
 
+#define BT_HCI_CMD_READ_LOCAL_CODECS           0x100b
+
 #define BT_HCI_CMD_READ_FAILED_CONTACT_COUNTER 0x1401
 struct bt_hci_cmd_read_failed_contact_counter {
        uint16_t handle;
@@ -1332,6 +1598,15 @@ struct bt_hci_rsp_write_remote_amp_assoc {
        uint8_t  phy_handle;
 } __attribute__ ((packed));
 
+#define BT_HCI_CMD_SET_TRIGGERED_CLOCK_CAPTURE 0x140d
+struct bt_hci_cmd_set_triggered_clock_capture {
+       uint16_t handle;
+       uint8_t  enable;
+       uint8_t  type;
+       uint8_t  lpo_allowed;
+       uint8_t  num_filter;
+} __attribute__ ((packed));
+
 #define BT_HCI_CMD_ENABLE_DUT_MODE             0x1803
 
 #define BT_HCI_CMD_WRITE_SSP_DEBUG_MODE                0x1804
@@ -1494,14 +1769,14 @@ struct bt_hci_rsp_le_encrypt {
 #define BT_HCI_CMD_LE_RAND                     0x2018
 struct bt_hci_rsp_le_rand {
        uint8_t  status;
-       uint8_t  number[8];
+       uint64_t number;
 } __attribute__ ((packed));
 
 #define BT_HCI_CMD_LE_START_ENCRYPT            0x2019
 struct bt_hci_cmd_le_start_encrypt {
        uint16_t handle;
-       uint8_t  number[8];
-       uint16_t diversifier;
+       uint64_t rand;
+       uint16_t ediv;
        uint8_t  ltk[16];
 } __attribute__ ((packed));
 
@@ -2025,6 +2300,12 @@ struct bt_hci_evt_slave_broadcast_channel_map_change {
        uint8_t  map[10];
 } __attribute__ ((packed));
 
+#define BT_HCI_EVT_INQUIRY_RESPONSE_NOTIFY     0x56
+struct bt_hci_evt_inquiry_response_notify {
+       uint8_t  lap[3];
+       int8_t   rssi;
+} __attribute__ ((packed));
+
 #define BT_HCI_EVT_AUTH_PAYLOAD_TIMEOUT_EXPIRED        0x57
 struct bt_hci_evt_auth_payload_timeout_expired {
        uint16_t handle;
@@ -2072,8 +2353,17 @@ struct bt_hci_evt_le_remote_features_complete {
 #define BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST    0x05
 struct bt_hci_evt_le_long_term_key_request {
        uint16_t handle;
-       uint8_t  number[8];
-       uint16_t diversifier;
+       uint64_t rand;
+       uint16_t ediv;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_LE_REMOTE_CONN_PARAM_REQUEST        0x06
+struct bt_hci_evt_le_remote_conn_param_request {
+       uint16_t handle;
+       uint16_t min_interval;
+       uint16_t max_interval;
+       uint16_t latency;
+       uint16_t supv_timeout;
 } __attribute__ ((packed));
 
 #define BT_HCI_ERR_SUCCESS                     0x00
@@ -2081,6 +2371,8 @@ struct bt_hci_evt_le_long_term_key_request {
 #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_AUTH_FAILURE                        0x05
+#define BT_HCI_ERR_PIN_OR_KEY_MISSING          0x06
 #define BT_HCI_ERR_MEM_CAPACITY_EXCEEDED       0x07
 #define BT_HCI_ERR_COMMAND_DISALLOWED          0x0c
 #define BT_HCI_ERR_UNSUPPORTED_FEATURE         0x11
diff --git a/monitor/btsnoop.c b/monitor/btsnoop.c
deleted file mode 100644 (file)
index b92fb74..0000000
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- *
- *  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 <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-
-#include "btsnoop.h"
-
-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 uint32_t btsnoop_type = 0;
-
-static int btsnoop_fd = -1;
-static uint16_t btsnoop_index = 0xffff;
-
-void btsnoop_create(const char *path, uint32_t type)
-{
-       struct btsnoop_hdr hdr;
-       ssize_t written;
-
-       if (btsnoop_fd >= 0)
-               return;
-
-       btsnoop_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
-                               S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-       if (btsnoop_fd < 0)
-               return;
-
-       btsnoop_type = type;
-
-       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) {
-               close(btsnoop_fd);
-               btsnoop_fd = -1;
-               return;
-       }
-}
-
-void btsnoop_write(struct timeval *tv, uint32_t flags,
-                                       const void *data, uint16_t size)
-{
-       struct btsnoop_pkt pkt;
-       uint64_t ts;
-       ssize_t written;
-
-       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;
-       }
-}
-
-static uint32_t get_flags_from_opcode(uint16_t opcode)
-{
-       switch (opcode) {
-       case BTSNOOP_OPCODE_NEW_INDEX:
-       case BTSNOOP_OPCODE_DEL_INDEX:
-               break;
-       case BTSNOOP_OPCODE_COMMAND_PKT:
-               return 0x02;
-       case BTSNOOP_OPCODE_EVENT_PKT:
-               return 0x03;
-       case BTSNOOP_OPCODE_ACL_TX_PKT:
-               return 0x00;
-       case BTSNOOP_OPCODE_ACL_RX_PKT:
-               return 0x01;
-       case BTSNOOP_OPCODE_SCO_TX_PKT:
-       case BTSNOOP_OPCODE_SCO_RX_PKT:
-               break;
-       }
-
-       return 0xff;
-}
-
-void btsnoop_write_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
-                                       const void *data, uint16_t size)
-{
-       uint32_t flags;
-
-       if (!tv)
-               return;
-
-       if (btsnoop_fd < 0)
-               return;
-
-       switch (btsnoop_type) {
-       case BTSNOOP_TYPE_HCI:
-               if (btsnoop_index == 0xffff)
-                       btsnoop_index = index;
-
-               if (index != btsnoop_index)
-                       return;
-
-               flags = get_flags_from_opcode(opcode);
-               if (flags == 0xff)
-                       return;
-               break;
-
-       case BTSNOOP_TYPE_EXTENDED_HCI:
-               flags = (index << 16) | opcode;
-               break;
-
-       default:
-               return;
-       }
-
-       btsnoop_write(tv, flags, data, size);
-}
-
-void btsnoop_write_phy(struct timeval *tv, uint16_t frequency,
-                                       const void *data, uint16_t size)
-{
-       uint32_t flags;
-
-       if (!tv)
-               return;
-
-       if (btsnoop_fd < 0)
-               return;
-
-       switch (btsnoop_type) {
-       case BTSNOOP_TYPE_EXTENDED_PHY:
-               flags = (1 << 16) | frequency;
-               break;
-
-       default:
-               return;
-       }
-
-       btsnoop_write(tv, flags, data, size);
-}
-
-int btsnoop_open(const char *path, uint32_t *type)
-{
-       struct btsnoop_hdr hdr;
-       ssize_t len;
-
-       if (btsnoop_fd >= 0) {
-               fprintf(stderr, "Too many open files\n");
-               return -1;
-       }
-
-       btsnoop_fd = open(path, O_RDONLY | O_CLOEXEC);
-       if (btsnoop_fd < 0) {
-               perror("Failed to open file");
-               return -1;
-       }
-
-       len = read(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE);
-       if (len < 0 || len != BTSNOOP_HDR_SIZE) {
-               perror("Failed to read header");
-               close(btsnoop_fd);
-               btsnoop_fd = -1;
-               return -1;
-       }
-
-       if (memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) {
-               fprintf(stderr, "Invalid btsnoop header\n");
-               close(btsnoop_fd);
-               btsnoop_fd = -1;
-               return -1;
-       }
-
-       if (ntohl(hdr.version) != btsnoop_version) {
-               fprintf(stderr, "Invalid btsnoop version\n");
-               close(btsnoop_fd);
-               btsnoop_fd = -1;
-               return -1;
-       }
-
-       btsnoop_type = ntohl(hdr.type);
-
-       if (type)
-               *type = btsnoop_type;
-
-       return 0;
-}
-
-static uint16_t get_opcode_from_flags(uint8_t type, uint32_t flags)
-{
-       switch (type) {
-       case HCI_COMMAND_PKT:
-               return BTSNOOP_OPCODE_COMMAND_PKT;
-       case HCI_ACLDATA_PKT:
-               if (flags & 0x01)
-                       return BTSNOOP_OPCODE_ACL_RX_PKT;
-               else
-                       return BTSNOOP_OPCODE_ACL_TX_PKT;
-       case HCI_SCODATA_PKT:
-               if (flags & 0x01)
-                       return BTSNOOP_OPCODE_SCO_RX_PKT;
-               else
-                       return BTSNOOP_OPCODE_SCO_TX_PKT;
-       case HCI_EVENT_PKT:
-               return BTSNOOP_OPCODE_EVENT_PKT;
-       case 0xff:
-               if (flags & 0x02) {
-                       if (flags & 0x01)
-                               return BTSNOOP_OPCODE_EVENT_PKT;
-                       else
-                               return BTSNOOP_OPCODE_COMMAND_PKT;
-               } else {
-                       if (flags & 0x01)
-                               return BTSNOOP_OPCODE_ACL_RX_PKT;
-                       else
-                               return BTSNOOP_OPCODE_ACL_TX_PKT;
-               }
-               break;
-       }
-
-       return 0xff;
-}
-
-int btsnoop_read_hci(struct timeval *tv, uint16_t *index, uint16_t *opcode,
-                                               void *data, uint16_t *size)
-{
-       struct btsnoop_pkt pkt;
-       uint32_t toread, flags;
-       uint64_t ts;
-       uint8_t pkt_type;
-       ssize_t len;
-
-       if (btsnoop_fd < 0)
-               return -1;
-
-       len = read(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
-       if (len == 0)
-               return -1;
-
-       if (len < 0 || len != BTSNOOP_PKT_SIZE) {
-               perror("Failed to read packet");
-               close(btsnoop_fd);
-               btsnoop_fd = -1;
-               return -1;
-       }
-
-       toread = ntohl(pkt.size);
-       flags = ntohl(pkt.flags);
-
-       ts = ntoh64(pkt.ts) - 0x00E03AB44A676000ll;
-       tv->tv_sec = (ts / 1000000ll) + 946684800ll;
-       tv->tv_usec = ts % 1000000ll;
-
-       switch (btsnoop_type) {
-       case BTSNOOP_TYPE_HCI:
-               *index = 0;
-               *opcode = get_opcode_from_flags(0xff, flags);
-               break;
-
-       case BTSNOOP_TYPE_UART:
-               len = read(btsnoop_fd, &pkt_type, 1);
-               if (len < 0) {
-                       perror("Failed to read packet type");
-                       close(btsnoop_fd);
-                       btsnoop_fd = -1;
-                       return -1;
-               }
-               toread--;
-
-               *index = 0;
-               *opcode = get_opcode_from_flags(pkt_type, flags);
-               break;
-
-       case BTSNOOP_TYPE_EXTENDED_HCI:
-               *index = flags >> 16;
-               *opcode = flags & 0xffff;
-               break;
-
-       default:
-               fprintf(stderr, "Unknown packet type\n");
-               close(btsnoop_fd);
-               btsnoop_fd = -1;
-               return -1;
-       }
-
-       len = read(btsnoop_fd, data, toread);
-       if (len < 0) {
-               perror("Failed to read data");
-               close(btsnoop_fd);
-               btsnoop_fd = -1;
-               return -1;
-       }
-
-       *size = toread;
-
-       return 0;
-}
-
-int btsnoop_read_phy(struct timeval *tv, uint16_t *frequency,
-                                               void *data, uint16_t *size)
-{
-       struct btsnoop_pkt pkt;
-       uint32_t toread, flags;
-       uint64_t ts;
-       ssize_t len;
-
-       if (btsnoop_fd < 0)
-               return -1;
-
-       len = read(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
-       if (len == 0)
-               return -1;
-
-       if (len < 0 || len != BTSNOOP_PKT_SIZE) {
-               perror("Failed to read packet");
-               close(btsnoop_fd);
-               btsnoop_fd = -1;
-               return -1;
-       }
-
-       toread = ntohl(pkt.size);
-       flags = ntohl(pkt.flags);
-
-       ts = ntoh64(pkt.ts) - 0x00E03AB44A676000ll;
-       tv->tv_sec = (ts / 1000000ll) + 946684800ll;
-       tv->tv_usec = ts % 1000000ll;
-
-       switch (btsnoop_type) {
-       case BTSNOOP_TYPE_EXTENDED_PHY:
-               if ((flags >> 16) != 1)
-                       break;
-               *frequency = flags & 0xffff;
-               break;
-
-       default:
-               fprintf(stderr, "Unknown packet type\n");
-               close(btsnoop_fd);
-               btsnoop_fd = -1;
-               return -1;
-       }
-
-       len = read(btsnoop_fd, data, toread);
-       if (len < 0) {
-               perror("Failed to read data");
-               close(btsnoop_fd);
-               btsnoop_fd = -1;
-               return -1;
-       }
-
-       *size = toread;
-
-       return 0;
-}
-
-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
deleted file mode 100644 (file)
index cdefb8c..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- *
- *  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>
-#include <sys/time.h>
-
-#define BTSNOOP_TYPE_HCI               1001
-#define BTSNOOP_TYPE_UART              1002
-#define BTSNOOP_TYPE_BCSP              1003
-#define BTSNOOP_TYPE_3WIRE             1004
-
-#define BTSNOOP_TYPE_EXTENDED_HCI      2001
-#define BTSNOOP_TYPE_EXTENDED_PHY      2002
-
-#define BTSNOOP_OPCODE_NEW_INDEX       0
-#define BTSNOOP_OPCODE_DEL_INDEX       1
-#define BTSNOOP_OPCODE_COMMAND_PKT     2
-#define BTSNOOP_OPCODE_EVENT_PKT       3
-#define BTSNOOP_OPCODE_ACL_TX_PKT      4
-#define BTSNOOP_OPCODE_ACL_RX_PKT      5
-#define BTSNOOP_OPCODE_SCO_TX_PKT      6
-#define BTSNOOP_OPCODE_SCO_RX_PKT      7
-
-void btsnoop_create(const char *path, uint32_t type);
-void btsnoop_write(struct timeval *tv, uint32_t flags,
-                                       const void *data, uint16_t size);
-void btsnoop_write_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
-                                       const void *data, uint16_t size);
-void btsnoop_write_phy(struct timeval *tv, uint16_t frequency,
-                                       const void *data, uint16_t size);
-int btsnoop_open(const char *path, uint32_t *type);
-int btsnoop_read_hci(struct timeval *tv, uint16_t *index, uint16_t *opcode,
-                                               void *data, uint16_t *size);
-int btsnoop_read_phy(struct timeval *tv, uint16_t *frequency,
-                                               void *data, uint16_t *size);
-void btsnoop_close(void);
index 4edefde..8197813 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 "lib/hci.h"
 #include "lib/mgmt.h"
 
+#include "src/shared/util.h"
+#include "src/shared/btsnoop.h"
 #include "mainloop.h"
 #include "display.h"
 #include "packet.h"
-#include "btsnoop.h"
 #include "hcidump.h"
+#include "ellisys.h"
 #include "control.h"
 
+static struct btsnoop *btsnoop_file = NULL;
 static bool hcidump_fallback = false;
 
 #define MAX_PACKET_SIZE                (1486 + 4)
@@ -105,7 +108,7 @@ static void mgmt_controller_error(uint16_t len, const void *buf)
 static const char *settings_str[] = {
        "powered", "connectable", "fast-connectable", "discoverable",
        "pairable", "link-security", "ssp", "br/edr", "hs", "le",
-       "advertising",
+       "advertising", "secure-conn", "debug-keys", "privacy",
 };
 
 static void mgmt_new_settings(uint16_t len, const void *buf)
@@ -118,7 +121,7 @@ static void mgmt_new_settings(uint16_t len, const void *buf)
                return;
        }
 
-       settings = bt_get_le32(buf);
+       settings = get_le32(buf);
 
        printf("@ New Settings: 0x%4.4x\n", settings);
 
@@ -204,7 +207,8 @@ static void mgmt_new_long_term_key(uint16_t len, const void *buf)
 
        ba2str(&ev->key.addr.bdaddr, str);
 
-       printf("@ New Long Term Key: %s (%d)\n", str, ev->key.addr.type);
+       printf("@ New Long Term Key: %s (%d) %s\n", str, ev->key.addr.type,
+                                       ev->key.master ? "Master" : "Slave");
 
        buf += sizeof(*ev);
        len -= sizeof(*ev);
@@ -223,7 +227,7 @@ static void mgmt_device_connected(uint16_t len, const void *buf)
                return;
        }
 
-       flags = btohl(ev->flags);
+       flags = le32_to_cpu(ev->flags);
        ba2str(&ev->addr.bdaddr, str);
 
        printf("@ Device Connected: %s (%d) flags 0x%4.4x\n",
@@ -381,7 +385,7 @@ static void mgmt_device_found(uint16_t len, const void *buf)
                return;
        }
 
-       flags = btohl(ev->flags);
+       flags = le32_to_cpu(ev->flags);
        ba2str(&ev->addr.bdaddr, str);
 
        printf("@ Device Found: %s (%d) rssi %d flags 0x%4.4x\n",
@@ -483,7 +487,7 @@ static void mgmt_passkey_notify(uint16_t len, const void *buf)
 
        ba2str(&ev->addr.bdaddr, str);
 
-       passkey = btohl(ev->passkey);
+       passkey = le32_to_cpu(ev->passkey);
 
        printf("@ Passkey Notify: %s (%d) passkey %06u entered %u\n",
                                str, ev->addr.type, passkey, ev->entered);
@@ -494,6 +498,48 @@ static void mgmt_passkey_notify(uint16_t len, const void *buf)
        packet_hexdump(buf, len);
 }
 
+static void mgmt_new_irk(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_new_irk *ev = buf;
+       char addr[18], rpa[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed New IRK control\n");
+               return;
+       }
+
+       ba2str(&ev->rpa, rpa);
+       ba2str(&ev->key.addr.bdaddr, addr);
+
+       printf("@ New IRK: %s (%d) %s\n", addr, ev->key.addr.type, rpa);
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
+static void mgmt_new_csrk(uint16_t len, const void *buf)
+{
+       const struct mgmt_ev_new_csrk *ev = buf;
+       char addr[18];
+
+       if (len < sizeof(*ev)) {
+               printf("* Malformed New CSRK control\n");
+               return;
+       }
+
+       ba2str(&ev->key.addr.bdaddr, addr);
+
+       printf("@ New CSRK: %s (%d) %s\n", addr, ev->key.addr.type,
+                                       ev->key.master ? "Master" : "Slave");
+
+       buf += sizeof(*ev);
+       len -= sizeof(*ev);
+
+       packet_hexdump(buf, len);
+}
+
 void control_message(uint16_t opcode, const void *data, uint16_t size)
 {
        switch (opcode) {
@@ -560,6 +606,12 @@ void control_message(uint16_t opcode, const void *data, uint16_t size)
        case MGMT_EV_PASSKEY_NOTIFY:
                mgmt_passkey_notify(size, data);
                break;
+       case MGMT_EV_NEW_IRK:
+               mgmt_new_irk(size, data);
+               break;
+       case MGMT_EV_NEW_CSRK:
+               mgmt_new_csrk(size, data);
+               break;
        default:
                printf("* Unknown control (code %d len %d)\n", opcode, size);
                packet_hexdump(data, size);
@@ -616,9 +668,9 @@ static void data_callback(int fd, uint32_t events, void *user_data)
                        }
                }
 
-               opcode = btohs(hdr.opcode);
-               index  = btohs(hdr.index);
-               pktlen = btohs(hdr.len);
+               opcode = le16_to_cpu(hdr.opcode);
+               index  = le16_to_cpu(hdr.index);
+               pktlen = le16_to_cpu(hdr.len);
 
                switch (data->channel) {
                case HCI_CHANNEL_CONTROL:
@@ -626,7 +678,10 @@ static void data_callback(int fd, uint32_t events, void *user_data)
                        break;
                case HCI_CHANNEL_MONITOR:
                        packet_monitor(tv, index, opcode, data->buf, pktlen);
-                       btsnoop_write_hci(tv, index, opcode, data->buf, pktlen);
+                       btsnoop_write_hci(btsnoop_file, tv, index, opcode,
+                                                       data->buf, pktlen);
+                       ellisys_inject_hci(tv, index, opcode,
+                                                       data->buf, pktlen);
                        break;
                }
        }
@@ -710,11 +765,11 @@ static void client_callback(int fd, uint32_t events, void *user_data)
 
        if (data->offset > MGMT_HDR_SIZE) {
                struct mgmt_hdr *hdr = (struct mgmt_hdr *) data->buf;
-               uint16_t pktlen = btohs(hdr->len);
+               uint16_t pktlen = le16_to_cpu(hdr->len);
 
                if (data->offset > pktlen + MGMT_HDR_SIZE) {
-                       uint16_t opcode = btohs(hdr->opcode);
-                       uint16_t index = btohs(hdr->index);
+                       uint16_t opcode = le16_to_cpu(hdr->opcode);
+                       uint16_t index = le16_to_cpu(hdr->index);
 
                        packet_monitor(NULL, index, opcode,
                                        data->buf + MGMT_HDR_SIZE, pktlen);
@@ -809,7 +864,7 @@ void control_server(const char *path)
 
 void control_writer(const char *path)
 {
-       btsnoop_create(path, BTSNOOP_TYPE_EXTENDED_HCI);
+       btsnoop_file = btsnoop_create(path, BTSNOOP_TYPE_MONITOR);
 }
 
 void control_reader(const char *path)
@@ -819,17 +874,20 @@ void control_reader(const char *path)
        uint32_t type;
        struct timeval tv;
 
-       if (btsnoop_open(path, &type) < 0)
+       btsnoop_file = btsnoop_open(path, BTSNOOP_FLAG_PKLG_SUPPORT);
+       if (!btsnoop_file)
                return;
 
+       type = btsnoop_get_type(btsnoop_file);
+
        switch (type) {
        case BTSNOOP_TYPE_HCI:
        case BTSNOOP_TYPE_UART:
-       case BTSNOOP_TYPE_EXTENDED_PHY:
+       case BTSNOOP_TYPE_SIMULATOR:
                packet_del_filter(PACKET_FILTER_SHOW_INDEX);
                break;
 
-       case BTSNOOP_TYPE_EXTENDED_HCI:
+       case BTSNOOP_TYPE_MONITOR:
                packet_add_filter(PACKET_FILTER_SHOW_INDEX);
                break;
        }
@@ -839,24 +897,28 @@ void control_reader(const char *path)
        switch (type) {
        case BTSNOOP_TYPE_HCI:
        case BTSNOOP_TYPE_UART:
-       case BTSNOOP_TYPE_EXTENDED_HCI:
+       case BTSNOOP_TYPE_MONITOR:
                while (1) {
                        uint16_t index, opcode;
 
-                       if (btsnoop_read_hci(&tv, &index, &opcode,
-                                                       buf, &pktlen) < 0)
+                       if (!btsnoop_read_hci(btsnoop_file, &tv, &index,
+                                                       &opcode, buf, &pktlen))
                                break;
 
+                       if (opcode == 0xffff)
+                               continue;
+
                        packet_monitor(&tv, index, opcode, buf, pktlen);
+                       ellisys_inject_hci(&tv, index, opcode, buf, pktlen);
                }
                break;
 
-       case BTSNOOP_TYPE_EXTENDED_PHY:
+       case BTSNOOP_TYPE_SIMULATOR:
                while (1) {
                        uint16_t frequency;
 
-                       if (btsnoop_read_phy(&tv, &frequency,
-                                                       buf, &pktlen) < 0)
+                       if (!btsnoop_read_phy(btsnoop_file, &tv, &frequency,
+                                                               buf, &pktlen))
                                break;
 
                        packet_simulator(&tv, frequency, buf, pktlen);
@@ -866,7 +928,7 @@ void control_reader(const char *path)
 
        close_pager();
 
-       btsnoop_close();
+       btsnoop_unref(btsnoop_file);
 }
 
 int control_tracing(void)
index 8781bd0..8de4c6c 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index 9ea11fb..912b37e 100644 (file)
@@ -2,8 +2,8 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
  *
  *
  *  This library is free software; you can redistribute it and/or
index 84f5d7f..772388b 100644 (file)
@@ -2,8 +2,8 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-2010  Marcel Holtmann <marcel@holtmann.org>
  *
  *
  *  This library is free software; you can redistribute it and/or
index af4171f..411af94 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index 885eb34..e627401 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
diff --git a/monitor/ellisys.c b/monitor/ellisys.c
new file mode 100644 (file)
index 0000000..bafbb5d
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include "src/shared/btsnoop.h"
+#include "ellisys.h"
+
+static int ellisys_fd = -1;
+static uint16_t ellisys_index = 0xffff;
+
+void ellisys_enable(const char *server, uint16_t port)
+{
+       struct sockaddr_in addr;
+       int fd;
+
+       if (ellisys_fd >= 0) {
+               fprintf(stderr, "Ellisys injection already enabled\n");
+               return;
+       }
+
+       fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+       if (fd < 0) {
+               perror("Failed to open UDP injection socket");
+               return;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(server);
+       addr.sin_port = htons(port);
+
+       if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Failed to connect UDP injection socket");
+               close(fd);
+               return;
+       }
+
+       ellisys_fd = fd;
+}
+
+void ellisys_inject_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
+                                       const void *data, uint16_t size)
+{
+       uint8_t msg[] = {
+               /* HCI Injection Service, Version 1 */
+               0x02, 0x00, 0x01,
+               /* DateTimeNs Object */
+               0x02, 0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               /* Bitrate Object, 12000000 bps */
+               0x80, 0x00, 0x1b, 0x37, 0x4b,
+               /* HCI Packet Type Object */
+               0x81, 0x00,
+               /* HCI Packet Data Object */
+               0x82
+       };
+       long nsec;
+       time_t t;
+       struct tm tm;
+       struct iovec iov[2];
+       int iovcnt;
+
+       if (!tv)
+               return;
+
+       if (ellisys_fd < 0)
+               return;
+
+       if (ellisys_index == 0xffff)
+               ellisys_index = index;
+
+       if (index != ellisys_index)
+               return;
+
+       t = tv->tv_sec;
+       localtime_r(&t, &tm);
+
+       nsec = ((tm.tm_sec + (tm.tm_min * 60) +
+                       (tm.tm_hour * 3600)) * 1000000l + tv->tv_usec) * 1000l;
+
+       msg[4]  = (1900 + tm.tm_year) & 0xff;
+       msg[5]  = (1900 + tm.tm_year) >> 8;
+       msg[6]  = (tm.tm_mon + 1) & 0xff;
+       msg[7]  = tm.tm_mday & 0xff;
+       msg[8]  = (nsec & 0x0000000000ffl);
+       msg[9]  = (nsec & 0x00000000ff00l) >> 8;
+       msg[10] = (nsec & 0x000000ff0000l) >> 16;
+       msg[11] = (nsec & 0x0000ff000000l) >> 24;
+       msg[12] = (nsec & 0x00ff00000000l) >> 32;
+       msg[13] = (nsec & 0xff0000000000l) >> 40;
+
+       switch (opcode) {
+       case BTSNOOP_OPCODE_COMMAND_PKT:
+               msg[20] = 0x01;
+               break;
+       case BTSNOOP_OPCODE_EVENT_PKT:
+               msg[20] = 0x84;
+               break;
+       case BTSNOOP_OPCODE_ACL_TX_PKT:
+               msg[20] = 0x02;
+               break;
+       case BTSNOOP_OPCODE_ACL_RX_PKT:
+               msg[20] = 0x82;
+               break;
+       case BTSNOOP_OPCODE_SCO_TX_PKT:
+               msg[20] = 0x03;
+               break;
+       case BTSNOOP_OPCODE_SCO_RX_PKT:
+               msg[20] = 0x83;
+               break;
+       default:
+               return;
+       }
+
+       iov[0].iov_base = msg;
+       iov[0].iov_len  = sizeof(msg);
+
+       if (size > 0) {
+               iov[1].iov_base = (void *) data;
+               iov[1].iov_len  = size;
+               iovcnt = 2;
+       } else
+               iovcnt = 1;
+
+       if (writev(ellisys_fd, iov, iovcnt) < 0)
+               perror("Failed to send Ellisys injection packet");
+}
diff --git a/monitor/ellisys.h b/monitor/ellisys.h
new file mode 100644 (file)
index 0000000..8be888d
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-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 <stdint.h>
+
+void ellisys_enable(const char *server, uint16_t port);
+
+void ellisys_inject_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
+                                       const void *data, uint16_t size);
index 9881bb3..1515f93 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index 3801c98..c908650 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
diff --git a/monitor/hwdb.c b/monitor/hwdb.c
new file mode 100644 (file)
index 0000000..6931660
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-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 <stdio.h>
+#include <string.h>
+
+#include "hwdb.h"
+
+#ifdef HAVE_UDEV_HWDB_NEW
+#include <libudev.h>
+
+bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model)
+{
+       struct udev *udev;
+       struct udev_hwdb *hwdb;
+       struct udev_list_entry *head, *entry;
+       bool result;
+
+       udev = udev_new();
+       if (!udev)
+               return false;
+
+       hwdb = udev_hwdb_new(udev);
+       if (!hwdb) {
+               result = false;
+               goto done;
+       }
+
+       *vendor = NULL;
+       *model = NULL;
+
+       head = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0);
+
+       udev_list_entry_foreach(entry, head) {
+               const char *name = udev_list_entry_get_name(entry);
+
+               if (!name)
+                       continue;
+
+               if (!*vendor && !strcmp(name, "ID_VENDOR_FROM_DATABASE"))
+                       *vendor = strdup(udev_list_entry_get_value(entry));
+               else if (!*model && !strcmp(name, "ID_MODEL_FROM_DATABASE"))
+                       *model = strdup(udev_list_entry_get_value(entry));
+       }
+
+       hwdb = udev_hwdb_unref(hwdb);
+
+       result = true;
+
+done:
+       udev = udev_unref(udev);
+
+       return result;
+}
+
+bool hwdb_get_company(const uint8_t *bdaddr, char **company)
+{
+       struct udev *udev;
+       struct udev_hwdb *hwdb;
+       struct udev_list_entry *head, *entry;
+       char modalias[11];
+       bool result;
+
+       if (!bdaddr[2] && !bdaddr[1] && !bdaddr[0])
+               return false;
+
+       sprintf(modalias, "OUI:%2.2X%2.2X%2.2X",
+                               bdaddr[5], bdaddr[4], bdaddr[3]);
+
+       udev = udev_new();
+       if (!udev)
+               return false;
+
+       hwdb = udev_hwdb_new(udev);
+       if (!hwdb) {
+               result = false;
+               goto done;
+       }
+
+       *company = NULL;
+
+       head = udev_hwdb_get_properties_list_entry(hwdb, modalias, 0);
+
+       udev_list_entry_foreach(entry, head) {
+               const char *name = udev_list_entry_get_name(entry);
+
+               if (name && !strcmp(name, "ID_OUI_FROM_DATABASE")) {
+                       *company = strdup(udev_list_entry_get_value(entry));
+                       break;
+               }
+       }
+
+       hwdb = udev_hwdb_unref(hwdb);
+
+       result = true;
+
+done:
+       udev = udev_unref(udev);
+
+       return result;
+}
+#else
+bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model)
+{
+       return false;
+}
+
+bool hwdb_get_company(const uint8_t *bdaddr, char **company)
+{
+       return false;
+}
+#endif
diff --git a/monitor/hwdb.h b/monitor/hwdb.h
new file mode 100644 (file)
index 0000000..79f505a
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-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 <stdint.h>
+#include <stdbool.h>
+
+bool hwdb_get_vendor_model(const char *modalias, char **vendor, char **model);
+bool hwdb_get_company(const uint8_t *bdaddr, char **company);
diff --git a/monitor/keys.c b/monitor/keys.c
new file mode 100644 (file)
index 0000000..4ccef22
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-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 "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/crypto.h"
+
+#include "keys.h"
+
+static const uint8_t empty_key[16] = { 0x00, };
+static const uint8_t empty_addr[6] = { 0x00, };
+
+static struct bt_crypto *crypto;
+
+struct irk_data {
+       uint8_t key[16];
+       uint8_t addr[6];
+       uint8_t addr_type;
+};
+
+static struct queue *irk_list;
+
+void keys_setup(void)
+{
+       crypto = bt_crypto_new();
+
+       irk_list = queue_new();
+}
+
+void keys_cleanup(void)
+{
+       bt_crypto_unref(crypto);
+
+       queue_destroy(irk_list, free);
+}
+
+void keys_update_identity_key(const uint8_t key[16])
+{
+       struct irk_data *irk;
+
+       irk = queue_peek_tail(irk_list);
+       if (irk && !memcmp(irk->key, empty_key, 16)) {
+               memcpy(irk->key, key, 16);
+               return;
+       }
+
+       irk = new0(struct irk_data, 1);
+       if (irk) {
+               memcpy(irk->key, key, 16);
+               if (!queue_push_tail(irk_list, irk))
+                       free(irk);
+       }
+}
+
+void keys_update_identity_addr(const uint8_t addr[6], uint8_t addr_type)
+{
+       struct irk_data *irk;
+
+       irk = queue_peek_tail(irk_list);
+       if (irk && !memcmp(irk->addr, empty_addr, 6)) {
+               memcpy(irk->addr, addr, 6);
+               irk->addr_type = addr_type;
+               return;
+       }
+
+       irk = new0(struct irk_data, 1);
+       if (irk) {
+               memcpy(irk->addr, addr, 6);
+               irk->addr_type = addr_type;
+               if (!queue_push_tail(irk_list, irk))
+                       free(irk);
+       }
+}
+
+struct resolve_data {
+       bool found;
+       uint8_t addr[6];
+       uint8_t ident[6];
+       uint8_t ident_type;
+};
+
+static void try_resolve_irk(void *data, void *user_data)
+{
+       struct irk_data *irk = data;
+       struct resolve_data *result = user_data;
+       uint8_t local_hash[3];
+
+       if (result->found)
+               return;
+
+       bt_crypto_ah(crypto, irk->key, result->addr + 3, local_hash);
+
+       if (!memcmp(result->addr, local_hash, 3)) {
+               result->found = true;
+               memcpy(result->ident, irk->addr, 6);
+               result->ident_type = irk->addr_type;
+       }
+}
+
+bool keys_resolve_identity(const uint8_t addr[6], uint8_t ident[6],
+                                                       uint8_t *ident_type)
+{
+       struct resolve_data result;
+
+       result.found = false;
+       memcpy(result.addr, addr, 6);
+
+       queue_foreach(irk_list, try_resolve_irk, &result);
+
+       if (result.found) {
+               memcpy(ident, result.ident, 6);
+               *ident_type = result.ident_type;
+               return true;
+       }
+
+       return false;
+}
diff --git a/monitor/keys.h b/monitor/keys.h
new file mode 100644 (file)
index 0000000..61ec50a
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-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 <stdint.h>
+#include <stdbool.h>
+
+void keys_setup(void);
+void keys_cleanup(void);
+
+void keys_update_identity_key(const uint8_t key[16]);
+void keys_update_identity_addr(const uint8_t addr[6], uint8_t addr_type);
+
+bool keys_resolve_identity(const uint8_t addr[6], uint8_t ident[6],
+                                                       uint8_t *ident_type);
index c215882..993aa8b 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <inttypes.h>
 
 #include <bluetooth/bluetooth.h>
 
+#include "src/shared/util.h"
 #include "bt.h"
 #include "packet.h"
 #include "display.h"
 #include "l2cap.h"
 #include "uuid.h"
+#include "keys.h"
 #include "sdp.h"
 
 #define MAX_CHAN 64
@@ -271,19 +274,19 @@ static void clear_fragment_buffer(uint16_t index)
 
 static void print_psm(uint16_t psm)
 {
-       print_field("PSM: %d (0x%4.4x)", btohs(psm), btohs(psm));
+       print_field("PSM: %d (0x%4.4x)", le16_to_cpu(psm), le16_to_cpu(psm));
 }
 
 static void print_cid(const char *type, uint16_t cid)
 {
-       print_field("%s CID: %d", type, btohs(cid));
+       print_field("%s CID: %d", type, le16_to_cpu(cid));
 }
 
 static void print_reject_reason(uint16_t reason)
 {
        const char *str;
 
-       switch (btohs(reason)) {
+       switch (le16_to_cpu(reason)) {
        case 0x0000:
                str = "Command not understood";
                break;
@@ -298,14 +301,14 @@ static void print_reject_reason(uint16_t reason)
                break;
        }
 
-       print_field("Reason: %s (0x%4.4x)", str, btohs(reason));
+       print_field("Reason: %s (0x%4.4x)", str, le16_to_cpu(reason));
 }
 
 static void print_conn_result(uint16_t result)
 {
        const char *str;
 
-       switch (btohs(result)) {
+       switch (le16_to_cpu(result)) {
        case 0x0000:
                str = "Connection successful";
                break;
@@ -332,14 +335,14 @@ static void print_conn_result(uint16_t result)
                break;
        }
 
-       print_field("Result: %s (0x%4.4x)", str, btohs(result));
+       print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
 }
 
 static void print_conn_status(uint16_t status)
 {
         const char *str;
 
-       switch (btohs(status)) {
+       switch (le16_to_cpu(status)) {
        case 0x0000:
                str = "No further information available";
                break;
@@ -354,26 +357,26 @@ static void print_conn_status(uint16_t status)
                break;
        }
 
-       print_field("Status: %s (0x%4.4x)", str, btohs(status));
+       print_field("Status: %s (0x%4.4x)", str, le16_to_cpu(status));
 }
 
 static void print_config_flags(uint16_t flags)
 {
        const char *str;
 
-       if (btohs(flags) & 0x0001)
+       if (le16_to_cpu(flags) & 0x0001)
                str = " (continuation)";
        else
                str = "";
 
-       print_field("Flags: 0x%4.4x%s", btohs(flags), str);
+       print_field("Flags: 0x%4.4x%s", le16_to_cpu(flags), str);
 }
 
 static void print_config_result(uint16_t result)
 {
        const char *str;
 
-       switch (btohs(result)) {
+       switch (le16_to_cpu(result)) {
        case 0x0000:
                str = "Success";
                break;
@@ -397,7 +400,7 @@ static void print_config_result(uint16_t result)
                break;
        }
 
-       print_field("Result: %s (0x%4.4x)", str, btohs(result));
+       print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
 }
 
 static struct {
@@ -454,11 +457,11 @@ static void print_config_options(const struct l2cap_frame *frame,
                switch (type) {
                case 0x01:
                        print_field("  MTU: %d",
-                                       bt_get_le16(data + consumed + 2));
+                                       get_le16(data + consumed + 2));
                        break;
                case 0x02:
                        print_field("  Flush timeout: %d",
-                                       bt_get_le16(data + consumed + 2));
+                                       get_le16(data + consumed + 2));
                        break;
                case 0x03:
                        switch (data[consumed + 3]) {
@@ -479,15 +482,15 @@ static void print_config_options(const struct l2cap_frame *frame,
                        print_field("  Service type: %s (0x%2.2x)",
                                                str, data[consumed + 3]);
                        print_field("  Token rate: 0x%8.8x",
-                                       bt_get_le32(data + consumed + 4));
+                                       get_le32(data + consumed + 4));
                        print_field("  Token bucket size: 0x%8.8x",
-                                       bt_get_le32(data + consumed + 8));
+                                       get_le32(data + consumed + 8));
                        print_field("  Peak bandwidth: 0x%8.8x",
-                                       bt_get_le32(data + consumed + 12));
+                                       get_le32(data + consumed + 12));
                        print_field("  Latency: 0x%8.8x",
-                                       bt_get_le32(data + consumed + 16));
+                                       get_le32(data + consumed + 16));
                        print_field("  Delay variation: 0x%8.8x",
-                                       bt_get_le32(data + consumed + 20));
+                                       get_le32(data + consumed + 20));
                         break;
                case 0x04:
                        if (response)
@@ -518,11 +521,11 @@ static void print_config_options(const struct l2cap_frame *frame,
                        print_field("  TX window size: %d", data[consumed + 3]);
                        print_field("  Max transmit: %d", data[consumed + 4]);
                        print_field("  Retransmission timeout: %d",
-                                       bt_get_le16(data + consumed + 5));
+                                       get_le16(data + consumed + 5));
                        print_field("  Monitor timeout: %d",
-                                       bt_get_le16(data + consumed + 7));
+                                       get_le16(data + consumed + 7));
                        print_field("  Maximum PDU size: %d",
-                                       bt_get_le16(data + consumed + 9));
+                                       get_le16(data + consumed + 9));
                        break;
                case 0x05:
                        switch (data[consumed + 2]) {
@@ -559,17 +562,17 @@ static void print_config_options(const struct l2cap_frame *frame,
                        print_field("  Service type: %s (0x%2.2x)",
                                                str, data[consumed + 3]);
                        print_field("  Maximum SDU size: 0x%4.4x",
-                                       bt_get_le16(data + consumed + 4));
+                                       get_le16(data + consumed + 4));
                        print_field("  SDU inter-arrival time: 0x%8.8x",
-                                       bt_get_le32(data + consumed + 6));
+                                       get_le32(data + consumed + 6));
                        print_field("  Access latency: 0x%8.8x",
-                                       bt_get_le32(data + consumed + 10));
+                                       get_le32(data + consumed + 10));
                        print_field("  Flush timeout: 0x%8.8x",
-                                       bt_get_le32(data + consumed + 14));
+                                       get_le32(data + consumed + 14));
                        break;
                case 0x07:
                        print_field("  Max window size: %d",
-                                       bt_get_le16(data + consumed + 2));
+                                       get_le16(data + consumed + 2));
                        break;
                default:
                        packet_hexdump(data + consumed + 2, len);
@@ -587,7 +590,7 @@ static void print_info_type(uint16_t type)
 {
        const char *str;
 
-       switch (btohs(type)) {
+       switch (le16_to_cpu(type)) {
        case 0x0001:
                str = "Connectionless MTU";
                break;
@@ -602,14 +605,14 @@ static void print_info_type(uint16_t type)
                break;
        }
 
-       print_field("Type: %s (0x%4.4x)", str, btohs(type));
+       print_field("Type: %s (0x%4.4x)", str, le16_to_cpu(type));
 }
 
 static void print_info_result(uint16_t result)
 {
        const char *str;
 
-       switch (btohs(result)) {
+       switch (le16_to_cpu(result)) {
        case 0x0000:
                str = "Success";
                break;
@@ -621,7 +624,7 @@ static void print_info_result(uint16_t result)
                break;
        }
 
-       print_field("Result: %s (0x%4.4x)", str, btohs(result));
+       print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
 }
 
 static struct {
@@ -697,7 +700,7 @@ static void print_move_result(uint16_t result)
 {
        const char *str;
 
-       switch (btohs(result)) {
+       switch (le16_to_cpu(result)) {
        case 0x0000:
                str = "Move success";
                break;
@@ -724,14 +727,14 @@ static void print_move_result(uint16_t result)
                break;
        }
 
-       print_field("Result: %s (0x%4.4x)", str, btohs(result));
+       print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
 }
 
 static void print_move_cfm_result(uint16_t result)
 {
        const char *str;
 
-       switch (btohs(result)) {
+       switch (le16_to_cpu(result)) {
        case 0x0000:
                str = "Move success - both sides succeed";
                break;
@@ -743,14 +746,14 @@ static void print_move_cfm_result(uint16_t result)
                break;
        }
 
-       print_field("Result: %s (0x%4.4x)", str, btohs(result));
+       print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
 }
 
 static void print_conn_param_result(uint16_t result)
 {
        const char *str;
 
-       switch (btohs(result)) {
+       switch (le16_to_cpu(result)) {
        case 0x0000:
                str = "Connection Parameters accepted";
                break;
@@ -762,7 +765,7 @@ static void print_conn_param_result(uint16_t result)
                break;
        }
 
-       print_field("Result: %s (0x%4.4x)", str, btohs(result));
+       print_field("Result: %s (0x%4.4x)", str, le16_to_cpu(result));
 }
 
 static void sig_cmd_reject(const struct l2cap_frame *frame)
@@ -777,7 +780,7 @@ static void sig_cmd_reject(const struct l2cap_frame *frame)
        data += sizeof(*pdu);
        size -= sizeof(*pdu);
 
-       switch (btohs(pdu->reason)) {
+       switch (le16_to_cpu(pdu->reason)) {
        case 0x0000:
                if (size != 0) {
                        print_text(COLOR_ERROR, "invalid data size");
@@ -791,7 +794,7 @@ static void sig_cmd_reject(const struct l2cap_frame *frame)
                        packet_hexdump(data, size);
                        break;
                }
-               print_field("MTU: %d", bt_get_le16(data));
+               print_field("MTU: %d", get_le16(data));
                break;
        case 0x0002:
                if (size != 4) {
@@ -799,10 +802,10 @@ static void sig_cmd_reject(const struct l2cap_frame *frame)
                        packet_hexdump(data, size);
                        break;
                }
-               dcid = bt_get_le16(data);
-               scid = bt_get_le16(data + 2);
-               print_cid("Destination", htobs(dcid));
-               print_cid("Source", htobs(scid));
+               dcid = get_le16(data);
+               scid = get_le16(data + 2);
+               print_cid("Destination", cpu_to_le16(dcid));
+               print_cid("Source", cpu_to_le16(scid));
                break;
        default:
                packet_hexdump(data, size);
@@ -817,7 +820,7 @@ static void sig_conn_req(const struct l2cap_frame *frame)
        print_psm(pdu->psm);
        print_cid("Source", pdu->scid);
 
-       assign_scid(frame, btohs(pdu->scid), btohs(pdu->psm), 0);
+       assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm), 0);
 }
 
 static void sig_conn_rsp(const struct l2cap_frame *frame)
@@ -829,7 +832,7 @@ static void sig_conn_rsp(const struct l2cap_frame *frame)
        print_conn_result(pdu->result);
        print_conn_status(pdu->status);
 
-       assign_dcid(frame, btohs(pdu->dcid), btohs(pdu->scid));
+       assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid));
 }
 
 static void sig_config_req(const struct l2cap_frame *frame)
@@ -838,7 +841,7 @@ static void sig_config_req(const struct l2cap_frame *frame)
 
        print_cid("Destination", pdu->dcid);
        print_config_flags(pdu->flags);
-       print_config_options(frame, 4, btohs(pdu->dcid), false);
+       print_config_options(frame, 4, le16_to_cpu(pdu->dcid), false);
 }
 
 static void sig_config_rsp(const struct l2cap_frame *frame)
@@ -848,7 +851,7 @@ static void sig_config_rsp(const struct l2cap_frame *frame)
        print_cid("Source", pdu->scid);
        print_config_flags(pdu->flags);
        print_config_result(pdu->result);
-       print_config_options(frame, 6, btohs(pdu->scid), true);
+       print_config_options(frame, 6, le16_to_cpu(pdu->scid), true);
 }
 
 static void sig_disconn_req(const struct l2cap_frame *frame)
@@ -866,7 +869,7 @@ static void sig_disconn_rsp(const struct l2cap_frame *frame)
        print_cid("Destination", pdu->dcid);
        print_cid("Source", pdu->scid);
 
-       release_scid(frame, btohs(pdu->scid));
+       release_scid(frame, le16_to_cpu(pdu->scid));
 }
 
 static void sig_echo_req(const struct l2cap_frame *frame)
@@ -898,7 +901,7 @@ static void sig_info_rsp(const struct l2cap_frame *frame)
        data += sizeof(*pdu);
        size -= sizeof(*pdu);
 
-       if (btohs(pdu->result) != 0x0000) {
+       if (le16_to_cpu(pdu->result) != 0x0000) {
                if (size > 0) {
                        print_text(COLOR_ERROR, "invalid data size");
                        packet_hexdump(data, size);
@@ -906,14 +909,14 @@ static void sig_info_rsp(const struct l2cap_frame *frame)
                return;
        }
 
-       switch (btohs(pdu->type)) {
+       switch (le16_to_cpu(pdu->type)) {
        case 0x0001:
                if (size != 2) {
                        print_text(COLOR_ERROR, "invalid data size");
                        packet_hexdump(data, size);
                        break;
                }
-               print_field("MTU: %d", bt_get_le16(data));
+               print_field("MTU: %d", get_le16(data));
                break;
        case 0x0002:
                if (size != 4) {
@@ -921,7 +924,7 @@ static void sig_info_rsp(const struct l2cap_frame *frame)
                        packet_hexdump(data, size);
                        break;
                }
-               print_features(bt_get_le32(data));
+               print_features(get_le32(data));
                break;
        case 0x0003:
                if (size != 8) {
@@ -929,7 +932,7 @@ static void sig_info_rsp(const struct l2cap_frame *frame)
                        packet_hexdump(data, size);
                        break;
                }
-               print_channels(bt_get_le64(data));
+               print_channels(get_le64(data));
                break;
        default:
                packet_hexdump(data, size);
@@ -945,7 +948,8 @@ static void sig_create_chan_req(const struct l2cap_frame *frame)
        print_cid("Source", pdu->scid);
        print_field("Controller ID: %d", pdu->ctrlid);
 
-       assign_scid(frame, btohs(pdu->scid), btohs(pdu->psm), pdu->ctrlid);
+       assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm),
+                                                               pdu->ctrlid);
 }
 
 static void sig_create_chan_rsp(const struct l2cap_frame *frame)
@@ -957,7 +961,7 @@ static void sig_create_chan_rsp(const struct l2cap_frame *frame)
        print_conn_result(pdu->result);
        print_conn_status(pdu->status);
 
-       assign_dcid(frame, btohs(pdu->dcid), btohs(pdu->scid));
+       assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid));
 }
 
 static void sig_move_chan_req(const struct l2cap_frame *frame)
@@ -995,10 +999,10 @@ static void sig_conn_param_req(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_pdu_conn_param_req *pdu = frame->data;
 
-       print_field("Min interval: %d", btohs(pdu->min_interval));
-       print_field("Max interval: %d", btohs(pdu->max_interval));
-       print_field("Slave latency: %d", btohs(pdu->latency));
-       print_field("Timeout multiplier: %d", btohs(pdu->timeout));
+       print_field("Min interval: %d", le16_to_cpu(pdu->min_interval));
+       print_field("Max interval: %d", le16_to_cpu(pdu->max_interval));
+       print_field("Slave latency: %d", le16_to_cpu(pdu->latency));
+       print_field("Timeout multiplier: %d", le16_to_cpu(pdu->timeout));
 }
 
 static void sig_conn_param_rsp(const struct l2cap_frame *frame)
@@ -1014,11 +1018,11 @@ static void sig_le_conn_req(const struct l2cap_frame *frame)
 
        print_psm(pdu->psm);
        print_cid("Source", pdu->scid);
-       print_field("MTU: %u", btohs(pdu->mtu));
-       print_field("MPS: %u", btohs(pdu->mps));
-       print_field("Credits: %u", btohs(pdu->credits));
+       print_field("MTU: %u", le16_to_cpu(pdu->mtu));
+       print_field("MPS: %u", le16_to_cpu(pdu->mps));
+       print_field("Credits: %u", le16_to_cpu(pdu->credits));
 
-       assign_scid(frame, btohs(pdu->scid), btohs(pdu->psm), 0);
+       assign_scid(frame, le16_to_cpu(pdu->scid), le16_to_cpu(pdu->psm), 0);
 }
 
 static void sig_le_conn_rsp(const struct l2cap_frame *frame)
@@ -1026,12 +1030,12 @@ static void sig_le_conn_rsp(const struct l2cap_frame *frame)
        const struct bt_l2cap_pdu_le_conn_rsp *pdu = frame->data;
 
        print_cid("Destination", pdu->dcid);
-       print_field("MTU: %u", btohs(pdu->mtu));
-       print_field("MPS: %u", btohs(pdu->mps));
-       print_field("Credits: %u", btohs(pdu->credits));
+       print_field("MTU: %u", le16_to_cpu(pdu->mtu));
+       print_field("MPS: %u", le16_to_cpu(pdu->mps));
+       print_field("Credits: %u", le16_to_cpu(pdu->credits));
        print_conn_result(pdu->result);
 
-       /*assign_dcid(frame, btohs(pdu->dcid), btohs(pdu->scid));*/
+       /*assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid));*/
 }
 
 static void sig_le_flowctl_creds(const struct l2cap_frame *frame)
@@ -1039,7 +1043,7 @@ static void sig_le_flowctl_creds(const struct l2cap_frame *frame)
        const struct bt_l2cap_pdu_le_flowctl_creds *pdu = frame->data;
 
        print_cid("Source", pdu->cid);
-       print_field("Credits: %u", btohs(pdu->credits));
+       print_field("Credits: %u", le16_to_cpu(pdu->credits));
 }
 
 struct sig_opcode_data {
@@ -1126,7 +1130,7 @@ static void bredr_sig_packet(uint16_t index, bool in, uint16_t handle,
                        return;
                }
 
-               len = btohs(hdr->len);
+               len = le16_to_cpu(hdr->len);
 
                data += 4;
                size -= 4;
@@ -1214,7 +1218,7 @@ static void le_sig_packet(uint16_t index, bool in, uint16_t handle,
                return;
        }
 
-       len = btohs(hdr->len);
+       len = le16_to_cpu(hdr->len);
 
        data += 4;
        size -= 4;
@@ -1286,7 +1290,7 @@ static void connless_packet(uint16_t index, bool in, uint16_t handle,
                return;
        }
 
-       psm = btohs(hdr->psm);
+       psm = le16_to_cpu(hdr->psm);
 
        data += 2;
        size -= 2;
@@ -1364,23 +1368,25 @@ static void amp_cmd_reject(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_amp_cmd_reject *pdu = frame->data;
 
-       print_field("Reason: 0x%4.4x", btohs(pdu->reason));
+       print_field("Reason: 0x%4.4x", le16_to_cpu(pdu->reason));
 }
 
 static void amp_discover_req(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_amp_discover_req *pdu = frame->data;
 
-       print_field("MTU/MPS size: %d", btohs(pdu->size));
-       print_field("Extended feature mask: 0x%4.4x", btohs(pdu->features));
+       print_field("MTU/MPS size: %d", le16_to_cpu(pdu->size));
+       print_field("Extended feature mask: 0x%4.4x",
+                                       le16_to_cpu(pdu->features));
 }
 
 static void amp_discover_rsp(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_amp_discover_rsp *pdu = frame->data;
 
-       print_field("MTU/MPS size: %d", btohs(pdu->size));
-       print_field("Extended feature mask: 0x%4.4x", btohs(pdu->features));
+       print_field("MTU/MPS size: %d", le16_to_cpu(pdu->size));
+       print_field("Extended feature mask: 0x%4.4x",
+                                       le16_to_cpu(pdu->features));
 
        print_controller_list(frame->data + 4, frame->size - 4);
 }
@@ -1422,12 +1428,13 @@ static void amp_get_info_rsp(const struct l2cap_frame *frame)
 
        print_field("Status: %s (0x%2.2x)", str, pdu->status);
 
-       print_field("Total bandwidth: %d kbps", btohl(pdu->total_bw));
-       print_field("Max guaranteed bandwidth: %d kbps", btohl(pdu->max_bw));
-       print_field("Min latency: %d", btohl(pdu->min_latency));
+       print_field("Total bandwidth: %d kbps", le32_to_cpu(pdu->total_bw));
+       print_field("Max guaranteed bandwidth: %d kbps",
+                                               le32_to_cpu(pdu->max_bw));
+       print_field("Min latency: %d", le32_to_cpu(pdu->min_latency));
 
-       print_field("PAL capabilities: 0x%4.4x", btohs(pdu->pal_cap));
-       print_field("Max ASSOC length: %d", btohs(pdu->max_assoc_len));
+       print_field("PAL capabilities: 0x%4.4x", le16_to_cpu(pdu->pal_cap));
+       print_field("Max ASSOC length: %d", le16_to_cpu(pdu->max_assoc_len));
 }
 
 static void amp_get_assoc_req(const struct l2cap_frame *frame)
@@ -1597,8 +1604,8 @@ static void amp_packet(uint16_t index, bool in, uint16_t handle,
                return;
        }
 
-       control = bt_get_le16(data);
-       fcs = bt_get_le16(data + size - 2);
+       control = get_le16(data);
+       fcs = get_le16(data + size - 2);
 
        print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF,
                                " %d dlen %d control 0x%4.4x fcs 0x%4.4x",
@@ -1615,7 +1622,7 @@ static void amp_packet(uint16_t index, bool in, uint16_t handle,
 
        opcode = *((const uint8_t *) (data + 2));
        ident = *((const uint8_t *) (data + 3));
-       len = bt_get_le16(data + 4);
+       len = get_le16(data + 4);
 
        if (len != size - 8) {
                print_text(COLOR_ERROR, "invalid manager packet size");
@@ -1690,20 +1697,20 @@ static void print_uuid(const char *label, const void *data, uint16_t size)
 
        switch (size) {
        case 2:
-               str = uuid16_to_str(bt_get_le16(data));
-               print_field("%s: %s (0x%4.4x)", label, str, bt_get_le16(data));
+               str = uuid16_to_str(get_le16(data));
+               print_field("%s: %s (0x%4.4x)", label, str, get_le16(data));
                break;
        case 4:
-               str = uuid32_to_str(bt_get_le32(data));
-               print_field("%s: %s (0x%8.8x)", label, str, bt_get_le32(data));
+               str = uuid32_to_str(get_le32(data));
+               print_field("%s: %s (0x%8.8x)", label, str, get_le32(data));
                break;
        case 16:
                str = uuid128_to_str(data);
                print_field("%s: %s (%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x)",
                                label, str,
-                               bt_get_le32(data + 12), bt_get_le16(data + 10),
-                               bt_get_le16(data + 8), bt_get_le16(data + 6),
-                               bt_get_le32(data + 2), bt_get_le16(data + 0));
+                               get_le32(data + 12), get_le16(data + 10),
+                               get_le16(data + 8), get_le16(data + 6),
+                               get_le32(data + 2), get_le16(data + 0));
                break;
        default:
                packet_hexdump(data, size);
@@ -1714,7 +1721,7 @@ static void print_uuid(const char *label, const void *data, uint16_t size)
 static void print_handle_range(const char *label, const void *data)
 {
        print_field("%s: 0x%4.4x-0x%4.4x", label,
-                               bt_get_le16(data), bt_get_le16(data + 2));
+                               get_le16(data), get_le16(data + 2));
 }
 
 static void print_data_list(const char *label, uint8_t length,
@@ -1730,7 +1737,7 @@ static void print_data_list(const char *label, uint8_t length,
        print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
 
        while (size >= length) {
-               print_field("Handle: 0x%4.4x", bt_get_le16(data));
+               print_field("Handle: 0x%4.4x", get_le16(data));
                print_hex_field("Value", data + 2, length - 2);
 
                data += length;
@@ -1765,7 +1772,7 @@ static void print_attribute_info(uint16_t type, const void *data, uint16_t len)
                        break;
                }
                print_field("  Properties: 0x%2.2x", *((uint8_t *) data));
-               print_field("  Handle: 0x%2.2x", bt_get_le16(data + 1));
+               print_field("  Handle: 0x%2.2x", get_le16(data + 1));
                print_uuid("  UUID", data + 3, len - 3);
                break;
        default:
@@ -1840,7 +1847,7 @@ static void att_error_response(const struct l2cap_frame *frame)
 
        print_field("%s (0x%2.2x)", att_opcode_to_str(pdu->request),
                                                        pdu->request);
-       print_field("Handle: 0x%4.4x", btohs(pdu->handle));
+       print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle));
        print_field("Error: %s (0x%2.2x)", str, pdu->error);
 }
 
@@ -1848,14 +1855,14 @@ static void att_exchange_mtu_req(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_att_exchange_mtu_req *pdu = frame->data;
 
-       print_field("Client RX MTU: %d", btohs(pdu->mtu));
+       print_field("Client RX MTU: %d", le16_to_cpu(pdu->mtu));
 }
 
 static void att_exchange_mtu_rsp(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_att_exchange_mtu_rsp *pdu = frame->data;
 
-       print_field("Server RX MTU: %d", btohs(pdu->mtu));
+       print_field("Server RX MTU: %d", le16_to_cpu(pdu->mtu));
 }
 
 static void att_find_info_req(const struct l2cap_frame *frame)
@@ -1878,7 +1885,7 @@ static const char *att_format_str(uint8_t format)
 static uint16_t print_info_data_16(const uint16_t *data, uint16_t len)
 {
        while (len >= 4) {
-               print_field("Handle: 0x%4.4x", bt_get_le16(data));
+               print_field("Handle: 0x%4.4x", get_le16(data));
                print_uuid("UUID", data + 2, 2);
                data += 4;
                len -= 4;
@@ -1890,7 +1897,7 @@ static uint16_t print_info_data_16(const uint16_t *data, uint16_t len)
 static uint16_t print_info_data_128(const uint16_t *data, uint16_t len)
 {
        while (len >= 18) {
-               print_field("Handle: 0x%4.4x", bt_get_le16(data));
+               print_field("Handle: 0x%4.4x", get_le16(data));
                print_uuid("UUID", data + 2, 16);
                data += 18;
                len -= 18;
@@ -1922,7 +1929,7 @@ static void att_find_by_type_val_req(const struct l2cap_frame *frame)
 
        print_handle_range("Handle range", frame->data);
 
-       type = bt_get_le16(frame->data + 4);
+       type = get_le16(frame->data + 4);
        print_attribute_info(type, frame->data + 6, frame->size - 6);
 }
 
@@ -1959,7 +1966,7 @@ static void att_read_req(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_att_read_req *pdu = frame->data;
 
-       print_field("Handle: 0x%4.4x", btohs(pdu->handle));
+       print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle));
 }
 
 static void att_read_rsp(const struct l2cap_frame *frame)
@@ -1969,8 +1976,8 @@ static void att_read_rsp(const struct l2cap_frame *frame)
 
 static void att_read_blob_req(const struct l2cap_frame *frame)
 {
-       print_field("Handle: 0x%4.4x", bt_get_le16(frame->data));
-       print_field("Offset: 0x%4.4x", bt_get_le16(frame->data + 2));
+       print_field("Handle: 0x%4.4x", get_le16(frame->data));
+       print_field("Offset: 0x%4.4x", get_le16(frame->data + 2));
 }
 
 static void att_read_blob_rsp(const struct l2cap_frame *frame)
@@ -1986,7 +1993,7 @@ static void att_read_multiple_req(const struct l2cap_frame *frame)
 
        for (i = 0; i < count; i++)
                print_field("Handle: 0x%4.4x",
-                                       bt_get_le16(frame->data + (i * 2)));
+                                       get_le16(frame->data + (i * 2)));
 }
 
 static void att_read_group_type_req(const struct l2cap_frame *frame)
@@ -2006,7 +2013,7 @@ static void att_read_group_type_rsp(const struct l2cap_frame *frame)
 
 static void att_write_req(const struct l2cap_frame *frame)
 {
-       print_field("Handle: 0x%4.4x", bt_get_le16(frame->data));
+       print_field("Handle: 0x%4.4x", get_le16(frame->data));
        print_hex_field("  Data", frame->data + 2, frame->size - 2);
 }
 
@@ -2016,15 +2023,15 @@ static void att_write_rsp(const struct l2cap_frame *frame)
 
 static void att_prepare_write_req(const struct l2cap_frame *frame)
 {
-       print_field("Handle: 0x%4.4x", bt_get_le16(frame->data));
-       print_field("Offset: 0x%4.4x", bt_get_le16(frame->data + 2));
+       print_field("Handle: 0x%4.4x", get_le16(frame->data));
+       print_field("Offset: 0x%4.4x", get_le16(frame->data + 2));
        print_hex_field("  Data", frame->data + 4, frame->size - 4);
 }
 
 static void att_prepare_write_rsp(const struct l2cap_frame *frame)
 {
-       print_field("Handle: 0x%4.4x", bt_get_le16(frame->data));
-       print_field("Offset: 0x%4.4x", bt_get_le16(frame->data + 2));
+       print_field("Handle: 0x%4.4x", get_le16(frame->data));
+       print_field("Offset: 0x%4.4x", get_le16(frame->data + 2));
        print_hex_field("  Data", frame->data + 4, frame->size - 4);
 }
 
@@ -2052,7 +2059,7 @@ static void att_handle_value_notify(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_att_handle_value_notify *pdu = frame->data;
 
-       print_field("Handle: 0x%4.4x", btohs(pdu->handle));
+       print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle));
        print_hex_field("  Data", frame->data + 2, frame->size - 2);
 }
 
@@ -2060,7 +2067,7 @@ static void att_handle_value_ind(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_att_handle_value_ind *pdu = frame->data;
 
-       print_field("Handle: 0x%4.4x", btohs(pdu->handle));
+       print_field("Handle: 0x%4.4x", le16_to_cpu(pdu->handle));
        print_hex_field("  Data", frame->data + 2, frame->size - 2);
 }
 
@@ -2070,7 +2077,7 @@ static void att_handle_value_conf(const struct l2cap_frame *frame)
 
 static void att_write_command(const struct l2cap_frame *frame)
 {
-       print_field("Handle: 0x%4.4x", bt_get_le16(frame->data));
+       print_field("Handle: 0x%4.4x", get_le16(frame->data));
        print_hex_field("  Data", frame->data + 2, frame->size - 2);
 }
 
@@ -2337,6 +2344,25 @@ static void print_smp_auth_req(uint8_t auth_req)
                        str, (auth_req & 0x04) ? "MITM" : "No MITM", auth_req);
 }
 
+static void print_smp_key_dist(const char *label, uint8_t dist)
+{
+       char str[19];
+
+       if (!(dist & 0x07)) {
+               strcpy(str, "<none> ");
+       } else {
+               str[0] = '\0';
+               if (dist & 0x01)
+                       strcat(str, "EncKey ");
+               if (dist & 0x02)
+                       strcat(str, "IdKey ");
+               if (dist & 0x04)
+                       strcat(str, "Sign ");
+       }
+
+       print_field("%s: %s(0x%2.2x)", label, str, dist);
+}
+
 static void smp_pairing_request(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_smp_pairing_request *pdu = frame->data;
@@ -2346,8 +2372,8 @@ static void smp_pairing_request(const struct l2cap_frame *frame)
        print_smp_auth_req(pdu->auth_req);
 
        print_field("Max encryption key size: %d", pdu->max_key_size);
-       print_field("Initiator key distribution: 0x%2.2x", pdu->init_key_dist);
-       print_field("Responder key distribution: 0x%2.2x", pdu->resp_key_dist);
+       print_smp_key_dist("Initiator key distribution", pdu->init_key_dist);
+       print_smp_key_dist("Responder key distribution", pdu->resp_key_dist);
 }
 
 static void smp_pairing_response(const struct l2cap_frame *frame)
@@ -2359,8 +2385,8 @@ static void smp_pairing_response(const struct l2cap_frame *frame)
        print_smp_auth_req(pdu->auth_req);
 
        print_field("Max encryption key size: %d", pdu->max_key_size);
-       print_field("Initiator key distribution: 0x%2.2x", pdu->init_key_dist);
-       print_field("Responder key distribution: 0x%2.2x", pdu->resp_key_dist);
+       print_smp_key_dist("Initiator key distribution", pdu->init_key_dist);
+       print_smp_key_dist("Responder key distribution", pdu->resp_key_dist);
 }
 
 static void smp_pairing_confirm(const struct l2cap_frame *frame)
@@ -2432,8 +2458,8 @@ static void smp_master_ident(const struct l2cap_frame *frame)
 {
        const struct bt_l2cap_smp_master_ident *pdu = frame->data;
 
-       print_field("EDIV: 0x%4.4x", btohs(pdu->ediv));
-       print_field("Rand: 0x%16.16" PRIx64, btohll(pdu->rand));
+       print_field("EDIV: 0x%4.4x", le16_to_cpu(pdu->ediv));
+       print_field("Rand: 0x%16.16" PRIx64, le64_to_cpu(pdu->rand));
 }
 
 static void smp_ident_info(const struct l2cap_frame *frame)
@@ -2441,6 +2467,8 @@ static void smp_ident_info(const struct l2cap_frame *frame)
        const struct bt_l2cap_smp_ident_info *pdu = frame->data;
 
        print_hex_field("Identity resolving key", pdu->irk, 16);
+
+       keys_update_identity_key(pdu->irk);
 }
 
 static void smp_ident_addr_info(const struct l2cap_frame *frame)
@@ -2449,6 +2477,8 @@ static void smp_ident_addr_info(const struct l2cap_frame *frame)
 
        print_addr_type(pdu->addr_type);
        print_addr(pdu->addr, pdu->addr_type);
+
+       keys_update_identity_addr(pdu->addr, pdu->addr_type);
 }
 
 static void smp_signing_info(const struct l2cap_frame *frame)
@@ -2640,8 +2670,8 @@ void l2cap_packet(uint16_t index, bool in, uint16_t handle, uint8_t flags,
                        return;
                }
 
-               len = btohs(hdr->len);
-               cid = btohs(hdr->cid);
+               len = le16_to_cpu(hdr->len);
+               cid = le16_to_cpu(hdr->cid);
 
                data += sizeof(*hdr);
                size -= sizeof(*hdr);
@@ -2715,8 +2745,8 @@ void l2cap_packet(uint16_t index, bool in, uint16_t handle, uint8_t flags,
                        return;
                }
 
-               len = btohs(hdr->len);
-               cid = btohs(hdr->cid);
+               len = le16_to_cpu(hdr->len);
+               cid = le16_to_cpu(hdr->cid);
 
                data += sizeof(*hdr);
                size -= sizeof(*hdr);
index 30bcb6a..0d18478 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index 1a66572..e9d0cf6 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 <config.h>
 #endif
 
+#include <stdio.h>
 #include <inttypes.h>
 
-#include <bluetooth/bluetooth.h>
-
+#include "src/shared/util.h"
 #include "display.h"
 #include "packet.h"
 #include "crc.h"
@@ -328,7 +328,7 @@ void ll_packet(uint16_t frequency, const void *data, uint8_t size)
                return;
        }
 
-       access_addr = btohl(hdr->access_addr);
+       access_addr = le32_to_cpu(hdr->access_addr);
 
        pdu_data = data + sizeof(*hdr);
        pdu_len = size - sizeof(*hdr) - 3;
@@ -380,11 +380,11 @@ static void conn_update_req(const void *data, uint8_t size)
        const struct bt_ll_conn_update_req *pdu = data;
 
        print_field("Transmit window size: %u", pdu->win_size);
-       print_field("Transmit window offset: %u", btohs(pdu->win_offset));
-       print_field("Connection interval: %u", btohs(pdu->interval));
-       print_field("Connection slave latency: %u", btohs(pdu->latency));
-       print_field("Connection supervision timeout: %u", btohs(pdu->timeout));;
-       print_field("Connection instant: %u", btohs(pdu->instant));
+       print_field("Transmit window offset: %u", le16_to_cpu(pdu->win_offset));
+       print_field("Connection interval: %u", le16_to_cpu(pdu->interval));
+       print_field("Connection slave latency: %u", le16_to_cpu(pdu->latency));
+       print_field("Connection supervision timeout: %u", le16_to_cpu(pdu->timeout));
+       print_field("Connection instant: %u", le16_to_cpu(pdu->instant));
 }
 
 static void channel_map_req(const void *data, uint8_t size)
@@ -392,32 +392,32 @@ static void channel_map_req(const void *data, uint8_t size)
        const struct bt_ll_channel_map_req *pdu = data;
 
        packet_print_channel_map_ll(pdu->map);
-       print_field("Connection instant: %u", btohs(pdu->instant));
+       print_field("Connection instant: %u", le16_to_cpu(pdu->instant));
 }
 
 static void terminate_ind(const void *data, uint8_t size)
 {
        const struct bt_ll_terminate_ind *pdu = data;
 
-       print_field("Error code: 0x%2.2x", pdu->error);
+       packet_print_error("Error code", pdu->error);
 }
 
 static void enc_req(const void *data, uint8_t size)
 {
        const struct bt_ll_enc_req *pdu = data;
 
-       print_field("Rand: 0x%16.16" PRIx64, btohll(pdu->rand));
-       print_field("EDIV: 0x%4.4x", btohs(pdu->ediv));
-       print_field("SKD (master): 0x%16.16" PRIx64, btohll(pdu->skd));
-       print_field("IV (master): 0x%8.8x", btohl(pdu->iv));
+       print_field("Rand: 0x%16.16" PRIx64, le64_to_cpu(pdu->rand));
+       print_field("EDIV: 0x%4.4x", le16_to_cpu(pdu->ediv));
+       print_field("SKD (master): 0x%16.16" PRIx64, le64_to_cpu(pdu->skd));
+       print_field("IV (master): 0x%8.8x", le32_to_cpu(pdu->iv));
 }
 
 static void enc_rsp(const void *data, uint8_t size)
 {
        const struct bt_ll_enc_rsp *pdu = data;
 
-       print_field("SKD (slave): 0x%16.16" PRIx64, btohll(pdu->skd));
-       print_field("IV (slave): 0x%8.8x", btohl(pdu->iv));
+       print_field("SKD (slave): 0x%16.16" PRIx64, le64_to_cpu(pdu->skd));
+       print_field("IV (slave): 0x%8.8x", le32_to_cpu(pdu->iv));
 }
 
 static const char *opcode_to_string(uint8_t opcode);
@@ -449,15 +449,15 @@ static void version_ind(const void *data, uint8_t size)
        const struct bt_ll_version_ind *pdu = data;
 
        packet_print_version("Version", pdu->version,
-                               "Subversion", btohs(pdu->subversion));
-       packet_print_company("Company", btohs(pdu->company));
+                               "Subversion", le16_to_cpu(pdu->subversion));
+       packet_print_company("Company", le16_to_cpu(pdu->company));
 }
 
 static void reject_ind(const void *data, uint8_t size)
 {
        const struct bt_ll_reject_ind *pdu = data;
 
-       print_field("Error code: 0x%2.2x", pdu->error);
+       packet_print_error("Error code", pdu->error);
 }
 
 struct llcp_data {
index 17d934b..ee28f40 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index e6db107..4a05973 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 <stdio.h>
 
+#include "src/shared/util.h"
 #include "display.h"
 #include "packet.h"
+#include "bt.h"
 #include "lmp.h"
 
 #define COLOR_OPCODE           COLOR_MAGENTA
 #define COLOR_OPCODE_UNKNOWN   COLOR_WHITE_BG
 
-#define ESC4(x) ((127 << 8) | (x))
+static const char *get_opcode_str(uint16_t opcode);
+
+static void print_opcode(uint16_t opcode)
+{
+       const char *str;
+
+       str = get_opcode_str(opcode);
+       if (!str)
+               str = "Unknown";
+
+       if (opcode & 0xff00)
+               print_field("Operation: %s (%u/%u)", str,
+                                               opcode >> 8, opcode & 0xff);
+       else
+               print_field("Operation: %s (%u)", str, opcode);
+}
+
+static void accepted(const void *data, uint8_t size)
+{
+       const struct bt_lmp_accepted *pdu = data;
+
+       print_opcode(pdu->opcode);
+}
+
+static void not_accepted(const void *data, uint8_t size)
+{
+       const struct bt_lmp_not_accepted *pdu = data;
+
+       print_opcode(pdu->opcode);
+       packet_print_error("Error code", pdu->error);
+}
+
+static void clkoffset_req(const void *data, uint8_t size)
+{
+}
+
+static void detach(const void *data, uint8_t size)
+{
+       const struct bt_lmp_detach *pdu = data;
+
+       packet_print_error("Error code", pdu->error);
+}
+
+static void au_rand(const void *data, uint8_t size)
+{
+       const struct bt_lmp_au_rand *pdu = data;
+
+       packet_hexdump(pdu->number, 16);
+}
+
+static void sres(const void *data, uint8_t size)
+{
+       const struct bt_lmp_sres *pdu = data;
+
+       packet_hexdump(pdu->response, 4);
+}
+
+static void encryption_mode_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_encryption_mode_req *pdu = data;
+       const char *str;
+
+       switch (pdu->mode) {
+       case 0x00:
+               str = "No encryption";
+               break;
+       case 0x01:
+               str = "Encryption";
+               break;
+       case 0x02:
+               str = "Encryption";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Mode: %s (%u)", str, pdu->mode);
+}
+
+static void encryption_key_size_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_encryption_key_size_req *pdu = data;
+
+       print_field("Key size: %u", pdu->key_size);
+}
+
+static void start_encryption_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_start_encryption_req *pdu = data;
+
+       packet_hexdump(pdu->number, 16);
+}
+
+static void stop_encryption_req(const void *data, uint8_t size)
+{
+}
+
+static void unsniff_req(const void *data, uint8_t size)
+{
+}
+
+static void max_power(const void *data, uint8_t size)
+{
+}
+
+static void min_power(const void *data, uint8_t size)
+{
+}
+
+static void auto_rate(const void *data, uint8_t size)
+{
+}
+
+static void version_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_version_req *pdu = data;
+
+       packet_print_version("Version", pdu->version,
+                               "Subversion", le16_to_cpu(pdu->subversion));
+       packet_print_company("Company", le16_to_cpu(pdu->company));
+}
+
+static void version_res(const void *data, uint8_t size)
+{
+       const struct bt_lmp_version_res *pdu = data;
+
+       packet_print_version("Version", pdu->version,
+                               "Subversion", le16_to_cpu(pdu->subversion));
+       packet_print_company("Company", le16_to_cpu(pdu->company));
+}
+
+static void features_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_features_req *pdu = data;
+
+       packet_print_features_lmp(pdu->features, 0x00);
+}
+
+static void features_res(const void *data, uint8_t size)
+{
+       const struct bt_lmp_features_res *pdu = data;
+
+       packet_print_features_lmp(pdu->features, 0x00);
+}
+
+static void max_slot(const void *data, uint8_t size)
+{
+       const struct bt_lmp_max_slot *pdu = data;
+
+       print_field("Slots: 0x%4.4x", pdu->slots);
+}
+
+static void max_slot_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_max_slot_req *pdu = data;
+
+       print_field("Slots: 0x%4.4x", pdu->slots);
+}
+
+static void timing_accuracy_req(const void *data, uint8_t size)
+{
+}
+
+static void timing_accuracy_res(const void *data, uint8_t size)
+{
+       const struct bt_lmp_timing_accuracy_res *pdu = data;
+
+       print_field("Drift: %u ppm", pdu->drift);
+       print_field("Jitter: %u usec", pdu->jitter);
+}
+
+static void setup_complete(const void *data, uint8_t size)
+{
+}
+
+static void use_semi_permanent_key(const void *data, uint8_t size)
+{
+}
+
+static void host_connection_req(const void *data, uint8_t size)
+{
+}
+
+static void page_scan_mode_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_page_scan_mode_req *pdu = data;
+       const char *str;
+
+       switch (pdu->scheme) {
+       case 0x00:
+               str = "Mandatory";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Paging scheme: %s (%u)", str, pdu->scheme);
+
+       if (pdu->scheme == 0x00) {
+               switch (pdu->settings) {
+               case 0x00:
+                       str = "R0";
+                       break;
+               case 0x01:
+                       str = "R1";
+                       break;
+               case 0x02:
+                       str = "R2";
+                       break;
+               default:
+                       str = "Reserved";
+                       break;
+               }
+       } else
+               str = "Reserved";
+
+       print_field("Paging scheme settings: %s (%u)", str, pdu->settings);
+}
+
+static void test_activate(const void *data, uint8_t size)
+{
+}
+
+static void encryption_key_size_mask_req(const void *data, uint8_t size)
+{
+}
+
+static void set_afh(const void *data, uint8_t size)
+{
+       const struct bt_lmp_set_afh *pdu = data;
+       const char *str;
+
+       print_field("Instant: %u", le32_to_cpu(pdu->instant));
+
+       switch (pdu->mode) {
+       case 0x00:
+               str = "Disabled";
+               break;
+       case 0x01:
+               str = "Enabled";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Mode: %s (0x%2.2x)", str, pdu->mode);
+       packet_print_channel_map_lmp(pdu->map);
+}
+
+static void encapsulated_header(const void *data, uint8_t size)
+{
+       const struct bt_lmp_encapsulated_header *pdu = data;
+       const char *str;
+
+       print_field("Major type: %u", pdu->major);
+       print_field("Minor type: %u", pdu->minor);
+
+       if (pdu->major == 0x01) {
+               switch (pdu->minor) {
+               case 0x01:
+                       str = "P-192 Public Key";
+                       break;
+               case 0x02:
+                       str = "P-256 Public Key";
+                       break;
+               default:
+                       str = "Reserved";
+                       break;
+               }
+
+               print_field("  %s", str);
+       }
+
+       print_field("Length: %u", pdu->length);
+}
+
+static void encapsulated_payload(const void *data, uint8_t size)
+{
+       const struct bt_lmp_encapsulated_payload *pdu = data;
+
+       packet_hexdump(pdu->data, 16);
+}
+
+static void simple_pairing_confirm(const void *data, uint8_t size)
+{
+       const struct bt_lmp_simple_pairing_confirm *pdu = data;
+
+       packet_hexdump(pdu->value, 16);
+}
+
+static void simple_pairing_number(const void *data, uint8_t size)
+{
+       const struct bt_lmp_simple_pairing_number *pdu = data;
+
+       packet_hexdump(pdu->value, 16);
+}
+
+static void dhkey_check(const void *data, uint8_t size)
+{
+       const struct bt_lmp_dhkey_check *pdu = data;
+
+       packet_hexdump(pdu->value, 16);
+}
+
+static void accepted_ext(const void *data, uint8_t size)
+{
+       const struct bt_lmp_accepted_ext *pdu = data;
+       uint16_t opcode;
+
+       switch (pdu->escape) {
+       case 127:
+               opcode = LMP_ESC4(pdu->opcode);
+               break;
+       default:
+               return;
+       }
+
+       print_opcode(opcode);
+}
+
+static void not_accepted_ext(const void *data, uint8_t size)
+{
+       const struct bt_lmp_not_accepted_ext *pdu = data;
+       uint16_t opcode;
+
+       switch (pdu->escape) {
+       case 127:
+               opcode = LMP_ESC4(pdu->opcode);
+               break;
+       default:
+               return;
+       }
+
+       print_opcode(opcode);
+       print_field("Error code: %u", pdu->error);
+}
+
+static void features_req_ext(const void *data, uint8_t size)
+{
+       const struct bt_lmp_features_req_ext *pdu = data;
+
+       print_field("Features page: %u", pdu->page);
+       print_field("Max supported page: %u", pdu->max_page);
+       packet_print_features_lmp(pdu->features, pdu->page);
+}
+
+static void features_res_ext(const void *data, uint8_t size)
+{
+       const struct bt_lmp_features_res_ext *pdu = data;
+
+       print_field("Features page: %u", pdu->page);
+       print_field("Max supported page: %u", pdu->max_page);
+       packet_print_features_lmp(pdu->features, pdu->page);
+}
+
+static void packet_type_table_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_packet_type_table_req *pdu = data;
+       const char *str;
+
+       switch (pdu->table) {
+       case 0x00:
+               str = "1 Mbps only";
+               break;
+       case 0x01:
+               str = "2/3 Mbps";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Table: %s (0x%2.2x)", str, pdu->table);
+}
+
+static void channel_classification_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_channel_classification_req *pdu = data;
+       const char *str;
+
+       switch (pdu->mode) {
+       case 0x00:
+               str = "Disabled";
+               break;
+       case 0x01:
+               str = "Enabled";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Reporting mode: %s (0x%2.2x)", str, pdu->mode);
+       print_field("Min interval: 0x%2.2x", pdu->min_interval);
+       print_field("Max interval: 0x%2.2x", pdu->max_interval);
+}
+
+static void channel_classification(const void *data, uint8_t size)
+{
+       const struct bt_lmp_channel_classification *pdu = data;
+       char str[21];
+       int i;
+
+       for (i = 0; i < 10; i++)
+               sprintf(str + (i * 2), "%2.2x", pdu->classification[i]);
+
+       print_field("Features: 0x%s", str);
+}
+
+static void pause_encryption_req(const void *data, uint8_t size)
+{
+}
+
+static void resume_encryption_req(const void *data, uint8_t size)
+{
+}
+
+static void io_capability_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_io_capability_req *pdu = data;
+       const char *str;
+
+       packet_print_io_capability(pdu->capability);
+
+       switch (pdu->oob_data) {
+       case 0x00:
+               str = "No authentication data received";
+               break;
+       case 0x01:
+               str = "Authentication data received";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("OOB data: %s (0x%2.2x)", str, pdu->oob_data);
+
+       packet_print_io_authentication(pdu->authentication);
+}
+
+static void io_capability_res(const void *data, uint8_t size)
+{
+       const struct bt_lmp_io_capability_res *pdu = data;
+       const char *str;
+
+       packet_print_io_capability(pdu->capability);
+
+       switch (pdu->oob_data) {
+       case 0x00:
+               str = "No authentication data received";
+               break;
+       case 0x01:
+               str = "Authentication data received";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("OOB data: %s (0x%2.2x)", str, pdu->oob_data);
+
+       packet_print_io_authentication(pdu->authentication);
+}
+
+static void numeric_comparison_failed(const void *data, uint8_t size)
+{
+}
+
+static void passkey_failed(const void *data, uint8_t size)
+{
+}
+
+static void oob_failed(const void *data, uint8_t size)
+{
+}
+
+static void power_control_req(const void *data, uint8_t size)
+{
+       const struct bt_lmp_power_control_req *pdu = data;
+       const char *str;
+
+       switch (pdu->request) {
+       case 0x00:
+               str = "Decrement power one step";
+               break;
+       case 0x01:
+               str = "Increment power one step";
+               break;
+       case 0x02:
+               str = "Increase to maximum power";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Request: %s (0x%2.2x)", str, pdu->request);
+}
+
+static void power_control_res(const void *data, uint8_t size)
+{
+       const struct bt_lmp_power_control_res *pdu = data;
+       const char *str;
+
+       print_field("Response: 0x%2.2x", pdu->response);
+
+       switch (pdu->response & 0x03) {
+       case 0x00:
+               str = "Not supported";
+               break;
+       case 0x01:
+               str = "Changed one step";
+               break;
+       case 0x02:
+               str = "Max power";
+               break;
+       case 0x03:
+               str = "Min power";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("  GFSK: %s", str);
+
+       switch ((pdu->response & 0x0c) >> 2) {
+       case 0x00:
+               str = "Not supported";
+               break;
+       case 0x01:
+               str = "Changed one step";
+               break;
+       case 0x02:
+               str = "Max power";
+               break;
+       case 0x03:
+               str = "Min power";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("  DQPSK: %s", str);
+
+       switch ((pdu->response & 0x30) >> 4) {
+       case 0x00:
+               str = "Not supported";
+               break;
+       case 0x01:
+               str = "Changed one step";
+               break;
+       case 0x02:
+               str = "Max power";
+               break;
+       case 0x03:
+               str = "Min power";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("  8DPSK: %s", str);
+}
+
+static void ping_req(const void *data, uint8_t size)
+{
+}
+
+static void ping_res(const void *data, uint8_t size)
+{
+}
 
 struct lmp_data {
        uint16_t opcode;
@@ -48,28 +627,28 @@ struct lmp_data {
 static const struct lmp_data lmp_table[] = {
        {  1, "LMP_name_req" },
        {  2, "LMP_name_res" },
-       {  3, "LMP_accepted" },
-       {  4, "LMP_not_accepted" },
-       {  5, "LMP_clkoffset_req" },
+       {  3, "LMP_accepted", accepted, 1, true },
+       {  4, "LMP_not_accepted", not_accepted, 2, true },
+       {  5, "LMP_clkoffset_req", clkoffset_req, 0, true },
        {  6, "LMP_clkoffset_res" },
-       {  7, "LMP_detach" },
+       {  7, "LMP_detach", detach, 1, true },
        {  8, "LMP_in_rand" },
        {  9, "LMP_comb_key" },
        { 10, "LMP_unit_key" },
-       { 11, "LMP_au_rand" },
-       { 12, "LMP_sres" },
+       { 11, "LMP_au_rand", au_rand, 16, true },
+       { 12, "LMP_sres", sres, 4, true },
        { 13, "LMP_temp_rand" },
        { 14, "LMP_temp_key" },
-       { 15, "LMP_encryption_mode_req" },
-       { 16, "LMP_encryption_key_size_req" },
-       { 17, "LMP_start_encryption_req" },
-       { 18, "LMP_stop_encryption_req" },
+       { 15, "LMP_encryption_mode_req", encryption_mode_req, 1, true },
+       { 16, "LMP_encryption_key_size_req", encryption_key_size_req, 1, true },
+       { 17, "LMP_start_encryption_req", start_encryption_req, 16, true },
+       { 18, "LMP_stop_encryption_req", stop_encryption_req, 0, true },
        { 19, "LMP_switch_req" },
        { 20, "LMP_hold" },
        { 21, "LMP_hold_req" },
        { 22, "LMP_sniff" },
        { 23, "LMP_sniff_req" },
-       { 24, "LMP_unsniff_req" },
+       { 24, "LMP_unsniff_req", unsniff_req, 0, true },
        { 25, "LMP_park_req" },
        { 26, "LMP_park" },
        { 27, "LMP_set_broadcast_scan_window" },
@@ -78,69 +657,80 @@ static const struct lmp_data lmp_table[] = {
        { 30, "LMP_unpark_PM_ADDR_req" },
        { 31, "LMP_incr_power_req" },
        { 32, "LMP_decr_power_req" },
-       { 33, "LMP_max_power" },
-       { 34, "LMP_min_power" },
-       { 35, "LMP_auto_rate" },
+       { 33, "LMP_max_power", max_power, 0, true },
+       { 34, "LMP_min_power", min_power, 0, true },
+       { 35, "LMP_auto_rate", auto_rate, 0, true },
        { 36, "LMP_preferred_rate" },
-       { 37, "LMP_version_req" },
-       { 38, "LMP_version_res" },
-       { 39, "LMP_features_req" },
-       { 40, "LMP_features_res" },
+       { 37, "LMP_version_req", version_req, 5, true },
+       { 38, "LMP_version_res", version_res, 5, true },
+       { 39, "LMP_features_req", features_req, 8, true },
+       { 40, "LMP_features_res", features_res, 8, true },
        { 41, "LMP_quality_of_service" },
        { 42, "LMP_quality_of_service_req" },
        { 43, "LMP_SCO_link_req" },
        { 44, "LMP_remove_SCO_link_req" },
-       { 45, "LMP_max_slot" },
-       { 46, "LMP_max_slot_req" },
-       { 47, "LMP_timing_accuracy_req" },
-       { 48, "LMP_timing_accuracy_res" },
-       { 49, "LMP_setup_complete" },
-       { 50, "LMP_use_semi_permanent_key" },
-       { 51, "LMP_host_connection_req" },
+       { 45, "LMP_max_slot", max_slot, 1, true },
+       { 46, "LMP_max_slot_req", max_slot_req, 1, true },
+       { 47, "LMP_timing_accuracy_req", timing_accuracy_req, 0, true },
+       { 48, "LMP_timing_accuracy_res", timing_accuracy_res, 2, true },
+       { 49, "LMP_setup_complete", setup_complete, 0, true },
+       { 50, "LMP_use_semi_permanent_key", use_semi_permanent_key, 0, true },
+       { 51, "LMP_host_connection_req", host_connection_req, 0, true },
        { 52, "LMP_slot_offset" },
        { 53, "LMP_page_mode_req" },
-       { 54, "LMP_Page_scan_mode_req" },
+       { 54, "LMP_page_scan_mode_req", page_scan_mode_req, 2, true },
        { 55, "LMP_supervision_timeout" },
-       { 56, "LMP_test_activate" },
+       { 56, "LMP_test_activate", test_activate, 0, true },
        { 57, "LMP_test_control" },
-       { 58, "LMP_encryption_key_size_mask_req" },
+       { 58, "LMP_encryption_key_size_mask_req", encryption_key_size_mask_req, 0, true },
        { 59, "LMP_encryption_key_size_mask_res" },
-       { 60, "LMP_set_AFH" },
-       { 61, "LMP_encapsulated_header" },
-       { 62, "LMP_encapsulated_payload" },
-       { 63, "LMP_simple_pairing_confirm" },
-       { 64, "LMP_simple_pairing_number" },
-       { 65, "LMP_DHkey_check" },
+       { 60, "LMP_set_AFH", set_afh, 15, true },
+       { 61, "LMP_encapsulated_header", encapsulated_header, 3, true },
+       { 62, "LMP_encapsulated_payload", encapsulated_payload, 16, true },
+       { 63, "LMP_simple_pairing_confirm", simple_pairing_confirm, 16, true },
+       { 64, "LMP_simple_pairing_number", simple_pairing_number, 16, true },
+       { 65, "LMP_DHkey_check", dhkey_check, 16, true },
        { 66, "LMP_pause_encryption_aes_req" },
-       { ESC4(1),  "LMP_accepted_ext" },
-       { ESC4(2),  "LMP_not_accepted_ext" },
-       { ESC4(3),  "LMP_features_req_ext" },
-       { ESC4(4),  "LMP_features_res_ext" },
-       { ESC4(5),  "LMP_clk_adj" },
-       { ESC4(6),  "LMP_clk_adj_ack" },
-       { ESC4(7),  "LMP_clk_adj_req" },
-       { ESC4(11), "LMP_packet_type_table" },
-       { ESC4(12), "LMP_eSCO_link_req" },
-       { ESC4(13), "LMP_remove_eSCO_link_req" },
-       { ESC4(16), "LMP_channel_classification_req" },
-       { ESC4(17), "LMP_channel_classification" },
-       { ESC4(21), "LMP_sniff_subrating_req" },
-       { ESC4(22), "LMP_sniff_subrating_res" },
-       { ESC4(23), "LMP_pause_encryption_req" },
-       { ESC4(24), "LMP_resume_encryption_req" },
-       { ESC4(25), "LMP_IO_capability_req" },
-       { ESC4(26), "LMP_IO_capability_res" },
-       { ESC4(27), "LMP_numeric_comparision_failed" },
-       { ESC4(28), "LMP_passkey_failed" },
-       { ESC4(29), "LMP_oob_failed" },
-       { ESC4(30), "LMP_keypress_notification" },
-       { ESC4(31), "LMP_power_control_req" },
-       { ESC4(32), "LMP_power_control_res" },
-       { ESC4(33), "LMP_ping_req" },
-       { ESC4(34), "LMP_ping_res" },
+       { LMP_ESC4(1),  "LMP_accepted_ext", accepted_ext, 2, true },
+       { LMP_ESC4(2),  "LMP_not_accepted_ext", not_accepted_ext, 3, true },
+       { LMP_ESC4(3),  "LMP_features_req_ext", features_req_ext, 10, true },
+       { LMP_ESC4(4),  "LMP_features_res_ext", features_res_ext, 10, true },
+       { LMP_ESC4(5),  "LMP_clk_adj" },
+       { LMP_ESC4(6),  "LMP_clk_adj_ack" },
+       { LMP_ESC4(7),  "LMP_clk_adj_req" },
+       { LMP_ESC4(11), "LMP_packet_type_table_req", packet_type_table_req, 1, true },
+       { LMP_ESC4(12), "LMP_eSCO_link_req" },
+       { LMP_ESC4(13), "LMP_remove_eSCO_link_req" },
+       { LMP_ESC4(16), "LMP_channel_classification_req", channel_classification_req, 5, true },
+       { LMP_ESC4(17), "LMP_channel_classification", channel_classification, 10, true },
+       { LMP_ESC4(21), "LMP_sniff_subrating_req" },
+       { LMP_ESC4(22), "LMP_sniff_subrating_res" },
+       { LMP_ESC4(23), "LMP_pause_encryption_req", pause_encryption_req, 0, true },
+       { LMP_ESC4(24), "LMP_resume_encryption_req", resume_encryption_req, 0, true },
+       { LMP_ESC4(25), "LMP_IO_capability_req", io_capability_req, 3, true },
+       { LMP_ESC4(26), "LMP_IO_capability_res", io_capability_res, 3, true },
+       { LMP_ESC4(27), "LMP_numeric_comparison_failed", numeric_comparison_failed, 0, true },
+       { LMP_ESC4(28), "LMP_passkey_failed", passkey_failed, 0, true },
+       { LMP_ESC4(29), "LMP_oob_failed", oob_failed, 0, true },
+       { LMP_ESC4(30), "LMP_keypress_notification" },
+       { LMP_ESC4(31), "LMP_power_control_req", power_control_req, 1, true },
+       { LMP_ESC4(32), "LMP_power_control_res", power_control_res, 1, true },
+       { LMP_ESC4(33), "LMP_ping_req", ping_req, 0, true },
+       { LMP_ESC4(34), "LMP_ping_res", ping_res, 0, true },
        { }
 };
 
+static const char *get_opcode_str(uint16_t opcode)
+{
+       int i;
+
+       for (i = 0; lmp_table[i].str; i++) {
+               if (lmp_table[i].opcode == opcode)
+                       return lmp_table[i].str;
+       }
+
+       return NULL;
+}
 
 void lmp_packet(const void *data, uint8_t size)
 {
@@ -155,7 +745,7 @@ void lmp_packet(const void *data, uint8_t size)
 
        switch (opcode) {
        case 127:
-               opcode = ESC4(((const uint8_t *) data)[1]);
+               opcode = LMP_ESC4(((const uint8_t *) data)[1]);
                off = 2;
                break;
        case 126:
@@ -187,10 +777,10 @@ void lmp_packet(const void *data, uint8_t size)
 
        if (opcode & 0xff00)
                print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
-                       " (%d/%d) TID %d", opcode >> 8, opcode & 0xff, tid);
+                       " (%u/%u) TID %u", opcode >> 8, opcode & 0xff, tid);
        else
                print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
-                                       " (%d) TID %d", opcode, tid);
+                                       " (%u) TID %d", opcode, tid);
 
        if (!lmp_data || !lmp_data->func) {
                packet_hexdump(data + off, size - off);
@@ -198,13 +788,13 @@ void lmp_packet(const void *data, uint8_t size)
        }
 
        if (lmp_data->fixed) {
-               if (size - 1 != lmp_data->size) {
+               if (size - off != lmp_data->size) {
                        print_text(COLOR_ERROR, "invalid packet size");
                        packet_hexdump(data + off, size - off);
                        return;
                }
        } else {
-               if (size - 1 < lmp_data->size) {
+               if (size - off < lmp_data->size) {
                        print_text(COLOR_ERROR, "too short packet");
                        packet_hexdump(data + off, size - off);
                        return;
@@ -213,3 +803,17 @@ void lmp_packet(const void *data, uint8_t size)
 
        lmp_data->func(data + off, size - off);
 }
+
+void lmp_todo(void)
+{
+       int i;
+
+       printf("LMP operations with missing decodings:\n");
+
+       for (i = 0; lmp_table[i].str; i++) {
+               if (lmp_table[i].func)
+                       continue;
+
+               printf("\t%s\n", lmp_table[i].str);
+       }
+}
index b2beda4..9b5393b 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
@@ -25,3 +25,5 @@
 #include <stdint.h>
 
 void lmp_packet(const void *data, uint8_t size);
+
+void lmp_todo(void);
index aed9b03..d4e8e6d 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 "mainloop.h"
 #include "packet.h"
+#include "lmp.h"
+#include "keys.h"
+#include "analyze.h"
+#include "ellisys.h"
 #include "control.h"
 
 static void signal_callback(int signum, void *user_data)
@@ -54,22 +58,26 @@ static void usage(void)
        printf("options:\n"
                "\t-r, --read <file>      Read traces in btsnoop format\n"
                "\t-w, --write <file>     Save traces in btsnoop format\n"
+               "\t-a, --analyze <file>   Analyze traces in btsnoop format\n"
                "\t-s, --server <socket>  Start monitor server socket\n"
                "\t-i, --index <num>      Show only specified controller\n"
                "\t-t, --time             Show time instead of time offset\n"
                "\t-T, --date             Show time and date information\n"
                "\t-S, --sco              Dump SCO traffic\n"
+               "\t-E, --ellisys [ip]     Send Ellisys HCI Injection\n"
                "\t-h, --help             Show help options\n");
 }
 
 static const struct option main_options[] = {
        { "read",    required_argument, NULL, 'r' },
        { "write",   required_argument, NULL, 'w' },
+       { "analyze", required_argument, NULL, 'a' },
        { "server",  required_argument, NULL, 's' },
        { "index",   required_argument, NULL, 'i' },
        { "time",    no_argument,       NULL, 't' },
        { "date",    no_argument,       NULL, 'T' },
        { "sco",     no_argument,       NULL, 'S' },
+       { "ellisys", required_argument, NULL, 'E' },
        { "todo",    no_argument,       NULL, '#' },
        { "version", no_argument,       NULL, 'v' },
        { "help",    no_argument,       NULL, 'h' },
@@ -79,7 +87,13 @@ static const struct option main_options[] = {
 int main(int argc, char *argv[])
 {
        unsigned long filter_mask = 0;
-       const char *str, *reader_path = NULL, *writer_path = NULL;
+       const char *reader_path = NULL;
+       const char *writer_path = NULL;
+       const char *analyze_path = NULL;
+       const char *ellisys_server = NULL;
+       unsigned short ellisys_port = 0;
+       const char *str;
+       int exit_status;
        sigset_t mask;
 
        mainloop_init();
@@ -89,7 +103,7 @@ int main(int argc, char *argv[])
        for (;;) {
                int opt;
 
-               opt = getopt_long(argc, argv, "r:w:s:i:tTSvh",
+               opt = getopt_long(argc, argv, "r:w:a:s:i:tTSE:vh",
                                                main_options, NULL);
                if (opt < 0)
                        break;
@@ -101,6 +115,9 @@ int main(int argc, char *argv[])
                case 'w':
                        writer_path = optarg;
                        break;
+               case 'a':
+                       analyze_path = optarg;
+                       break;
                case 's':
                        control_server(optarg);
                        break;
@@ -127,8 +144,13 @@ int main(int argc, char *argv[])
                case 'S':
                        filter_mask |= PACKET_FILTER_SHOW_SCO_DATA;
                        break;
+               case 'E':
+                       ellisys_server = optarg;
+                       ellisys_port = 24352;
+                       break;
                case '#':
                        packet_todo();
+                       lmp_todo();
                        return EXIT_SUCCESS;
                case 'v':
                        printf("%s\n", VERSION);
@@ -146,6 +168,11 @@ int main(int argc, char *argv[])
                return EXIT_FAILURE;
        }
 
+       if (reader_path && analyze_path) {
+               fprintf(stderr, "Display and analyze can't be combined\n");
+               return EXIT_FAILURE;
+       }
+
        sigemptyset(&mask);
        sigaddset(&mask, SIGINT);
        sigaddset(&mask, SIGTERM);
@@ -154,9 +181,19 @@ int main(int argc, char *argv[])
 
        printf("Bluetooth monitor ver %s\n", VERSION);
 
+       keys_setup();
+
        packet_set_filter(filter_mask);
 
+       if (analyze_path) {
+               analyze_trace(analyze_path);
+               return EXIT_SUCCESS;
+       }
+
        if (reader_path) {
+               if (ellisys_server)
+                       ellisys_enable(ellisys_server, ellisys_port);
+
                control_reader(reader_path);
                return EXIT_SUCCESS;
        }
@@ -164,8 +201,15 @@ int main(int argc, char *argv[])
        if (writer_path)
                control_writer(writer_path);
 
+       if (ellisys_server)
+               ellisys_enable(ellisys_server, ellisys_port);
+
        if (control_tracing() < 0)
                return EXIT_FAILURE;
 
-       return mainloop_run();
+       exit_status = mainloop_run();
+
+       keys_cleanup();
+
+       return exit_status;
 }
index 1cc787e..8d4b391 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
@@ -222,9 +222,6 @@ int mainloop_modify_fd(int fd, uint32_t events)
        if (!data)
                return -ENXIO;
 
-       if (data->events == events)
-               return 0;
-
        memset(&ev, 0, sizeof(ev));
        ev.events = events;
        ev.data.ptr = data;
@@ -290,20 +287,21 @@ static void timeout_callback(int fd, uint32_t events, void *user_data)
                data->callback(data->fd, data->user_data);
 }
 
-static inline int timeout_set(int fd, unsigned int seconds)
+static inline int timeout_set(int fd, unsigned int msec)
 {
        struct itimerspec itimer;
+       unsigned int sec = msec / 1000;
 
        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;
+       itimer.it_value.tv_sec = sec;
+       itimer.it_value.tv_nsec = (msec - (sec * 1000)) * 1000;
 
        return timerfd_settime(fd, 0, &itimer, NULL);
 }
 
-int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback,
+int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback,
                                void *user_data, mainloop_destroy_func destroy)
 {
        struct timeout_data *data;
@@ -326,8 +324,8 @@ int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback,
                return -EIO;
        }
 
-       if (seconds > 0) {
-               if (timeout_set(data->fd, seconds) < 0) {
+       if (msec > 0) {
+               if (timeout_set(data->fd, msec) < 0) {
                        close(data->fd);
                        free(data);
                        return -EIO;
@@ -344,10 +342,10 @@ int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback,
        return data->fd;
 }
 
-int mainloop_modify_timeout(int id, unsigned int seconds)
+int mainloop_modify_timeout(int id, unsigned int msec)
 {
-       if (seconds > 0) {
-               if (timeout_set(id, seconds) < 0)
+       if (msec > 0) {
+               if (timeout_set(id, msec) < 0)
                        return -EIO;
        }
 
index 04745d2..dafec8b 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
@@ -40,9 +40,9 @@ int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
 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,
+int mainloop_add_timeout(unsigned int msec, mainloop_timeout_func callback,
                                void *user_data, mainloop_destroy_func destroy);
-int mainloop_modify_timeout(int fd, unsigned int seconds);
+int mainloop_modify_timeout(int fd, unsigned int msec);
 int mainloop_remove_timeout(int id);
 
 int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback,
index 6ec7d9b..d8ff2da 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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 <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 
+#include "src/shared/util.h"
+#include "src/shared/btsnoop.h"
 #include "display.h"
 #include "bt.h"
 #include "ll.h"
+#include "hwdb.h"
+#include "keys.h"
 #include "uuid.h"
 #include "l2cap.h"
 #include "control.h"
-#include "btsnoop.h"
 #include "vendor.h"
 #include "packet.h"
 
 
 #define COLOR_UNKNOWN_ERROR            COLOR_WHITE_BG
 #define COLOR_UNKNOWN_FEATURE_BIT      COLOR_WHITE_BG
+#define COLOR_UNKNOWN_COMMAND_BIT      COLOR_WHITE_BG
 #define COLOR_UNKNOWN_EVENT_MASK       COLOR_WHITE_BG
 #define COLOR_UNKNOWN_LE_STATES                COLOR_WHITE_BG
 #define COLOR_UNKNOWN_SERVICE_CLASS    COLOR_WHITE_BG
+#define COLOR_UNKNOWN_PKT_TYPE_BIT     COLOR_WHITE_BG
 
 #define COLOR_PHY_PACKET               COLOR_BLUE
 
@@ -394,27 +399,55 @@ static void print_reason(uint8_t reason)
        print_error("Reason", reason);
 }
 
-static void print_bdaddr(const uint8_t *bdaddr)
+void packet_print_error(const char *label, uint8_t error)
 {
-       print_field("Address: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
-                                       " (OUI %2.2X-%2.2X-%2.2X)",
-                                       bdaddr[5], bdaddr[4], bdaddr[3],
-                                       bdaddr[2], bdaddr[1], bdaddr[0],
-                                       bdaddr[5], bdaddr[4], bdaddr[3]);
+       print_error(label, error);
 }
 
-static void print_addr(const char *label, const uint8_t *addr,
-                                               uint8_t addr_type)
+static void print_addr_type(const char *label, uint8_t addr_type)
+{
+       const char *str;
+
+       switch (addr_type) {
+       case 0x00:
+               str = "Public";
+               break;
+       case 0x01:
+               str = "Random";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("%s: %s (0x%2.2x)", label, str, addr_type);
+}
+
+static void print_addr_resolve(const char *label, const uint8_t *addr,
+                                       uint8_t addr_type, bool resolve)
 {
        const char *str;
+       char *company;
 
        switch (addr_type) {
        case 0x00:
-               print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
-                               " (OUI %2.2X-%2.2X-%2.2X)", label,
-                                       addr[5], addr[4], addr[3],
-                                       addr[2], addr[1], addr[0],
-                                       addr[5], addr[4], addr[3]);
+               if (!hwdb_get_company(addr, &company))
+                       company = NULL;
+
+               if (company) {
+                       print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
+                                       " (%s)", label, addr[5], addr[4],
+                                                       addr[3], addr[2],
+                                                       addr[1], addr[0],
+                                                       company);
+                       free(company);
+               } else {
+                       print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X"
+                                       " (OUI %2.2X-%2.2X-%2.2X)", label,
+                                               addr[5], addr[4], addr[3],
+                                               addr[2], addr[1], addr[0],
+                                               addr[5], addr[4], addr[3]);
+               }
                break;
        case 0x01:
                switch ((addr[5] & 0xc0) >> 6) {
@@ -435,6 +468,16 @@ static void print_addr(const char *label, const uint8_t *addr,
                print_field("%s: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X (%s)",
                                        label, addr[5], addr[4], addr[3],
                                        addr[2], addr[1], addr[0], str);
+
+               if (resolve && (addr[5] & 0xc0) == 0x40) {
+                       uint8_t ident[6], ident_type;
+
+                       if (keys_resolve_identity(addr, ident, &ident_type)) {
+                               print_addr_type("  Identity type", ident_type);
+                               print_addr_resolve("  Identity", ident,
+                                                       ident_type, false);
+                       }
+               }
                break;
        default:
                print_field("%s: %2.2X-%2.2X-%2.2X-%2.2X-%2.2X-%2.2X",
@@ -444,23 +487,15 @@ static void print_addr(const char *label, const uint8_t *addr,
        }
 }
 
-static void print_addr_type(const char *label, uint8_t addr_type)
+static void print_addr(const char *label, const uint8_t *addr,
+                                               uint8_t addr_type)
 {
-       const char *str;
-
-       switch (addr_type) {
-       case 0x00:
-               str = "Public";
-               break;
-       case 0x01:
-               str = "Random";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
+       print_addr_resolve(label, addr, addr_type, true);
+}
 
-       print_field("%s: %s (0x%2.2x)", label, str, addr_type);
+static void print_bdaddr(const uint8_t *bdaddr)
+{
+       print_addr("Address", bdaddr, 0x00);
 }
 
 static void print_lt_addr(uint8_t lt_addr)
@@ -470,7 +505,7 @@ static void print_lt_addr(uint8_t lt_addr)
 
 static void print_handle(uint16_t handle)
 {
-       print_field("Handle: %d", btohs(handle));
+       print_field("Handle: %d", le16_to_cpu(handle));
 }
 
 static void print_phy_handle(uint8_t phy_handle)
@@ -478,9 +513,82 @@ static void print_phy_handle(uint8_t phy_handle)
        print_field("Physical handle: %d", phy_handle);
 }
 
+static const struct {
+       uint8_t bit;
+       const char *str;
+} pkt_type_table[] = {
+       {  1, "2-DH1 may not be used"   },
+       {  2, "3-DH1 may not be used"   },
+       {  3, "DM1 may be used"         },
+       {  4, "DH1 may be used"         },
+       {  8, "2-DH3 may not be used"   },
+       {  9, "3-DH3 may not be used"   },
+       { 10, "DM3 may be used"         },
+       { 11, "DH3 may be used"         },
+       { 12, "3-DH5 may not be used"   },
+       { 13, "3-DH5 may not be used"   },
+       { 14, "DM5 may be used"         },
+       { 15, "DH5 may be used"         },
+       { }
+};
+
 static void print_pkt_type(uint16_t pkt_type)
 {
-       print_field("Packet type: 0x%4.4x", btohs(pkt_type));
+       uint16_t mask;
+       int i;
+
+       print_field("Packet type: 0x%4.4x", le16_to_cpu(pkt_type));
+
+       mask = le16_to_cpu(pkt_type);
+
+       for (i = 0; pkt_type_table[i].str; i++) {
+               if (le16_to_cpu(pkt_type) & (1 << pkt_type_table[i].bit)) {
+                       print_field("  %s", pkt_type_table[i].str);
+                       mask &= ~(1 << pkt_type_table[i].bit);
+               }
+       }
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_PKT_TYPE_BIT,
+                               "  Unknown packet types (0x%4.4x)", mask);
+}
+
+static const struct {
+       uint8_t bit;
+       const char *str;
+} pkt_type_sco_table[] = {
+       {  0, "HV1 may be used"         },
+       {  1, "HV2 may be used"         },
+       {  2, "HV3 may be used"         },
+       {  3, "EV3 may be used"         },
+       {  4, "EV4 may be used"         },
+       {  5, "EV5 may be used"         },
+       {  6, "2-EV3 may not be used"   },
+       {  7, "3-EV3 may not be used"   },
+       {  8, "2-EV5 may not be used"   },
+       {  9, "3-EV5 may not be used"   },
+       { }
+};
+
+static void print_pkt_type_sco(uint16_t pkt_type)
+{
+       uint16_t mask;
+       int i;
+
+       print_field("Packet type: 0x%4.4x", le16_to_cpu(pkt_type));
+
+       mask = le16_to_cpu(pkt_type);
+
+       for (i = 0; pkt_type_sco_table[i].str; i++) {
+               if (le16_to_cpu(pkt_type) & (1 << pkt_type_sco_table[i].bit)) {
+                       print_field("  %s", pkt_type_sco_table[i].str);
+                       mask &= ~(1 << pkt_type_sco_table[i].bit);
+               }
+       }
+
+       if (mask)
+               print_text(COLOR_UNKNOWN_PKT_TYPE_BIT,
+                               "  Unknown packet types (0x%4.4x)", mask);
 }
 
 static void print_iac(const uint8_t *lap)
@@ -753,6 +861,77 @@ static void print_dev_class(const uint8_t *dev_class)
                                "  Unknown service class (0x%2.2x)", mask);
 }
 
+static const struct {
+       uint16_t val;
+       bool generic;
+       const char *str;
+} appearance_table[] = {
+       {    0, true,  "Unknown"                },
+       {   64, true,  "Phone"                  },
+       {  128, true,  "Computer"               },
+       {  192, true,  "Watch"                  },
+       {  193, false, "Sports Watch"           },
+       {  256, true,  "Clock"                  },
+       {  320, true,  "Display"                },
+       {  384, true,  "Remote Control"         },
+       {  448, true,  "Eye-glasses"            },
+       {  512, true,  "Tag"                    },
+       {  576, true,  "Keyring"                },
+       {  640, true,  "Media Player"           },
+       {  704, true,  "Barcode Scanner"        },
+       {  768, true,  "Thermometer"            },
+       {  769, false, "Thermometer: Ear"       },
+       {  832, true,  "Heart Rate Sensor"      },
+       {  833, false, "Heart Rate Belt"        },
+       {  896, true,  "Blood Pressure"         },
+       {  897, false, "Blood Pressure: Arm"    },
+       {  898, false, "Blood Pressure: Wrist"  },
+       {  960, true,  "Human Interface Device" },
+       {  961, false, "Keyboard"               },
+       {  962, false, "Mouse"                  },
+       {  963, false, "Joystick"               },
+       {  964, false, "Gamepad"                },
+       {  965, false, "Digitizer Tablet"       },
+       {  966, false, "Card Reader"            },
+       {  967, false, "Digital Pen"            },
+       {  968, false, "Barcode Scanner"        },
+       { 1024, true,  "Glucose Meter"          },
+       { 1088, true,  "Running Walking Sensor" },
+       { 1152, true,  "Cycling"                },
+       { 1216, true,  "Undefined"              },
+
+       { 3136, true,  "Pulse Oximeter"         },
+       { 3200, true,  "Undefined"              },
+
+       { 5184, true,  "Outdoor Sports Activity"},
+       { 5248, true,  "Undefined"              },
+       { }
+};
+
+static void print_appearance(uint16_t appearance)
+{
+       const char *str = NULL;
+       int i, type = 0;
+
+       for (i = 0; appearance_table[i].str; i++) {
+               if (appearance_table[i].generic) {
+                       if (appearance < appearance_table[i].val)
+                               break;
+                       type = i;
+               }
+
+               if (appearance_table[i].val == appearance) {
+                       str = appearance_table[i].str;
+                       break;
+               }
+       }
+
+       if (!str)
+               str = appearance_table[type].str;
+
+       print_field("Appearance: %s (0x%4.4x)", str, appearance);
+}
+
 static void print_num_broadcast_retrans(uint8_t num_retrans)
 {
        print_field("Number of broadcast retransmissions: %u", num_retrans);
@@ -845,7 +1024,76 @@ static void print_host_flow_control(uint8_t enable)
 
 static void print_voice_setting(uint16_t setting)
 {
-       print_field("Setting: 0x%4.4x", btohs(setting));
+       uint8_t input_coding = (le16_to_cpu(setting) & 0x0300) >> 8;
+       uint8_t input_data_format = (le16_to_cpu(setting) & 0xc0) >> 6;
+       uint8_t air_coding_format = le16_to_cpu(setting) & 0x0003;
+       const char *str;
+
+       print_field("Setting: 0x%4.4x", le16_to_cpu(setting));
+
+       switch (input_coding) {
+       case 0x00:
+               str = "Linear";
+               break;
+       case 0x01:
+               str ="u-law";
+               break;
+       case 0x02:
+               str = "A-law";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("  Input Coding: %s", str);
+
+       switch (input_data_format) {
+       case 0x00:
+               str = "1's complement";
+               break;
+       case 0x01:
+               str = "2's complement";
+               break;
+       case 0x02:
+               str = "Sign-Magnitude";
+               break;
+       case 0x03:
+               str = "Unsigned";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("  Input Data Format: %s", str);
+
+       if (input_coding == 0x00) {
+               print_field("  Input Sample Size: %s",
+                       le16_to_cpu(setting) & 0x20 ? "16-bit" : "8-bit");
+               print_field("  # of bits padding at MSB: %d",
+                                       (le16_to_cpu(setting) & 0x1c) >> 2);
+       }
+
+       switch (air_coding_format) {
+       case 0x00:
+               str = "CVSD";
+               break;
+       case 0x01:
+               str ="u-law";
+               break;
+       case 0x02:
+               str = "A-law";
+               break;
+       case 0x03:
+               str = "Transparent Data";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("  Air Coding Format: %s", str);
 }
 
 static void print_retransmission_effort(uint8_t effort)
@@ -900,7 +1148,7 @@ static void print_scan_enable(uint8_t scan_enable)
 
 static void print_link_policy(uint16_t link_policy)
 {
-       uint16_t policy = btohs(link_policy);
+       uint16_t policy = le16_to_cpu(link_policy);
 
        print_field("Link policy: 0x%4.4x", policy);
 
@@ -1083,7 +1331,7 @@ static void print_secure_conn_support(uint8_t support)
 static void print_auth_payload_timeout(uint16_t timeout)
 {
        print_field("Timeout: %d msec (0x%4.4x)",
-                                       btohs(timeout) * 10, btohs(timeout));
+                       le16_to_cpu(timeout) * 10, le16_to_cpu(timeout));
 }
 
 static void print_pscan_rep_mode(uint8_t pscan_rep_mode)
@@ -1158,21 +1406,47 @@ static void print_pscan_mode(uint8_t pscan_mode)
 
 static void print_clock_offset(uint16_t clock_offset)
 {
-       print_field("Clock offset: 0x%4.4x", btohs(clock_offset));
+       print_field("Clock offset: 0x%4.4x", le16_to_cpu(clock_offset));
 }
 
 static void print_clock(uint32_t clock)
 {
-       print_field("Clock: 0x%8.8x", btohl(clock));
+       print_field("Clock: 0x%8.8x", le32_to_cpu(clock));
+}
+
+static void print_clock_type(uint8_t type)
+{
+       const char *str;
+
+       switch (type) {
+       case 0x00:
+               str = "Local clock";
+               break;
+       case 0x01:
+               str = "Piconet clock";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Type: %s (0x%2.2x)", str, type);
 }
 
 static void print_clock_accuracy(uint16_t accuracy)
 {
-       if (btohs(accuracy) == 0xffff)
-               print_field("Accuracy: Unknown (0x%4.4x)", btohs(accuracy));
+       if (le16_to_cpu(accuracy) == 0xffff)
+               print_field("Accuracy: Unknown (0x%4.4x)",
+                                               le16_to_cpu(accuracy));
        else
                print_field("Accuracy: %.4f msec (0x%4.4x)",
-                               btohs(accuracy) * 0.3125, btohs(accuracy));
+                                               le16_to_cpu(accuracy) * 0.3125,
+                                               le16_to_cpu(accuracy));
+}
+
+static void print_lpo_allowed(uint8_t lpo_allowed)
+{
+       print_field("LPO allowed: 0x%2.2x", lpo_allowed);
 }
 
 static void print_broadcast_fragment(uint8_t fragment)
@@ -1246,7 +1520,7 @@ static void print_encr_mode_change(uint8_t encr_mode, uint16_t handle)
        const char *str;
        uint8_t conn_type;
 
-       conn_type = get_type(btohs(handle));
+       conn_type = get_type(le16_to_cpu(handle));
 
        switch (encr_mode) {
        case 0x00:
@@ -1410,19 +1684,29 @@ static void print_pin_code(const uint8_t *pin_code, uint8_t pin_len)
        print_field("PIN code: %s", str);
 }
 
-static void print_hash(const char *label, const uint8_t *hash)
+static void print_hash_p192(const uint8_t *hash)
+{
+       print_key("Hash C from P-192", hash);
+}
+
+static void print_hash_p256(const uint8_t *hash)
 {
-       print_key("Hash C from %s", hash);
+       print_key("Hash C from P-256", hash);
 }
 
-static void print_randomizer(const char *label, const uint8_t *randomizer)
+static void print_randomizer_p192(const uint8_t *randomizer)
 {
-       print_key("Randomizer R with %s", randomizer);
+       print_key("Randomizer R with P-192", randomizer);
+}
+
+static void print_randomizer_p256(const uint8_t *randomizer)
+{
+       print_key("Randomizer R with P-256", randomizer);
 }
 
 static void print_passkey(uint32_t passkey)
 {
-       print_field("Passkey: %06d", btohl(passkey));
+       print_field("Passkey: %06d", le32_to_cpu(passkey));
 }
 
 static void print_io_capability(uint8_t capability)
@@ -1506,6 +1790,16 @@ static void print_authentication(uint8_t authentication)
        print_field("Authentication: %s (0x%2.2x)", str, authentication);
 }
 
+void packet_print_io_capability(uint8_t capability)
+{
+       print_io_capability(capability);
+}
+
+void packet_print_io_authentication(uint8_t authentication)
+{
+       print_authentication(authentication);
+}
+
 static void print_location_domain_aware(uint8_t aware)
 {
        const char *str;
@@ -1622,10 +1916,10 @@ static void print_flow_spec(const char *label, const uint8_t *data)
 
        print_field("%s flow spec: 0x%2.2x", label, data[0]);
        print_field("  Service type: %s (0x%2.2x)", str, data[1]);
-       print_field("  Maximum SDU size: 0x%4.4x", bt_get_le16(data + 2));
-       print_field("  SDU inter-arrival time: 0x%8.8x", bt_get_le32(data + 4));
-       print_field("  Access latency: 0x%8.8x", bt_get_le32(data + 8));
-       print_field("  Flush timeout: 0x%8.8x", bt_get_le32(data + 12));
+       print_field("  Maximum SDU size: 0x%4.4x", get_le16(data + 2));
+       print_field("  SDU inter-arrival time: 0x%8.8x", get_le32(data + 4));
+       print_field("  Access latency: 0x%8.8x", get_le32(data + 8));
+       print_field("  Flush timeout: 0x%8.8x", get_le32(data + 12));
 }
 
 static void print_short_range_mode(uint8_t mode)
@@ -1702,13 +1996,13 @@ static void print_rssi(int8_t rssi)
 static void print_slot_625(const char *label, uint16_t value)
 {
         print_field("%s: %.3f msec (0x%4.4x)", label,
-                                       btohs(value) * 0.625, btohs(value));
+                               le16_to_cpu(value) * 0.625, le16_to_cpu(value));
 }
 
 static void print_slot_125(const char *label, uint16_t value)
 {
        print_field("%s: %.2f msec (0x%4.4x)", label,
-                                       btohs(value) * 1.25, btohs(value));
+                               le16_to_cpu(value) * 1.25, le16_to_cpu(value));
 }
 
 static void print_timeout(uint16_t timeout)
@@ -1782,13 +2076,39 @@ static void print_name(const uint8_t *name)
 
 static void print_channel_map(const uint8_t *map)
 {
+       unsigned int count = 0, start = 0;
        char str[21];
-       int i;
+       int i, n;
 
        for (i = 0; i < 10; i++)
                sprintf(str + (i * 2), "%2.2x", map[i]);
 
        print_field("Channel map: 0x%s", str);
+
+       for (i = 0; i < 10; i++) {
+               for (n = 0; n < 8; n++) {
+                       if (map[i] & (1 << n)) {
+                               if (count == 0)
+                                       start = (i * 8) + n;
+                               count++;
+                               continue;
+                       }
+
+                       if (count > 1) {
+                               print_field("  Channel %u-%u",
+                                               start, start + count - 1 );
+                               count = 0;
+                       } else if (count > 0) {
+                               print_field("  Channel %u", start);
+                               count = 0;
+                       }
+               }
+       }
+}
+
+void packet_print_channel_map_lmp(const uint8_t *map)
+{
+       print_channel_map(map);
 }
 
 static void print_flush_timeout(uint16_t timeout)
@@ -1841,13 +2161,13 @@ void packet_print_version(const char *label, uint8_t version,
 static void print_hci_version(uint8_t version, uint16_t revision)
 {
        packet_print_version("HCI version", version,
-                               "Revision", btohs(revision));
+                               "Revision", le16_to_cpu(revision));
 }
 
 static void print_lmp_version(uint8_t version, uint16_t subversion)
 {
        packet_print_version("LMP version", version,
-                               "Subversion", btohs(subversion));
+                               "Subversion", le16_to_cpu(subversion));
 }
 
 static void print_pal_version(uint8_t version, uint16_t subversion)
@@ -1864,7 +2184,9 @@ static void print_pal_version(uint8_t version, uint16_t subversion)
        }
 
        print_field("PAL version: %s (0x%2.2x) - Subversion %d (0x%4.4x)",
-                       str, version, btohs(subversion), btohs(subversion));
+                                               str, version,
+                                               le16_to_cpu(subversion),
+                                               le16_to_cpu(subversion));
 }
 
 void packet_print_company(const char *label, uint16_t company)
@@ -1874,7 +2196,7 @@ void packet_print_company(const char *label, uint16_t company)
 
 static void print_manufacturer(uint16_t manufacturer)
 {
-       packet_print_company("Manufacturer", btohs(manufacturer));
+       packet_print_company("Manufacturer", le16_to_cpu(manufacturer));
 }
 
 static const char *get_supported_command(int bit);
@@ -1901,7 +2223,12 @@ static void print_commands(const uint8_t *commands)
                                continue;
 
                        cmd = get_supported_command((i * 8) + n);
-                       print_field("  %s (Octet %d - Bit %d)", cmd, i, n);
+                       if (cmd)
+                               print_field("  %s (Octet %d - Bit %d)",
+                                                               cmd, i, n);
+                       else
+                               print_text(COLOR_UNKNOWN_COMMAND_BIT,
+                                               "  Octet %d - Bit %d ", i, n);
                }
        }
 }
@@ -2057,6 +2384,11 @@ static void print_features(uint8_t page, const uint8_t *features_array,
                                                "(0x%16.16" PRIx64 ")", mask);
 }
 
+void packet_print_features_lmp(const uint8_t *features, uint8_t page)
+{
+       print_features(page, features, 0x00);
+}
+
 void packet_print_features_ll(const uint8_t *features)
 {
        print_features(0, features, 0x01);
@@ -2138,13 +2470,34 @@ static void print_le_states(const uint8_t *states_array)
 
 static void print_le_channel_map(const uint8_t *map)
 {
+       unsigned int count = 0, start = 0;
        char str[11];
-       int i;
+       int i, n;
 
        for (i = 0; i < 5; i++)
                sprintf(str + (i * 2), "%2.2x", map[i]);
 
        print_field("Channel map: 0x%s", str);
+
+       for (i = 0; i < 5; i++) {
+               for (n = 0; n < 8; n++) {
+                       if (map[i] & (1 << n)) {
+                               if (count == 0)
+                                       start = (i * 8) + n;
+                               count++;
+                               continue;
+                       }
+
+                       if (count > 1) {
+                               print_field("  Channel %u-%u",
+                                               start, start + count - 1 );
+                               count = 0;
+                       } else if (count > 0) {
+                               print_field("  Channel %u", start);
+                               count = 0;
+                       }
+               }
+       }
 }
 
 void packet_print_channel_map_ll(const uint8_t *map)
@@ -2152,9 +2505,14 @@ void packet_print_channel_map_ll(const uint8_t *map)
        print_le_channel_map(map);
 }
 
-static void print_random_number(const uint8_t *number)
+static void print_random_number(uint64_t rand)
+{
+       print_field("Random number: 0x%16.16" PRIx64, le64_to_cpu(rand));
+}
+
+static void print_encrypted_diversifier(uint16_t ediv)
 {
-       print_hex_field("Random number", number, 8);
+       print_field("Encrypted diversifier: 0x%4.4x", le16_to_cpu(ediv));
 }
 
 static const struct {
@@ -2385,50 +2743,91 @@ static void print_fec(uint8_t fec)
 static void print_manufacturer_apple(const void *data, uint8_t data_len)
 {
        uint8_t type = *((uint8_t *) data);
-       uint8_t len;
-       const uint8_t *uuid;
-       uint16_t minor, major;
-       int8_t tx_power;
-       char identifier[100];
 
        if (data_len < 1)
                return;
 
-       switch (type) {
-       case 0x01:
+       if (type == 0x01) {
+               char identifier[100];
+
                snprintf(identifier, sizeof(identifier) - 1, "%s",
                                                (const char *) (data + 1));
+
                print_field("  Identifier: %s", identifier);
-               break;
-       case 0x02:
-               len = *((uint8_t *) (data + 1));
-               if (len != 0x15 || len != data_len - 2) {
-                       print_hex_field("  Data", data, data_len);
+               return;
+       }
+
+       while (data_len > 0) {
+               uint8_t len;
+               const char *str;
+
+               type = *((uint8_t *) data);
+               data++;
+               data_len--;
+
+               if (type == 0x00)
+                       continue;
+
+               if (data_len < 1)
+                       break;
+
+               switch (type) {
+               case 0x02:
+                       str = "iBeacon";
+                       break;
+               case 0x05:
+                       str = "AirDrop";
+                       break;
+               case 0x09:
+                       str = "Apple TV";
+                       break;
+               default:
+                       str = "Unknown";
                        break;
                }
 
-               uuid = data + 2;
-               print_field("  iBeacon: %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
-                               bt_get_le32(&uuid[12]), bt_get_le16(&uuid[10]),
-                               bt_get_le16(&uuid[8]), bt_get_le16(&uuid[6]),
-                               bt_get_le32(&uuid[2]), bt_get_le16(&uuid[0]));
+               print_field("  Type: %s (%u)", str, type);
 
-               major = bt_get_le16(data + 18);
-               minor = bt_get_le16(data + 20);
-               print_field("  Version: %u.%u", major, minor);
+               len = *((uint8_t *) data);
+               data++;
+               data_len--;
 
-               tx_power = *(int8_t *) (data + 22);
-               print_field("  TX power: %d dB", tx_power);
-               break;
-       default:
-               print_hex_field("  Data", data, data_len);
-               break;
+               if (len < 1)
+                       continue;
+
+               if (len > data_len)
+                       break;
+
+               if (type == 0x02 && len == 0x15) {
+                       const uint8_t *uuid;
+                       uint16_t minor, major;
+                       int8_t tx_power;
+
+                       uuid = data;
+                       print_field("  UUID: %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+                               get_le32(&uuid[12]), get_le16(&uuid[10]),
+                               get_le16(&uuid[8]), get_le16(&uuid[6]),
+                               get_le32(&uuid[2]), get_le16(&uuid[0]));
+
+                       major = get_le16(data + 16);
+                       minor = get_le16(data + 18);
+                       print_field("  Version: %u.%u", major, minor);
+
+                       tx_power = *(int8_t *) (data + 20);
+                       print_field("  TX power: %d dB", tx_power);
+               } else
+                       print_hex_field("  Data", data, len);
+
+               data += len;
+               data_len -= len;
        }
+
+       packet_hexdump(data, data_len);
 }
 
 static void print_manufacturer_data(const void *data, uint8_t data_len)
 {
-       uint16_t company = bt_get_le16(data);
+       uint16_t company = get_le16(data);
 
        packet_print_company("Company", company);
 
@@ -2445,35 +2844,63 @@ static void print_manufacturer_data(const void *data, uint8_t data_len)
 
 static void print_device_id(const void *data, uint8_t data_len)
 {
-       uint16_t source;
+       uint16_t source, vendor, product, version;
+       char modalias[26], *vendor_str, *product_str;
        const char *str;
 
        if (data_len < 8)
                return;
 
-       source = bt_get_le16(data);
+       source = get_le16(data);
+       vendor = get_le16(data + 2);
+       product = get_le16(data + 4);
+       version = get_le16(data + 6);
 
        switch (source) {
        case 0x0001:
                str = "Bluetooth SIG assigned";
+               sprintf(modalias, "bluetooth:v%04Xp%04Xd%04X",
+                                               vendor, product, version);
                break;
        case 0x0002:
                str = "USB Implementer's Forum assigned";
+               sprintf(modalias, "usb:v%04Xp%04Xd%04X",
+                                               vendor, product, version);
                break;
        default:
                str = "Reserved";
+               modalias[0] = '\0';
                break;
        }
 
        print_field("Device ID: %s (0x%4.4x)", str, source);
 
-       if (source == 0x0001)
-               packet_print_company("  Vendor", bt_get_le16(data + 2));
+       if (!hwdb_get_vendor_model(modalias, &vendor_str, &product_str)) {
+               vendor_str = NULL;
+               product_str = NULL;
+       }
+
+       if (source != 0x0001) {
+               if (vendor_str)
+                       print_field("  Vendor: %s (0x%4.4x)",
+                                               vendor_str, vendor);
+               else
+                       print_field("  Vendor: 0x%4.4x", vendor);
+       } else
+               packet_print_company("  Vendor", vendor);
+
+       if (product_str)
+               print_field("  Product: %s (0x%4.4x)", product_str, product);
        else
-               print_field("  Vendor: 0x%4.4x", bt_get_le16(data + 2));
+               print_field("  Product: 0x%4.4x", product);
+
+       print_field("  Version: %u.%u.%u (0x%4.4x)",
+                                       (version & 0xff00) >> 8,
+                                       (version & 0x00f0) >> 4,
+                                       (version & 0x000f), version);
 
-       print_field("  Product: 0x%4.4x", bt_get_le16(data + 4));
-       print_field("  Version: 0x%4.4x", bt_get_le16(data + 6));
+       free(vendor_str);
+       free(product_str);
 }
 
 static void print_uuid16_list(const char *label, const void *data,
@@ -2485,7 +2912,7 @@ static void print_uuid16_list(const char *label, const void *data,
        print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
 
        for (i = 0; i < count; i++) {
-               uint16_t uuid = bt_get_le16(data + (i * 2));
+               uint16_t uuid = get_le16(data + (i * 2));
                print_field("  %s (0x%4.4x)", uuid16_to_str(uuid), uuid);
        }
 }
@@ -2499,7 +2926,7 @@ static void print_uuid32_list(const char *label, const void *data,
        print_field("%s: %u entr%s", label, count, count == 1 ? "y" : "ies");
 
        for (i = 0; i < count; i++) {
-               uint32_t uuid = bt_get_le32(data + (i * 4));
+               uint32_t uuid = get_le32(data + (i * 4));
                print_field("  %s (0x%8.8x)", uuid32_to_str(uuid), uuid);
        }
 }
@@ -2516,9 +2943,9 @@ static void print_uuid128_list(const char *label, const void *data,
                const uint8_t *uuid = data + (i * 16);
 
                print_field("  %8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
-                               bt_get_le32(&uuid[12]), bt_get_le16(&uuid[10]),
-                               bt_get_le16(&uuid[8]), bt_get_le16(&uuid[6]),
-                               bt_get_le32(&uuid[2]), bt_get_le16(&uuid[0]));
+                               get_le32(&uuid[12]), get_le16(&uuid[10]),
+                               get_le16(&uuid[8]), get_le16(&uuid[6]),
+                               get_le32(&uuid[2]), get_le16(&uuid[0]));
        }
 }
 
@@ -2663,13 +3090,13 @@ static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le)
                case BT_EIR_SSP_HASH_P192:
                        if (data_len < 16)
                                break;
-                       print_hash("P-192", data);
+                       print_hash_p192(data);
                        break;
 
                case BT_EIR_SSP_RANDOMIZER_P192:
                        if (data_len < 16)
                                break;
-                       print_randomizer("P-192", data);
+                       print_randomizer_p192(data);
                        break;
 
                case BT_EIR_DEVICE_ID:
@@ -2688,8 +3115,8 @@ static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le)
                        if (data_len < 4)
                                break;
                        print_field("Slave Conn. Interval: 0x%4.4x - 0x%4.4x",
-                                                       bt_get_le16(&data[0]),
-                                                       bt_get_le16(&data[2]));
+                                                       get_le16(&data[0]),
+                                                       get_le16(&data[2]));
                        break;
 
                case BT_EIR_SERVICE_UUID16:
@@ -2710,7 +3137,7 @@ static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le)
                        if (data_len < 2)
                                break;
                        sprintf(label, "Service Data (UUID 0x%4.4x)",
-                                                       bt_get_le16(&data[0]));
+                                                       get_le16(&data[0]));
                        print_hex_field(label, &data[2], data_len - 2);
                        break;
 
@@ -2729,19 +3156,19 @@ static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le)
                case BT_EIR_GAP_APPEARANCE:
                        if (data_len < 2)
                                break;
-                       print_field("Appearance: 0x%4.4x", bt_get_le16(data));
+                       print_appearance(get_le16(data));
                        break;
 
                case BT_EIR_SSP_HASH_P256:
                        if (data_len < 16)
                                break;
-                       print_hash("P-256", data);
+                       print_hash_p256(data);
                        break;
 
                case BT_EIR_SSP_RANDOMIZER_P256:
                        if (data_len < 16)
                                break;
-                       print_randomizer("P-256", data);
+                       print_randomizer_p256(data);
                        break;
 
                case BT_EIR_3D_INFO_DATA:
@@ -2846,22 +3273,17 @@ void packet_control(struct timeval *tv, uint16_t index, uint16_t opcode,
        control_message(opcode, data, size);
 }
 
-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
+static int addr2str(const uint8_t *addr, char *str)
+{
+       return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+                       addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
+}
 
 #define MAX_INDEX 16
 
 struct index_data {
-       uint8_t  type;
-       bdaddr_t bdaddr;
+       uint8_t type;
+       uint8_t bdaddr[6];
 };
 
 static struct index_data index_list[MAX_INDEX];
@@ -2869,7 +3291,7 @@ static struct index_data 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;
+       const struct btsnoop_opcode_new_index *ni;
        char str[18], extra_str[24];
 
        if (index_filter && index_number != index)
@@ -2886,17 +3308,17 @@ void packet_monitor(struct timeval *tv, uint16_t index, uint16_t opcode,
 
                if (index < MAX_INDEX) {
                        index_list[index].type = ni->type;
-                       bacpy(&index_list[index].bdaddr, &ni->bdaddr);
+                       memcpy(index_list[index].bdaddr, ni->bdaddr, 6);
                }
 
-               ba2str(&ni->bdaddr, str);
+               addr2str(ni->bdaddr, str);
                packet_new_index(tv, index, str, ni->type, ni->bus, ni->name);
                break;
        case BTSNOOP_OPCODE_DEL_INDEX:
                if (index < MAX_INDEX)
-                       ba2str(&index_list[index].bdaddr, str);
+                       addr2str(index_list[index].bdaddr, str);
                else
-                       ba2str(BDADDR_ANY, str);
+                       sprintf(str, "00:00:00:00:00:00");
 
                packet_del_index(tv, index, str);
                break;
@@ -3025,7 +3447,7 @@ static void add_sco_conn_cmd(const void *data, uint8_t size)
        const struct bt_hci_cmd_add_sco_conn *cmd = data;
 
        print_handle(cmd->handle);
-       print_pkt_type(cmd->pkt_type);
+       print_pkt_type_sco(cmd->pkt_type);
 }
 
 static void create_conn_cancel_cmd(const void *data, uint8_t size)
@@ -3179,7 +3601,7 @@ static void read_lmp_handle_rsp(const void *data, uint8_t size)
        print_status(rsp->status);
        print_handle(rsp->handle);
        print_field("LMP handle: %d", rsp->lmp_handle);
-       print_field("Reserved: %d", btohl(rsp->reserved));
+       print_field("Reserved: %d", le32_to_cpu(rsp->reserved));
 }
 
 static void setup_sync_conn_cmd(const void *data, uint8_t size)
@@ -3187,12 +3609,12 @@ static void setup_sync_conn_cmd(const void *data, uint8_t size)
        const struct bt_hci_cmd_setup_sync_conn *cmd = data;
 
        print_handle(cmd->handle);
-       print_field("Transmit bandwidth: %d", btohl(cmd->tx_bandwidth));
-       print_field("Receive bandwidth: %d", btohl(cmd->rx_bandwidth));
-       print_field("Max latency: %d", btohs(cmd->max_latency));
+       print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth));
+       print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth));
+       print_field("Max latency: %d", le16_to_cpu(cmd->max_latency));
        print_voice_setting(cmd->voice_setting);
        print_retransmission_effort(cmd->retrans_effort);
-       print_pkt_type(cmd->pkt_type);
+       print_pkt_type_sco(cmd->pkt_type);
 }
 
 static void accept_sync_conn_request_cmd(const void *data, uint8_t size)
@@ -3200,12 +3622,12 @@ static void accept_sync_conn_request_cmd(const void *data, uint8_t size)
        const struct bt_hci_cmd_accept_sync_conn_request *cmd = data;
 
        print_bdaddr(cmd->bdaddr);
-       print_field("Transmit bandwidth: %d", btohl(cmd->tx_bandwidth));
-       print_field("Receive bandwidth: %d", btohl(cmd->rx_bandwidth));
-       print_field("Max latency: %d", btohs(cmd->max_latency));
+       print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth));
+       print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth));
+       print_field("Max latency: %d", le16_to_cpu(cmd->max_latency));
        print_voice_setting(cmd->voice_setting);
        print_retransmission_effort(cmd->retrans_effort);
-       print_pkt_type(cmd->pkt_type);
+       print_pkt_type_sco(cmd->pkt_type);
 }
 
 static void reject_sync_conn_request_cmd(const void *data, uint8_t size)
@@ -3260,8 +3682,8 @@ static void remote_oob_data_request_reply_cmd(const void *data, uint8_t size)
        const struct bt_hci_cmd_remote_oob_data_request_reply *cmd = data;
 
        print_bdaddr(cmd->bdaddr);
-       print_hash("P-192", cmd->hash);
-       print_randomizer("P-192", cmd->randomizer);
+       print_hash_p192(cmd->hash);
+       print_randomizer_p192(cmd->randomizer);
 }
 
 static void remote_oob_data_request_neg_reply_cmd(const void *data, uint8_t size)
@@ -3382,7 +3804,7 @@ static void set_slave_broadcast_cmd(const void *data, uint8_t size)
 
        print_field("Enable: 0x%2.2x", cmd->enable);
        print_lt_addr(cmd->lt_addr);
-       print_field("LPO allowed: 0x%2.2x", cmd->lpo_allowed);
+       print_lpo_allowed(cmd->lpo_allowed);
        print_pkt_type(cmd->pkt_type);
        print_slot_625("Min interval", cmd->min_interval);
        print_slot_625("Max interval", cmd->max_interval);
@@ -3406,8 +3828,9 @@ static void set_slave_broadcast_receive_cmd(const void *data, uint8_t size)
        print_bdaddr(cmd->bdaddr);
        print_lt_addr(cmd->lt_addr);
        print_interval(cmd->interval);
-       print_field("Offset: 0x%8.8x", btohl(cmd->offset));
-       print_field("Next broadcast instant: 0x%4.4x", btohs(cmd->instant));
+       print_field("Offset: 0x%8.8x", le32_to_cpu(cmd->offset));
+       print_field("Next broadcast instant: 0x%4.4x",
+                                       le16_to_cpu(cmd->instant));
        print_slot_625("Supervision timeout", cmd->timeout);
        print_field("Remote timing accuracy: %d ppm", cmd->accuracy);
        print_field("Skip: 0x%2.2x", cmd->skip);
@@ -3439,10 +3862,10 @@ static void remote_oob_ext_data_request_reply_cmd(const void *data, uint8_t size
        const struct bt_hci_cmd_remote_oob_ext_data_request_reply *cmd = data;
 
        print_bdaddr(cmd->bdaddr);
-       print_hash("P-192", cmd->hash192);
-       print_randomizer("P-192", cmd->randomizer192);
-       print_hash("P-256", cmd->hash256);
-       print_randomizer("P-256", cmd->randomizer256);
+       print_hash_p192(cmd->hash192);
+       print_randomizer_p192(cmd->randomizer192);
+       print_hash_p256(cmd->hash256);
+       print_randomizer_p256(cmd->randomizer256);
 }
 
 static void hold_mode_cmd(const void *data, uint8_t size)
@@ -3497,10 +3920,10 @@ static void qos_setup_cmd(const void *data, uint8_t size)
 
        print_service_type(cmd->service_type);
 
-       print_field("Token rate: %d", btohl(cmd->token_rate));
-       print_field("Peak bandwidth: %d", btohl(cmd->peak_bandwidth));
-       print_field("Latency: %d", btohl(cmd->latency));
-       print_field("Delay variation: %d", btohl(cmd->delay_variation));
+       print_field("Token rate: %d", le32_to_cpu(cmd->token_rate));
+       print_field("Peak bandwidth: %d", le32_to_cpu(cmd->peak_bandwidth));
+       print_field("Latency: %d", le32_to_cpu(cmd->latency));
+       print_field("Delay variation: %d", le32_to_cpu(cmd->delay_variation));
 }
 
 static void role_discovery_cmd(const void *data, uint8_t size)
@@ -3584,10 +4007,11 @@ static void flow_spec_cmd(const void *data, uint8_t size)
        print_flow_direction(cmd->direction);
        print_service_type(cmd->service_type);
 
-       print_field("Token rate: %d", btohl(cmd->token_rate));
-       print_field("Token bucket size: %d", btohl(cmd->token_bucket_size));
-       print_field("Peak bandwidth: %d", btohl(cmd->peak_bandwidth));
-       print_field("Access latency: %d", btohl(cmd->access_latency));
+       print_field("Token rate: %d", le32_to_cpu(cmd->token_rate));
+       print_field("Token bucket size: %d",
+                                       le32_to_cpu(cmd->token_bucket_size));
+       print_field("Peak bandwidth: %d", le32_to_cpu(cmd->peak_bandwidth));
+       print_field("Access latency: %d", le32_to_cpu(cmd->access_latency));
 }
 
 static void sniff_subrating_cmd(const void *data, uint8_t size)
@@ -3742,8 +4166,8 @@ static void read_stored_link_key_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_read_stored_link_key *rsp = data;
 
        print_status(rsp->status);
-       print_field("Max num keys: %d", btohs(rsp->max_num_keys));
-       print_field("Num keys: %d", btohs(rsp->num_keys));
+       print_field("Max num keys: %d", le16_to_cpu(rsp->max_num_keys));
+       print_field("Num keys: %d", le16_to_cpu(rsp->num_keys));
 }
 
 static void write_stored_link_key_cmd(const void *data, uint8_t size)
@@ -3776,7 +4200,7 @@ static void delete_stored_link_key_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_delete_stored_link_key *rsp = data;
 
        print_status(rsp->status);
-       print_field("Num keys: %d", btohs(rsp->num_keys));
+       print_field("Num keys: %d", le16_to_cpu(rsp->num_keys));
 }
 
 static void write_local_name_cmd(const void *data, uint8_t size)
@@ -4039,9 +4463,11 @@ static void host_buffer_size_cmd(const void *data, uint8_t size)
        const struct bt_hci_cmd_host_buffer_size *cmd = data;
 
        print_field("ACL MTU: %-4d ACL max packet: %d",
-                               btohs(cmd->acl_mtu), btohs(cmd->acl_max_pkt));
+                                       le16_to_cpu(cmd->acl_mtu),
+                                       le16_to_cpu(cmd->acl_max_pkt));
        print_field("SCO MTU: %-4d SCO max packet: %d",
-                               cmd->sco_mtu, btohs(cmd->sco_max_pkt));
+                                       cmd->sco_mtu,
+                                       le16_to_cpu(cmd->sco_max_pkt));
 }
 
 static void read_link_supv_timeout_cmd(const void *data, uint8_t size)
@@ -4248,8 +4674,8 @@ static void read_local_oob_data_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_read_local_oob_data *rsp = data;
 
        print_status(rsp->status);
-       print_hash("P-192", rsp->hash);
-       print_randomizer("P-192", rsp->randomizer);
+       print_hash_p192(rsp->hash);
+       print_randomizer_p192(rsp->randomizer);
 }
 
 static void read_inquiry_resp_tx_power_rsp(const void *data, uint8_t size)
@@ -4445,7 +4871,8 @@ static void read_sync_train_params_rsp(const void *data, uint8_t size)
        print_status(rsp->status);
        print_interval(rsp->interval);
        print_field("Timeout: %.3f msec (0x%8.8x)",
-                       btohl(rsp->timeout) * 0.625, btohl(rsp->timeout));
+                                       le32_to_cpu(rsp->timeout) * 0.625,
+                                       le32_to_cpu(rsp->timeout));
        print_field("Service Data: 0x%2.2x", rsp->service_data);
 }
 
@@ -4456,7 +4883,8 @@ static void write_sync_train_params_cmd(const void *data, uint8_t size)
        print_slot_625("Min interval", cmd->min_interval);
        print_slot_625("Max interval", cmd->max_interval);
        print_field("Timeout: %.3f msec (0x%8.8x)",
-                       btohl(cmd->timeout) * 0.625, btohl(cmd->timeout));
+                                       le32_to_cpu(cmd->timeout) * 0.625,
+                                       le32_to_cpu(cmd->timeout));
        print_field("Service Data: 0x%2.2x", cmd->service_data);
 }
 
@@ -4520,10 +4948,10 @@ static void read_local_oob_ext_data_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_read_local_oob_ext_data *rsp = data;
 
        print_status(rsp->status);
-       print_hash("P-192", rsp->hash192);
-       print_randomizer("P-192", rsp->randomizer192);
-       print_hash("P-256", rsp->hash256);
-       print_randomizer("P-256", rsp->randomizer256);
+       print_hash_p192(rsp->hash192);
+       print_randomizer_p192(rsp->randomizer192);
+       print_hash_p256(rsp->hash256);
+       print_randomizer_p256(rsp->randomizer256);
 }
 
 static void read_local_version_rsp(const void *data, uint8_t size)
@@ -4583,9 +5011,11 @@ static void read_buffer_size_rsp(const void *data, uint8_t size)
 
        print_status(rsp->status);
        print_field("ACL MTU: %-4d ACL max packet: %d",
-                               btohs(rsp->acl_mtu), btohs(rsp->acl_max_pkt));
+                                       le16_to_cpu(rsp->acl_mtu),
+                                       le16_to_cpu(rsp->acl_max_pkt));
        print_field("SCO MTU: %-4d SCO max packet: %d",
-                               rsp->sco_mtu, btohs(rsp->sco_max_pkt));
+                                       rsp->sco_mtu,
+                                       le16_to_cpu(rsp->sco_max_pkt));
 }
 
 static void read_country_code_rsp(const void *data, uint8_t size)
@@ -4623,9 +5053,9 @@ static void read_data_block_size_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_read_data_block_size *rsp = data;
 
        print_status(rsp->status);
-       print_field("Max ACL length: %d", btohs(rsp->max_acl_len));
-       print_field("Block length: %d", btohs(rsp->block_len));
-       print_field("Num blocks: %d", btohs(rsp->num_blocks));
+       print_field("Max ACL length: %d", le16_to_cpu(rsp->max_acl_len));
+       print_field("Block length: %d", le16_to_cpu(rsp->block_len));
+       print_field("Num blocks: %d", le16_to_cpu(rsp->num_blocks));
 }
 
 static void read_failed_contact_counter_cmd(const void *data, uint8_t size)
@@ -4641,7 +5071,7 @@ static void read_failed_contact_counter_rsp(const void *data, uint8_t size)
 
        print_status(rsp->status);
        print_handle(rsp->handle);
-       print_field("Counter: %u", htobs(rsp->counter));
+       print_field("Counter: %u", le16_to_cpu(rsp->counter));
 }
 
 static void reset_failed_contact_counter_cmd(const void *data, uint8_t size)
@@ -4711,23 +5141,9 @@ static void read_afh_channel_map_rsp(const void *data, uint8_t size)
 static void read_clock_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_read_clock *cmd = data;
-       const char *str;
 
        print_handle(cmd->handle);
-
-       switch (cmd->type) {
-       case 0x00:
-               str = "Local clock";
-               break;
-       case 0x01:
-               str = "Piconet clock";
-               break;
-       default:
-               str = "Reserved";
-               break;
-       }
-
-       print_field("Type: %s (0x%2.2x)", str, cmd->type);
+       print_clock_type(cmd->type);
 }
 
 static void read_clock_rsp(const void *data, uint8_t size)
@@ -4764,10 +5180,11 @@ static void read_local_amp_info_rsp(const void *data, uint8_t size)
        print_status(rsp->status);
        print_amp_status(rsp->amp_status);
 
-       print_field("Total bandwidth: %d kbps", btohl(rsp->total_bw));
-       print_field("Max guaranteed bandwidth: %d kbps", btohl(rsp->max_bw));
-       print_field("Min latency: %d", btohl(rsp->min_latency));
-       print_field("Max PDU size: %d", btohl(rsp->max_pdu));
+       print_field("Total bandwidth: %d kbps", le32_to_cpu(rsp->total_bw));
+       print_field("Max guaranteed bandwidth: %d kbps",
+                                               le32_to_cpu(rsp->max_bw));
+       print_field("Min latency: %d", le32_to_cpu(rsp->min_latency));
+       print_field("Max PDU size: %d", le32_to_cpu(rsp->max_pdu));
 
        switch (rsp->amp_type) {
        case 0x00:
@@ -4783,10 +5200,11 @@ static void read_local_amp_info_rsp(const void *data, uint8_t size)
 
        print_field("Controller type: %s (0x%2.2x)", str, rsp->amp_type);
 
-       print_field("PAL capabilities: 0x%4.4x", btohs(rsp->pal_cap));
-       print_field("Max ASSOC length: %d", btohs(rsp->max_assoc_len));
-       print_field("Max flush timeout: %d", btohl(rsp->max_flush_to));
-       print_field("Best effort flush timeout: %d", btohl(rsp->be_flush_to));
+       print_field("PAL capabilities: 0x%4.4x", le16_to_cpu(rsp->pal_cap));
+       print_field("Max ASSOC length: %d", le16_to_cpu(rsp->max_assoc_len));
+       print_field("Max flush timeout: %d", le32_to_cpu(rsp->max_flush_to));
+       print_field("Best effort flush timeout: %d",
+                                       le32_to_cpu(rsp->be_flush_to));
 }
 
 static void read_local_amp_assoc_cmd(const void *data, uint8_t size)
@@ -4794,8 +5212,8 @@ static void read_local_amp_assoc_cmd(const void *data, uint8_t size)
        const struct bt_hci_cmd_read_local_amp_assoc *cmd = data;
 
        print_phy_handle(cmd->phy_handle);
-       print_field("Length so far: %d", btohs(cmd->len_so_far));
-       print_field("Max ASSOC length: %d", btohs(cmd->max_assoc_len));
+       print_field("Length so far: %d", le16_to_cpu(cmd->len_so_far));
+       print_field("Max ASSOC length: %d", le16_to_cpu(cmd->max_assoc_len));
 }
 
 static void read_local_amp_assoc_rsp(const void *data, uint8_t size)
@@ -4804,7 +5222,8 @@ static void read_local_amp_assoc_rsp(const void *data, uint8_t size)
 
        print_status(rsp->status);
        print_phy_handle(rsp->phy_handle);
-       print_field("Remaining ASSOC length: %d", btohs(rsp->remain_assoc_len));
+       print_field("Remaining ASSOC length: %d",
+                                       le16_to_cpu(rsp->remain_assoc_len));
 
        packet_hexdump(data + 4, size - 4);
 }
@@ -4814,8 +5233,9 @@ static void write_remote_amp_assoc_cmd(const void *data, uint8_t size)
        const struct bt_hci_cmd_write_remote_amp_assoc *cmd = data;
 
        print_phy_handle(cmd->phy_handle);
-       print_field("Length so far: %d", btohs(cmd->len_so_far));
-       print_field("Remaining ASSOC length: %d", btohs(cmd->remain_assoc_len));
+       print_field("Length so far: %d", le16_to_cpu(cmd->len_so_far));
+       print_field("Remaining ASSOC length: %d",
+                                       le16_to_cpu(cmd->remain_assoc_len));
 
        packet_hexdump(data + 5, size - 5);
 }
@@ -4828,6 +5248,32 @@ static void write_remote_amp_assoc_rsp(const void *data, uint8_t size)
        print_phy_handle(rsp->phy_handle);
 }
 
+static void set_triggered_clock_capture_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_set_triggered_clock_capture *cmd = data;
+       const char *str;
+
+       print_handle(cmd->handle);
+
+       switch (cmd->enable) {
+       case 0x00:
+               str = "Disabled";
+               break;
+       case 0x01:
+               str = "Enabled";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Capture: %s (0x%2.2x)", str, cmd->enable);
+
+       print_clock_type(cmd->type);
+       print_lpo_allowed(cmd->lpo_allowed);
+       print_field("Clock captures to filter: %u", cmd->num_filter);
+}
+
 static void write_ssp_debug_mode_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_write_ssp_debug_mode *cmd = data;
@@ -4847,7 +5293,7 @@ static void le_read_buffer_size_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_le_read_buffer_size *rsp = data;
 
        print_status(rsp->status);
-       print_field("Data packet length: %d", btohs(rsp->le_mtu));
+       print_field("Data packet length: %d", le16_to_cpu(rsp->le_mtu));
        print_field("Num data packets: %d", rsp->le_max_pkt);
 }
 
@@ -5099,9 +5545,10 @@ static void le_create_conn_cmd(const void *data, uint8_t size)
 
        print_slot_125("Min connection interval", cmd->min_interval);
        print_slot_125("Max connection interval", cmd->max_interval);
-       print_field("Connection latency: 0x%4.4x", btohs(cmd->latency));
+       print_field("Connection latency: 0x%4.4x", le16_to_cpu(cmd->latency));
        print_field("Supervision timeout: %d msec (0x%4.4x)",
-               btohs(cmd->supv_timeout) * 10, btohs(cmd->supv_timeout));
+                                       le16_to_cpu(cmd->supv_timeout) * 10,
+                                       le16_to_cpu(cmd->supv_timeout));
        print_slot_625("Min connection length", cmd->min_length);
        print_slot_625("Max connection length", cmd->max_length);
 }
@@ -5137,9 +5584,10 @@ static void le_conn_update_cmd(const void *data, uint8_t size)
        print_handle(cmd->handle);
        print_slot_125("Min connection interval", cmd->min_interval);
        print_slot_125("Max connection interval", cmd->max_interval);
-       print_field("Connection latency: 0x%4.4x", btohs(cmd->latency));
+       print_field("Connection latency: 0x%4.4x", le16_to_cpu(cmd->latency));
        print_field("Supervision timeout: %d msec (0x%4.4x)",
-               btohs(cmd->supv_timeout) * 10, btohs(cmd->supv_timeout));
+                                       le16_to_cpu(cmd->supv_timeout) * 10,
+                                       le16_to_cpu(cmd->supv_timeout));
        print_slot_625("Min connection length", cmd->min_length);
        print_slot_625("Max connection length", cmd->max_length);
 }
@@ -5203,9 +5651,8 @@ static void le_start_encrypt_cmd(const void *data, uint8_t size)
        const struct bt_hci_cmd_le_start_encrypt *cmd = data;
 
        print_handle(cmd->handle);
-       print_random_number(cmd->number);
-       print_field("Encryption diversifier: 0x%4.4x",
-                                       btohs(cmd->diversifier));
+       print_random_number(cmd->rand);
+       print_encrypted_diversifier(cmd->ediv);
        print_key("Long term key", cmd->ltk);
 }
 
@@ -5271,7 +5718,7 @@ static void le_test_end_rsp(const void *data, uint8_t size)
        const struct bt_hci_rsp_le_test_end *rsp = data;
 
        print_status(rsp->status);
-       print_field("Number of packets: %d", btohs(rsp->num_packets));
+       print_field("Number of packets: %d", le16_to_cpu(rsp->num_packets));
 }
 
 struct opcode_data {
@@ -5792,7 +6239,9 @@ static const struct opcode_data opcode_table[] = {
                                write_remote_amp_assoc_cmd, 6, false,
                                write_remote_amp_assoc_rsp, 2, true },
        { 0x140c, 243, "Get MWS Transport Layer Configuration" },
-       { 0x140d, 245, "Set Triggered Clock Capture" },
+       { 0x140d, 245, "Set Triggered Clock Capture",
+                               set_triggered_clock_capture_cmd, 6, true,
+                               status_rsp, 1, true },
 
        /* OGF 6 - Testing */
        { 0x1801, 128, "Read Loopback Mode" },
@@ -5946,7 +6395,7 @@ static void conn_complete_evt(const void *data, uint8_t size)
        print_encr_mode(evt->encr_mode);
 
        if (evt->status == 0x00)
-               assign_handle(btohs(evt->handle), 0x00);
+               assign_handle(le16_to_cpu(evt->handle), 0x00);
 }
 
 static void conn_request_evt(const void *data, uint8_t size)
@@ -5967,7 +6416,7 @@ static void disconnect_complete_evt(const void *data, uint8_t size)
        print_reason(evt->reason);
 
        if (evt->status == 0x00)
-               release_handle(btohs(evt->handle));
+               release_handle(le16_to_cpu(evt->handle));
 }
 
 static void auth_complete_evt(const void *data, uint8_t size)
@@ -6042,16 +6491,16 @@ static void qos_setup_complete_evt(const void *data, uint8_t size)
 
        print_service_type(evt->service_type);
 
-       print_field("Token rate: %d", btohl(evt->token_rate));
-       print_field("Peak bandwidth: %d", btohl(evt->peak_bandwidth));
-       print_field("Latency: %d", btohl(evt->latency));
-       print_field("Delay variation: %d", btohl(evt->delay_variation));
+       print_field("Token rate: %d", le32_to_cpu(evt->token_rate));
+       print_field("Peak bandwidth: %d", le32_to_cpu(evt->peak_bandwidth));
+       print_field("Latency: %d", le32_to_cpu(evt->latency));
+       print_field("Delay variation: %d", le32_to_cpu(evt->delay_variation));
 }
 
 static void cmd_complete_evt(const void *data, uint8_t size)
 {
        const struct bt_hci_evt_cmd_complete *evt = data;
-       uint16_t opcode = btohs(evt->opcode);
+       uint16_t opcode = le16_to_cpu(evt->opcode);
        uint16_t ogf = cmd_opcode_ogf(opcode);
        uint16_t ocf = cmd_opcode_ocf(opcode);
        const struct opcode_data *opcode_data = NULL;
@@ -6072,15 +6521,32 @@ static void cmd_complete_evt(const void *data, uint8_t size)
                        opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
                opcode_str = opcode_data->str;
        } else {
-               opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
-               opcode_str = "Unknown";
+               if (ogf == 0x3f) {
+                       opcode_color = COLOR_HCI_COMMAND;
+                       opcode_str = "Vendor";
+               } else {
+                       opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
+                       opcode_str = "Unknown";
+               }
        }
 
        print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
                        " (0x%2.2x|0x%4.4x) ncmd %d", ogf, ocf, evt->ncmd);
 
        if (!opcode_data || !opcode_data->rsp_func) {
-               packet_hexdump(data + 3, size - 3);
+               if (size > 3) {
+                       uint8_t status = *((uint8_t *) (data + 3));
+
+                       print_status(status);
+                       packet_hexdump(data + 4, size - 4);
+               }
+               return;
+       }
+
+       if (opcode_data->rsp_size > 1 && size - 3 == 1) {
+               uint8_t status = *((uint8_t *) (data + 3));
+
+               print_status(status);
                return;
        }
 
@@ -6104,7 +6570,7 @@ static void cmd_complete_evt(const void *data, uint8_t size)
 static void cmd_status_evt(const void *data, uint8_t size)
 {
        const struct bt_hci_evt_cmd_status *evt = data;
-       uint16_t opcode = btohs(evt->opcode);
+       uint16_t opcode = le16_to_cpu(evt->opcode);
        uint16_t ogf = cmd_opcode_ogf(opcode);
        uint16_t ocf = cmd_opcode_ocf(opcode);
        const struct opcode_data *opcode_data = NULL;
@@ -6122,8 +6588,13 @@ static void cmd_status_evt(const void *data, uint8_t size)
                opcode_color = COLOR_HCI_COMMAND;
                opcode_str = opcode_data->str;
        } else {
-               opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
-               opcode_str = "Unknown";
+               if (ogf == 0x3f) {
+                       opcode_color = COLOR_HCI_COMMAND;
+                       opcode_str = "Vendor";
+               } else {
+                       opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
+                       opcode_str = "Unknown";
+               }
        }
 
        print_indent(6, opcode_color, "", opcode_str, COLOR_OFF,
@@ -6161,7 +6632,7 @@ static void num_completed_packets_evt(const void *data, uint8_t size)
 
        print_field("Num handles: %d", evt->num_handles);
        print_handle(evt->handle);
-       print_field("Count: %d", btohs(evt->count));
+       print_field("Count: %d", le16_to_cpu(evt->count));
 
        if (size > sizeof(*evt))
                packet_hexdump(data + sizeof(*evt), size - sizeof(*evt));
@@ -6281,10 +6752,11 @@ static void flow_spec_complete_evt(const void *data, uint8_t size)
        print_flow_direction(evt->direction);
        print_service_type(evt->service_type);
 
-       print_field("Token rate: %d", btohl(evt->token_rate));
-       print_field("Token bucket size: %d", btohl(evt->token_bucket_size));
-       print_field("Peak bandwidth: %d", btohl(evt->peak_bandwidth));
-       print_field("Access latency: %d", btohl(evt->access_latency));
+       print_field("Token rate: %d", le32_to_cpu(evt->token_rate));
+       print_field("Token bucket size: %d",
+                                       le32_to_cpu(evt->token_bucket_size));
+       print_field("Peak bandwidth: %d", le32_to_cpu(evt->peak_bandwidth));
+       print_field("Access latency: %d", le32_to_cpu(evt->access_latency));
 }
 
 static void inquiry_result_with_rssi_evt(const void *data, uint8_t size)
@@ -6323,8 +6795,8 @@ static void sync_conn_complete_evt(const void *data, uint8_t size)
        print_link_type(evt->link_type);
        print_field("Transmission interval: 0x%2.2x", evt->tx_interval);
        print_field("Retransmission window: 0x%2.2x", evt->retrans_window);
-       print_field("RX packet length: %d", btohs(evt->rx_pkt_len));
-       print_field("TX packet length: %d", btohs(evt->tx_pkt_len));
+       print_field("RX packet length: %d", le16_to_cpu(evt->rx_pkt_len));
+       print_field("TX packet length: %d", le16_to_cpu(evt->tx_pkt_len));
        print_air_mode(evt->air_mode);
 }
 
@@ -6336,8 +6808,8 @@ static void sync_conn_changed_evt(const void *data, uint8_t size)
        print_handle(evt->handle);
        print_field("Transmission interval: 0x%2.2x", evt->tx_interval);
        print_field("Retransmission window: 0x%2.2x", evt->retrans_window);
-       print_field("RX packet length: %d", btohs(evt->rx_pkt_len));
-       print_field("TX packet length: %d", btohs(evt->tx_pkt_len));
+       print_field("RX packet length: %d", le16_to_cpu(evt->rx_pkt_len));
+       print_field("TX packet length: %d", le16_to_cpu(evt->tx_pkt_len));
 }
 
 static void sniff_subrating_evt(const void *data, uint8_t size)
@@ -6576,7 +7048,8 @@ static void num_completed_data_blocks_evt(const void *data, uint8_t size)
 {
        const struct bt_hci_evt_num_completed_data_blocks *evt = data;
 
-       print_field("Total num data blocks: %d", btohs(evt->total_num_blocks));
+       print_field("Total num data blocks: %d",
+                               le16_to_cpu(evt->total_num_blocks));
        print_field("Num handles: %d", evt->num_handles);
        print_handle(evt->handle);
        print_field("Num packets: %d", evt->num_packets);
@@ -6616,10 +7089,11 @@ static void sync_train_received_evt(const void *data, uint8_t size)
 
        print_status(evt->status);
        print_bdaddr(evt->bdaddr);
-       print_field("Offset: 0x%8.8x", btohl(evt->offset));
+       print_field("Offset: 0x%8.8x", le32_to_cpu(evt->offset));
        print_channel_map(evt->map);
        print_lt_addr(evt->lt_addr);
-       print_field("Next broadcast instant: 0x%4.4x", btohs(evt->instant));
+       print_field("Next broadcast instant: 0x%4.4x",
+                                       le16_to_cpu(evt->instant));
        print_interval(evt->interval);
        print_field("Service Data: 0x%2.2x", evt->service_data);
 }
@@ -6630,8 +7104,8 @@ static void slave_broadcast_receive_evt(const void *data, uint8_t size)
 
        print_bdaddr(evt->bdaddr);
        print_lt_addr(evt->lt_addr);
-       print_field("Clock: 0x%8.8x", btohl(evt->clock));
-       print_field("Offset: 0x%8.8x", btohl(evt->offset));
+       print_field("Clock: 0x%8.8x", le32_to_cpu(evt->clock));
+       print_field("Offset: 0x%8.8x", le32_to_cpu(evt->offset));
        print_field("Receive status: 0x%2.2x", evt->status);
        print_broadcast_fragment(evt->fragment);
        print_field("Length: %d", evt->length);
@@ -6670,6 +7144,14 @@ static void slave_broadcast_channel_map_change_evt(const void *data, uint8_t siz
        print_channel_map(evt->map);
 }
 
+static void inquiry_response_notify_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_inquiry_response_notify *evt = data;
+
+       print_iac(evt->lap);
+       print_rssi(evt->rssi);
+}
+
 static void auth_payload_timeout_expired_evt(const void *data, uint8_t size)
 {
        const struct bt_hci_evt_auth_payload_timeout_expired *evt = data;
@@ -6689,11 +7171,12 @@ static void le_conn_complete_evt(const void *data, uint8_t size)
        print_slot_125("Connection interval", evt->interval);
        print_slot_125("Connection latency", evt->latency);
        print_field("Supervision timeout: %d msec (0x%4.4x)",
-               btohs(evt->supv_timeout) * 10, btohs(evt->supv_timeout));
+                                       le16_to_cpu(evt->supv_timeout) * 10,
+                                       le16_to_cpu(evt->supv_timeout));
        print_field("Master clock accuracy: 0x%2.2x", evt->clock_accuracy);
 
        if (evt->status == 0x00)
-               assign_handle(btohs(evt->handle), 0x01);
+               assign_handle(le16_to_cpu(evt->handle), 0x01);
 }
 
 static void le_adv_report_evt(const void *data, uint8_t size)
@@ -6705,6 +7188,7 @@ static void le_adv_report_evt(const void *data, uint8_t size)
 
        print_num_reports(evt->num_reports);
 
+report:
        switch (evt->event_type) {
        case 0x00:
                str = "Connectable undirected - ADV_IND";
@@ -6737,8 +7221,12 @@ static void le_adv_report_evt(const void *data, uint8_t size)
 
        evt_len = sizeof(*evt) + evt->data_len + 1;
 
-       if (size > evt_len)
-               packet_hexdump(data + evt_len, size - evt_len);
+       if (size > evt_len) {
+               data += evt_len - 1;
+               size -= evt_len - 1;
+               evt = data;
+               goto report;
+       }
 }
 
 static void le_conn_update_complete_evt(const void *data, uint8_t size)
@@ -6750,7 +7238,8 @@ static void le_conn_update_complete_evt(const void *data, uint8_t size)
        print_slot_125("Connection interval", evt->interval);
        print_slot_125("Connection latency", evt->latency);
        print_field("Supervision timeout: %d msec (0x%4.4x)",
-               btohs(evt->supv_timeout) * 10, btohs(evt->supv_timeout));
+                                       le16_to_cpu(evt->supv_timeout) * 10,
+                                       le16_to_cpu(evt->supv_timeout));
 }
 
 static void le_remote_features_complete_evt(const void *data, uint8_t size)
@@ -6767,9 +7256,8 @@ static void le_long_term_key_request_evt(const void *data, uint8_t size)
        const struct bt_hci_evt_le_long_term_key_request *evt = data;
 
        print_handle(evt->handle);
-       print_random_number(evt->number);
-       print_field("Encryption diversifier: 0x%4.4x",
-                                       btohs(evt->diversifier));
+       print_random_number(evt->rand);
+       print_encrypted_diversifier(evt->ediv);
 }
 
 struct subevent_data {
@@ -7003,7 +7491,8 @@ static const struct event_data event_table[] = {
                                slave_page_response_timeout_evt, 0, true },
        { 0x55, "Connectionless Slave Broadcast Channel Map Change",
                                slave_broadcast_channel_map_change_evt, 10, true },
-       { 0x56, "Inquiry Response Notification" },
+       { 0x56, "Inquiry Response Notification",
+                               inquiry_response_notify_evt, 4, true },
        { 0x57, "Authenticated Payload Timeout Expired",
                                auth_payload_timeout_expired_evt, 2, true },
        { 0xfe, "Testing" },
@@ -7033,7 +7522,7 @@ 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 opcode = le16_to_cpu(hdr->opcode);
        uint16_t ogf = cmd_opcode_ogf(opcode);
        uint16_t ocf = cmd_opcode_ocf(opcode);
        const struct opcode_data *opcode_data = NULL;
@@ -7066,8 +7555,13 @@ void packet_hci_command(struct timeval *tv, uint16_t index,
                        opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
                opcode_str = opcode_data->str;
        } else {
-               opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
-               opcode_str = "Unknown";
+               if (ogf == 0x3f) {
+                       opcode_color = COLOR_HCI_COMMAND;
+                       opcode_str = "Vendor";
+               } else {
+                       opcode_color = COLOR_HCI_COMMAND_UNKNOWN;
+                       opcode_str = "Unknown";
+               }
        }
 
        sprintf(extra_str, "(0x%2.2x|0x%4.4x) plen %d", ogf, ocf, hdr->plen);
@@ -7080,6 +7574,13 @@ void packet_hci_command(struct timeval *tv, uint16_t index,
                return;
        }
 
+       if (size != hdr->plen) {
+               print_text(COLOR_ERROR, "invalid packet size (%u != %u)", size,
+                                                               hdr->plen);
+               packet_hexdump(data, size);
+               return;
+       }
+
        if (opcode_data->cmd_fixed) {
                if (hdr->plen != opcode_data->cmd_size) {
                        print_text(COLOR_ERROR, "invalid packet size");
@@ -7145,6 +7646,13 @@ void packet_hci_event(struct timeval *tv, uint16_t index,
                return;
        }
 
+       if (size != hdr->plen) {
+               print_text(COLOR_ERROR, "invalid packet size (%u != %u)", size,
+                                                               hdr->plen);
+               packet_hexdump(data, size);
+               return;
+       }
+
        if (event_data->fixed) {
                if (hdr->plen != event_data->size) {
                        print_text(COLOR_ERROR, "invalid packet size");
@@ -7166,8 +7674,8 @@ void packet_hci_acldata(struct timeval *tv, uint16_t index, bool in,
                                        const void *data, uint16_t size)
 {
        const struct bt_hci_acl_hdr *hdr = data;
-       uint16_t handle = btohs(hdr->handle);
-       uint16_t dlen = btohs(hdr->dlen);
+       uint16_t handle = le16_to_cpu(hdr->handle);
+       uint16_t dlen = le16_to_cpu(hdr->dlen);
        uint8_t flags = acl_flags(handle);
        char handle_str[16], extra_str[32];
 
@@ -7209,7 +7717,7 @@ 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);
+       uint16_t handle = le16_to_cpu(hdr->handle);
        uint8_t flags = acl_flags(handle);
        char handle_str[16], extra_str[32];
 
index d44849c..c39816b 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
@@ -40,13 +40,18 @@ void packet_del_filter(unsigned long filter);
 void packet_select_index(uint16_t index);
 
 void packet_hexdump(const unsigned char *buf, uint16_t len);
+void packet_print_error(const char *label, uint8_t error);
 void packet_print_version(const char *label, uint8_t version,
                                const char *sublabel, uint16_t subversion);
 void packet_print_company(const char *label, uint16_t company);
 void packet_print_addr(const char *label, const void *data, bool random);
 void packet_print_ad(const void *data, uint8_t size);
+void packet_print_features_lmp(const uint8_t *features, uint8_t page);
 void packet_print_features_ll(const uint8_t *features);
+void packet_print_channel_map_lmp(const uint8_t *map);
 void packet_print_channel_map_ll(const uint8_t *map);
+void packet_print_io_capability(uint8_t capability);
+void packet_print_io_authentication(uint8_t authentication);
 
 void packet_control(struct timeval *tv, uint16_t index, uint16_t opcode,
                                        const void *data, uint16_t size);
diff --git a/monitor/rfcomm.h b/monitor/rfcomm.h
new file mode 100644 (file)
index 0000000..c157352
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2011-2014  Intel Corporation
+ *  Copyright (C) 2002-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 RFCOMM_SABM    0x2f
+#define RFCOMM_DISC    0x43
+#define RFCOMM_UA      0x63
+#define RFCOMM_DM      0x0f
+#define RFCOMM_UIH     0xef
+
+#define RFCOMM_GET_TYPE(control)       ((control) & 0xef)
+#define RFCOMM_GET_DLCI(address)       ((address & 0xfc) >> 2)
+#define RFCOMM_GET_CHANNEL(address)    ((address & 0xf8) >> 3)
+#define RFCOMM_GET_DIR(address)                ((address & 0x04) >> 2)
+#define RFCOMM_TEST_EA(length)         ((length & 0x01))
+
+struct rfcomm_hdr {
+       uint8_t address;
+       uint8_t control;
+       uint8_t length;
+} __attribute__((packed));
+
+struct rfcomm_cmd {
+       uint8_t address;
+       uint8_t control;
+       uint8_t length;
+       uint8_t fcs;
+} __attribute__((packed));
+
+#define RFCOMM_TEST    0x08
+#define RFCOMM_FCON    0x28
+#define RFCOMM_FCOFF   0x18
+#define RFCOMM_MSC     0x38
+#define RFCOMM_RPN     0x24
+#define RFCOMM_RLS     0x14
+#define RFCOMM_PN      0x20
+#define RFCOMM_NSC     0x04
+
+#define RFCOMM_TEST_CR(type)           ((type & 0x02))
+#define RFCOMM_GET_MCC_TYPE(type)      ((type & 0xfc) >> 2)
+
+struct rfcomm_mcc {
+       uint8_t type;
+       uint8_t length;
+} __attribute__((packed));
+
+struct rfcomm_msc {
+       uint8_t dlci;
+       uint8_t v24_sig;
+} __attribute__((packed));
+
+struct rfcomm_pn {
+       uint8_t  dlci;
+       uint8_t  flow_ctrl;
+       uint8_t  priority;
+       uint8_t  ack_timer;
+       uint16_t mtu;
+       uint8_t  max_retrans;
+       uint8_t  credits;
+} __attribute__((packed));
index 4eb398b..a0ab314 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
@@ -33,6 +33,8 @@
 
 #include <bluetooth/bluetooth.h>
 
+#include "src/shared/util.h"
+
 #include "bt.h"
 #include "packet.h"
 #include "display.h"
@@ -89,14 +91,13 @@ static void print_uint(uint8_t indent, const uint8_t *data, uint32_t size)
                print_field("%*c0x%2.2x", indent, ' ', data[0]);
                break;
        case 2:
-               print_field("%*c0x%4.4x", indent, ' ', bt_get_be16(data));
+               print_field("%*c0x%4.4x", indent, ' ', get_be16(data));
                break;
        case 4:
-               print_field("%*c0x%8.8x", indent, ' ', bt_get_be32(data));
+               print_field("%*c0x%8.8x", indent, ' ', get_be32(data));
                break;
        case 8:
-               print_field("%*c0x%16.16" PRIx64, indent, ' ',
-                                                       bt_get_be64(data));
+               print_field("%*c0x%16.16" PRIx64, indent, ' ', get_be64(data));
                break;
        default:
                packet_hexdump(data, size);
@@ -114,26 +115,26 @@ static void print_uuid(uint8_t indent, const uint8_t *data, uint32_t size)
        switch (size) {
        case 2:
                print_field("%*c%s (0x%4.4x)", indent, ' ',
-                       uuid16_to_str(bt_get_be16(data)), bt_get_be16(data));
+                       uuid16_to_str(get_be16(data)), get_be16(data));
                break;
        case 4:
                print_field("%*c%s (0x%8.8x)", indent, ' ',
-                       uuid32_to_str(bt_get_be32(data)), bt_get_be32(data));
+                       uuid32_to_str(get_be32(data)), get_be32(data));
                break;
        case 16:
                /* BASE_UUID = 00000000-0000-1000-8000-00805F9B34FB */
                print_field("%*c%8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.4x",
                                indent, ' ',
-                               bt_get_be32(data), bt_get_be16(data + 4),
-                               bt_get_be16(data + 6), bt_get_be16(data + 8),
-                               bt_get_be16(data + 10), bt_get_be32(data + 12));
-               if (bt_get_be16(data + 4) == 0x0000 &&
-                               bt_get_be16(data + 6) == 0x1000 &&
-                               bt_get_be16(data + 8) == 0x8000 &&
-                               bt_get_be16(data + 10) == 0x0080 &&
-                               bt_get_be32(data + 12) == 0x5F9B34FB)
+                               get_be32(data), get_be16(data + 4),
+                               get_be16(data + 6), get_be16(data + 8),
+                               get_be16(data + 10), get_be32(data + 12));
+               if (get_be16(data + 4) == 0x0000 &&
+                               get_be16(data + 6) == 0x1000 &&
+                               get_be16(data + 8) == 0x8000 &&
+                               get_be16(data + 10) == 0x0080 &&
+                               get_be32(data + 12) == 0x5F9B34FB)
                        print_field("%*c%s", indent, ' ',
-                               uuid32_to_str(bt_get_be32(data)));
+                               uuid32_to_str(get_be32(data)));
                break;
        default:
                packet_hexdump(data, size);
@@ -233,9 +234,9 @@ static uint32_t get_size(const uint8_t *data, uint32_t size)
                        case 8:
                                return data[1];
                        case 16:
-                               return bt_get_be16(data + 1);
+                               return get_be16(data + 1);
                        case 32:
-                               return bt_get_be32(data + 1);
+                               return get_be32(data + 1);
                        default:
                                return 0;
                        }
@@ -319,9 +320,9 @@ static uint32_t get_bytes(const uint8_t *data, uint32_t size)
        case 5:
                return 2 + data[1];
        case 6:
-               return 3 + bt_get_be16(data + 1);
+               return 3 + get_be16(data + 1);
        case 7:
-               return 5 + bt_get_be32(data + 1);
+               return 5 + get_be32(data + 1);
        }
 
        return 0;
@@ -354,7 +355,7 @@ static void print_attr(uint32_t position, uint8_t indent, uint8_t type,
        int i;
 
        if ((position % 2) == 0) {
-               uint16_t id = bt_get_be16(data);
+               uint16_t id = get_be16(data);
                const char *str = "Unknown";
 
                for (i = 0; attribute_table[i].str; i++) {
@@ -509,7 +510,7 @@ static uint16_t common_rsp(const struct l2cap_frame *frame,
                return 0;
        }
 
-       bytes = bt_get_be16(frame->data);
+       bytes = get_be16(frame->data);
        print_field("Attribute bytes: %d", bytes);
 
        if (bytes > frame->size - 2) {
@@ -533,7 +534,7 @@ static void error_rsp(const struct l2cap_frame *frame, struct tid_data *tid)
                return;
        }
 
-       error = bt_get_be16(frame->data);
+       error = get_be16(frame->data);
 
        print_field("Error code: 0x%2.2x", error);
 }
@@ -554,7 +555,7 @@ static void service_req(const struct l2cap_frame *frame, struct tid_data *tid)
        decode_data_elements(0, 2, frame->data, search_bytes, NULL);
 
        print_field("Max record count: %d",
-                               bt_get_be16(frame->data + search_bytes));
+                               get_be16(frame->data + search_bytes));
 
        print_continuation(frame->data + search_bytes + 2,
                                        frame->size - search_bytes - 2);
@@ -573,14 +574,14 @@ static void service_rsp(const struct l2cap_frame *frame, struct tid_data *tid)
                return;
        }
 
-       count = bt_get_be16(frame->data + 2);
+       count = get_be16(frame->data + 2);
 
-       print_field("Total record count: %d", bt_get_be16(frame->data));
+       print_field("Total record count: %d", get_be16(frame->data));
        print_field("Current record count: %d", count);
 
        for (i = 0; i < count; i++)
                print_field("Record handle: 0x%4.4x",
-                               bt_get_be32(frame->data + 4 + (i * 4)));
+                               get_be32(frame->data + 4 + (i * 4)));
 
        print_continuation(frame->data + 4 + (count * 4),
                                        frame->size - 4 - (count * 4));
@@ -596,8 +597,8 @@ static void attr_req(const struct l2cap_frame *frame, struct tid_data *tid)
                return;
        }
 
-       print_field("Record handle: 0x%4.4x", bt_get_be32(frame->data));
-       print_field("Max attribute bytes: %d", bt_get_be16(frame->data + 4));
+       print_field("Record handle: 0x%4.4x", get_be32(frame->data));
+       print_field("Max attribute bytes: %d", get_be16(frame->data + 4));
 
        attr_bytes = get_bytes(frame->data + 6, frame->size - 6);
        print_field("Attribute list: [len %d]", attr_bytes);
@@ -643,7 +644,7 @@ static void search_attr_req(const struct l2cap_frame *frame,
        decode_data_elements(0, 2, frame->data, search_bytes, NULL);
 
        print_field("Max record count: %d",
-                               bt_get_be16(frame->data + search_bytes));
+                               get_be16(frame->data + search_bytes));
 
        attr_bytes = get_bytes(frame->data + search_bytes + 2,
                                frame->size - search_bytes - 2);
@@ -703,8 +704,8 @@ void sdp_packet(const struct l2cap_frame *frame, uint16_t channel)
        }
 
        pdu = *((uint8_t *) frame->data);
-       tid = bt_get_be16(frame->data + 1);
-       plen = bt_get_be16(frame->data + 3);
+       tid = get_be16(frame->data + 1);
+       plen = get_be16(frame->data + 3);
 
        if (frame->size != plen + 5) {
                print_text(COLOR_ERROR, "invalid frame size");
index c339772..ca2cd45 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index 3d39a47..4b7448e 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index 2a33c27..f467f51 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index 31d48ee..8d3b614 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index 7dbd284..9ac9a4b 100644 (file)
@@ -2,22 +2,22 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2011-2012  Intel Corporation
- *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2011-2014  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 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 program is distributed in the hope that it will be useful,
+ *  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 General Public License for more details.
+ *  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 General Public License
- *  along with this program; if not, write to the Free Software
+ *  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
  *
  */
index 75deea0..e89a92b 100644 (file)
 
 #include <glib.h>
 #include <gdbus/gdbus.h>
-#include <btio/btio.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/rfcomm.h>
 #include <bluetooth/sdp.h>
 #include <bluetooth/sdp_lib.h>
 
+#include "btio/btio.h"
+
 #include "log.h"
 #include "transport.h"
 #include "bluetooth.h"
index 8138b1e..cb176e4 100644 (file)
@@ -240,8 +240,6 @@ static void session_free(struct obc_session *session)
        if (session->p)
                pending_request_free(session->p);
 
-       sessions = g_slist_remove(sessions, session);
-
        g_free(session->path);
        g_free(session->owner);
        g_free(session->source);
@@ -250,6 +248,25 @@ static void session_free(struct obc_session *session)
        g_free(session);
 }
 
+static void disconnect_complete(GObex *obex, GError *err, GObexPacket *rsp,
+                                                       void *user_data)
+{
+       struct obc_session *session = user_data;
+
+       DBG("");
+
+       if (err)
+               error("%s", err->message);
+
+       /* Disconnect transport */
+       if (session->id > 0 && session->transport != NULL) {
+               session->transport->disconnect(session->id);
+               session->id = 0;
+       }
+
+       session_free(session);
+}
+
 void obc_session_unref(struct obc_session *session)
 {
        int refs;
@@ -261,6 +278,19 @@ void obc_session_unref(struct obc_session *session)
        if (refs > 0)
                return;
 
+       sessions = g_slist_remove(sessions, session);
+
+       if (!session->obex)
+               goto disconnect;
+
+       /* Wait OBEX Disconnect to complete if command succeed otherwise
+        * proceed with transport disconnection since there is nothing else to
+        * be done */
+       if (g_obex_disconnect(session->obex, disconnect_complete, session,
+                                                                       NULL))
+               return;
+
+disconnect:
        /* Disconnect transport */
        if (session->id > 0 && session->transport != NULL) {
                session->transport->disconnect(session->id);
index 5a8d4f2..bd2b1ab 100644 (file)
@@ -60,6 +60,7 @@ struct transfer_callback {
 enum {
        TRANSFER_STATUS_QUEUED = 0,
        TRANSFER_STATUS_ACTIVE,
+       TRANSFER_STATUS_SUSPENDED,
        TRANSFER_STATUS_COMPLETE,
        TRANSFER_STATUS_ERROR
 };
@@ -79,6 +80,7 @@ struct obc_transfer {
        char *name;             /* Transfer object name */
        char *type;             /* Transfer object type */
        int fd;
+       guint req;
        guint xfer;
        gint64 size;
        gint64 transferred;
@@ -157,6 +159,14 @@ static DBusMessage *obc_transfer_cancel(DBusConnection *connection,
                                ERROR_INTERFACE ".InProgress",
                                "Cancellation already in progress");
 
+       if (transfer->req > 0) {
+               if (!g_obex_cancel_req(transfer->obex, transfer->req, TRUE))
+                       return g_dbus_create_error(message,
+                                               ERROR_INTERFACE ".Failed",
+                                               "Failed");
+               transfer->req = 0;
+       }
+
        if (transfer->xfer == 0) {
                struct transfer_callback *callback = transfer->callback;
 
@@ -187,6 +197,65 @@ static DBusMessage *obc_transfer_cancel(DBusConnection *connection,
        return NULL;
 }
 
+static void transfer_set_status(struct obc_transfer *transfer, uint8_t status)
+{
+       if (transfer->status == status)
+               return;
+
+       transfer->status = status;
+
+       g_dbus_emit_property_changed(transfer->conn, transfer->path,
+                                       TRANSFER_INTERFACE, "Status");
+}
+
+static DBusMessage *obc_transfer_suspend(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct obc_transfer *transfer = user_data;
+       const char *sender;
+
+       sender = dbus_message_get_sender(message);
+       if (g_strcmp0(transfer->owner, sender) != 0)
+               return g_dbus_create_error(message,
+                               ERROR_INTERFACE ".NotAuthorized",
+                               "Not Authorized");
+
+       if (transfer->xfer == 0)
+               return g_dbus_create_error(message,
+                               ERROR_INTERFACE ".NotInProgress",
+                               "Not in progress");
+
+       g_obex_suspend(transfer->obex);
+
+       transfer_set_status(transfer, TRANSFER_STATUS_SUSPENDED);
+
+       return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *obc_transfer_resume(DBusConnection *connection,
+                                       DBusMessage *message, void *user_data)
+{
+       struct obc_transfer *transfer = user_data;
+       const char *sender;
+
+       sender = dbus_message_get_sender(message);
+       if (g_strcmp0(transfer->owner, sender) != 0)
+               return g_dbus_create_error(message,
+                               ERROR_INTERFACE ".NotAuthorized",
+                               "Not Authorized");
+
+       if (transfer->xfer == 0)
+               return g_dbus_create_error(message,
+                               ERROR_INTERFACE ".NotInProgress",
+                               "Not in progress");
+
+       g_obex_resume(transfer->obex);
+
+       transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE);
+
+       return g_dbus_create_reply(message, DBUS_TYPE_INVALID);
+}
+
 static gboolean name_exists(const GDBusPropertyTable *property, void *data)
 {
        struct obc_transfer *transfer = data;
@@ -269,6 +338,8 @@ static const char *status2str(uint8_t status)
                return "queued";
        case TRANSFER_STATUS_ACTIVE:
                return "active";
+       case TRANSFER_STATUS_SUSPENDED:
+               return "suspended";
        case TRANSFER_STATUS_COMPLETE:
                return "complete";
        case TRANSFER_STATUS_ERROR:
@@ -300,6 +371,8 @@ static gboolean get_session(const GDBusPropertyTable *property,
 }
 
 static const GDBusMethodTable obc_transfer_methods[] = {
+       { GDBUS_METHOD("Suspend", NULL, NULL, obc_transfer_suspend) },
+       { GDBUS_METHOD("Resume", NULL, NULL, obc_transfer_resume) },
        { GDBUS_ASYNC_METHOD("Cancel", NULL, NULL,
                                obc_transfer_cancel) },
        { }
@@ -319,6 +392,9 @@ static void obc_transfer_free(struct obc_transfer *transfer)
 {
        DBG("%p", transfer);
 
+       if (transfer->req > 0)
+               g_obex_cancel_req(transfer->obex, transfer->req, TRUE);
+
        if (transfer->xfer)
                g_obex_cancel_transfer(transfer->xfer, NULL, NULL);
 
@@ -549,14 +625,6 @@ static gboolean get_xfer_progress(const void *buf, gsize len,
        return TRUE;
 }
 
-static void transfer_set_status(struct obc_transfer *transfer, uint8_t status)
-{
-       transfer->status = status;
-
-       g_dbus_emit_property_changed(transfer->conn, transfer->path,
-                                       TRANSFER_INTERFACE, "Status");
-}
-
 static void xfer_complete(GObex *obex, GError *err, gpointer user_data)
 {
        struct obc_transfer *transfer = user_data;
@@ -630,17 +698,22 @@ static void get_xfer_progress_first(GObex *obex, GError *err, GObexPacket *rsp,
        }
 
        if (rspcode == G_OBEX_RSP_SUCCESS) {
+               transfer->req = 0;
                xfer_complete(obex, err, transfer);
                return;
        }
 
-       if (!g_obex_srm_active(obex)) {
-               req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
+       if (g_obex_srm_active(obex) ||
+                               transfer->status == TRANSFER_STATUS_SUSPENDED)
+               return;
+
+       transfer->req = 0;
 
-               transfer->xfer = g_obex_get_req_pkt(obex, req, get_xfer_progress,
+       req = g_obex_packet_new(G_OBEX_OP_GET, TRUE, G_OBEX_HDR_INVALID);
+
+       transfer->xfer = g_obex_get_req_pkt(obex, req, get_xfer_progress,
                                                xfer_complete, transfer,
                                                &err);
-       }
 }
 
 static gssize put_xfer_progress(void *buf, gsize len, gpointer user_data)
@@ -689,7 +762,8 @@ static gboolean report_progress(gpointer data)
                return FALSE;
        }
 
-       if (transfer->status != TRANSFER_STATUS_ACTIVE)
+       if (transfer->status != TRANSFER_STATUS_ACTIVE &&
+                               transfer->status != TRANSFER_STATUS_SUSPENDED)
                transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE);
 
        g_dbus_emit_property_changed(transfer->conn, transfer->path,
@@ -724,11 +798,11 @@ static gboolean transfer_start_get(struct obc_transfer *transfer, GError **err)
                g_obex_packet_add_header(req, hdr);
        }
 
-       transfer->xfer = g_obex_send_req(transfer->obex, req,
+       transfer->req = g_obex_send_req(transfer->obex, req,
                                                FIRST_PACKET_TIMEOUT,
                                                get_xfer_progress_first,
                                                transfer, err);
-       if (transfer->xfer == 0)
+       if (transfer->req == 0)
                return FALSE;
 
        if (transfer->path == NULL)
index 143d0c2..cf326cc 100644 (file)
@@ -35,8 +35,8 @@
 
 #include <glib.h>
 #include <gdbus/gdbus.h>
-#include <btio/btio.h>
 
+#include "btio/btio.h"
 #include "lib/uuid.h"
 
 #include "obexd.h"
index 4740188..acac3aa 100644 (file)
@@ -188,7 +188,7 @@ static void phonebook_size_result(const char *buffer, size_t bufsize,
 
        DBG("vcards %d", vcards);
 
-       phonebooksize = htons(vcards);
+       phonebooksize = vcards;
 
        pbap->obj->apparam = g_obex_apparam_set_uint16(NULL, PHONEBOOKSIZE_TAG,
                                                                phonebooksize);
@@ -365,7 +365,7 @@ static int generate_response(void *user_data)
 
        if (max == 0) {
                /* Ignore all other parameter and return PhoneBookSize */
-               uint16_t size = htons(g_slist_length(pbap->cache.entries));
+               uint16_t size = g_slist_length(pbap->cache.entries);
 
                pbap->obj->apparam = g_obex_apparam_set_uint16(
                                                        pbap->obj->apparam,
index 441cff2..fff33c1 100644 (file)
@@ -37,7 +37,7 @@
 #define PB_CALLS_INCOMING_FOLDER "/telecom/ich"
 #define PB_CALLS_MISSED_FOLDER "/telecom/mch"
 #define PB_CALLS_OUTGOING_FOLDER "/telecom/och"
-#define PB_LUID_FOLDER "/telecom/luid"
+#define PB_LUID_FOLDER "/telecom/pb/luid"
 
 #define PB_CONTACTS "/telecom/pb.vcf"
 #define PB_CALLS_COMBINED "/telecom/cch.vcf"
index cec8a39..326e56f 100644 (file)
@@ -33,9 +33,9 @@
 #include <sys/socket.h>
 #include <inttypes.h>
 
-#include <btio/btio.h>
 #include <gobex/gobex.h>
 
+#include "btio/btio.h"
 #include "obexd.h"
 #include "obex.h"
 #include "obex-priv.h"
index 8a7a8a3..7b4634e 100644 (file)
@@ -40,9 +40,9 @@
 #include <inttypes.h>
 
 #include <glib.h>
-#include <btio/btio.h>
 #include <gobex/gobex.h>
 
+#include "btio/btio.h"
 #include "obexd.h"
 #include "log.h"
 #include "obex.h"
 #include "service.h"
 #include "transport.h"
 
-/* Challenge request */
-#define NONCE_TAG 0x00
-#define OPTIONS_TAG 0x01 /* Optional */
-#define REALM_TAG 0x02 /* Optional */
-
-#define NONCE_LEN 16
-
-/* Challenge response */
-#define DIGEST_TAG 0x00
-#define USER_ID_TAG 0x01 /* Optional */
-#define DIGEST_NONCE_TAG 0x02 /* Optional */
-
 static GSList *sessions = NULL;
 
 typedef struct {
@@ -305,53 +293,6 @@ static time_t parse_iso8610(const char *val, int size)
        return time;
 }
 
-static uint8_t *extract_nonce(const uint8_t *buffer, unsigned int hlen)
-{
-       struct auth_header *hdr;
-       uint8_t *nonce = NULL;
-       uint32_t len = 0;
-
-       while (len < hlen) {
-               hdr = (void *) buffer + len;
-
-               switch (hdr->tag) {
-               case NONCE_TAG:
-                       if (hdr->len != NONCE_LEN)
-                               return NULL;
-
-                       nonce = hdr->val;
-                       break;
-               }
-
-               len += hdr->len + sizeof(struct auth_header);
-       }
-
-       return nonce;
-}
-
-static uint8_t *challenge_response(const uint8_t *nonce)
-{
-       GChecksum *md5;
-       uint8_t *result;
-       size_t size;
-
-       result = g_new0(uint8_t, NONCE_LEN);
-
-       md5 = g_checksum_new(G_CHECKSUM_MD5);
-       if (md5 == NULL)
-               return result;
-
-       g_checksum_update(md5, nonce, NONCE_LEN);
-       g_checksum_update(md5, (uint8_t *) ":BlueZ", 6);
-
-       size = NONCE_LEN;
-       g_checksum_get_digest(md5, result, &size);
-
-       g_checksum_free(md5);
-
-       return result;
-}
-
 static void parse_service(struct obex_session *os, GObexPacket *req)
 {
        GObexHeader *hdr;
@@ -377,36 +318,6 @@ probe:
                                                who, who_size);
 }
 
-static void parse_authchal(struct obex_session *session, GObexPacket *req,
-                                                       GObexPacket *rsp)
-{
-       GObexHeader *hdr;
-       const guint8 *data, *nonce = NULL;
-       gsize len;
-       uint8_t challenge[18];
-       struct auth_header *auth = (struct auth_header *) challenge;
-       uint8_t *response;
-
-       hdr = g_obex_packet_get_header(req, G_OBEX_HDR_AUTHCHAL);
-       if (hdr == NULL)
-               return;
-
-       if (!g_obex_header_get_bytes(hdr, &data, &len))
-               return;
-
-       nonce = extract_nonce(data, len);
-       DBG("AUTH CHALLENGE REQUEST");
-
-       response = challenge_response(nonce);
-       auth->tag = DIGEST_TAG;
-       auth->len = NONCE_LEN;
-       memcpy(auth->val, response, NONCE_LEN);
-
-       hdr = g_obex_header_new_bytes(G_OBEX_HDR_AUTHRESP, challenge,
-                                                       sizeof(challenge));
-       g_obex_packet_add_header(rsp, hdr);
-}
-
 static void cmd_connect(GObex *obex, GObexPacket *req, void *user_data)
 {
        struct obex_session *os = user_data;
@@ -438,8 +349,6 @@ static void cmd_connect(GObex *obex, GObexPacket *req, void *user_data)
 
        rsp = g_obex_packet_new(G_OBEX_RSP_SUCCESS, TRUE, G_OBEX_HDR_INVALID);
 
-       parse_authchal(os, req, rsp);
-
        if (os->service->target) {
                hdr = g_obex_header_new_bytes(G_OBEX_HDR_WHO,
                                                os->service->target,
@@ -964,6 +873,10 @@ static void cmd_put(GObex *obex, GObexPacket *req, gpointer user_data)
 
        os->cmd = G_OBEX_OP_PUT;
 
+       /* Set size to unknown if a body header exists */
+       if (g_obex_packet_get_body(req))
+               os->size = OBJECT_SIZE_UNKNOWN;
+
        parse_name(os, req);
        parse_length(os, req);
        parse_time(os, req);
index 8c98c12..d63da93 100644 (file)
 #include <stdlib.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <errno.h>
 
 #include <bluetooth/bluetooth.h>
 #include <glib.h>
 
-#include "plugin.h"
-#include "adapter.h"
-#include "device.h"
-#include "log.h"
-#include "storage.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/log.h"
+#include "src/storage.h"
 
 /*
  * Plugin to handle automatic pairing of devices with reduced user
@@ -157,19 +158,27 @@ static int autopair_init(void)
 {
        /* Initialize the random seed from /dev/urandom */
        unsigned int seed;
-       int fd;
+       int fd, err;
+       ssize_t n;
 
        fd = open("/dev/urandom", O_RDONLY);
-       if (fd >= 0) {
-               ssize_t n;
-
-               n = read(fd, &seed, sizeof(seed));
-               if (n < (ssize_t) sizeof(seed))
-                       seed = time(NULL);
+       if (fd < 0) {
+               err = -errno;
+               error("Failed to open /dev/urandom: %s (%d)", strerror(-err),
+                                                                       -err);
+               return err;
+       }
 
+       n = read(fd, &seed, sizeof(seed));
+       if (n < (ssize_t) sizeof(seed)) {
+               err = (n == -1) ? -errno : -EIO;
+               error("Failed to read %zu bytes from /dev/urandom",
+                                                               sizeof(seed));
                close(fd);
-       } else
-               seed = time(NULL);
+               return err;
+       }
+
+       close(fd);
 
        srand(seed);
 
index ff31290..536ad06 100644 (file)
@@ -22,8 +22,8 @@
 #include <config.h>
 #endif
 
-#include "plugin.h"
-#include "log.h"
+#include "src/plugin.h"
+#include "src/log.h"
 
 static int dummy_init(void)
 {
index 9b4187a..6e20b1f 100644 (file)
 
 #include <glib.h>
 #include <errno.h>
-#include <adapter.h>
 
 #include "lib/uuid.h"
-#include "plugin.h"
-#include "hcid.h"
-#include "log.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/shared/util.h"
+#include "src/log.h"
 #include "attrib/gattrib.h"
 #include "attrib/gatt-service.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
 #include "attrib/att-database.h"
-#include "attrib-server.h"
+#include "src/attrib-server.h"
 
 /* FIXME: Not defined by SIG? UUID128? */
 #define OPCODES_SUPPORTED_UUID          0xA001
@@ -115,8 +115,8 @@ static gboolean register_battery_service(struct btd_adapter *adapter)
        return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
                        /* battery state characteristic */
                        GATT_OPT_CHR_UUID16, BATTERY_STATE_UUID,
-                       GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ |
-                                                       ATT_CHAR_PROPER_NOTIFY,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+                                                       GATT_CHR_PROP_NOTIFY,
                        GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
                                                battery_state_read, adapter,
 
@@ -149,7 +149,7 @@ static void register_termometer_service(struct gatt_example_adapter *adapter,
 
        /* Thermometer: primary service definition */
        bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
-       att_put_u16(THERM_HUMIDITY_SVC_UUID, &atval[0]);
+       put_le16(THERM_HUMIDITY_SVC_UUID, &atval[0]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 2);
 
@@ -157,27 +157,27 @@ static void register_termometer_service(struct gatt_example_adapter *adapter,
 
        /* 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]);
+               put_le16(manuf1[0], &atval[0]);
+               put_le16(manuf1[1], &atval[2]);
+               put_le16(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]);
+               put_le16(manuf2[0], &atval[0]);
+               put_le16(manuf2[1], &atval[2]);
+               put_le16(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]);
+       atval[0] = GATT_CHR_PROP_READ;
+       put_le16(h + 1, &atval[1]);
+       put_le16(TEMPERATURE_UUID, &atval[3]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 5);
 
@@ -192,9 +192,9 @@ static void register_termometer_service(struct gatt_example_adapter *adapter,
        bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
        atval[0] = 0x0E;
        atval[1] = 0xFE;
-       att_put_u16(FMT_CELSIUS_UUID, &atval[2]);
+       put_le16(FMT_CELSIUS_UUID, &atval[2]);
        atval[4] = 0x01;
-       att_put_u16(FMT_OUTSIDE_UUID, &atval[5]);
+       put_le16(FMT_OUTSIDE_UUID, &atval[5]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 7);
 
@@ -207,9 +207,9 @@ static void register_termometer_service(struct gatt_example_adapter *adapter,
 
        /* 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]);
+       atval[0] = GATT_CHR_PROP_READ;
+       put_le16(h + 1, &atval[1]);
+       put_le16(RELATIVE_HUMIDITY_UUID, &atval[3]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 5);
 
@@ -223,9 +223,9 @@ static void register_termometer_service(struct gatt_example_adapter *adapter,
        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]);
+       put_le16(FMT_PERCENT_UUID, &atval[2]);
+       put_le16(BLUETOOTH_SIG_UUID, &atval[4]);
+       put_le16(FMT_OUTSIDE_UUID, &atval[6]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 8);
 
@@ -270,15 +270,15 @@ static void register_manuf1_service(struct gatt_example_adapter *adapter,
 
        /* Secondary Service: Manufacturer Service */
        bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
-       att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
+       put_le16(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]);
+       atval[0] = GATT_CHR_PROP_READ;
+       put_le16(h + 1, &atval[1]);
+       put_le16(MANUFACTURER_NAME_UUID, &atval[3]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 5);
 
@@ -291,9 +291,9 @@ static void register_manuf1_service(struct gatt_example_adapter *adapter,
 
        /* 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]);
+       atval[0] = GATT_CHR_PROP_READ;
+       put_le16(h + 1, &atval[1]);
+       put_le16(MANUFACTURER_SERIAL_UUID, &atval[3]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 5);
 
@@ -334,15 +334,15 @@ static void register_manuf2_service(struct gatt_example_adapter *adapter,
 
        /* Secondary Service: Manufacturer Service */
        bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
-       att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
+       put_le16(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]);
+       atval[0] = GATT_CHR_PROP_READ;
+       put_le16(h + 1, &atval[1]);
+       put_le16(MANUFACTURER_NAME_UUID, &atval[3]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 5);
 
@@ -355,9 +355,9 @@ static void register_manuf2_service(struct gatt_example_adapter *adapter,
 
        /* 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]);
+       atval[0] = GATT_CHR_PROP_READ;
+       put_le16(h + 1, &atval[1]);
+       put_le16(MANUFACTURER_SERIAL_UUID, &atval[3]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 5);
 
@@ -395,15 +395,15 @@ static void register_vendor_service(struct gatt_example_adapter *adapter,
 
        /* Secondary Service: Vendor Specific Service */
        bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
-       att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]);
+       put_le16(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]);
+       atval[0] = GATT_CHR_PROP_READ;
+       put_le16(h + 1, &atval[1]);
+       put_le16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 5);
 
@@ -465,17 +465,17 @@ static void register_weight_service(struct gatt_example_adapter *adapter,
        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]);
+               put_le16(vendor[0], &atval[0]);
+               put_le16(vendor[1], &atval[2]);
+               put_le16(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]);
+       atval[0] = GATT_CHR_PROP_READ;
+       put_le16(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);
@@ -493,9 +493,9 @@ static void register_weight_service(struct gatt_example_adapter *adapter,
        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]);
+       put_le16(FMT_KILOGRAM_UUID, &atval[2]);
+       put_le16(BLUETOOTH_SIG_UUID, &atval[4]);
+       put_le16(FMT_HANGING_UUID, &atval[6]);
        attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 8);
 
index 92a71e0..d4d72d3 100644 (file)
 #include <stdlib.h>
 #include <gdbus/gdbus.h>
 
-#include "dbus-common.h"
-#include "plugin.h"
-#include "adapter.h"
-#include "log.h"
+#include "src/dbus-common.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/log.h"
 
 /* http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm */
 
index 35fdaeb..137d601 100644 (file)
@@ -25,6 +25,7 @@
 #include <config.h>
 #endif
 
+#include <stdlib.h>
 #include <errno.h>
 #include <gdbus/gdbus.h>
 
 #include <bluetooth/hci.h>
 #include <bluetooth/sdp.h>
 
-#include "plugin.h"
-#include "log.h"
-#include "dbus-common.h"
-#include "adapter.h"
-#include "device.h"
-#include "eir.h"
-#include "agent.h"
-#include "hcid.h"
+#include "src/plugin.h"
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/eir.h"
+#include "src/agent.h"
+#include "src/hcid.h"
 
 #define NEARD_NAME "org.neard"
 #define NEARD_PATH "/"
@@ -77,7 +78,7 @@ struct oob_params {
 
 static void free_oob_params(struct oob_params *params)
 {
-       g_slist_free_full(params->services, g_free);
+       g_slist_free_full(params->services, free);
        g_free(params->name);
        g_free(params->hash);
        g_free(params->randomizer);
@@ -326,7 +327,7 @@ static int check_device(struct btd_device *device)
                return -ENOENT;
 
        /* If already paired */
-       if (device_is_paired(device)) {
+       if (device_is_paired(device, BDADDR_BREDR)) {
                DBG("already paired");
                return -EALREADY;
        }
@@ -643,7 +644,8 @@ static void store_params(struct btd_adapter *adapter, struct btd_device *device,
                btd_device_device_set_name(device, params->name);
        }
 
-       /* TODO handle UUIDs? */
+       if (params->services)
+               device_add_eir_uuids(device, params->services);
 
        if (params->hash) {
                btd_adapter_add_remote_oob_data(adapter, &params->address,
@@ -767,42 +769,39 @@ static DBusMessage *request_oob(DBusConnection *conn, DBusMessage *msg,
        if (err < 0)
                return error_reply(msg, -err);
 
-       if (bacmp(&remote.address, BDADDR_ANY) == 0)
-               goto read_local;
-
-       device = btd_adapter_get_device(adapter, &remote.address,
-                                                               BDADDR_BREDR);
+       if (bacmp(&remote.address, BDADDR_ANY) == 0) {
+               if (btd_adapter_get_powered(adapter))
+                       goto read_local;
 
-       err = check_device(device);
-       if (err < 0) {
-               free_oob_params(&remote);
+               goto done;
+       }
 
-               if (err == -EALREADY)
-                       return create_request_oob_reply(adapter, NULL, NULL,
-                                                                       msg);
+       device = btd_adapter_get_device(adapter, &remote.address, BDADDR_BREDR);
 
-               return error_reply(msg, -err);
-       }
+       err = check_device(device);
+       if (err < 0)
+               goto done;
 
        if (!btd_adapter_get_pairable(adapter)) {
-               free_oob_params(&remote);
-
-               return error_reply(msg, ENONET);
+               err = -ENONET;
+               goto done;
        }
 
        store_params(adapter, device, &remote);
 
-       if (!remote.hash || !btd_adapter_get_powered(adapter)) {
-               free_oob_params(&remote);
-               return create_request_oob_reply(adapter, NULL, NULL, msg);
-       }
+       if (remote.hash && btd_adapter_get_powered(adapter))
+               goto read_local;
+done:
+       free_oob_params(&remote);
+
+       if (err < 0 && err != -EALREADY)
+               return error_reply(msg, -err);
+
+       return create_request_oob_reply(adapter, NULL, NULL, msg);
 
 read_local:
        free_oob_params(&remote);
 
-       if (!btd_adapter_get_powered(adapter))
-               return create_request_oob_reply(adapter, NULL, NULL, msg);
-
        err = btd_adapter_read_local_oob_data(adapter);
        if (err < 0)
                return error_reply(msg, -err);
index 0292482..6fb0729 100644 (file)
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
+#include <time.h>
 
 #include <glib.h>
 
 #include "lib/uuid.h"
+#include "lib/mgmt.h"
 #include "src/log.h"
 #include "src/plugin.h"
 #include "src/adapter.h"
 #include "src/device.h"
 #include "src/service.h"
 #include "src/profile.h"
+#include "src/hcid.h"
 
 #define CONTROL_CONNECT_TIMEOUT 2
 #define SOURCE_RETRY_TIMEOUT 2
 #define SOURCE_RETRIES 1
 #define SINK_RETRIES SOURCE_RETRIES
 
+/* Tracking of remote services to be auto-reconnected upon link loss */
+
+#define RECONNECT_GIVE_UP (3 * 60)
+#define RECONNECT_TIMEOUT 1
+
+struct reconnect_data {
+       struct btd_device *dev;
+       bool reconnect;
+       GSList *services;
+       guint timer;
+       bool active;
+       time_t start;
+       int timeout;
+};
+
+static const char *default_reconnect[] = {
+                       HSP_AG_UUID, HFP_AG_UUID, A2DP_SOURCE_UUID, NULL };
+static char **reconnect_uuids = NULL;
+static GSList *reconnects = NULL;
+
 static unsigned int service_id = 0;
 static GSList *devices = NULL;
 
@@ -402,12 +425,114 @@ static void target_cb(struct btd_service *service,
        }
 }
 
+static void reconnect_reset(struct reconnect_data *reconnect)
+{
+       reconnect->start = 0;
+       reconnect->timeout = 1;
+
+       if (reconnect->timer > 0) {
+               g_source_remove(reconnect->timer);
+               reconnect->timer = 0;
+       }
+}
+
+static bool reconnect_match(const char *uuid)
+{
+       char **str;
+
+       if (!reconnect_uuids)
+               return false;
+
+       for (str = reconnect_uuids; *str; str++) {
+               if (!bt_uuid_strcmp(uuid, *str))
+                       return true;
+       }
+
+       return false;
+}
+
+static struct reconnect_data *reconnect_find(struct btd_device *dev)
+{
+       GSList *l;
+
+       for (l = reconnects; l; l = g_slist_next(l)) {
+               struct reconnect_data *reconnect = l->data;
+
+               if (reconnect->dev == dev)
+                       return reconnect;
+       }
+
+       return NULL;
+}
+
+static struct reconnect_data *reconnect_add(struct btd_service *service)
+{
+       struct btd_device *dev = btd_service_get_device(service);
+       struct reconnect_data *reconnect;
+
+       reconnect = reconnect_find(dev);
+       if (!reconnect) {
+               reconnect = g_new0(struct reconnect_data, 1);
+               reconnect->dev = dev;
+               reconnects = g_slist_append(reconnects, reconnect);
+       }
+
+       if (g_slist_find(reconnect->services, service))
+               return reconnect;
+
+       reconnect->services = g_slist_append(reconnect->services,
+                                               btd_service_ref(service));
+
+       return reconnect;
+}
+
+static void reconnect_destroy(gpointer data)
+{
+       struct reconnect_data *reconnect = data;
+
+       if (reconnect->timer > 0)
+               g_source_remove(reconnect->timer);
+
+       g_slist_free_full(reconnect->services,
+                                       (GDestroyNotify) btd_service_unref);
+       g_free(reconnect);
+}
+
+static void reconnect_remove(struct btd_service *service)
+{
+       struct btd_device *dev = btd_service_get_device(service);
+       struct reconnect_data *reconnect;
+       GSList *l;
+
+       reconnect = reconnect_find(dev);
+       if (!reconnect)
+               return;
+
+       l = g_slist_find(reconnect->services, service);
+       if (!l)
+               return;
+
+       reconnect->services = g_slist_delete_link(reconnect->services, l);
+       btd_service_unref(service);
+
+       if (reconnect->services)
+               return;
+
+       reconnects = g_slist_remove(reconnects, reconnect);
+
+       if (reconnect->timer > 0)
+               g_source_remove(reconnect->timer);
+
+       g_free(reconnect);
+}
+
 static void service_cb(struct btd_service *service,
                                                btd_service_state_t old_state,
                                                btd_service_state_t new_state,
                                                void *user_data)
 {
        struct btd_profile *profile = btd_service_get_profile(service);
+       struct reconnect_data *reconnect;
 
        if (g_str_equal(profile->remote_uuid, A2DP_SINK_UUID))
                sink_cb(service, old_state, new_state);
@@ -417,17 +542,178 @@ static void service_cb(struct btd_service *service,
                controller_cb(service, old_state, new_state);
        else if (g_str_equal(profile->remote_uuid, AVRCP_TARGET_UUID))
                target_cb(service, old_state, new_state);
+
+       /*
+        * Return if the reconnection feature is not enabled (all
+        * subsequent code in this function is about that).
+        */
+       if (!reconnect_uuids || !reconnect_uuids[0])
+               return;
+
+       /*
+        * We're only interested in reconnecting profiles which have set
+        * auto_connect to true.
+        */
+       if (!profile->auto_connect)
+               return;
+
+       /*
+        * If the service went away remove it from the reconnection
+        * tracking. The function will remove the entire tracking data
+        * if this was the last service for the device.
+        */
+       if (new_state == BTD_SERVICE_STATE_UNAVAILABLE) {
+               reconnect_remove(service);
+               return;
+       }
+
+       if (new_state != BTD_SERVICE_STATE_CONNECTED)
+               return;
+
+       /*
+        * Add an entry to track reconnections. The function will return
+        * an existing entry if there is one.
+        */
+       reconnect = reconnect_add(service);
+
+       reconnect->active = false;
+       reconnect_reset(reconnect);
+
+       /*
+        * Should this device be reconnected? A matching UUID might not
+        * be the first profile that's connected so we might have an
+        * entry but with the reconnect flag set to false.
+        */
+       if (!reconnect->reconnect)
+               reconnect->reconnect = reconnect_match(profile->remote_uuid);
+
+       DBG("Added %s reconnect %u", profile->name, reconnect->reconnect);
+}
+
+static gboolean reconnect_timeout(gpointer data)
+{
+       struct reconnect_data *reconnect = data;
+       int err;
+
+       DBG("Reconnecting profiles");
+
+       /* Mark the GSource as invalid */
+       reconnect->timer = 0;
+
+       /* Increase timeout for the next attempt if this * one fails. */
+       reconnect->timeout *= 2;
+
+       err = btd_device_connect_services(reconnect->dev, reconnect->services);
+       if (err < 0) {
+               error("Reconnecting services failed: %s (%d)",
+                                                       strerror(-err), -err);
+               reconnect_reset(reconnect);
+               return FALSE;
+       }
+
+       reconnect->active = true;
+
+       if (!reconnect->start)
+               reconnect->start = time(NULL);
+
+       return FALSE;
+}
+
+static void disconnect_cb(struct btd_device *dev, uint8_t reason)
+{
+       struct reconnect_data *reconnect;
+
+       DBG("reason %u", reason);
+
+       if (reason != MGMT_DEV_DISCONN_TIMEOUT)
+               return;
+
+       reconnect = reconnect_find(dev);
+       if (!reconnect || !reconnect->reconnect)
+               return;
+
+       DBG("Device %s identified for auto-reconnection",
+                                                       device_get_path(dev));
+
+       reconnect->timer = g_timeout_add_seconds(reconnect->timeout,
+                                                       reconnect_timeout,
+                                                       reconnect);
+}
+
+static void conn_fail_cb(struct btd_device *dev, uint8_t status)
+{
+       struct reconnect_data *reconnect;
+       time_t duration;
+
+       DBG("status %u", status);
+
+       reconnect = reconnect_find(dev);
+       if (!reconnect || !reconnect->reconnect)
+               return;
+
+       if (!reconnect->active)
+               return;
+
+       reconnect->active = false;
+
+       /* Give up if we were powered off */
+       if (status == MGMT_STATUS_NOT_POWERED) {
+               reconnect_reset(reconnect);
+               return;
+       }
+
+       /* Give up if we've tried for too long */
+       duration = time(NULL) - reconnect->start;
+       if (duration + reconnect->timeout >= RECONNECT_GIVE_UP) {
+               reconnect_reset(reconnect);
+               return;
+       }
+
+       reconnect->timer = g_timeout_add_seconds(reconnect->timeout,
+                                                       reconnect_timeout,
+                                                       reconnect);
 }
 
 static int policy_init(void)
 {
+       GError *gerr = NULL;
+       GKeyFile *conf;
+
        service_id = btd_service_add_state_cb(service_cb, NULL);
 
+       conf = btd_get_main_conf();
+       if (!conf) {
+               reconnect_uuids = g_strdupv((char **) default_reconnect);
+               goto add_cb;
+       }
+
+       reconnect_uuids = g_key_file_get_string_list(conf, "Policy",
+                                                       "ReconnectUUIDs",
+                                                       NULL, &gerr);
+       if (gerr) {
+               g_error_free(gerr);
+               reconnect_uuids = g_strdupv((char **) default_reconnect);
+               goto add_cb;
+       }
+add_cb:
+       if (reconnect_uuids && reconnect_uuids[0]) {
+               btd_add_disconnect_cb(disconnect_cb);
+               btd_add_conn_fail_cb(conn_fail_cb);
+       }
+
        return 0;
 }
 
 static void policy_exit(void)
 {
+       btd_remove_disconnect_cb(disconnect_cb);
+       btd_remove_conn_fail_cb(conn_fail_cb);
+
+       if (reconnect_uuids)
+               g_strfreev(reconnect_uuids);
+
+       g_slist_free_full(reconnects, reconnect_destroy);
+
        g_slist_free_full(devices, policy_remove);
 
        btd_service_remove_state_cb(service_id);
index 45fa170..1b7bb30 100644 (file)
@@ -125,8 +125,25 @@ static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
        return ret;
 }
 
-static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
-                                                       gpointer user_data)
+static uint8_t calc_leds_bitmap(int number)
+{
+       uint8_t bitmap = 0;
+
+       /* TODO we could support up to 10 (1 + 2 + 3 + 4) */
+       if (number > 7)
+               return bitmap;
+
+       if (number > 4) {
+               bitmap |= 0x10;
+               number -= 4;
+       }
+
+       bitmap |= 0x01 << number;
+
+       return bitmap;
+}
+
+static void set_leds_hidraw(int fd, uint8_t leds_bitmap)
 {
        /*
         * the total time the led is active (0xff means forever)
@@ -147,36 +164,37 @@ static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
                0xff, 0x27, 0x10, 0x00, 0x32, /* LED_1 */
                0x00, 0x00, 0x00, 0x00, 0x00,
        };
-       int number = GPOINTER_TO_INT(user_data);
        int ret;
-       int fd;
-
-       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
-               return FALSE;
-
-       DBG("number %d", number);
-
-       /* TODO we could support up to 10 (1 + 2 + 3 + 4) */
-       if (number > 7)
-               return FALSE;
-
-       if (number > 4) {
-               leds_report[10] |= 0x10;
-               number -= 4;
-       }
-
-       leds_report[10] |= 0x01 << number;
 
-       fd = g_io_channel_unix_get_fd(channel);
+       leds_report[10] = leds_bitmap;
 
        ret = write(fd, leds_report, sizeof(leds_report));
        if (ret == sizeof(leds_report))
-               return FALSE;
+               return;
 
        if (ret < 0)
                error("sixaxis: failed to set LEDS (%s)", strerror(errno));
        else
                error("sixaxis: failed to set LEDS (%d bytes written)", ret);
+}
+
+static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       int number = GPOINTER_TO_INT(user_data);
+       uint8_t bitmap;
+       int fd;
+
+       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
+               return FALSE;
+
+       DBG("number %d", number);
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       bitmap = calc_leds_bitmap(number);
+       if (bitmap != 0)
+               set_leds_hidraw(fd, bitmap);
 
        return FALSE;
 }
@@ -197,7 +215,8 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter)
        /* This can happen if controller was plugged while already connected
         * eg. to charge up battery.
         * Don't set LEDs in that case, hence return false */
-       device = btd_adapter_find_device(adapter, &device_bdaddr);
+       device = btd_adapter_find_device(adapter, &device_bdaddr,
+                                                       BDADDR_BREDR);
        if (device && btd_device_is_connected(device))
                return false;
 
@@ -228,7 +247,6 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter)
        btd_device_set_pnpid(device, devices[index].source, devices[index].vid,
                                devices[index].pid, devices[index].version);
        btd_device_set_temporary(device, FALSE);
-       btd_device_set_trusted(device, TRUE);
 
        return true;
 }
@@ -301,7 +319,7 @@ static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
        struct udev_device *hid_parent;
        uint16_t vid, pid;
        const char *hid_id;
-       int i;
+       guint i;
 
        hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
                                                                "hid", NULL);
@@ -313,7 +331,7 @@ static int get_supported_device(struct udev_device *udevice, uint16_t *bus)
        if (sscanf(hid_id, "%hx:%hx:%hx", bus, &vid, &pid) != 3)
                return -1;
 
-       for (i = 0; G_N_ELEMENTS(devices); i++) {
+       for (i = 0; i < G_N_ELEMENTS(devices); i++) {
                if (devices[i].vid == vid && devices[i].pid == pid)
                        return i;
        }
index 96a6569..bd8820e 100644 (file)
 #include <bluetooth/bluetooth.h>
 #include <glib.h>
 
-#include "plugin.h"
-#include "adapter.h"
-#include "device.h"
-#include "log.h"
-#include "storage.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/log.h"
+#include "src/storage.h"
 
 /*
  * Nintendo Wii Remote devices require the bdaddr of the host as pin input for
@@ -90,7 +90,6 @@ static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device,
        product = btd_device_get_product(device);
 
        device_get_name(device, name, sizeof(name));
-       name[sizeof(name) - 1] = 0;
 
        for (i = 0; i < G_N_ELEMENTS(wii_ids); ++i) {
                if (vendor == wii_ids[i][0] && product == wii_ids[i][1])
index 4565470..1612d6c 100644 (file)
 #include <stdlib.h>
 
 #include "lib/uuid.h"
-#include "plugin.h"
-#include "dbus-common.h"
+#include "src/plugin.h"
+#include "src/dbus-common.h"
 #include "attrib/att.h"
-#include "adapter.h"
-#include "device.h"
+#include "src/adapter.h"
+#include "src/device.h"
 #include "attrib/att-database.h"
-#include "log.h"
+#include "src/log.h"
 #include "attrib/gatt-service.h"
 #include "attrib/gattrib.h"
-#include "attrib-server.h"
+#include "src/attrib-server.h"
 #include "attrib/gatt.h"
-#include "profile.h"
-#include "error.h"
-#include "textfile.h"
-#include "attio.h"
+#include "src/profile.h"
+#include "src/error.h"
+#include "src/textfile.h"
+#include "src/attio.h"
 
 #define PHONE_ALERT_STATUS_SVC_UUID    0x180E
 #define ALERT_NOTIF_SVC_UUID           0x1811
@@ -363,6 +363,20 @@ end:
        return result;
 }
 
+static void destroy_notify_callback(guint8 status, const guint8 *pdu, guint16 len,
+                                                       gpointer user_data)
+{
+       struct notify_callback *cb = user_data;
+
+       DBG("status=%#x", status);
+
+       btd_device_remove_attio_callback(cb->device, cb->id);
+       btd_device_unref(cb->device);
+       g_free(cb->notify_data->value);
+       g_free(cb->notify_data);
+       g_free(cb);
+}
+
 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
 {
        struct notify_callback *cb = user_data;
@@ -398,7 +412,9 @@ static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
                                        al_adapter->hnd_value[type],
                                        al_adapter->hnd_ccc[type]);
 
-       g_attrib_send(attrib, 0, pdu, len, NULL, NULL, NULL);
+       g_attrib_send(attrib, 0, pdu, len, destroy_notify_callback, cb, NULL);
+
+       return;
 
 end:
        btd_device_remove_attio_callback(cb->device, cb->id);
@@ -790,8 +806,8 @@ static void register_phone_alert_service(struct alert_adapter *al_adapter)
        gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
                        /* Alert Status characteristic */
                        GATT_OPT_CHR_UUID16, ALERT_STATUS_CHR_UUID,
-                       GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ |
-                                                       ATT_CHAR_PROPER_NOTIFY,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+                                                       GATT_CHR_PROP_NOTIFY,
                        GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
                        alert_status_read, al_adapter->adapter,
                        GATT_OPT_CCC_GET_HANDLE,
@@ -800,13 +816,13 @@ static void register_phone_alert_service(struct alert_adapter *al_adapter)
                        &al_adapter->hnd_value[NOTIFY_ALERT_STATUS],
                        /* Ringer Control Point characteristic */
                        GATT_OPT_CHR_UUID16, RINGER_CP_CHR_UUID,
-                       GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_WRITE_WITHOUT_RESP,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE_WITHOUT_RESP,
                        GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
                        ringer_cp_write, NULL,
                        /* Ringer Setting characteristic */
                        GATT_OPT_CHR_UUID16, RINGER_SETTING_CHR_UUID,
-                       GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ |
-                                                       ATT_CHAR_PROPER_NOTIFY,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+                                                       GATT_CHR_PROP_NOTIFY,
                        GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
                        ringer_setting_read, al_adapter->adapter,
                        GATT_OPT_CCC_GET_HANDLE,
@@ -893,35 +909,35 @@ static void register_alert_notif_service(struct alert_adapter *al_adapter)
        gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
                        /* Supported New Alert Category */
                        GATT_OPT_CHR_UUID16, SUPP_NEW_ALERT_CAT_CHR_UUID,
-                       GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
                        GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
                        supp_new_alert_cat_read, al_adapter->adapter,
                        GATT_OPT_CHR_VALUE_GET_HANDLE,
                        &al_adapter->supp_new_alert_cat_handle,
                        /* New Alert */
                        GATT_OPT_CHR_UUID16, NEW_ALERT_CHR_UUID,
-                       GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_NOTIFY,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
                        GATT_OPT_CCC_GET_HANDLE,
                        &al_adapter->hnd_ccc[NOTIFY_NEW_ALERT],
                        GATT_OPT_CHR_VALUE_GET_HANDLE,
                        &al_adapter->hnd_value[NOTIFY_NEW_ALERT],
                        /* Supported Unread Alert Category */
                        GATT_OPT_CHR_UUID16, SUPP_UNREAD_ALERT_CAT_CHR_UUID,
-                       GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
                        GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
                        supp_unread_alert_cat_read, al_adapter->adapter,
                        GATT_OPT_CHR_VALUE_GET_HANDLE,
                        &al_adapter->supp_unread_alert_cat_handle,
                        /* Unread Alert Status */
                        GATT_OPT_CHR_UUID16, UNREAD_ALERT_CHR_UUID,
-                       GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_NOTIFY,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
                        GATT_OPT_CCC_GET_HANDLE,
                        &al_adapter->hnd_ccc[NOTIFY_UNREAD_ALERT],
                        GATT_OPT_CHR_VALUE_GET_HANDLE,
                        &al_adapter->hnd_value[NOTIFY_UNREAD_ALERT],
                        /* Alert Notification Control Point */
                        GATT_OPT_CHR_UUID16, ALERT_NOTIF_CP_CHR_UUID,
-                       GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_WRITE,
+                       GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE,
                        GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
                        alert_notif_cp_write, NULL,
                        GATT_OPT_INVALID);
index 29a1593..cabdd66 100644 (file)
 #include "src/device.h"
 #include "src/profile.h"
 #include "src/service.h"
+#include "src/log.h"
+#include "src/sdpd.h"
 
-#include "log.h"
 #include "avdtp.h"
 #include "sink.h"
 #include "source.h"
 #include "a2dp.h"
 #include "a2dp-codecs.h"
-#include "sdpd.h"
 #include "media.h"
 
 /* The duration that streams without users are allowed to stay in
index 6669ddc..74d3512 100644 (file)
 #include <bluetooth/l2cap.h>
 
 #include <glib.h>
-#include <btio/btio.h>
 
+#include "btio/btio.h"
 #include "lib/uuid.h"
 #include "src/adapter.h"
 #include "src/device.h"
 
-#include "log.h"
-#include "error.h"
-#include "uinput.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/uinput.h"
+
 #include "avctp.h"
 #include "avrcp.h"
 
@@ -237,25 +238,53 @@ static struct {
        { "ROOT MENU",          AVC_ROOT_MENU,          KEY_MENU },
        { "CONTENTS MENU",      AVC_CONTENTS_MENU,      KEY_PROGRAM },
        { "FAVORITE MENU",      AVC_FAVORITE_MENU,      KEY_FAVORITES },
+       { "EXIT",               AVC_EXIT,               KEY_EXIT },
+       { "ON DEMAND MENU",     AVC_ON_DEMAND_MENU,     KEY_MENU },
+       { "APPS MENU",          AVC_APPS_MENU,          KEY_MENU },
+       { "0",                  AVC_0,                  KEY_0 },
+       { "1",                  AVC_1,                  KEY_1 },
+       { "2",                  AVC_2,                  KEY_2 },
+       { "3",                  AVC_3,                  KEY_3 },
+       { "4",                  AVC_4,                  KEY_4 },
+       { "5",                  AVC_5,                  KEY_5 },
+       { "6",                  AVC_6,                  KEY_6 },
+       { "7",                  AVC_7,                  KEY_7 },
+       { "8",                  AVC_8,                  KEY_8 },
+       { "9",                  AVC_9,                  KEY_9 },
+       { "DOT",                AVC_DOT,                KEY_DOT },
        { "ENTER",              AVC_ENTER,              KEY_ENTER },
        { "CHANNEL UP",         AVC_CHANNEL_UP,         KEY_CHANNELUP },
        { "CHANNEL DOWN",       AVC_CHANNEL_DOWN,       KEY_CHANNELDOWN },
+       { "CHANNEL PREVIOUS",   AVC_CHANNEL_PREVIOUS,   KEY_LAST },
        { "INPUT SELECT",       AVC_INPUT_SELECT,       KEY_CONFIG },
+       { "INFO",               AVC_INFO,               KEY_INFO },
        { "HELP",               AVC_HELP,               KEY_HELP },
        { "POWER",              AVC_POWER,              KEY_POWER2 },
        { "VOLUME UP",          AVC_VOLUME_UP,          KEY_VOLUMEUP },
        { "VOLUME DOWN",        AVC_VOLUME_DOWN,        KEY_VOLUMEDOWN },
+       { "MUTE",               AVC_MUTE,               KEY_MUTE },
        { "PLAY",               AVC_PLAY,               KEY_PLAYCD },
        { "STOP",               AVC_STOP,               KEY_STOPCD },
        { "PAUSE",              AVC_PAUSE,              KEY_PAUSECD },
        { "FORWARD",            AVC_FORWARD,            KEY_NEXTSONG },
        { "BACKWARD",           AVC_BACKWARD,           KEY_PREVIOUSSONG },
+       { "RECORD",             AVC_RECORD,             KEY_RECORD },
        { "REWIND",             AVC_REWIND,             KEY_REWIND },
        { "FAST FORWARD",       AVC_FAST_FORWARD,       KEY_FASTFORWARD },
+       { "LIST",               AVC_LIST,               KEY_LIST },
        { "F1",                 AVC_F1,                 KEY_F1 },
        { "F2",                 AVC_F2,                 KEY_F2 },
        { "F3",                 AVC_F3,                 KEY_F3 },
        { "F4",                 AVC_F4,                 KEY_F4 },
+       { "F5",                 AVC_F5,                 KEY_F5 },
+       { "F6",                 AVC_F6,                 KEY_F6 },
+       { "F7",                 AVC_F7,                 KEY_F7 },
+       { "F8",                 AVC_F8,                 KEY_F8 },
+       { "F9",                 AVC_F9,                 KEY_F9 },
+       { "RED",                AVC_RED,                KEY_RED },
+       { "GREEN",              AVC_GREEN,              KEY_GREEN },
+       { "BLUE",               AVC_BLUE,               KEY_BLUE },
+       { "YELLOW",             AVC_YELLOW,             KEY_YELLOW },
        { NULL }
 };
 
@@ -313,7 +342,7 @@ static size_t handle_panel_passthrough(struct avctp *session,
 
        if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) {
                *code = AVC_CTYPE_REJECTED;
-               return 0;
+               return operand_count;
        }
 
        if (operand_count == 0)
@@ -379,7 +408,7 @@ static size_t handle_panel_passthrough(struct avctp *session,
                DBG("AV/C: unknown button 0x%02X %s",
                                                operands[0] & 0x7F, status);
                *code = AVC_CTYPE_NOT_IMPLEMENTED;
-               return 0;
+               return operand_count;
        }
 
 done:
@@ -903,8 +932,7 @@ failed:
        return FALSE;
 }
 
-static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
-                               gpointer data)
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
 {
        struct avctp *session = data;
        struct avctp_channel *control = session->control;
@@ -924,24 +952,24 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
        if (ret <= 0)
                goto failed;
 
-       if ((unsigned int) ret < sizeof(struct avctp_header)) {
+       if (ret < AVCTP_HEADER_LENGTH) {
                error("Too small AVCTP packet");
                goto failed;
        }
 
        avctp = (struct avctp_header *) buf;
 
-       ret -= sizeof(struct avctp_header);
-       if ((unsigned int) ret < sizeof(struct avc_header)) {
-               error("Too small AVCTP packet");
+       ret -= AVCTP_HEADER_LENGTH;
+       if (ret < AVC_HEADER_LENGTH) {
+               error("Too small AVC packet");
                goto failed;
        }
 
-       avc = (struct avc_header *) (buf + sizeof(struct avctp_header));
+       avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH);
 
-       ret -= sizeof(struct avc_header);
+       ret -= AVC_HEADER_LENGTH;
 
-       operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header);
+       operands = (uint8_t *) avc + AVC_HEADER_LENGTH;
        operand_count = ret;
 
        if (avctp->cr == AVCTP_RESPONSE) {
@@ -1371,7 +1399,8 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
 
        DBG("AVCTP: incoming connect from %s", address);
 
-       device = btd_adapter_find_device(adapter_find(&src), &dst);
+       device = btd_adapter_find_device(adapter_find(&src), &dst,
+                                                               BDADDR_BREDR);
        if (!device)
                return;
 
@@ -1582,36 +1611,16 @@ int avctp_send_browsing_req(struct avctp *session,
        return 0;
 }
 
-static char *op2str(uint8_t op)
-{
-       switch (op & 0x7f) {
-       case AVC_VOLUME_UP:
-               return "VOLUME UP";
-       case AVC_VOLUME_DOWN:
-               return "VOLUME DOWN";
-       case AVC_MUTE:
-               return "MUTE";
-       case AVC_PLAY:
-               return "PLAY";
-       case AVC_STOP:
-               return "STOP";
-       case AVC_PAUSE:
-               return "PAUSE";
-       case AVC_RECORD:
-               return "RECORD";
-       case AVC_REWIND:
-               return "REWIND";
-       case AVC_FAST_FORWARD:
-               return "FAST FORWARD";
-       case AVC_EJECT:
-               return "EJECT";
-       case AVC_FORWARD:
-               return "FORWARD";
-       case AVC_BACKWARD:
-               return "BACKWARD";
-       default:
-               return "UNKNOWN";
+static const char *op2str(uint8_t op)
+{
+       int i;
+
+       for (i = 0; key_map[i].name != NULL; i++) {
+               if ((op & 0x7F) == key_map[i].avc)
+                       return key_map[i].name;
        }
+
+       return "UNKNOWN";
 }
 
 static int avctp_passthrough_press(struct avctp *session, uint8_t op)
index f9c665e..05fceb4 100644 (file)
 #define AVC_ROOT_MENU                  0x09
 #define AVC_CONTENTS_MENU              0x0b
 #define AVC_FAVORITE_MENU              0x0c
+#define AVC_EXIT                       0x0d
+#define AVC_ON_DEMAND_MENU             0x0e
+#define AVC_APPS_MENU                  0x0f
+#define AVC_0                          0x20
+#define AVC_1                          0x21
+#define AVC_2                          0x22
+#define AVC_3                          0x23
+#define AVC_4                          0x24
+#define AVC_5                          0x25
+#define AVC_6                          0x26
+#define AVC_7                          0x27
+#define AVC_8                          0x28
+#define AVC_9                          0x29
+#define AVC_DOT                                0x2a
 #define AVC_ENTER                      0x2b
 #define AVC_CHANNEL_UP                 0x30
 #define AVC_CHANNEL_DOWN               0x31
+#define AVC_CHANNEL_PREVIOUS           0x32
 #define AVC_INPUT_SELECT               0x34
+#define AVC_INFO                       0x35
 #define AVC_HELP                       0x36
+#define AVC_PAGE_UP                    0x37
+#define AVC_PAGE_DOWN                  0x38
+#define AVC_LOCK                       0x3a
 #define AVC_POWER                      0x40
 #define AVC_VOLUME_UP                  0x41
 #define AVC_VOLUME_DOWN                        0x42
 #define AVC_EJECT                      0x4a
 #define AVC_FORWARD                    0x4b
 #define AVC_BACKWARD                   0x4c
+#define AVC_LIST                       0x4d
 #define AVC_F1                         0x71
 #define AVC_F2                         0x72
 #define AVC_F3                         0x73
 #define AVC_F4                         0x74
+#define AVC_F5                         0x75
+#define AVC_F6                         0x76
+#define AVC_F7                         0x77
+#define AVC_F8                         0x78
+#define AVC_F9                         0x79
+#define AVC_RED                                0x7a
+#define AVC_GREEN                      0x7b
+#define AVC_BLUE                       0x7c
+#define AVC_YELLOW                     0x7c
 
 struct avctp;
 
index f866b39..8a7d1c0 100644 (file)
 #include <bluetooth/sdp_lib.h>
 
 #include <glib.h>
-#include <btio/btio.h>
 
-#include "log.h"
+#include "src/log.h"
 
+#include "btio/btio.h"
 #include "lib/uuid.h"
 #include "src/adapter.h"
 #include "src/device.h"
@@ -2467,7 +2467,8 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
 
        DBG("AVDTP: incoming connect from %s", address);
 
-       device = btd_adapter_find_device(adapter_find(&src), &dst);
+       device = btd_adapter_find_device(adapter_find(&src), &dst,
+                                                               BDADDR_BREDR);
        if (!device)
                goto drop;
 
index cd027c6..da2a746 100644 (file)
 #include "src/profile.h"
 #include "src/service.h"
 
-#include "log.h"
-#include "error.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/sdpd.h"
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+
 #include "avctp.h"
 #include "avrcp.h"
-#include "sdpd.h"
-#include "dbus-common.h"
 #include "control.h"
 #include "player.h"
 #include "transport.h"
@@ -281,72 +283,72 @@ static sdp_record_t *avrcp_ct_record(void)
                return NULL;
 
        sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
-       root = sdp_list_append(0, &root_uuid);
+       root = sdp_list_append(NULL, &root_uuid);
        sdp_set_browse_groups(record, root);
 
        /* Service Class ID List */
        sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
-       svclass_id = sdp_list_append(0, &avrct);
+       svclass_id = sdp_list_append(NULL, &avrct);
        sdp_uuid16_create(&avrctr, AV_REMOTE_CONTROLLER_SVCLASS_ID);
        svclass_id = sdp_list_append(svclass_id, &avrctr);
        sdp_set_service_classes(record, svclass_id);
 
        /* Protocol Descriptor List */
        sdp_uuid16_create(&l2cap, L2CAP_UUID);
-       proto[0] = sdp_list_append(0, &l2cap);
+       proto[0] = sdp_list_append(NULL, &l2cap);
        psm[0] = sdp_data_alloc(SDP_UINT16, &lp);
        proto[0] = sdp_list_append(proto[0], psm[0]);
-       apseq = sdp_list_append(0, proto[0]);
+       apseq = sdp_list_append(NULL, proto[0]);
 
        sdp_uuid16_create(&avctp, AVCTP_UUID);
-       proto[1] = sdp_list_append(0, &avctp);
+       proto[1] = sdp_list_append(NULL, &avctp);
        version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
        proto[1] = sdp_list_append(proto[1], version);
        apseq = sdp_list_append(apseq, proto[1]);
 
-       aproto = sdp_list_append(0, apseq);
+       aproto = sdp_list_append(NULL, apseq);
        sdp_set_access_protos(record, aproto);
 
        /* Additional Protocol Descriptor List */
        sdp_uuid16_create(&l2cap, L2CAP_UUID);
-       proto1[0] = sdp_list_append(0, &l2cap);
+       proto1[0] = sdp_list_append(NULL, &l2cap);
        psm[1] = sdp_data_alloc(SDP_UINT16, &ap);
        proto1[0] = sdp_list_append(proto1[0], psm[1]);
-       apseq1 = sdp_list_append(0, proto1[0]);
+       apseq1 = sdp_list_append(NULL, proto1[0]);
 
        sdp_uuid16_create(&avctp, AVCTP_UUID);
-       proto1[1] = sdp_list_append(0, &avctp);
+       proto1[1] = sdp_list_append(NULL, &avctp);
        proto1[1] = sdp_list_append(proto1[1], version);
        apseq1 = sdp_list_append(apseq1, proto1[1]);
 
-       aproto1 = sdp_list_append(0, apseq1);
+       aproto1 = sdp_list_append(NULL, apseq1);
        sdp_set_add_access_protos(record, aproto1);
 
        /* 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]);
+       pfseq = sdp_list_append(NULL, &profile[0]);
        sdp_set_profile_descs(record, pfseq);
 
        features = sdp_data_alloc(SDP_UINT16, &feat);
        sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
 
-       sdp_set_info_attr(record, "AVRCP CT", 0, 0);
+       sdp_set_info_attr(record, "AVRCP CT", NULL, NULL);
 
        free(psm[0]);
        free(psm[1]);
        free(version);
-       sdp_list_free(proto[0], 0);
-       sdp_list_free(proto[1], 0);
-       sdp_list_free(apseq, 0);
-       sdp_list_free(proto1[0], 0);
-       sdp_list_free(proto1[1], 0);
-       sdp_list_free(aproto1, 0);
-       sdp_list_free(apseq1, 0);
-       sdp_list_free(pfseq, 0);
-       sdp_list_free(aproto, 0);
-       sdp_list_free(root, 0);
-       sdp_list_free(svclass_id, 0);
+       sdp_list_free(proto[0], NULL);
+       sdp_list_free(proto[1], NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(proto1[0], NULL);
+       sdp_list_free(proto1[1], NULL);
+       sdp_list_free(aproto1, NULL);
+       sdp_list_free(apseq1, NULL);
+       sdp_list_free(pfseq, NULL);
+       sdp_list_free(aproto, NULL);
+       sdp_list_free(root, NULL);
+       sdp_list_free(svclass_id, NULL);
 
        return record;
 }
@@ -375,67 +377,67 @@ static sdp_record_t *avrcp_tg_record(void)
                return NULL;
 
        sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
-       root = sdp_list_append(0, &root_uuid);
+       root = sdp_list_append(NULL, &root_uuid);
        sdp_set_browse_groups(record, root);
 
        /* Service Class ID List */
        sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
-       svclass_id = sdp_list_append(0, &avrtg);
+       svclass_id = sdp_list_append(NULL, &avrtg);
        sdp_set_service_classes(record, svclass_id);
 
        /* Protocol Descriptor List */
        sdp_uuid16_create(&l2cap, L2CAP_UUID);
-       proto_control[0] = sdp_list_append(0, &l2cap);
+       proto_control[0] = sdp_list_append(NULL, &l2cap);
        psm_control = sdp_data_alloc(SDP_UINT16, &lp);
        proto_control[0] = sdp_list_append(proto_control[0], psm_control);
-       apseq = sdp_list_append(0, proto_control[0]);
+       apseq = sdp_list_append(NULL, proto_control[0]);
 
        sdp_uuid16_create(&avctp, AVCTP_UUID);
-       proto_control[1] = sdp_list_append(0, &avctp);
+       proto_control[1] = sdp_list_append(NULL, &avctp);
        version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
        proto_control[1] = sdp_list_append(proto_control[1], version);
        apseq = sdp_list_append(apseq, proto_control[1]);
 
-       aproto_control = sdp_list_append(0, apseq);
+       aproto_control = sdp_list_append(NULL, apseq);
        sdp_set_access_protos(record, aproto_control);
-       proto_browsing[0] = sdp_list_append(0, &l2cap);
+       proto_browsing[0] = sdp_list_append(NULL, &l2cap);
        psm_browsing = sdp_data_alloc(SDP_UINT16, &lp_browsing);
        proto_browsing[0] = sdp_list_append(proto_browsing[0], psm_browsing);
-       apseq_browsing = sdp_list_append(0, proto_browsing[0]);
+       apseq_browsing = sdp_list_append(NULL, proto_browsing[0]);
 
-       proto_browsing[1] = sdp_list_append(0, &avctp);
+       proto_browsing[1] = sdp_list_append(NULL, &avctp);
        proto_browsing[1] = sdp_list_append(proto_browsing[1], version);
        apseq_browsing = sdp_list_append(apseq_browsing, proto_browsing[1]);
 
-       aproto_browsing = sdp_list_append(0, apseq_browsing);
+       aproto_browsing = sdp_list_append(NULL, apseq_browsing);
        sdp_set_add_access_protos(record, aproto_browsing);
 
        /* 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]);
+       pfseq = sdp_list_append(NULL, &profile[0]);
        sdp_set_profile_descs(record, pfseq);
 
        features = sdp_data_alloc(SDP_UINT16, &feat);
        sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
 
-       sdp_set_info_attr(record, "AVRCP TG", 0, 0);
+       sdp_set_info_attr(record, "AVRCP TG", NULL, NULL);
 
        free(psm_browsing);
-       sdp_list_free(proto_browsing[0], 0);
-       sdp_list_free(proto_browsing[1], 0);
-       sdp_list_free(apseq_browsing, 0);
-       sdp_list_free(aproto_browsing, 0);
+       sdp_list_free(proto_browsing[0], NULL);
+       sdp_list_free(proto_browsing[1], NULL);
+       sdp_list_free(apseq_browsing, NULL);
+       sdp_list_free(aproto_browsing, NULL);
 
        free(psm_control);
        free(version);
-       sdp_list_free(proto_control[0], 0);
-       sdp_list_free(proto_control[1], 0);
-       sdp_list_free(apseq, 0);
-       sdp_list_free(aproto_control, 0);
-       sdp_list_free(pfseq, 0);
-       sdp_list_free(root, 0);
-       sdp_list_free(svclass_id, 0);
+       sdp_list_free(proto_control[0], NULL);
+       sdp_list_free(proto_control[1], NULL);
+       sdp_list_free(apseq, NULL);
+       sdp_list_free(aproto_control, NULL);
+       sdp_list_free(pfseq, NULL);
+       sdp_list_free(root, NULL);
+       sdp_list_free(svclass_id, NULL);
 
        return record;
 }
@@ -489,11 +491,11 @@ static uint32_t get_company_id(const uint8_t cid[3])
  *
  * Set three-byte Company_ID into outgoing AVRCP message
  */
-static void set_company_id(uint8_t cid[3], const uint32_t cid_in)
+static void set_company_id(uint8_t cid[3], uint32_t cid_in)
 {
-       cid[0] = cid_in >> 16;
-       cid[1] = cid_in >> 8;
-       cid[2] = cid_in;
+       cid[0] = (cid_in & 0xff0000) >> 16;
+       cid[1] = (cid_in & 0x00ff00) >> 8;
+       cid[2] = (cid_in & 0x0000ff);
 }
 
 static const char *attr_to_str(uint8_t attr)
@@ -920,6 +922,7 @@ static uint8_t avrcp_handle_get_capabilities(struct avrcp *session,
 
                return AVC_CTYPE_STABLE;
        case CAP_EVENTS_SUPPORTED:
+               pdu->params[1] = 0;
                for (i = 1; i <= AVRCP_EVENT_LAST; i++) {
                        if (session->supported_events & (1 << i)) {
                                pdu->params[1]++;
@@ -938,11 +941,19 @@ err:
        return AVC_CTYPE_REJECTED;
 }
 
+static struct avrcp_player *target_get_player(struct avrcp *session)
+{
+       if (!session->target)
+               return NULL;
+
+       return session->target->player;
+}
+
 static uint8_t avrcp_handle_list_player_attributes(struct avrcp *session,
                                                struct avrcp_header *pdu,
                                                uint8_t transaction)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
        uint16_t len = ntohs(pdu->params_len);
        unsigned int i;
 
@@ -974,7 +985,7 @@ static uint8_t avrcp_handle_list_player_values(struct avrcp *session,
                                                struct avrcp_header *pdu,
                                                uint8_t transaction)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
        uint16_t len = ntohs(pdu->params_len);
        unsigned int i;
 
@@ -1043,9 +1054,9 @@ static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
                                                struct avrcp_header *pdu,
                                                uint8_t transaction)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
        uint16_t len = ntohs(pdu->params_len);
-       uint64_t identifier = bt_get_le64(&pdu->params[0]);
+       uint64_t identifier = get_le64(&pdu->params[0]);
        uint16_t pos;
        uint8_t nattr;
        GList *attr_ids;
@@ -1071,7 +1082,7 @@ static uint8_t avrcp_handle_get_element_attributes(struct avrcp *session,
                for (i = 0, len = 0, attr_ids = NULL; i < nattr; i++) {
                        uint32_t id;
 
-                       id = bt_get_be32(&pdu->params[9] + (i * sizeof(id)));
+                       id = get_be32(&pdu->params[9] + (i * sizeof(id)));
 
                        /* Don't add invalid attributes */
                        if (id == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL ||
@@ -1115,7 +1126,7 @@ static uint8_t avrcp_handle_get_current_player_value(struct avrcp *session,
                                                struct avrcp_header *pdu,
                                                uint8_t transaction)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
        uint16_t len = ntohs(pdu->params_len);
        uint8_t *settings;
        unsigned int i;
@@ -1174,7 +1185,7 @@ static uint8_t avrcp_handle_set_player_value(struct avrcp *session,
                                                struct avrcp_header *pdu,
                                                uint8_t transaction)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
        uint16_t len = ntohs(pdu->params_len);
        unsigned int i;
        uint8_t *param;
@@ -1293,7 +1304,7 @@ static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
                                                struct avrcp_header *pdu,
                                                uint8_t transaction)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
        uint16_t len = ntohs(pdu->params_len);
        uint32_t position;
        uint32_t duration;
@@ -1337,7 +1348,7 @@ static GList *player_list_settings(struct avrcp_player *player)
 
 static bool avrcp_handle_play(struct avrcp *session)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
 
        if (player == NULL)
                return false;
@@ -1347,7 +1358,7 @@ static bool avrcp_handle_play(struct avrcp *session)
 
 static bool avrcp_handle_stop(struct avrcp *session)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
 
        if (player == NULL)
                return false;
@@ -1357,7 +1368,7 @@ static bool avrcp_handle_stop(struct avrcp *session)
 
 static bool avrcp_handle_pause(struct avrcp *session)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
 
        if (player == NULL)
                return false;
@@ -1367,7 +1378,7 @@ static bool avrcp_handle_pause(struct avrcp *session)
 
 static bool avrcp_handle_next(struct avrcp *session)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
 
        if (player == NULL)
                return false;
@@ -1377,7 +1388,7 @@ static bool avrcp_handle_next(struct avrcp *session)
 
 static bool avrcp_handle_previous(struct avrcp *session)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
 
        if (player == NULL)
                return false;
@@ -1420,7 +1431,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
                                                struct avrcp_header *pdu,
                                                uint8_t transaction)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
        struct btd_device *dev = session->dev;
        uint16_t len = ntohs(pdu->params_len);
        uint64_t uid;
@@ -1446,7 +1457,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
                break;
        case AVRCP_EVENT_TRACK_CHANGED:
                len = 9;
-               uid = player_get_uid(session->target->player);
+               uid = player_get_uid(player);
                memcpy(&pdu->params[1], &uid, sizeof(uint64_t));
 
                break;
@@ -1508,7 +1519,7 @@ static uint8_t avrcp_handle_request_continuing(struct avrcp *session,
                                                struct avrcp_header *pdu,
                                                uint8_t transaction)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
        uint16_t len = ntohs(pdu->params_len);
        struct pending_pdu *pending;
 
@@ -1582,14 +1593,12 @@ static uint8_t avrcp_handle_set_absolute_volume(struct avrcp *session,
        if (len != 1)
                goto err;
 
-       volume = pdu->params[0] & 0x7F;
-       if (volume > 127)
-               goto err;
-
        if (!player)
                goto err;
 
-       media_transport_update_device_volume(session->dev, pdu->params[0]);
+       volume = pdu->params[0] & 0x7F;
+
+       media_transport_update_device_volume(session->dev, volume);
 
        return AVC_CTYPE_ACCEPTED;
 
@@ -1650,7 +1659,7 @@ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
        }
 
        DBG("AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
-                       pdu->pdu_id, company_id, pdu->params_len);
+                       pdu->pdu_id, company_id, ntohs(pdu->params_len));
 
        pdu->packet_type = 0;
        pdu->rsvd = 0;
@@ -1665,7 +1674,7 @@ static size_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction,
                        break;
        }
 
-       if (!handler || handler->code != *code) {
+       if (handler->pdu_id != pdu->pdu_id || handler->code != *code) {
                pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND;
                goto err_metadata;
        }
@@ -1722,16 +1731,16 @@ static size_t handle_browsing_pdu(struct avctp *conn,
        struct avrcp_browsing_header *pdu = (void *) operands;
 
        DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id,
-                                                       pdu->param_len);
+                                                       ntohs(pdu->param_len));
 
        for (handler = browsing_handlers; handler->pdu_id; handler++) {
                if (handler->pdu_id == pdu->pdu_id)
-                       break;
+                       goto done;
        }
 
-       if (handler == NULL || handler->func == NULL)
-               return avrcp_browsing_general_reject(operands);
+       return avrcp_browsing_general_reject(operands);
 
+done:
        session->transaction = transaction;
        handler->func(session, pdu, transaction);
        return AVRCP_BROWSING_HEADER_LENGTH + ntohs(pdu->param_len);
@@ -1747,7 +1756,7 @@ size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands)
        pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR;
 
        DBG("rejecting AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
-                               pdu->pdu_id, company_id, pdu->params_len);
+                       pdu->pdu_id, company_id, ntohs(pdu->params_len));
 
        return AVRCP_HEADER_LENGTH + 1;
 }
@@ -1897,9 +1906,9 @@ static gboolean avrcp_player_value_rsp(struct avctp *conn,
 static void avrcp_get_current_player_value(struct avrcp *session,
                                                uint8_t *attrs, uint8_t count)
 {
-       uint8_t buf[AVRCP_HEADER_LENGTH + 5];
+       uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_ATTRIBUTE_LAST + 1];
        struct avrcp_header *pdu = (void *) buf;
-       int i;
+       uint16_t length = AVRCP_HEADER_LENGTH + count + 1;
 
        memset(buf, 0, sizeof(buf));
 
@@ -1909,11 +1918,10 @@ static void avrcp_get_current_player_value(struct avrcp *session,
        pdu->params_len = htons(count + 1);
        pdu->params[0] = count;
 
-       for (i = 0; count > 0; count--, i++)
-               pdu->params[i + 1] = attrs[i];
+       memcpy(pdu->params + 1, attrs, count);
 
        avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
-                                       AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+                                       AVC_SUBUNIT_PANEL, buf, length,
                                        avrcp_player_value_rsp, session);
 }
 
@@ -1922,22 +1930,32 @@ static gboolean avrcp_list_player_attributes_rsp(struct avctp *conn,
                                        uint8_t *operands, size_t operand_count,
                                        void *user_data)
 {
+       uint8_t attrs[AVRCP_ATTRIBUTE_LAST];
        struct avrcp *session = user_data;
        struct avrcp_header *pdu = (void *) operands;
-       uint8_t count;
+       uint8_t len, count = 0;
+       int i;
 
        if (code == AVC_CTYPE_REJECTED)
                return FALSE;
 
-       count = pdu->params[0];
+       len = pdu->params[0];
 
        if (ntohs(pdu->params_len) < count) {
                error("Invalid parameters");
                return FALSE;
        }
 
-       avrcp_get_current_player_value(session, &pdu->params[1],
-                                                       pdu->params[0]);
+       for (i = 0; len > 0; len--, i++) {
+               /* Don't query invalid attributes */
+               if (pdu->params[i + 1] == AVRCP_ATTRIBUTE_ILEGAL ||
+                               pdu->params[i + 1] > AVRCP_ATTRIBUTE_LAST)
+                       continue;
+
+               attrs[count++] = pdu->params[i + 1];
+       }
+
+       avrcp_get_current_player_value(session, attrs, count);
 
        return FALSE;
 }
@@ -1972,13 +1990,13 @@ static void avrcp_parse_attribute_list(struct avrcp_player *player,
                uint32_t id;
                uint16_t charset, len;
 
-               id = bt_get_be32(&operands[i]);
+               id = get_be32(&operands[i]);
                i += sizeof(uint32_t);
 
-               charset = bt_get_be16(&operands[i]);
+               charset = get_be16(&operands[i]);
                i += sizeof(uint16_t);
 
-               len = bt_get_be16(&operands[i]);
+               len = get_be16(&operands[i]);
                i += sizeof(uint16_t);
 
                if (charset == 106) {
@@ -2108,9 +2126,9 @@ static struct media_item *parse_media_element(struct avrcp *session,
        if (len < 13)
                return NULL;
 
-       uid = bt_get_be64(&operands[0]);
+       uid = get_be64(&operands[0]);
 
-       namelen = MIN(bt_get_be16(&operands[11]), sizeof(name) - 1);
+       namelen = MIN(get_be16(&operands[11]), sizeof(name) - 1);
        if (namelen > 0) {
                memcpy(name, &operands[13], namelen);
                name[namelen] = '\0';
@@ -2133,24 +2151,33 @@ static struct media_item *parse_media_folder(struct avrcp *session,
 {
        struct avrcp_player *player = session->controller->player;
        struct media_player *mp = player->user_data;
+       struct media_item *item;
        uint16_t namelen;
        char name[255];
        uint64_t uid;
        uint8_t type;
+       uint8_t playable;
 
        if (len < 12)
                return NULL;
 
-       uid = bt_get_be64(&operands[0]);
-       type = operands[9];
+       uid = get_be64(&operands[0]);
+       type = operands[8];
+       playable = operands[9];
 
-       namelen = MIN(bt_get_be16(&operands[12]), sizeof(name) - 1);
+       namelen = MIN(get_be16(&operands[12]), sizeof(name) - 1);
        if (namelen > 0) {
                memcpy(name, &operands[14], namelen);
                name[namelen] = '\0';
        }
 
-       return media_player_create_folder(mp, name, type, uid);
+       item = media_player_create_folder(mp, name, type, uid);
+       if (!item)
+               return NULL;
+
+       media_item_set_playable(item, playable & 0x01);
+
+       return item;
 }
 
 static void avrcp_list_items(struct avrcp *session, uint32_t start,
@@ -2185,7 +2212,7 @@ static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
                goto done;
        }
 
-       count = bt_get_be16(&operands[6]);
+       count = get_be16(&operands[6]);
        if (count == 0)
                goto done;
 
@@ -2195,7 +2222,7 @@ static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
                uint16_t len;
 
                type = operands[i++];
-               len = bt_get_be16(&operands[i]);
+               len = get_be16(&operands[i]);
                i += 2;
 
                if (type != 0x03 && type != 0x02) {
@@ -2210,7 +2237,7 @@ static gboolean avrcp_list_items_rsp(struct avctp *conn, uint8_t *operands,
 
                if (type == 0x03)
                        item = parse_media_element(session, &operands[i], len);
-               else if (type == 0x02)
+               else
                        item = parse_media_folder(session, &operands[i], len);
 
                if (item) {
@@ -2256,8 +2283,8 @@ static void avrcp_list_items(struct avrcp *session, uint32_t start,
 
        pdu->params[0] = player->scope;
 
-       bt_put_be32(start, &pdu->params[1]);
-       bt_put_be32(end, &pdu->params[5]);
+       put_be32(start, &pdu->params[1]);
+       put_be32(end, &pdu->params[5]);
 
        pdu->params[9] = 1;
 
@@ -2292,7 +2319,7 @@ static gboolean avrcp_change_path_rsp(struct avctp *conn,
                goto done;
        }
 
-       ret = bt_get_be32(&pdu->params[1]);
+       ret = get_be32(&pdu->params[1]);
 
 done:
        if (ret < 0) {
@@ -2327,10 +2354,10 @@ static gboolean avrcp_set_browsed_player_rsp(struct avctp *conn,
                                                        operand_count < 13)
                return FALSE;
 
-       player->uid_counter = bt_get_be16(&pdu->params[1]);
+       player->uid_counter = get_be16(&pdu->params[1]);
        player->browsed = true;
 
-       items = bt_get_be32(&pdu->params[3]);
+       items = get_be32(&pdu->params[3]);
 
        depth = pdu->params[9];
 
@@ -2422,8 +2449,8 @@ static void avrcp_get_item_attributes(struct avrcp *session, uint64_t uid)
 
        pdu->pdu_id = AVRCP_GET_ITEM_ATTRIBUTES;
        pdu->params[0] = 0x03;
-       bt_put_be64(uid, &pdu->params[1]);
-       bt_put_be16(player->uid_counter, &pdu->params[9]);
+       put_be64(uid, &pdu->params[1]);
+       put_be16(player->uid_counter, &pdu->params[9]);
        pdu->param_len = htons(12);
 
        avctp_send_browsing_req(session->conn, buf, sizeof(buf),
@@ -2607,9 +2634,9 @@ static void avrcp_change_path(struct avrcp *session, uint8_t direction,
        struct avrcp_browsing_header *pdu = (void *) buf;
 
        memset(buf, 0, sizeof(buf));
-       bt_put_be16(player->uid_counter, &pdu->params[0]);
+       put_be16(player->uid_counter, &pdu->params[0]);
        pdu->params[2] = direction;
-       bt_put_be64(uid, &pdu->params[3]);
+       put_be64(uid, &pdu->params[3]);
        pdu->pdu_id = AVRCP_CHANGE_PATH;
        pdu->param_len = htons(11);
 
@@ -2653,8 +2680,8 @@ static gboolean avrcp_search_rsp(struct avctp *conn, uint8_t *operands,
                goto done;
        }
 
-       player->uid_counter = bt_get_be16(&pdu->params[1]);
-       ret = bt_get_be32(&pdu->params[3]);
+       player->uid_counter = get_be16(&pdu->params[1]);
+       ret = get_be32(&pdu->params[3]);
 
 done:
        media_player_search_complete(mp, ret);
@@ -2673,8 +2700,8 @@ static void avrcp_search(struct avrcp *session, const char *string)
        stringlen = strnlen(string, sizeof(buf) - len);
        len += stringlen;
 
-       bt_put_be16(AVRCP_CHARSET_UTF8, &pdu->params[0]);
-       bt_put_be16(stringlen, &pdu->params[2]);
+       put_be16(AVRCP_CHARSET_UTF8, &pdu->params[0]);
+       put_be16(stringlen, &pdu->params[2]);
        memcpy(&pdu->params[4], string, stringlen);
        pdu->pdu_id = AVRCP_SEARCH;
        pdu->param_len = htons(len - AVRCP_BROWSING_HEADER_LENGTH);
@@ -2711,8 +2738,8 @@ static void avrcp_play_item(struct avrcp *session, uint64_t uid)
        pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
 
        pdu->params[0] = player->scope;
-       bt_put_be64(uid, &pdu->params[1]);
-       bt_put_be16(player->uid_counter, &pdu->params[9]);
+       put_be64(uid, &pdu->params[1]);
+       put_be16(player->uid_counter, &pdu->params[9]);
 
        length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
 
@@ -2757,8 +2784,8 @@ static void avrcp_add_to_nowplaying(struct avrcp *session, uint64_t uid)
        pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
 
        pdu->params[0] = player->scope;
-       bt_put_be64(uid, &pdu->params[1]);
-       bt_put_be16(player->uid_counter, &pdu->params[9]);
+       put_be64(uid, &pdu->params[1]);
+       put_be16(player->uid_counter, &pdu->params[9]);
 
        length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
 
@@ -2867,7 +2894,7 @@ avrcp_parse_media_player_item(struct avrcp *session, uint8_t *operands,
        if (len < 28)
                return NULL;
 
-       id = bt_get_be16(&operands[0]);
+       id = get_be16(&operands[0]);
 
        player = find_ct_player(session, id);
        if (player == NULL) {
@@ -2881,7 +2908,7 @@ avrcp_parse_media_player_item(struct avrcp *session, uint8_t *operands,
 
        media_player_set_type(mp, type_to_string(operands[2]));
 
-       subtype = bt_get_be32(&operands[3]);
+       subtype = get_be32(&operands[3]);
 
        media_player_set_subtype(mp, subtype_to_string(subtype));
 
@@ -2895,7 +2922,7 @@ avrcp_parse_media_player_item(struct avrcp *session, uint8_t *operands,
 
        avrcp_player_parse_features(player, &operands[8]);
 
-       namelen = bt_get_be16(&operands[26]);
+       namelen = get_be16(&operands[26]);
        if (namelen > 0 && namelen + 28 == len) {
                namelen = MIN(namelen, sizeof(name) - 1);
                memcpy(name, &operands[28], namelen);
@@ -2955,7 +2982,7 @@ static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn,
                return FALSE;
 
        removed = g_slist_copy(session->controller->players);
-       count = bt_get_be16(&operands[6]);
+       count = get_be16(&operands[6]);
 
        for (i = 8; count && i < operand_count; count--) {
                struct avrcp_player *player;
@@ -2963,7 +2990,7 @@ static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn,
                uint16_t len;
 
                type = operands[i++];
-               len = bt_get_be16(&operands[i]);
+               len = get_be16(&operands[i]);
                i += 2;
 
                if (type != 0x01) {
@@ -3009,10 +3036,10 @@ static void avrcp_get_media_player_list(struct avrcp *session)
 static void avrcp_volume_changed(struct avrcp *session,
                                                struct avrcp_header *pdu)
 {
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
        uint8_t volume;
 
-       if (player == NULL)
+       if (!player)
                return;
 
        volume = pdu->params[1] & 0x7F;
@@ -3045,7 +3072,7 @@ static void avrcp_track_changed(struct avrcp *session,
 {
        if (session->browsing_id) {
                struct avrcp_player *player = session->controller->player;
-               player->uid = bt_get_be64(&pdu->params[1]);
+               player->uid = get_be64(&pdu->params[1]);
                avrcp_get_item_attributes(session, player->uid);
        } else
                avrcp_get_element_attributes(session);
@@ -3085,7 +3112,7 @@ static void avrcp_addressed_player_changed(struct avrcp *session,
                                                struct avrcp_header *pdu)
 {
        struct avrcp_player *player = session->controller->player;
-       uint16_t id = bt_get_be16(&pdu->params[1]);
+       uint16_t id = get_be16(&pdu->params[1]);
 
        if (player != NULL && player->id == id)
                return;
@@ -3097,7 +3124,7 @@ static void avrcp_addressed_player_changed(struct avrcp *session,
                        return;
        }
 
-       player->uid_counter = bt_get_be16(&pdu->params[3]);
+       player->uid_counter = get_be16(&pdu->params[3]);
        session->controller->player = player;
 
        if (player->features != NULL)
@@ -3110,7 +3137,7 @@ static void avrcp_uids_changed(struct avrcp *session, struct avrcp_header *pdu)
 {
        struct avrcp_player *player = session->controller->player;
 
-       player->uid_counter = bt_get_be16(&pdu->params[1]);
+       player->uid_counter = get_be16(&pdu->params[1]);
 }
 
 static gboolean avrcp_handle_event(struct avctp *conn,
@@ -3497,6 +3524,8 @@ static void session_destroy(struct avrcp *session)
 
        server->sessions = g_slist_remove(server->sessions, session);
 
+       session_abort_pending_pdu(session);
+
        if (session->browsing_timer > 0)
                g_source_remove(session->browsing_timer);
 
@@ -3684,7 +3713,7 @@ static gboolean avrcp_handle_set_volume(struct avctp *conn,
                                        void *user_data)
 {
        struct avrcp *session = user_data;
-       struct avrcp_player *player = session->target->player;
+       struct avrcp_player *player = target_get_player(session);
        struct avrcp_header *pdu = (void *) operands;
        uint8_t volume;
 
@@ -3704,7 +3733,7 @@ int avrcp_set_volume(struct btd_device *dev, uint8_t volume)
 {
        struct avrcp_server *server;
        struct avrcp *session;
-       uint8_t buf[AVRCP_HEADER_LENGTH + 2];
+       uint8_t buf[AVRCP_HEADER_LENGTH + 1];
        struct avrcp_header *pdu = (void *) buf;
 
        server = find_server(servers, device_get_adapter(dev));
@@ -3724,30 +3753,14 @@ int avrcp_set_volume(struct btd_device *dev, uint8_t volume)
 
        DBG("volume=%u", volume);
 
-       if (session->target) {
-               pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
-               pdu->params[0] = volume;
-               pdu->params_len = htons(1);
+       pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
+       pdu->params[0] = volume;
+       pdu->params_len = htons(1);
 
-               return avctp_send_vendordep_req(session->conn,
+       return avctp_send_vendordep_req(session->conn,
                                        AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL,
                                        buf, sizeof(buf),
                                        avrcp_handle_set_volume, session);
-       } else if (session->registered_events &
-                                       (1 << AVRCP_EVENT_VOLUME_CHANGED)) {
-               uint8_t id = AVRCP_EVENT_VOLUME_CHANGED;
-               pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
-               pdu->params[0] = AVRCP_EVENT_VOLUME_CHANGED;
-               pdu->params[1] = volume;
-               pdu->params_len = htons(2);
-
-               return avctp_send_vendordep(session->conn,
-                                       session->transaction_events[id],
-                                       AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
-                                       buf, sizeof(buf));
-       }
-
-       return 0;
 }
 
 static int avrcp_connect(struct btd_service *service)
index 4c520ca..6ac5294 100644 (file)
@@ -28,6 +28,7 @@
 #define AVRCP_ATTRIBUTE_REPEAT_MODE    0x02
 #define AVRCP_ATTRIBUTE_SHUFFLE                0x03
 #define AVRCP_ATTRIBUTE_SCAN           0x04
+#define AVRCP_ATTRIBUTE_LAST           AVRCP_ATTRIBUTE_SCAN
 
 /* equalizer values */
 #define AVRCP_EQUALIZER_OFF            0x01
index 5f88a94..ab94a57 100644 (file)
 #include "src/profile.h"
 #include "src/service.h"
 
-#include "log.h"
-#include "error.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/sdpd.h"
+#include "src/uuid-helper.h"
+#include "src/dbus-common.h"
+
 #include "avctp.h"
 #include "control.h"
-#include "sdpd.h"
-#include "glib-helper.h"
-#include "dbus-common.h"
 
 static GSList *devices = NULL;
 
index 5edd3de..b71196a 100644 (file)
 #include "src/dbus-common.h"
 #include "src/profile.h"
 
-#include "glib-helper.h"
-#include "log.h"
-#include "error.h"
+#include "src/uuid-helper.h"
+#include "src/log.h"
+#include "src/error.h"
+
 #include "avdtp.h"
 #include "media.h"
 #include "transport.h"
index 6150c8a..de608a3 100644 (file)
 #include <dbus/dbus.h>
 #include <gdbus/gdbus.h>
 
-#include "log.h"
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/error.h"
+
 #include "player.h"
-#include "dbus-common.h"
-#include "error.h"
 
 #define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
 #define MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
@@ -392,7 +393,7 @@ static gboolean browsable_exists(const GDBusPropertyTable *property, void *data)
 {
        struct media_player *mp = data;
 
-       return mp->scope != NULL;
+       return mp->folder != NULL;
 }
 
 static gboolean get_browsable(const GDBusPropertyTable *property,
@@ -401,7 +402,7 @@ static gboolean get_browsable(const GDBusPropertyTable *property,
        struct media_player *mp = data;
        dbus_bool_t value;
 
-       if (mp->scope == NULL)
+       if (mp->folder == NULL)
                return FALSE;
 
        DBG("%s", mp->browsable ? "true" : "false");
@@ -418,7 +419,7 @@ static gboolean searchable_exists(const GDBusPropertyTable *property,
 {
        struct media_player *mp = data;
 
-       return mp->scope != NULL;
+       return mp->folder != NULL;
 }
 
 static gboolean get_searchable(const GDBusPropertyTable *property,
@@ -777,19 +778,16 @@ static DBusMessage *media_folder_search(DBusConnection *conn, DBusMessage *msg,
        dbus_message_iter_init(msg, &iter);
 
        if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
-               return btd_error_failed(msg, strerror(EINVAL));
+               return btd_error_invalid_args(msg);
 
        dbus_message_iter_get_basic(&iter, &string);
 
-       if (!mp->searchable || folder != mp->folder)
-               return btd_error_failed(msg, strerror(ENOTSUP));
+       if (!mp->searchable || folder != mp->folder || !cb->cbs->search)
+               return btd_error_not_supported(msg);
 
        if (folder->msg != NULL)
                return btd_error_failed(msg, strerror(EINVAL));
 
-       if (cb->cbs->search == NULL)
-               return btd_error_failed(msg, strerror(ENOTSUP));
-
        err = cb->cbs->search(mp, string, cb->user_data);
        if (err < 0)
                return btd_error_failed(msg, strerror(-err));
@@ -807,7 +805,8 @@ static int parse_filters(struct media_player *player, DBusMessageIter *iter,
        int ctype;
 
        *start = 0;
-       *end = folder->number_of_items ? folder->number_of_items : UINT32_MAX;
+       *end = folder->number_of_items ? folder->number_of_items - 1 :
+                                                               UINT32_MAX;
 
        ctype = dbus_message_iter_get_arg_type(iter);
        if (ctype != DBUS_TYPE_ARRAY)
@@ -996,14 +995,14 @@ static DBusMessage *media_folder_change_folder(DBusConnection *conn,
        if (!dbus_message_get_args(msg, NULL,
                                        DBUS_TYPE_OBJECT_PATH, &path,
                                        DBUS_TYPE_INVALID))
-               return btd_error_failed(msg, strerror(EINVAL));
+               return btd_error_invalid_args(msg);
 
        if (folder->msg != NULL)
                return btd_error_failed(msg, strerror(EBUSY));
 
        folder = media_player_find_folder(mp, path);
        if (folder == NULL)
-               return btd_error_failed(msg, strerror(EINVAL));
+               return btd_error_invalid_args(msg);
 
        if (mp->scope == folder)
                return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
@@ -1014,8 +1013,16 @@ static DBusMessage *media_folder_change_folder(DBusConnection *conn,
                return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
        }
 
+       /*
+        * ChangePath can only navigate one level up/down so check if folder
+        * is direct child or parent of the current folder otherwise fail.
+        */
+       if (!g_slist_find(mp->folder->subfolders, folder) &&
+                               !g_slist_find(folder->subfolders, mp->folder))
+               return btd_error_invalid_args(msg);
+
        if (cb->cbs->change_folder == NULL)
-               return btd_error_failed(msg, strerror(ENOTSUP));
+               return btd_error_not_supported(msg);
 
        err = cb->cbs->change_folder(mp, folder->item->name, folder->item->uid,
                                                                cb->user_data);
@@ -1471,11 +1478,8 @@ static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
        struct player_callback *cb = mp->cb;
        int err;
 
-       if (!item->playable)
-               return btd_error_failed(msg, strerror(ENOTSUP));
-
-       if (cb->cbs->play_item == NULL)
-               return btd_error_failed(msg, strerror(ENOTSUP));
+       if (!item->playable || !cb->cbs->play_item)
+               return btd_error_not_supported(msg);
 
        err = cb->cbs->play_item(mp, item->path, item->uid, cb->user_data);
        if (err < 0)
@@ -1492,11 +1496,8 @@ static DBusMessage *media_item_add_to_nowplaying(DBusConnection *conn,
        struct player_callback *cb = mp->cb;
        int err;
 
-       if (!item->playable)
-               return btd_error_failed(msg, strerror(ENOTSUP));
-
-       if (cb->cbs->play_item == NULL)
-               return btd_error_failed(msg, strerror(ENOTSUP));
+       if (!item->playable || !cb->cbs->play_item)
+               return btd_error_not_supported(msg);
 
        err = cb->cbs->add_to_nowplaying(mp, item->path, item->uid,
                                                        cb->user_data);
index 4f39622..d16af23 100644 (file)
 #include <dbus/dbus.h>
 #include <gdbus/gdbus.h>
 
-#include "log.h"
+#include "src/log.h"
 
 #include "src/adapter.h"
 #include "src/device.h"
 #include "src/service.h"
+#include "src/error.h"
+#include "src/dbus-common.h"
 
 #include "avdtp.h"
 #include "media.h"
 #include "a2dp.h"
-#include "error.h"
 #include "sink.h"
-#include "dbus-common.h"
 
 #define STREAM_SETUP_RETRY_TIMER 2
 
index 7b129b7..843b3e8 100644 (file)
 #include <dbus/dbus.h>
 #include <gdbus/gdbus.h>
 
-#include "log.h"
+#include "src/log.h"
 
 #include "src/adapter.h"
 #include "src/device.h"
 #include "src/service.h"
+#include "src/error.h"
+#include "src/dbus-common.h"
 
 #include "avdtp.h"
 #include "media.h"
 #include "a2dp.h"
-#include "error.h"
 #include "source.h"
-#include "dbus-common.h"
 
 struct source {
        struct btd_service *service;
index 087c0ee..c82fbb5 100644 (file)
@@ -36,8 +36,9 @@
 #include "src/device.h"
 #include "src/dbus-common.h"
 
-#include "log.h"
-#include "error.h"
+#include "src/log.h"
+#include "src/error.h"
+
 #include "avdtp.h"
 #include "media.h"
 #include "transport.h"
index 6ecc985..6b00da7 100644 (file)
 #include <gdbus/gdbus.h>
 
 #include "lib/uuid.h"
-#include "plugin.h"
-#include "adapter.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
-#include "dbus-common.h"
-#include "error.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+#include "src/error.h"
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
-#include "attio.h"
-#include "log.h"
+#include "src/attio.h"
+#include "src/log.h"
 
 /* min length for ATT indication or notification: opcode (1b) + handle (2b) */
 #define ATT_HDR_LEN 3
@@ -380,7 +381,7 @@ static void read_feature_cb(guint8 status, const guint8 *pdu, guint16 len,
                return;
        }
 
-       csc->feature = att_get_u16(value);
+       csc->feature = get_le16(value);
 
        if ((csc->feature & MULTI_SENSOR_LOC_SUPPORT)
                                                && (csc->locations == NULL))
@@ -418,13 +419,12 @@ static void read_location_cb(guint8 status, const guint8 *pdu,
                                        CYCLINGSPEED_INTERFACE, "Location");
 }
 
-static void discover_desc_cb(guint8 status, const guint8 *pdu,
-                                       guint16 len, gpointer user_data)
+static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
 {
        struct characteristic *ch = user_data;
-       struct att_data_list *list = NULL;
-       uint8_t format;
-       int i;
+       struct gatt_desc *desc;
+       uint8_t attr_val[2];
+       char *msg = NULL;
 
        if (status != 0) {
                error("Discover %s descriptors failed: %s", ch->uuid,
@@ -432,55 +432,31 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu,
                goto done;
        }
 
-       list = dec_find_info_resp(pdu, len, &format);
-       if (list == NULL)
-               goto done;
+       /* There will be only one descriptor on list and it will be CCC */
+       desc = descs->data;
 
-       if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
-               goto done;
+       if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
+               ch->csc->measurement_ccc_handle = desc->handle;
 
-       for (i = 0; i < list->num; i++) {
-               uint8_t *value;
-               uint16_t handle, uuid;
-               uint8_t attr_val[2];
-               char *msg;
-
-               value = list->data[i];
-               handle = att_get_u16(value);
-               uuid = att_get_u16(value + 2);
-
-               if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
-                       continue;
-
-               if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
-                       ch->csc->measurement_ccc_handle = handle;
-
-                       if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
-                               att_put_u16(0x0000, attr_val);
-                               msg = g_strdup("Disable measurement");
-                       } else {
-                               att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
-                                                               attr_val);
-                               msg = g_strdup("Enable measurement");
-                       }
-
-               } else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) {
-                       att_put_u16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val);
-                       msg = g_strdup("Enable SC Control Point indications");
+               if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
+                       put_le16(0x0000, attr_val);
+                       msg = g_strdup("Disable measurement");
                } else {
-                       break;
+                       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
+                                                       attr_val);
+                       msg = g_strdup("Enable measurement");
                }
+       } else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) {
+               put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val);
+               msg = g_strdup("Enable SC Control Point indications");
+       } else {
+               goto done;
+       }
 
-               gatt_write_char(ch->csc->attrib, handle, attr_val,
+       gatt_write_char(ch->csc->attrib, desc->handle, attr_val,
                                        sizeof(attr_val), char_write_cb, msg);
 
-               /* We only want CCC, can break here */
-               break;
-       }
-
 done:
-       if (list)
-               att_data_list_free(list);
        g_free(ch);
 }
 
@@ -489,6 +465,7 @@ static void discover_desc(struct csc *csc, struct gatt_char *c,
 {
        struct characteristic *ch;
        uint16_t start, end;
+       bt_uuid_t uuid;
 
        start = c->value_handle + 1;
 
@@ -506,7 +483,10 @@ static void discover_desc(struct csc *csc, struct gatt_char *c,
        ch->csc = csc;
        memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
 
-       gatt_discover_char_desc(csc->attrib, start, end, discover_desc_cb, ch);
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+       gatt_discover_desc(csc->attrib, start, end, &uuid, discover_desc_cb,
+                                                                       ch);
 }
 
 static void update_watcher(gpointer data, gpointer user_data)
@@ -573,8 +553,8 @@ static void process_measurement(struct csc *csc, const uint8_t *pdu,
                }
 
                m.has_wheel_rev = true;
-               m.wheel_rev = att_get_u32(pdu);
-               m.last_wheel_time = att_get_u16(pdu + 4);
+               m.wheel_rev = get_le32(pdu);
+               m.last_wheel_time = get_le16(pdu + 4);
                pdu += 6;
                len -= 6;
        }
@@ -586,8 +566,8 @@ static void process_measurement(struct csc *csc, const uint8_t *pdu,
                }
 
                m.has_crank_rev = true;
-               m.crank_rev = att_get_u16(pdu);
-               m.last_crank_time = att_get_u16(pdu + 2);
+               m.crank_rev = get_le16(pdu);
+               m.last_crank_time = get_le16(pdu + 2);
                pdu += 4;
                len -= 4;
        }
@@ -759,7 +739,7 @@ done:
                g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL);
 }
 
-static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
 {
        struct csc *csc = user_data;
        uint16_t feature_val_handle = 0;
@@ -816,7 +796,7 @@ static void enable_measurement(gpointer data, gpointer user_data)
        if (csc->attrib == NULL || !handle)
                return;
 
-       att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
        msg = g_strdup("Enable measurement");
 
        gatt_write_char(csc->attrib, handle, value, sizeof(value),
@@ -833,7 +813,7 @@ static void disable_measurement(gpointer data, gpointer user_data)
        if (csc->attrib == NULL || !handle)
                return;
 
-       att_put_u16(0x0000, value);
+       put_le16(0x0000, value);
        msg = g_strdup("Disable measurement");
 
        gatt_write_char(csc->attrib, handle, value, sizeof(value),
@@ -1166,7 +1146,7 @@ static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn,
        csc->pending_req = req;
 
        att_val[0] = SET_CUMULATIVE_VALUE;
-       att_put_u32(value, att_val + 1);
+       put_le32(value, att_val + 1);
 
        gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
                sizeof(att_val), controlpoint_write_cb, req);
index 8343bb5..208598a 100644 (file)
 #include <glib.h>
 
 #include "lib/uuid.h"
-#include "plugin.h"
-#include "adapter.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
 #include "attrib/gattrib.h"
-#include "attio.h"
+#include "src/attio.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
-#include "log.h"
+#include "src/log.h"
 
 #define PNP_ID_SIZE    7
 
@@ -96,8 +97,8 @@ static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
                return;
        }
 
-       btd_device_set_pnpid(ch->d->dev, value[0], att_get_u16(&value[1]),
-                               att_get_u16(&value[3]), att_get_u16(&value[5]));
+       btd_device_set_pnpid(ch->d->dev, value[0], get_le16(&value[1]),
+                               get_le16(&value[3]), get_le16(&value[5]));
 }
 
 static void process_deviceinfo_char(struct characteristic *ch)
@@ -107,8 +108,8 @@ static void process_deviceinfo_char(struct characteristic *ch)
                                                        read_pnpid_cb, ch);
 }
 
-static void configure_deviceinfo_cb(GSList *characteristics, guint8 status,
-                                                       gpointer user_data)
+static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
 {
        struct deviceinfo *d = user_data;
        GSList *l;
index 5d6880f..d240450 100644 (file)
 #include <errno.h>
 
 #include <glib.h>
-#include <btio/btio.h>
 
+#include "btio/btio.h"
 #include "lib/uuid.h"
-#include "plugin.h"
-#include "adapter.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
 #include "attrib/att.h"
 #include "attrib/gattrib.h"
-#include "attio.h"
+#include "src/attio.h"
 #include "attrib/gatt.h"
-#include "log.h"
-#include "textfile.h"
+#include "src/log.h"
+#include "src/textfile.h"
 
 /* Generic Attribute/Access Service */
 struct gas {
@@ -165,7 +166,7 @@ static void gap_appearance_cb(guint8 status, const guint8 *pdu, guint16 plen,
        }
 
        atval = list->data[0] + 2; /* skip handle value */
-       app = att_get_u16(atval);
+       app = get_le16(atval);
 
        DBG("GAP Appearance: 0x%04x", app);
 
@@ -177,6 +178,7 @@ done:
 
 static void indication_cb(const uint8_t *pdu, uint16_t len, gpointer user_data)
 {
+       uint8_t bdaddr_type;
        struct gas *gas = user_data;
        uint16_t start, end, olen;
        size_t plen;
@@ -187,8 +189,8 @@ static void indication_cb(const uint8_t *pdu, uint16_t len, gpointer user_data)
                return;
        }
 
-       start = att_get_u16(&pdu[3]);
-       end = att_get_u16(&pdu[5]);
+       start = get_le16(&pdu[3]);
+       end = get_le16(&pdu[5]);
 
        DBG("Service Changed start: 0x%04X end: 0x%04X", start, end);
 
@@ -197,7 +199,8 @@ static void indication_cb(const uint8_t *pdu, uint16_t len, gpointer user_data)
        olen = enc_confirmation(opdu, plen);
        g_attrib_send(gas->attrib, 0, opdu, olen, NULL, NULL, NULL);
 
-       if (device_is_bonded(gas->device) == FALSE) {
+       bdaddr_type = btd_device_get_bdaddr_type(gas->device);
+       if (!device_is_bonded(gas->device, bdaddr_type)) {
                DBG("Ignoring Service Changed: device is not bonded");
                return;
        }
@@ -230,53 +233,36 @@ static void write_ccc(GAttrib *attrib, uint16_t handle, gpointer user_data)
 {
        uint8_t value[2];
 
-       att_put_u16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
+       put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
        gatt_write_char(attrib, handle, value, sizeof(value), ccc_written_cb,
                                                                user_data);
 }
 
-static void gatt_descriptors_cb(guint8 status, const guint8 *pdu, guint16 len,
-                                                       gpointer user_data)
+static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data)
 {
        struct gas *gas = user_data;
-       struct att_data_list *list;
-       int i;
-       uint8_t format;
+       struct gatt_desc *desc;
 
-       if (status) {
-               error("Discover all GATT characteristic descriptors: %s",
+       if (status != 0) {
+               error("Discover Service Changed CCC failed: %s",
                                                        att_ecode2str(status));
                return;
        }
 
-       list = dec_find_info_resp(pdu, len, &format);
-       if (list == NULL)
-               return;
-
-       if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
-               goto done;
-
-       for (i = 0; i < list->num; i++) {
-               uint16_t uuid16, ccc;
-               uint8_t *value;
+       /* There will be only one descriptor on list and it will be CCC */
+       desc = descs->data;
 
-               value = list->data[i];
-               ccc = att_get_u16(value);
-               uuid16 = att_get_u16(&value[2]);
-               DBG("CCC: 0x%04x UUID: 0x%04x", ccc, uuid16);
-               write_ccc(gas->attrib, ccc, user_data);
-       }
-
-done:
-       att_data_list_free(list);
+       DBG("CCC: 0x%04x", desc->handle);
+       write_ccc(gas->attrib, desc->handle, user_data);
 }
 
-static void gatt_characteristic_cb(GSList *characteristics, guint8 status,
-                                                       gpointer user_data)
+static void gatt_characteristic_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
 {
        struct gas *gas = user_data;
        struct gatt_char *chr;
        uint16_t start, end;
+       bt_uuid_t uuid;
 
        if (status) {
                error("Discover Service Changed handle: %s", att_ecode2str(status));
@@ -294,7 +280,10 @@ static void gatt_characteristic_cb(GSList *characteristics, guint8 status,
        }
 
        gas->changed_handle = chr->value_handle;
-       gatt_discover_char_desc(gas->attrib, start, end, gatt_descriptors_cb,
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+       gatt_discover_desc(gas->attrib, start, end, &uuid, discover_ccc_cb,
                                                                        gas);
 }
 
index 6203fa8..48dad52 100644 (file)
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdbool.h>
-#include <sdpd.h>
 #include <unistd.h>
 
 #include <glib.h>
 
+#include <bluetooth/bluetooth.h>
 #include <bluetooth/l2cap.h>
 #include <gdbus/gdbus.h>
-#include <dbus-common.h>
-#include <log.h>
-#include <error.h>
-#include <adapter.h>
-#include <device.h>
-#include <btio/btio.h>
+#include "src/dbus-common.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/sdpd.h"
+#include "btio/btio.h"
 
 #include "mcap_lib.h"
 #include "hdp_types.h"
@@ -2144,7 +2145,8 @@ static struct hdp_device *create_health_device(struct btd_device *device)
        if (!g_dbus_register_interface(btd_get_dbus_connection(),
                                        path, HEALTH_DEVICE,
                                        health_device_methods,
-                                       health_device_signals, NULL,
+                                       health_device_signals,
+                                       health_device_properties,
                                        dev, health_device_destroy)) {
                error("D-Bus failed to register %s interface", HEALTH_DEVICE);
                goto fail;
index e705ee9..6dc9acf 100644 (file)
 #endif
 
 #include <errno.h>
-
 #include <gdbus/gdbus.h>
 
-#include "plugin.h"
+#include "src/plugin.h"
+
 #include "hdp_manager.h"
 
 static int hdp_init(void)
index 1bb6007..1882043 100644 (file)
 #include <bluetooth/sdp.h>
 #include <bluetooth/sdp_lib.h>
 
+#include "btio/btio.h"
 #include "lib/uuid.h"
-#include <btio/btio.h>
-#include <adapter.h>
-#include <device.h>
-#include <profile.h>
-#include <service.h>
-#include <glib-helper.h>
-#include <log.h>
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/uuid-helper.h"
+#include "src/log.h"
 
 #include "hdp_types.h"
-
 #include "hdp_manager.h"
 #include "hdp.h"
 
index 7de87a8..58770b5 100644 (file)
 
 #include <gdbus/gdbus.h>
 
-#include <adapter.h>
-#include <device.h>
-
-#include <sdpd.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
 #include <bluetooth/sdp_lib.h>
-#include <sdp-client.h>
-#include <glib-helper.h>
+
+#include "src/adapter.h"
+#include "src/device.h"
+
+#include "src/sdpd.h"
+#include "src/sdp-client.h"
+#include "src/uuid-helper.h"
 
 #include "lib/uuid.h"
 #include "btio/btio.h"
 
-#include "log.h"
+#include "src/log.h"
+#include "src/dbus-common.h"
 
-#include "dbus-common.h"
 #include "mcap.h"
 #include "mcap_lib.h"
 #include "hdp_types.h"
@@ -864,7 +867,7 @@ gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app,
 
        bt_string2uuid(&uuid, HDP_UUID);
        if (bt_search_service(src, dst, &uuid, get_mdep_cb, mdep_data,
-                                                       free_mdep_data) < 0) {
+                                               free_mdep_data, 0) < 0) {
                g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
                                                "Can't get remote SDP record");
                g_free(mdep_data);
@@ -1092,7 +1095,7 @@ gboolean hdp_establish_mcl(struct hdp_device *device,
 
        bt_string2uuid(&uuid, HDP_UUID);
        if (bt_search_service(src, dst, &uuid, search_cb, conn_data,
-                                               destroy_con_mcl_data) < 0) {
+                                       destroy_con_mcl_data, 0) < 0) {
                g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
                                                "Can't get remote SDP record");
                g_free(conn_data);
@@ -1161,7 +1164,7 @@ gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func,
 
        bt_string2uuid(&uuid, HDP_UUID);
        if (bt_search_service(src, dst, &uuid, get_dcpsm_cb, dcpsm_data,
-                                                       free_dcpsm_data) < 0) {
+                                               free_dcpsm_data, 0) < 0) {
                g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
                                                "Can't get remote SDP record");
                g_free(dcpsm_data);
index 6d821f3..102ec85 100644 (file)
@@ -34,9 +34,9 @@
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/l2cap.h>
 
-#include <btio/btio.h>
-#include <log.h>
-#include <error.h>
+#include "btio/btio.h"
+#include "src/log.h"
+#include "src/error.h"
 
 #include "mcap.h"
 #include "mcap_lib.h"
index 0d9f17d..cc89d47 100644 (file)
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/l2cap.h>
-#include <adapter.h>
-#include <btio/btio.h>
-#include <log.h>
+
+#include "btio/btio.h"
+#include "src/adapter.h"
+#include "src/log.h"
 
 #include "mcap.h"
 #include "mcap_lib.h"
@@ -55,7 +56,7 @@ struct mcap_csp {
        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 */
+       uint8_t         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 */
@@ -88,8 +89,6 @@ struct sync_set_data {
        gboolean role;
 };
 
-#define hton64(x)     ntoh64(x)
-
 static gboolean csp_caps_initialized = FALSE;
 struct csp_caps _caps;
 
index d87f0ff..287ea72 100644 (file)
 #include <gdbus/gdbus.h>
 
 #include "lib/uuid.h"
-#include "plugin.h"
-#include "adapter.h"
-#include "dbus-common.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
-#include "error.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/dbus-common.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/shared/util.h"
+#include "src/service.h"
+#include "src/error.h"
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
-#include "attio.h"
-#include "log.h"
+#include "src/attio.h"
+#include "src/log.h"
 
 #define HEART_RATE_INTERFACE           "org.bluez.HeartRate1"
 #define HEART_RATE_MANAGER_INTERFACE   "org.bluez.HeartRateManager1"
@@ -324,7 +325,7 @@ static void process_measurement(struct heartrate *hr, const uint8_t *pdu,
                        return;
                }
 
-               m.value = att_get_u16(pdu);
+               m.value = get_le16(pdu);
                pdu += 2;
                len -= 2;
        } else {
@@ -345,7 +346,7 @@ static void process_measurement(struct heartrate *hr, const uint8_t *pdu,
                }
 
                m.has_energy = TRUE;
-               m.energy = att_get_u16(pdu);
+               m.energy = get_le16(pdu);
                pdu += 2;
                len -= 2;
        }
@@ -362,7 +363,7 @@ static void process_measurement(struct heartrate *hr, const uint8_t *pdu,
                m.interval = g_new(uint16_t, m.num_interval);
 
                for (i = 0; i < m.num_interval; pdu += 2, i++)
-                       m.interval[i] = att_get_u16(pdu);
+                       m.interval[i] = get_le16(pdu);
        }
 
        if (flags & SENSOR_CONTACT_SUPPORT) {
@@ -390,13 +391,12 @@ static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
        process_measurement(hr, pdu + 3, len - 3);
 }
 
-static void discover_ccc_cb(guint8 status, const guint8 *pdu,
-                                               guint16 len, gpointer user_data)
+static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data)
 {
        struct heartrate *hr = user_data;
-       struct att_data_list *list;
-       uint8_t format;
-       int i;
+       struct gatt_desc *desc;
+       uint8_t attr_val[2];
+       char *msg;
 
        if (status != 0) {
                error("Discover Heart Rate Measurement descriptors failed: %s",
@@ -404,50 +404,28 @@ static void discover_ccc_cb(guint8 status, const guint8 *pdu,
                return;
        }
 
-       list = dec_find_info_resp(pdu, len, &format);
-       if (list == NULL)
-               return;
-
-       if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
-               goto done;
-
-       for (i = 0; i < list->num; i++) {
-               uint8_t *value;
-               uint16_t handle, uuid;
-               char *msg;
-               uint8_t attr_val[2];
-
-               value = list->data[i];
-               handle = att_get_u16(value);
-               uuid = att_get_u16(value + 2);
-
-               if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
-                       continue;
+       /* There will be only one descriptor on list and it will be CCC */
+       desc = descs->data;
 
-               hr->measurement_ccc_handle = handle;
+       hr->measurement_ccc_handle = desc->handle;
 
-               if (g_slist_length(hr->hradapter->watchers) == 0) {
-                       att_put_u16(0x0000, attr_val);
-                       msg = g_strdup("Disable measurement");
-               } else {
-                       att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val);
-                       msg = g_strdup("Enable measurement");
-               }
-
-               gatt_write_char(hr->attrib, handle, attr_val,
-                                       sizeof(attr_val), char_write_cb, msg);
-
-               break;
+       if (g_slist_length(hr->hradapter->watchers) == 0) {
+               put_le16(0x0000, attr_val);
+               msg = g_strdup("Disable measurement");
+       } else {
+               put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val);
+               msg = g_strdup("Enable measurement");
        }
 
-done:
-       att_data_list_free(list);
+       gatt_write_char(hr->attrib, desc->handle, attr_val, sizeof(attr_val),
+                                                       char_write_cb, msg);
 }
 
 static void discover_measurement_ccc(struct heartrate *hr,
                                struct gatt_char *c, struct gatt_char *c_next)
 {
        uint16_t start, end;
+       bt_uuid_t uuid;
 
        start = c->value_handle + 1;
 
@@ -461,10 +439,12 @@ static void discover_measurement_ccc(struct heartrate *hr,
                return;
        }
 
-       gatt_discover_char_desc(hr->attrib, start, end, discover_ccc_cb, hr);
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+       gatt_discover_desc(hr->attrib, start, end, &uuid, discover_ccc_cb, hr);
 }
 
-static void discover_char_cb(GSList *chars, guint8 status, gpointer user_data)
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
 {
        struct heartrate *hr = user_data;
 
@@ -510,7 +490,7 @@ static void enable_measurement(gpointer data, gpointer user_data)
        if (hr->attrib == NULL || !handle)
                return;
 
-       att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
        msg = g_strdup("Enable measurement");
 
        gatt_write_char(hr->attrib, handle, value, sizeof(value),
@@ -527,7 +507,7 @@ static void disable_measurement(gpointer data, gpointer user_data)
        if (hr->attrib == NULL || !handle)
                return;
 
-       att_put_u16(0x0000, value);
+       put_le16(0x0000, value);
        msg = g_strdup("Disable measurement");
 
        gatt_write_char(hr->attrib, handle, value, sizeof(value),
index 62f6dbb..52222ea 100644 (file)
@@ -3,6 +3,7 @@
  *  BlueZ - Bluetooth protocol stack for Linux
  *
  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2014       Google Inc.
  *
  *
  *  This program is free software; you can redistribute it and/or modify
 
 #include <gdbus/gdbus.h>
 
-#include "log.h"
+#include "src/log.h"
 
+#include "btio/btio.h"
 #include "lib/uuid.h"
-#include "../src/adapter.h"
-#include "../src/device.h"
-#include "../src/profile.h"
-#include "../src/service.h"
-#include "../src/storage.h"
-#include "../src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/storage.h"
+#include "src/dbus-common.h"
+#include "src/error.h"
+#include "src/sdp-client.h"
 
 #include "device.h"
-#include "error.h"
-#include <btio/btio.h>
-
-#include "sdp-client.h"
+#include "hidp_defs.h"
+#include "uhid_copy.h"
 
 #define INPUT_INTERFACE "org.bluez.Input1"
 
+#define UHID_DEVICE_FILE "/dev/uhid"
+
 enum reconnect_mode_t {
        RECONNECT_NONE = 0,
        RECONNECT_DEVICE,
@@ -79,20 +83,33 @@ struct input_device {
        struct hidp_connadd_req *req;
        guint                   dc_id;
        bool                    disable_sdp;
-       char                    *name;
        enum reconnect_mode_t   reconnect_mode;
        guint                   reconnect_timer;
        uint32_t                reconnect_attempt;
+       bool                    uhid_enabled;
+       int                     uhid_fd;
+       guint                   uhid_watch;
+       bool                    uhid_created;
+       uint8_t                 report_req_pending;
+       guint                   report_req_timer;
+       uint32_t                report_rsp_id;
 };
 
 static int idle_timeout = 0;
+static bool uhid_enabled = false;
 
 void input_set_idle_timeout(int timeout)
 {
        idle_timeout = timeout;
 }
 
+void input_enable_userspace_hid(bool state)
+{
+       uhid_enabled = state;
+}
+
 static void input_device_enter_reconnect_mode(struct input_device *idev);
+static int connection_disconnect(struct input_device *idev, uint32_t flags);
 
 static void input_device_free(struct input_device *idev)
 {
@@ -101,7 +118,6 @@ static void input_device_free(struct input_device *idev)
 
        btd_service_unref(idev->service);
        btd_device_unref(idev->device);
-       g_free(idev->name);
        g_free(idev->path);
 
        if (idev->ctrl_watch > 0)
@@ -127,14 +143,189 @@ static void input_device_free(struct input_device *idev)
        if (idev->reconnect_timer > 0)
                g_source_remove(idev->reconnect_timer);
 
+       if (idev->report_req_timer > 0)
+               g_source_remove(idev->report_req_timer);
+
        g_free(idev);
 }
 
+static bool hidp_send_message(GIOChannel *chan, uint8_t hdr,
+                                       const uint8_t *data, size_t size)
+{
+       int fd;
+       ssize_t len;
+       uint8_t msg[size + 1];
+
+       if (data == NULL)
+               size = 0;
+
+       msg[0] = hdr;
+       if (size > 0)
+               memcpy(&msg[1], data, size);
+       ++size;
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       len = write(fd, msg, size);
+       if (len < 0) {
+               error("BT socket write error: %s (%d)", strerror(errno), errno);
+               return false;
+       }
+
+       if ((size_t) len < size) {
+               error("BT socket write error: partial write (%zd of %zu bytes)",
+                                                               len, size);
+               return false;
+       }
+
+       return true;
+}
+
+static bool hidp_send_ctrl_message(struct input_device *idev, uint8_t hdr,
+                                       const uint8_t *data, size_t size)
+{
+       return hidp_send_message(idev->ctrl_io, hdr, data, size);
+}
+
+static bool hidp_send_intr_message(struct input_device *idev, uint8_t hdr,
+                                       const uint8_t *data, size_t size)
+{
+       return hidp_send_message(idev->intr_io, hdr, data, size);
+}
+
+static bool uhid_send_feature_answer(struct input_device *idev,
+                                       const uint8_t *data, size_t size,
+                                       uint32_t id, uint16_t err)
+{
+       struct uhid_event ev;
+       ssize_t len;
+
+       if (data == NULL)
+               size = 0;
+
+       if (size > sizeof(ev.u.feature_answer.data))
+               size = sizeof(ev.u.feature_answer.data);
+
+       if (!idev->uhid_created) {
+               DBG("HID report (%zu bytes) dropped", size);
+               return false;
+       }
+
+       memset(&ev, 0, sizeof(ev));
+       ev.type = UHID_FEATURE_ANSWER;
+       ev.u.feature_answer.id = id;
+       ev.u.feature_answer.err = err;
+       ev.u.feature_answer.size = size;
+
+       if (size > 0)
+               memcpy(ev.u.feature_answer.data, data, size);
+
+       len = write(idev->uhid_fd, &ev, sizeof(ev));
+       if (len < 0) {
+               error("uHID dev write error: %s (%d)", strerror(errno), errno);
+               return false;
+       }
+
+       /* uHID kernel driver does not handle partial writes */
+       if ((size_t) len < sizeof(ev)) {
+               error("uHID dev write error: partial write (%zd of %zu bytes)",
+                                                       len, sizeof(ev));
+               return false;
+       }
+
+       DBG("HID report (%zu bytes) -> uHID fd %d", size, idev->uhid_fd);
+
+       return true;
+}
+
+static bool uhid_send_input_report(struct input_device *idev,
+                                       const uint8_t *data, size_t size)
+{
+       struct uhid_event ev;
+       ssize_t len;
+
+       if (data == NULL)
+               size = 0;
+
+       if (size > sizeof(ev.u.input.data))
+               size = sizeof(ev.u.input.data);
+
+       if (!idev->uhid_created) {
+               DBG("HID report (%zu bytes) dropped", size);
+               return false;
+       }
+
+       memset(&ev, 0, sizeof(ev));
+       ev.type = UHID_INPUT;
+       ev.u.input.size = size;
+
+       if (size > 0)
+               memcpy(ev.u.input.data, data, size);
+
+       len = write(idev->uhid_fd, &ev, sizeof(ev));
+       if (len < 0) {
+               error("uHID dev write error: %s (%d)", strerror(errno), errno);
+               return false;
+       }
+
+       /* uHID kernel driver does not handle partial writes */
+       if ((size_t) len < sizeof(ev)) {
+               error("uHID dev write error: partial write (%zd of %zu bytes)",
+                                                       len, sizeof(ev));
+               return false;
+       }
+
+       DBG("HID report (%zu bytes) -> uHID fd %d", size, idev->uhid_fd);
+
+       return true;
+}
+
+static bool hidp_recv_intr_data(GIOChannel *chan, struct input_device *idev)
+{
+       int fd;
+       ssize_t len;
+       uint8_t hdr;
+       uint8_t data[UHID_DATA_MAX + 1];
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       len = read(fd, data, sizeof(data));
+       if (len < 0) {
+               error("BT socket read error: %s (%d)", strerror(errno), errno);
+               return false;
+       }
+
+       if (len == 0) {
+               DBG("BT socket read returned 0 bytes");
+               return true;
+       }
+
+       hdr = data[0];
+       if (hdr != (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) {
+               DBG("unsupported HIDP protocol header 0x%02x", hdr);
+               return true;
+       }
+
+       if (len < 2) {
+               DBG("received empty HID report");
+               return true;
+       }
+
+       uhid_send_input_report(idev, data + 1, len - 1);
+
+       return true;
+}
+
 static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
 {
        struct input_device *idev = data;
        char address[18];
 
+       if (cond & G_IO_IN) {
+               if (hidp_recv_intr_data(chan, idev) && (cond == G_IO_IN))
+                       return TRUE;
+       }
+
        ba2str(&idev->dst, address);
 
        DBG("Device %s disconnected", address);
@@ -167,11 +358,170 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data
        return FALSE;
 }
 
+static void hidp_recv_ctrl_handshake(struct input_device *idev, uint8_t param)
+{
+       bool pending_req_complete = false;
+       uint8_t pending_req_type;
+
+       DBG("");
+
+       pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK;
+
+       switch (param) {
+       case HIDP_HSHK_SUCCESSFUL:
+               if (pending_req_type == HIDP_TRANS_SET_REPORT) {
+                       DBG("SET_REPORT successful");
+                       pending_req_complete = true;
+               } else
+                       DBG("Spurious HIDP_HSHK_SUCCESSFUL");
+               break;
+
+       case HIDP_HSHK_NOT_READY:
+       case HIDP_HSHK_ERR_INVALID_REPORT_ID:
+       case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
+       case HIDP_HSHK_ERR_INVALID_PARAMETER:
+       case HIDP_HSHK_ERR_UNKNOWN:
+       case HIDP_HSHK_ERR_FATAL:
+               if (pending_req_type == HIDP_TRANS_GET_REPORT) {
+                       DBG("GET_REPORT failed (%u)", param);
+                       uhid_send_feature_answer(idev, NULL, 0,
+                                               idev->report_rsp_id, EIO);
+                       pending_req_complete = true;
+               } else if (pending_req_type == HIDP_TRANS_SET_REPORT) {
+                       DBG("SET_REPORT failed (%u)", param);
+                       pending_req_complete = true;
+               } else
+                       DBG("Spurious HIDP_HSHK_ERR");
+
+               if (param == HIDP_HSHK_ERR_FATAL)
+                       hidp_send_ctrl_message(idev, HIDP_TRANS_HID_CONTROL |
+                                               HIDP_CTRL_SOFT_RESET, NULL, 0);
+               break;
+
+       default:
+               hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE |
+                               HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
+               break;
+       }
+
+       if (pending_req_complete) {
+               idev->report_req_pending = 0;
+               if (idev->report_req_timer > 0) {
+                       g_source_remove(idev->report_req_timer);
+                       idev->report_req_timer = 0;
+               }
+               idev->report_rsp_id = 0;
+       }
+}
+
+static void hidp_recv_ctrl_hid_control(struct input_device *idev, uint8_t param)
+{
+       DBG("");
+
+       if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG)
+               connection_disconnect(idev, 0);
+}
+
+static void hidp_recv_ctrl_data(struct input_device *idev, uint8_t param,
+                                       const uint8_t *data, size_t size)
+{
+       uint8_t pending_req_type;
+       uint8_t pending_req_param;
+
+       DBG("");
+
+       pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK;
+       if (pending_req_type != HIDP_TRANS_GET_REPORT) {
+               DBG("Spurious DATA on control channel");
+               return;
+       }
+
+       pending_req_param = idev->report_req_pending & HIDP_HEADER_PARAM_MASK;
+       if (pending_req_param != param) {
+               DBG("Received DATA RTYPE doesn't match pending request RTYPE");
+               return;
+       }
+
+       switch (param) {
+       case HIDP_DATA_RTYPE_FEATURE:
+       case HIDP_DATA_RTYPE_INPUT:
+       case HIDP_DATA_RTYPE_OUPUT:
+               uhid_send_feature_answer(idev, data + 1, size - 1,
+                                                       idev->report_rsp_id, 0);
+               break;
+
+       case HIDP_DATA_RTYPE_OTHER:
+               DBG("Received DATA_RTYPE_OTHER");
+               break;
+
+       default:
+               hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE |
+                               HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0);
+               break;
+       }
+
+       idev->report_req_pending = 0;
+       if (idev->report_req_timer > 0) {
+               g_source_remove(idev->report_req_timer);
+               idev->report_req_timer = 0;
+       }
+       idev->report_rsp_id = 0;
+}
+
+static bool hidp_recv_ctrl_message(GIOChannel *chan, struct input_device *idev)
+{
+       int fd;
+       ssize_t len;
+       uint8_t hdr, type, param;
+       uint8_t data[UHID_DATA_MAX + 1];
+
+       fd = g_io_channel_unix_get_fd(chan);
+
+       len = read(fd, data, sizeof(data));
+       if (len < 0) {
+               error("BT socket read error: %s (%d)", strerror(errno), errno);
+               return false;
+       }
+
+       if (len == 0) {
+               DBG("BT socket read returned 0 bytes");
+               return true;
+       }
+
+       hdr = data[0];
+       type = hdr & HIDP_HEADER_TRANS_MASK;
+       param = hdr & HIDP_HEADER_PARAM_MASK;
+
+       switch (type) {
+       case HIDP_TRANS_HANDSHAKE:
+               hidp_recv_ctrl_handshake(idev, param);
+               break;
+       case HIDP_TRANS_HID_CONTROL:
+               hidp_recv_ctrl_hid_control(idev, param);
+               break;
+       case HIDP_TRANS_DATA:
+               hidp_recv_ctrl_data(idev, param, data, len);
+               break;
+       default:
+               error("unsupported HIDP control message");
+               hidp_send_ctrl_message(idev, HIDP_TRANS_HANDSHAKE |
+                               HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0);
+               break;
+       }
+
+       return true;
+}
+
 static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
 {
        struct input_device *idev = data;
        char address[18];
 
+       if (cond & G_IO_IN) {
+               if (hidp_recv_ctrl_message(chan, idev) && (cond == G_IO_IN))
+                       return TRUE;
+       }
+
        ba2str(&idev->dst, address);
 
        DBG("Device %s disconnected", address);
@@ -196,6 +546,205 @@ static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data
        return FALSE;
 }
 
+#define REPORT_REQ_TIMEOUT  3
+
+static gboolean hidp_report_req_timeout(gpointer data)
+{
+       struct input_device *idev = data;
+       uint8_t pending_req_type;
+       const char *req_type_str;
+       char address[18];
+
+       ba2str(&idev->dst, address);
+       pending_req_type = idev->report_req_pending & HIDP_HEADER_TRANS_MASK;
+
+       switch (pending_req_type) {
+       case HIDP_TRANS_GET_REPORT:
+               req_type_str = "GET_REPORT";
+               break;
+       case HIDP_TRANS_SET_REPORT:
+               req_type_str = "SET_REPORT";
+               break;
+       default:
+               /* Should never happen */
+               req_type_str = "OTHER_TRANS";
+               break;
+       }
+
+       DBG("Device %s HIDP %s request timed out", address, req_type_str);
+
+       idev->report_req_pending = 0;
+       idev->report_req_timer = 0;
+       idev->report_rsp_id = 0;
+
+       return FALSE;
+}
+
+static void hidp_send_set_report(struct input_device *idev,
+                                                       struct uhid_event *ev)
+{
+       uint8_t hdr;
+       bool sent;
+
+       DBG("");
+
+       switch (ev->u.output.rtype) {
+       case UHID_FEATURE_REPORT:
+               /* Send SET_REPORT on control channel */
+               if (idev->report_req_pending) {
+                       DBG("Old GET_REPORT or SET_REPORT still pending");
+                       return;
+               }
+
+               hdr = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE;
+               sent = hidp_send_ctrl_message(idev, hdr, ev->u.output.data,
+                                                       ev->u.output.size);
+               if (sent) {
+                       idev->report_req_pending = hdr;
+                       idev->report_req_timer =
+                               g_timeout_add_seconds(REPORT_REQ_TIMEOUT,
+                                               hidp_report_req_timeout, idev);
+               }
+               break;
+       case UHID_OUTPUT_REPORT:
+               /* Send DATA on interrupt channel */
+               hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT;
+               hidp_send_intr_message(idev, hdr, ev->u.output.data,
+                                                       ev->u.output.size);
+               break;
+       default:
+               DBG("Unsupported HID report type %u", ev->u.output.rtype);
+               return;
+       }
+}
+
+static void hidp_send_get_report(struct input_device *idev,
+                                                       struct uhid_event *ev)
+{
+       uint8_t hdr;
+       bool sent;
+
+       DBG("");
+
+       if (idev->report_req_pending) {
+               DBG("Old GET_REPORT or SET_REPORT still pending");
+               uhid_send_feature_answer(idev, NULL, 0, ev->u.feature.id,
+                                                                       EBUSY);
+               return;
+       }
+
+       /* Send GET_REPORT on control channel */
+       switch (ev->u.feature.rtype) {
+       case UHID_FEATURE_REPORT:
+               hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE;
+               break;
+       case UHID_INPUT_REPORT:
+               hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT;
+               break;
+       case UHID_OUTPUT_REPORT:
+               hdr = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT;
+               break;
+       default:
+               DBG("Unsupported HID report type %u", ev->u.feature.rtype);
+               return;
+       }
+
+       sent = hidp_send_ctrl_message(idev, hdr, &ev->u.feature.rnum,
+                                               sizeof(ev->u.feature.rnum));
+       if (sent) {
+               idev->report_req_pending = hdr;
+               idev->report_req_timer =
+                       g_timeout_add_seconds(REPORT_REQ_TIMEOUT,
+                                               hidp_report_req_timeout, idev);
+               idev->report_rsp_id = ev->u.feature.id;
+       }
+}
+
+static gboolean uhid_watch_cb(GIOChannel *chan, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct input_device *idev = user_data;
+       int fd;
+       ssize_t len;
+       struct uhid_event ev;
+
+       if (cond & (G_IO_ERR | G_IO_NVAL))
+               goto failed;
+
+       fd = g_io_channel_unix_get_fd(chan);
+       memset(&ev, 0, sizeof(ev));
+
+       len = read(fd, &ev, sizeof(ev));
+       if (len < 0) {
+               error("uHID dev read error: %s (%d)", strerror(errno), errno);
+               goto failed;
+       }
+
+       if ((size_t) len < sizeof(ev.type)) {
+               error("uHID dev read returned too few bytes");
+               goto failed;
+       }
+
+       DBG("uHID event type %u received (%zd bytes)", ev.type, len);
+
+       switch (ev.type) {
+       case UHID_START:
+       case UHID_STOP:
+               /* These are called to start and stop the underlying hardware.
+                * For HID we open the channels before creating the device so
+                * the hardware is always ready. No need to handle these.
+                * Note that these are also called when the kernel switches
+                * between device-drivers loaded on the HID device. But we can
+                * simply keep the hardware alive during transitions and it
+                * works just fine.
+                * The kernel never destroys a device itself! Only an explicit
+                * UHID_DESTROY request can remove a device.
+                */
+               break;
+       case UHID_OPEN:
+       case UHID_CLOSE:
+               /* OPEN/CLOSE are sent whenever user-space opens any interface
+                * provided by the kernel HID device. Whenever the open-count
+                * is non-zero we must be ready for I/O. As long as it is zero,
+                * we can decide to drop all I/O and put the device
+                * asleep This is optional, though. Moreover, some
+                * special device drivers are buggy in that regard, so
+                * maybe we just keep I/O always awake like HIDP in the
+                * kernel does.
+                */
+               break;
+       case UHID_OUTPUT:
+               hidp_send_set_report(idev, &ev);
+               break;
+       case UHID_FEATURE:
+               hidp_send_get_report(idev, &ev);
+               break;
+       case UHID_OUTPUT_EV:
+               /* This is only sent by kernels prior to linux-3.11. It
+                * requires us to parse HID-descriptors in user-space to
+                * properly handle it. This is redundant as the kernel
+                * does it already. That's why newer kernels assemble
+                * the output-reports and send it to us via UHID_OUTPUT.
+                * We never implemented this, so we rely on users to use
+                * recent-enough kernels if they want this feature. No reason
+                * to implement this for older kernels.
+                */
+               DBG("Unsupported uHID output event: type %u code %u value %d",
+                               ev.u.output_ev.type, ev.u.output_ev.code,
+                               ev.u.output_ev.value);
+               break;
+       default:
+               warn("unexpected uHID event");
+               break;
+       }
+
+       return TRUE;
+
+failed:
+       idev->uhid_watch = 0;
+       return FALSE;
+}
+
 static void epox_endian_quirk(unsigned char *data, int size)
 {
        /* USAGE_PAGE (Keyboard)        05 07
@@ -336,6 +885,100 @@ static int ioctl_connadd(struct hidp_connadd_req *req)
        return err;
 }
 
+static bool ioctl_is_connected(struct input_device *idev)
+{
+       struct hidp_conninfo ci;
+       int ctl;
+
+       /* Standard HID */
+       ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP);
+       if (ctl < 0) {
+               error("Can't open HIDP control socket");
+               return false;
+       }
+
+       memset(&ci, 0, sizeof(ci));
+       bacpy(&ci.bdaddr, &idev->dst);
+       if (ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) {
+               error("Can't get HIDP connection info");
+               close(ctl);
+               return false;
+       }
+
+       close(ctl);
+
+       if (ci.state != BT_CONNECTED)
+               return false;
+
+       return true;
+}
+
+static int ioctl_disconnect(struct input_device *idev, uint32_t flags)
+{
+       struct hidp_conndel_req req;
+       struct hidp_conninfo ci;
+       int ctl, err = 0;
+
+       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)) {
+               close(ctl);
+               return -ENOTCONN;
+       }
+
+       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);
+       }
+
+       close(ctl);
+
+       return err;
+}
+
+static int uhid_connadd(struct input_device *idev, struct hidp_connadd_req *req)
+{
+       int err = 0;
+       struct uhid_event ev;
+
+       if (!idev->uhid_created) {
+               /* create uHID device */
+               memset(&ev, 0, sizeof(ev));
+               ev.type = UHID_CREATE;
+               strncpy((char *) ev.u.create.name, req->name,
+                                               sizeof(ev.u.create.name) - 1);
+               ba2str(&idev->src, (char *) ev.u.create.phys);
+               ba2str(&idev->dst, (char *) ev.u.create.uniq);
+               ev.u.create.vendor = req->vendor;
+               ev.u.create.product = req->product;
+               ev.u.create.version = req->version;
+               ev.u.create.country = req->country;
+               ev.u.create.bus = BUS_BLUETOOTH;
+               ev.u.create.rd_data = req->rd_data;
+               ev.u.create.rd_size = req->rd_size;
+
+               if (write(idev->uhid_fd, &ev, sizeof(ev)) < 0) {
+                       err = -errno;
+                       error("Failed to create uHID device: %s (%d)",
+                                                       strerror(-err), -err);
+               } else
+                       idev->uhid_created = true;
+       }
+
+       return err;
+}
+
 static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition,
                                                                gpointer data)
 {
@@ -344,7 +987,11 @@ static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition,
 
        DBG("");
 
-       err = ioctl_connadd(idev->req);
+       if (idev->uhid_enabled)
+               err = uhid_connadd(idev, idev->req);
+       else
+               err = ioctl_connadd(idev->req);
+
        if (err < 0) {
                error("ioctl_connadd(): %s (%d)", strerror(-err), -err);
 
@@ -421,8 +1068,8 @@ static int hidp_add_connection(struct input_device *idev)
        req->product = btd_device_get_product(idev->device);
        req->version = btd_device_get_version(idev->device);
 
-       if (idev->name)
-               strncpy(req->name, idev->name, sizeof(req->name) - 1);
+       if (device_name_known(idev->device))
+               device_get_name(idev->device, req->name, sizeof(req->name));
 
        /* Encryption is mandatory for keyboards */
        if (req->subclass & 0x40) {
@@ -442,7 +1089,10 @@ static int hidp_add_connection(struct input_device *idev)
                return 0;
        }
 
-       err = ioctl_connadd(req);
+       if (idev->uhid_enabled)
+               err = uhid_connadd(idev, req);
+       else
+               err = ioctl_connadd(req);
 
 cleanup:
        g_free(req->rd_data);
@@ -451,37 +1101,16 @@ cleanup:
        return err;
 }
 
-static int is_connected(struct input_device *idev)
+static bool is_connected(struct input_device *idev)
 {
-       struct hidp_conninfo ci;
-       int ctl;
-
-       /* 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;
+       if (idev->uhid_enabled)
+               return (idev->intr_io != NULL && idev->ctrl_io != NULL);
        else
-               return 1;
+               return ioctl_is_connected(idev);
 }
 
 static int connection_disconnect(struct input_device *idev, uint32_t flags)
 {
-       struct hidp_conndel_req req;
-       struct hidp_conninfo ci;
-       int ctl, err = 0;
-
        if (!is_connected(idev))
                return -ENOTCONN;
 
@@ -491,34 +1120,10 @@ static int connection_disconnect(struct input_device *idev, uint32_t flags)
        if (idev->ctrl_io)
                g_io_channel_shutdown(idev->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;
+       if (idev->uhid_enabled)
+               return 0;
+       else
+               return ioctl_disconnect(idev, flags);
 }
 
 static void disconnect_cb(struct btd_device *device, gboolean removal,
@@ -557,6 +1162,7 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
                                                        gpointer user_data)
 {
        struct input_device *idev = user_data;
+       GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
        int err;
 
        if (conn_err) {
@@ -568,9 +1174,11 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
        if (err < 0)
                goto failed;
 
-       idev->intr_watch = g_io_add_watch(idev->intr_io,
-                                       G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                                       intr_watch_cb, idev);
+       if (idev->uhid_enabled)
+               cond |= G_IO_IN;
+
+       idev->intr_watch = g_io_add_watch(idev->intr_io, cond, intr_watch_cb,
+                                                                       idev);
 
        return;
 
@@ -596,6 +1204,7 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err,
                                                        gpointer user_data)
 {
        struct input_device *idev = user_data;
+       GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
        GIOChannel *io;
        GError *err = NULL;
 
@@ -620,9 +1229,11 @@ static void control_connect_cb(GIOChannel *chan, GError *conn_err,
 
        idev->intr_io = io;
 
-       idev->ctrl_watch = g_io_add_watch(idev->ctrl_io,
-                                       G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                                       ctrl_watch_cb, idev);
+       if (idev->uhid_enabled)
+               cond |= G_IO_IN;
+
+       idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond, ctrl_watch_cb,
+                                                                       idev);
 
        return;
 
@@ -641,12 +1252,12 @@ static int dev_connect(struct input_device *idev)
                bt_clear_cached_session(&idev->src, &idev->dst);
 
        io = bt_io_connect(control_connect_cb, idev,
-                       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);
+                               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);
        idev->ctrl_io = io;
 
        if (err == NULL)
@@ -664,8 +1275,9 @@ static gboolean input_device_auto_reconnect(gpointer user_data)
 
        DBG("path=%s, attempt=%d", idev->path, idev->reconnect_attempt);
 
-       /* Stop the recurrent reconnection attempts if the device is reconnected
-        * or is marked for removal. */
+       /* Stop the recurrent reconnection attempts if the device is
+        * reconnected or is marked for removal.
+        */
        if (device_is_temporary(idev->device) ||
                                        btd_device_is_connected(idev->device))
                return FALSE;
@@ -704,14 +1316,16 @@ static void input_device_enter_reconnect_mode(struct input_device *idev)
        DBG("path=%s reconnect_mode=%s", idev->path,
                                reconnect_mode_to_string(idev->reconnect_mode));
 
-       /* Only attempt an auto-reconnect when the device is required to accept
-        * reconnections from the host. */
+       /* Only attempt an auto-reconnect when the device is required to
+        * accept reconnections from the host.
+        */
        if (idev->reconnect_mode != RECONNECT_ANY &&
                                idev->reconnect_mode != RECONNECT_HOST)
                return;
 
-       /* If the device is temporary we are not required to reconnect with the
-        * device. This is likely the case of a removing device. */
+       /* If the device is temporary we are not required to reconnect
+        * with the device. This is likely the case of a removing device.
+        */
        if (device_is_temporary(idev->device) ||
                                        btd_device_is_connected(idev->device))
                return;
@@ -809,7 +1423,6 @@ static struct input_device *input_device_new(struct btd_service *service)
        const sdp_record_t *rec = btd_device_get_record(device, p->remote_uuid);
        struct btd_adapter *adapter = device_get_adapter(device);
        struct input_device *idev;
-       char name[HCI_MAX_NAME_LENGTH + 1];
 
        if (!rec)
                return NULL;
@@ -822,10 +1435,7 @@ static struct input_device *input_device_new(struct btd_service *service)
        idev->path = g_strdup(path);
        idev->handle = rec->handle;
        idev->disable_sdp = is_device_sdp_disable(rec);
-
-       device_get_name(device, name, HCI_MAX_NAME_LENGTH);
-       if (strlen(name) > 0)
-               idev->name = g_strdup(name);
+       idev->uhid_enabled = uhid_enabled;
 
        /* Initialize device properties */
        extract_hid_props(idev, rec);
@@ -855,6 +1465,8 @@ int input_device_register(struct btd_service *service)
        struct btd_device *device = btd_service_get_device(service);
        const char *path = device_get_path(device);
        struct input_device *idev;
+       int err;
+       GIOChannel *io;
 
        DBG("%s", path);
 
@@ -862,6 +1474,24 @@ int input_device_register(struct btd_service *service)
        if (!idev)
                return -EINVAL;
 
+       if (idev->uhid_enabled) {
+               idev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC);
+               if (idev->uhid_fd < 0) {
+                       err = errno;
+                       error("Failed to open uHID device: %s (%d)",
+                                                       strerror(err), err);
+                       input_device_free(idev);
+                       return -err;
+               }
+
+               io = g_io_channel_unix_new(idev->uhid_fd);
+               g_io_channel_set_encoding(io, NULL, NULL);
+               idev->uhid_watch = g_io_add_watch(io,
+                                               G_IO_IN | G_IO_ERR | G_IO_NVAL,
+                                               uhid_watch_cb, idev);
+               g_io_channel_unref(io);
+       }
+
        if (g_dbus_register_interface(btd_get_dbus_connection(),
                                        idev->path, INPUT_INTERFACE,
                                        NULL, NULL,
@@ -883,7 +1513,7 @@ static struct input_device *find_device(const bdaddr_t *src,
        struct btd_device *device;
        struct btd_service *service;
 
-       device = btd_adapter_find_device(adapter_find(src), dst);
+       device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
        if (device == NULL)
                return NULL;
 
@@ -899,12 +1529,31 @@ void input_device_unregister(struct btd_service *service)
        struct btd_device *device = btd_service_get_device(service);
        const char *path = device_get_path(device);
        struct input_device *idev = btd_service_get_user_data(service);
+       struct uhid_event ev;
 
        DBG("%s", path);
 
        g_dbus_unregister_interface(btd_get_dbus_connection(),
                                                idev->path, INPUT_INTERFACE);
 
+       if (idev->uhid_enabled) {
+               if (idev->uhid_watch) {
+                       g_source_remove(idev->uhid_watch);
+                       idev->uhid_watch = 0;
+               }
+
+               if (idev->uhid_created) {
+                       memset(&ev, 0, sizeof(ev));
+                       ev.type = UHID_DESTROY;
+                       if (write(idev->uhid_fd, &ev, sizeof(ev)) < 0)
+                               error("Failed to destroy uHID device: %s (%d)",
+                                                       strerror(errno), errno);
+               }
+
+               close(idev->uhid_fd);
+               idev->uhid_fd = -1;
+       }
+
        input_device_free(idev);
 }
 
@@ -913,17 +1562,15 @@ static int input_device_connadd(struct input_device *idev)
        int err;
 
        err = input_device_connected(idev);
-       if (err < 0)
-               goto error;
-
-       return 0;
+       if (err == 0)
+               return 0;
 
-error:
        if (idev->ctrl_io) {
                g_io_channel_shutdown(idev->ctrl_io, FALSE, NULL);
                g_io_channel_unref(idev->ctrl_io);
                idev->ctrl_io = NULL;
        }
+
        if (idev->intr_io) {
                g_io_channel_shutdown(idev->intr_io, FALSE, NULL);
                g_io_channel_unref(idev->intr_io);
@@ -945,28 +1592,30 @@ 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);
+       GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
 
        DBG("idev %p psm %d", idev, psm);
 
        if (!idev)
                return -ENOENT;
 
+       if (idev->uhid_enabled)
+               cond |= G_IO_IN;
+
        switch (psm) {
        case L2CAP_PSM_HIDP_CTRL:
                if (idev->ctrl_io)
                        return -EALREADY;
                idev->ctrl_io = g_io_channel_ref(io);
-               idev->ctrl_watch = g_io_add_watch(idev->ctrl_io,
-                                       G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                                       ctrl_watch_cb, idev);
+               idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond,
+                                                       ctrl_watch_cb, idev);
                break;
        case L2CAP_PSM_HIDP_INTR:
                if (idev->intr_io)
                        return -EALREADY;
                idev->intr_io = g_io_channel_ref(io);
-               idev->intr_watch = g_io_add_watch(idev->intr_io,
-                                       G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                                       intr_watch_cb, idev);
+               idev->intr_watch = g_io_add_watch(idev->intr_io, cond,
+                                                       intr_watch_cb, idev);
                break;
        }
 
index da2149c..51a9aee 100644 (file)
@@ -28,6 +28,7 @@ struct input_device;
 struct input_conn;
 
 void input_set_idle_timeout(int timeout);
+void input_enable_userspace_hid(bool state);
 
 int input_device_register(struct btd_service *service);
 void input_device_unregister(struct btd_service *service);
diff --git a/profiles/input/hidp_defs.h b/profiles/input/hidp_defs.h
new file mode 100644 (file)
index 0000000..5dc479a
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2003-2014  Marcel Holtmann <marcel@holtmann.org>
+ *  Copyright (C) 2014       Google Inc.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __HIDP_DEFS_H
+#define __HIDP_DEFS_H
+
+/* HIDP header masks */
+#define HIDP_HEADER_TRANS_MASK                 0xf0
+#define HIDP_HEADER_PARAM_MASK                 0x0f
+
+/* HIDP transaction types */
+#define HIDP_TRANS_HANDSHAKE                   0x00
+#define HIDP_TRANS_HID_CONTROL                 0x10
+#define HIDP_TRANS_GET_REPORT                  0x40
+#define HIDP_TRANS_SET_REPORT                  0x50
+#define HIDP_TRANS_GET_PROTOCOL                        0x60
+#define HIDP_TRANS_SET_PROTOCOL                        0x70
+#define HIDP_TRANS_GET_IDLE                    0x80
+#define HIDP_TRANS_SET_IDLE                    0x90
+#define HIDP_TRANS_DATA                                0xa0
+#define HIDP_TRANS_DATC                                0xb0
+
+/* HIDP handshake results */
+#define HIDP_HSHK_SUCCESSFUL                   0x00
+#define HIDP_HSHK_NOT_READY                    0x01
+#define HIDP_HSHK_ERR_INVALID_REPORT_ID                0x02
+#define HIDP_HSHK_ERR_UNSUPPORTED_REQUEST      0x03
+#define HIDP_HSHK_ERR_INVALID_PARAMETER                0x04
+#define HIDP_HSHK_ERR_UNKNOWN                  0x0e
+#define HIDP_HSHK_ERR_FATAL                    0x0f
+
+/* HIDP control operation parameters */
+#define HIDP_CTRL_NOP                          0x00
+#define HIDP_CTRL_HARD_RESET                   0x01
+#define HIDP_CTRL_SOFT_RESET                   0x02
+#define HIDP_CTRL_SUSPEND                      0x03
+#define HIDP_CTRL_EXIT_SUSPEND                 0x04
+#define HIDP_CTRL_VIRTUAL_CABLE_UNPLUG         0x05
+
+/* HIDP data transaction headers */
+#define HIDP_DATA_RTYPE_MASK                   0x03
+#define HIDP_DATA_RSRVD_MASK                   0x0c
+#define HIDP_DATA_RTYPE_OTHER                  0x00
+#define HIDP_DATA_RTYPE_INPUT                  0x01
+#define HIDP_DATA_RTYPE_OUPUT                  0x02
+#define HIDP_DATA_RTYPE_FEATURE                        0x03
+
+/* HIDP protocol header parameters */
+#define HIDP_PROTO_BOOT                                0x00
+#define HIDP_PROTO_REPORT                      0x01
+
+#define HIDP_VIRTUAL_CABLE_UNPLUG              0
+#define HIDP_BOOT_PROTOCOL_MODE                        1
+#define HIDP_BLUETOOTH_VENDOR_ID               9
+#define HIDP_WAITING_FOR_RETURN                        10
+#define HIDP_WAITING_FOR_SEND_ACK              11
+
+#endif /* __HIDP_DEFS_H */
index 4647fef..e82e827 100644 (file)
 
 #include <glib.h>
 
-#include "log.h"
+#include "src/log.h"
 
 #include "lib/uuid.h"
 #include "src/adapter.h"
 #include "src/device.h"
 #include "src/profile.h"
 #include "src/service.h"
+#include "src/shared/util.h"
+
+#include "src/plugin.h"
 
-#include "plugin.h"
 #include "suspend.h"
 #include "attrib/att.h"
 #include "attrib/gattrib.h"
-#include "attio.h"
+#include "src/attio.h"
 #include "attrib/gatt.h"
 
 #define HOG_UUID               "00001812-0000-1000-8000-00805f9b34fb"
@@ -74,6 +76,7 @@
 
 #define HOG_REPORT_MAP_MAX_SIZE        512
 #define HID_INFO_SIZE                  4
+#define ATT_NOTIFICATION_HEADER_SIZE   3
 
 struct hog_device {
        uint16_t                id;
@@ -100,46 +103,55 @@ struct report {
        struct hog_device       *hogdev;
 };
 
-struct disc_desc_cb_data {
-       uint16_t end;
-       gpointer data;
-};
-
 static gboolean suspend_supported = FALSE;
 static GSList *devices = NULL;
 
-static void report_value_cb(const uint8_t *pdu, uint16_t len,
-                                                       gpointer user_data)
+static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
 {
        struct report *report = user_data;
        struct hog_device *hogdev = report->hogdev;
        struct uhid_event ev;
-       uint16_t report_size = len - 3;
        uint8_t *buf;
+       ssize_t status;
 
-       if (len < 3) { /* 1-byte opcode + 2-byte handle */
+       if (len < ATT_NOTIFICATION_HEADER_SIZE) {
                error("Malformed ATT notification");
                return;
        }
 
+       pdu += ATT_NOTIFICATION_HEADER_SIZE;
+       len -= ATT_NOTIFICATION_HEADER_SIZE;
+
        memset(&ev, 0, sizeof(ev));
        ev.type = UHID_INPUT;
-       ev.u.input.size = MIN(report_size, UHID_DATA_MAX);
-
        buf = ev.u.input.data;
+
        if (hogdev->has_report_id) {
-               *buf = report->id;
-               buf++;
-               ev.u.input.size++;
+               buf[0] = report->id;
+               len = MIN(len, sizeof(ev.u.input.data) - 1);
+               memcpy(buf + 1, pdu, len);
+               ev.u.input.size = ++len;
+       } else {
+               len = MIN(len, sizeof(ev.u.input.data));
+               memcpy(buf, pdu, len);
+               ev.u.input.size = len;
        }
 
-       memcpy(buf, &pdu[3], MIN(report_size, UHID_DATA_MAX));
+       status = write(hogdev->uhid_fd, &ev, sizeof(ev));
+       if (status < 0) {
+               error("uHID dev write error: %s (%d)", strerror(errno), errno);
+               return;
+       }
 
-       if (write(hogdev->uhid_fd, &ev, sizeof(ev)) < 0)
-               error("uHID write failed: %s", strerror(errno));
-       else
-               DBG("Report from HoG device 0x%04X written to uHID fd %d",
-                                               hogdev->id, hogdev->uhid_fd);
+       /* uHID kernel driver does not handle partial writes */
+       if ((size_t) status < sizeof(ev)) {
+               error("uHID dev write error: partial write (%zd of %zu bytes)",
+                                                       status, sizeof(ev));
+               return;
+       }
+
+       DBG("HoG report (%u bytes) -> uHID fd %d", ev.u.input.size,
+                                                       hogdev->uhid_fd);
 }
 
 static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
@@ -197,94 +209,56 @@ static void external_report_reference_cb(guint8 status, const guint8 *pdu,
                                        guint16 plen, gpointer user_data);
 
 
-static void discover_descriptor_cb(guint8 status, const guint8 *pdu,
-                                       guint16 len, gpointer user_data)
+static void discover_descriptor_cb(uint8_t status, GSList *descs,
+                                                               void *user_data)
 {
-       struct disc_desc_cb_data *ddcb_data = user_data;
        struct report *report;
        struct hog_device *hogdev;
-       struct att_data_list *list = NULL;
        GAttrib *attrib = NULL;
-       uint8_t format;
-       uint16_t handle = 0xffff;
-       uint16_t end = ddcb_data->end;
-       int i;
-
-       if (status == ATT_ECODE_ATTR_NOT_FOUND) {
-               DBG("Discover all characteristic descriptors finished");
-               goto done;
-       }
 
        if (status != 0) {
-               error("Discover all characteristic descriptors failed: %s",
+               error("Discover all descriptors failed: %s",
                                                        att_ecode2str(status));
-               goto done;
-       }
-
-       list = dec_find_info_resp(pdu, len, &format);
-       if (list == NULL)
                return;
+       }
 
-       if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
-               goto done;
-
-       for (i = 0; i < list->num; i++) {
-               uint16_t uuid16;
-               uint8_t *value;
-
-               value = list->data[i];
-               handle = att_get_u16(value);
-               uuid16 = att_get_u16(&value[2]);
+       for ( ; descs; descs = descs->next) {
+               struct gatt_desc *desc = descs->data;
 
-               switch (uuid16) {
+               switch (desc->uuid16) {
                case GATT_CLIENT_CHARAC_CFG_UUID:
-                       report = ddcb_data->data;
+                       report = user_data;
                        attrib = report->hogdev->attrib;
-                       write_ccc(handle, report);
+                       write_ccc(desc->handle, report);
                        break;
                case GATT_REPORT_REFERENCE:
-                       report = ddcb_data->data;
+                       report = user_data;
                        attrib = report->hogdev->attrib;
-                       gatt_read_char(attrib, handle,
+                       gatt_read_char(attrib, desc->handle,
                                                report_reference_cb, report);
                        break;
                case GATT_EXTERNAL_REPORT_REFERENCE:
-                       hogdev = ddcb_data->data;
+                       hogdev = user_data;
                        attrib = hogdev->attrib;
-                       gatt_read_char(attrib, handle,
+                       gatt_read_char(attrib, desc->handle,
                                        external_report_reference_cb, hogdev);
                        break;
                }
        }
-
-done:
-       att_data_list_free(list);
-
-       if (handle != 0xffff && handle < end)
-               gatt_discover_char_desc(attrib, handle + 1, end,
-                                       discover_descriptor_cb, ddcb_data);
-       else
-               g_free(ddcb_data);
 }
 
 static void discover_descriptor(GAttrib *attrib, uint16_t start, uint16_t end,
                                                        gpointer user_data)
 {
-       struct disc_desc_cb_data *ddcb_data;
-
        if (start > end)
                return;
 
-       ddcb_data = g_new0(struct disc_desc_cb_data, 1);
-       ddcb_data->end = end;
-       ddcb_data->data = user_data;
-
-       gatt_discover_char_desc(attrib, start, end, discover_descriptor_cb,
-                                                               ddcb_data);
+       gatt_discover_desc(attrib, start, end, NULL,
+                                       discover_descriptor_cb, user_data);
 }
 
-static void external_service_char_cb(GSList *chars, guint8 status,
-                                                       gpointer user_data)
+static void external_service_char_cb(uint8_t status, GSList *chars,
+                                                               void *user_data)
 {
        struct hog_device *hogdev = user_data;
        struct gatt_primary *prim = hogdev->hog_primary;
@@ -335,7 +309,7 @@ static void external_report_reference_cb(guint8 status, const guint8 *pdu,
                return;
        }
 
-       uuid16 = att_get_u16(&pdu[1]);
+       uuid16 = get_le16(&pdu[1]);
        DBG("External report reference read, external report characteristic "
                                                "UUID: 0x%04x", uuid16);
        bt_uuid16_create(&uuid, uuid16);
@@ -343,14 +317,78 @@ static void external_report_reference_cb(guint8 status, const guint8 *pdu,
                                        external_service_char_cb, hogdev);
 }
 
+static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len,
+                                                               bool *is_long)
+{
+       if (!blen)
+               return false;
+
+       *is_long = (buf[0] == 0xfe);
+
+       if (*is_long) {
+               if (blen < 3)
+                       return false;
+
+               /*
+                * long item:
+                * byte 0 -> 0xFE
+                * byte 1 -> data size
+                * byte 2 -> tag
+                * + data
+                */
+
+               *len = buf[1] + 3;
+       } else {
+               uint8_t b_size;
+
+               /*
+                * short item:
+                * byte 0[1..0] -> data size (=0, 1, 2, 4)
+                * byte 0[3..2] -> type
+                * byte 0[7..4] -> tag
+                * + data
+                */
+
+               b_size = buf[0] & 0x03;
+               *len = (b_size ? 1 << (b_size - 1) : 0) + 1;
+       }
+
+       /* item length should be no more than input buffer length */
+       return *len <= blen;
+}
+
+static char *item2string(char *str, uint8_t *buf, uint8_t len)
+{
+       char *p = str;
+       int i;
+
+       /*
+        * Since long item tags are not defined except for vendor ones, we
+        * just ensure that short items are printed properly (up to 5 bytes).
+        */
+       for (i = 0; i < 6 && i < len; i++)
+               p += sprintf(p, " %02x", buf[i]);
+
+       /*
+        * If there are some data left, just add continuation mark to indicate
+        * this.
+        */
+       if (i < len)
+               sprintf(p, " ...");
+
+       return str;
+}
+
 static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
                                                        gpointer user_data)
 {
        struct hog_device *hogdev = user_data;
+       struct btd_adapter *adapter = device_get_adapter(hogdev->device);
        uint8_t value[HOG_REPORT_MAP_MAX_SIZE];
        struct uhid_event ev;
        uint16_t vendor_src, vendor, product, version;
        ssize_t vlen;
+       char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
        int i;
 
        if (status != 0) {
@@ -365,19 +403,25 @@ static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
        }
 
        DBG("Report MAP:");
-       for (i = 0; i < vlen; i++) {
-               switch (value[i]) {
-               case 0x85:
-               case 0x86:
-               case 0x87:
-                       hogdev->has_report_id = TRUE;
-               }
+       for (i = 0; i < vlen;) {
+               ssize_t ilen = 0;
+               bool long_item = false;
+
+               if (get_descriptor_item_info(&value[i], vlen - i, &ilen,
+                                                               &long_item)) {
+                       /* Report ID is short item with prefix 100001xx */
+                       if (!long_item && (value[i] & 0xfc) == 0x84)
+                               hogdev->has_report_id = TRUE;
+
+                       DBG("\t%s", item2string(itemstr, &value[i], ilen));
 
-               if (i % 2 == 0) {
-                       if (i + 1 == vlen)
-                               DBG("\t %02x", value[i]);
-                       else
-                               DBG("\t %02x %02x", value[i], value[i + 1]);
+                       i += ilen;
+               } else {
+                       error("Report Map parsing failed at %d", i);
+
+                       /* Just print remaining items at once and break */
+                       DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
+                       break;
                }
        }
 
@@ -391,7 +435,13 @@ static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
        /* create uHID device */
        memset(&ev, 0, sizeof(ev));
        ev.type = UHID_CREATE;
-       strcpy((char *) ev.u.create.name, "bluez-hog-device");
+       if (device_name_known(hogdev->device))
+               device_get_name(hogdev->device, (char *) ev.u.create.name,
+                                               sizeof(ev.u.create.name));
+       else
+               strcpy((char *) ev.u.create.name, "bluez-hog-device");
+       ba2str(btd_adapter_get_address(adapter), (char *) ev.u.create.phys);
+       ba2str(device_get_address(hogdev->device), (char *) ev.u.create.uniq);
        ev.u.create.vendor = vendor;
        ev.u.create.product = product;
        ev.u.create.version = version;
@@ -423,7 +473,7 @@ static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
                return;
        }
 
-       hogdev->bcdhid = att_get_u16(&value[0]);
+       hogdev->bcdhid = get_le16(&value[0]);
        hogdev->bcountrycode = value[2];
        hogdev->flags = value[3];
 
@@ -463,7 +513,7 @@ static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
                                                                hogdev->id);
 }
 
-static void char_discovered_cb(GSList *chars, guint8 status, gpointer user_data)
+static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
 {
        struct hog_device *hogdev = user_data;
        struct gatt_primary *prim = hogdev->hog_primary;
@@ -589,10 +639,10 @@ static void forward_report(struct hog_device *hogdev,
        if (hogdev->attrib == NULL)
                return;
 
-       if (report->decl->properties & ATT_CHAR_PROPER_WRITE)
+       if (report->decl->properties & GATT_CHR_PROP_WRITE)
                gatt_write_char(hogdev->attrib, report->decl->value_handle,
                                data, size, output_written_cb, hogdev);
-       else if (report->decl->properties & ATT_CHAR_PROPER_WRITE_WITHOUT_RESP)
+       else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
                gatt_write_cmd(hogdev->attrib, report->decl->value_handle,
                                                data, size, NULL, NULL);
 }
index abfb64f..3e1d65a 100644 (file)
@@ -7,3 +7,7 @@
 # Set idle timeout (in minutes) before the connection will
 # be disconnect (defaults to 0 for no timeout)
 #IdleTimeout=30
+
+# Enable HID protocol handling in userspace input profile
+# Defaults to false (HIDP handled in HIDP kernel module)
+#UserspaceHID=true
index 660043e..9712d2c 100644 (file)
 #include <bluetooth/sdp.h>
 #include <bluetooth/sdp_lib.h>
 
-#include "log.h"
-#include "plugin.h"
+#include "src/log.h"
+#include "src/plugin.h"
 
 #include "lib/uuid.h"
-#include "../src/adapter.h"
-#include "../src/device.h"
-#include "../src/profile.h"
-#include "../src/service.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
 
 #include "device.h"
 #include "server.h"
@@ -97,15 +97,24 @@ static int input_init(void)
        config = load_config_file(CONFIGDIR "/input.conf");
        if (config) {
                int idle_timeout;
+               gboolean uhid_enabled;
 
                idle_timeout = g_key_file_get_integer(config, "General",
-                                               "IdleTimeout", &err);
-               if (err) {
-                       DBG("input.conf: %s", err->message);
-                       g_error_free(err);
-               }
-
-               input_set_idle_timeout(idle_timeout * 60);
+                                                       "IdleTimeout", &err);
+               if (!err) {
+                       DBG("input.conf: IdleTimeout=%d", idle_timeout);
+                       input_set_idle_timeout(idle_timeout * 60);
+               } else
+                       g_clear_error(&err);
+
+               uhid_enabled = g_key_file_get_boolean(config, "General",
+                                                       "UserspaceHID", &err);
+               if (!err) {
+                       DBG("input.conf: UserspaceHID=%s", uhid_enabled ?
+                                                       "true" : "false");
+                       input_enable_userspace_hid(uhid_enabled);
+               } else
+                       g_clear_error(&err);
        }
 
        btd_profile_register(&input_profile);
index f6f85a0..772a605 100644 (file)
 #include <glib.h>
 #include <dbus/dbus.h>
 
-#include "log.h"
+#include "src/log.h"
 
-#include "glib-helper.h"
+#include "src/uuid-helper.h"
 #include "btio/btio.h"
 #include "lib/uuid.h"
-#include "../src/adapter.h"
-#include "../src/device.h"
-#include "../src/profile.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
 
 #include "device.h"
 #include "server.h"
@@ -68,15 +68,10 @@ struct sixaxis_data {
        uint16_t psm;
 };
 
-static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data);
-
 static void sixaxis_sdp_cb(struct btd_device *dev, int err, void *user_data)
 {
        struct sixaxis_data *data = user_data;
-       struct input_server *server;
-       GError *gerr = NULL;
        const bdaddr_t *src;
-       GSList *l;
 
        DBG("err %d (%s)", err, strerror(-err));
 
@@ -85,29 +80,10 @@ static void sixaxis_sdp_cb(struct btd_device *dev, int err, void *user_data)
 
        src = btd_adapter_get_address(device_get_adapter(dev));
 
-       l = g_slist_find_custom(servers, src, server_cmp);
-       if (!l)
-               goto fail;
-
-       server = l->data;
-
-       err = input_device_set_channel(src, device_get_address(dev),
-                                                       data->psm, data->chan);
-       if (err < 0)
+       if (input_device_set_channel(src, device_get_address(dev), data->psm,
+                                                               data->chan) < 0)
                goto fail;
 
-       if (server->confirm) {
-               if (!bt_io_accept(server->confirm, connect_event_cb, server,
-                                                               NULL, &gerr)) {
-                       error("bt_io_accept: %s", gerr->message);
-                       g_error_free(gerr);
-                       goto fail;
-               }
-
-               g_io_channel_unref(server->confirm);
-               server->confirm = NULL;
-       }
-
        g_io_channel_unref(data->chan);
        g_free(data);
 
@@ -125,10 +101,7 @@ static void sixaxis_browse_sdp(const bdaddr_t *src, const bdaddr_t *dst,
        struct btd_device *device;
        struct sixaxis_data *data;
 
-       if (psm != L2CAP_PSM_HIDP_CTRL)
-               return;
-
-       device = btd_adapter_find_device(adapter_find(src), dst);
+       device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
        if (!device)
                return;
 
@@ -136,25 +109,33 @@ static void sixaxis_browse_sdp(const bdaddr_t *src, const bdaddr_t *dst,
        data->chan = g_io_channel_ref(chan);
        data->psm = psm;
 
-       device_discover_services(device);
+       if (psm == L2CAP_PSM_HIDP_CTRL)
+               device_discover_services(device);
+
        device_wait_for_svc_complete(device, sixaxis_sdp_cb, data);
 }
 
 static bool dev_is_sixaxis(const bdaddr_t *src, const bdaddr_t *dst)
 {
        struct btd_device *device;
+       uint16_t vid, pid;
 
-       device = btd_adapter_find_device(adapter_find(src), dst);
+       device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
        if (!device)
                return false;
 
-       if (btd_device_get_vendor(device) != 0x054c)
-               return false;
+       vid = btd_device_get_vendor(device);
+       pid = btd_device_get_product(device);
 
-       if (btd_device_get_product(device) != 0x0268)
-               return false;
+       /* DualShock 3 */
+       if (vid == 0x054c && pid == 0x0268)
+               return true;
+
+       /* DualShock 4 */
+       if (vid == 0x054c && pid == 0x05c4)
+               return true;
 
-       return true;
+       return false;
 }
 
 static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data)
@@ -228,7 +209,7 @@ static void auth_callback(DBusError *derr, void *user_data)
                goto reject;
        }
 
-       if (!input_device_exists(&src, &dst) && dev_is_sixaxis(&src, &dst))
+       if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst))
                return;
 
        if (!bt_io_accept(server->confirm, connect_event_cb, server,
index 75dd536..542ae25 100644 (file)
@@ -37,7 +37,7 @@
 
 #include <glib.h>
 
-#include "log.h"
+#include "src/log.h"
 #include "suspend.h"
 
 #define HOG_SUSPEND_FIFO       "/tmp/hogsuspend"
index 0a719a2..87304c5 100644 (file)
 
 #include <glib.h>
 
-#include "log.h"
-#include "bnep.h"
+#include "src/log.h"
+#include "src/shared/util.h"
 #include "lib/uuid.h"
+#include "btio/btio.h"
+
+#include "bnep.h"
 
 #define CON_SETUP_RETRIES      3
 #define CON_SETUP_TO           9
@@ -67,30 +70,21 @@ struct __service_16 {
        uint16_t src;
 } __attribute__ ((packed));
 
-struct bnep_conn {
+struct bnep {
        GIOChannel      *io;
        uint16_t        src;
        uint16_t        dst;
+       bdaddr_t        dst_addr;
+       char    iface[16];
        guint   attempts;
        guint   setup_to;
-       void    *data;
+       guint   watch;
        bnep_connect_cb conn_cb;
+       void    *conn_data;
+       bnep_disconnect_cb disconn_cb;
+       void    *disconn_data;
 };
 
-static void free_bnep_connect(struct bnep_conn *bc)
-{
-       if (!bc)
-               return;
-
-       if (bc->io) {
-               g_io_channel_unref(bc->io);
-               bc->io = NULL;
-       }
-
-       g_free(bc);
-       bc = NULL;
-}
-
 uint16_t bnep_service_id(const char *svc)
 {
        int i;
@@ -161,7 +155,7 @@ int bnep_cleanup(void)
        return 0;
 }
 
-int bnep_kill_connection(const bdaddr_t *dst)
+static int bnep_conndel(const bdaddr_t *dst)
 {
        struct bnep_conndel_req req;
 
@@ -177,13 +171,14 @@ int bnep_kill_connection(const bdaddr_t *dst)
        return 0;
 }
 
-int bnep_connadd(int sk, uint16_t role, char *dev)
+static int bnep_connadd(int sk, uint16_t role, char *dev)
 {
        struct bnep_connadd_req req;
 
-       memset(dev, 0, 16);
        memset(&req, 0, sizeof(req));
-       strcpy(req.device, "bnep%d");
+       strncpy(req.device, dev, 16);
+       req.device[15] = '\0';
+
        req.sock = sk;
        req.role = role;
        if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
@@ -197,7 +192,7 @@ int bnep_connadd(int sk, uint16_t role, char *dev)
        return 0;
 }
 
-int bnep_if_up(const char *devname)
+static int bnep_if_up(const char *devname)
 {
        struct ifreq ifr;
        int sk, err;
@@ -210,7 +205,7 @@ int bnep_if_up(const char *devname)
        ifr.ifr_flags |= IFF_UP;
        ifr.ifr_flags |= IFF_MULTICAST;
 
-       err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+       err = ioctl(sk, SIOCSIFFLAGS, (void *) &ifr);
 
        close(sk);
 
@@ -222,7 +217,7 @@ int bnep_if_up(const char *devname)
        return 0;
 }
 
-int bnep_if_down(const char *devname)
+static int bnep_if_down(const char *devname)
 {
        struct ifreq ifr;
        int sk, err;
@@ -235,7 +230,7 @@ int bnep_if_down(const char *devname)
        ifr.ifr_flags &= ~IFF_UP;
 
        /* Bring down the interface */
-       err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr);
+       err = ioctl(sk, SIOCSIFFLAGS, (void *) &ifr);
 
        close(sk);
 
@@ -247,23 +242,33 @@ int bnep_if_down(const char *devname)
        return 0;
 }
 
+static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+                                                               gpointer data)
+{
+       struct bnep *session = data;
+
+       if (session->disconn_cb)
+               session->disconn_cb(session->disconn_data);
+
+       return FALSE;
+}
+
 static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
                                                                gpointer data)
 {
-       struct bnep_conn *bc = data;
+       struct bnep *session = data;
        struct bnep_control_rsp *rsp;
        struct timeval timeo;
        char pkt[BNEP_MTU];
-       char iface[16];
        ssize_t r;
        int sk;
 
        if (cond & G_IO_NVAL)
-               goto failed;
+               return FALSE;
 
-       if (bc->setup_to > 0) {
-               g_source_remove(bc->setup_to);
-               bc->setup_to = 0;
+       if (session->setup_to > 0) {
+               g_source_remove(session->setup_to);
+               session->setup_to = 0;
        }
 
        if (cond & (G_IO_HUP | G_IO_ERR)) {
@@ -310,30 +315,35 @@ static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
        timeo.tv_sec = 0;
        setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
 
-       sk = g_io_channel_unix_get_fd(bc->io);
-       if (bnep_connadd(sk, bc->src, iface)) {
+       sk = g_io_channel_unix_get_fd(session->io);
+       if (bnep_connadd(sk, session->src, session->iface)) {
                error("bnep conn could not be added");
                goto failed;
        }
 
-       if (bnep_if_up(iface)) {
-               error("could not up %s", iface);
+       if (bnep_if_up(session->iface)) {
+               error("could not up %s", session->iface);
+               bnep_conndel(&session->dst_addr);
                goto failed;
        }
 
-       bc->conn_cb(chan, iface, 0, bc->data);
-       free_bnep_connect(bc);
+       session->watch = g_io_add_watch(session->io,
+                                       G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       (GIOFunc) bnep_watchdog_cb, session);
+       g_io_channel_unref(session->io);
+       session->io = NULL;
+
+       session->conn_cb(session->iface, 0, session->conn_data);
 
        return FALSE;
 
 failed:
-       bc->conn_cb(NULL, NULL, -EIO, bc->data);
-       free_bnep_connect(bc);
+       session->conn_cb(NULL, -EIO, session->conn_data);
 
        return FALSE;
 }
 
-static int bnep_setup_conn_req(struct bnep_conn *bc)
+static int bnep_setup_conn_req(struct bnep *session)
 {
        struct bnep_setup_conn_req *req;
        struct __service_16 *s;
@@ -346,67 +356,142 @@ static int bnep_setup_conn_req(struct bnep_conn *bc)
        req->ctrl = BNEP_SETUP_CONN_REQ;
        req->uuid_size = 2;     /* 16bit UUID */
        s = (void *) req->service;
-       s->src = htons(bc->src);
-       s->dst = htons(bc->dst);
+       s->src = htons(session->src);
+       s->dst = htons(session->dst);
 
-       fd = g_io_channel_unix_get_fd(bc->io);
+       fd = g_io_channel_unix_get_fd(session->io);
        if (write(fd, pkt, sizeof(*req) + sizeof(*s)) < 0) {
                error("bnep connection req send failed: %s", strerror(errno));
                return -errno;
        }
 
-       bc->attempts++;
+       session->attempts++;
 
        return 0;
 }
 
 static gboolean bnep_conn_req_to(gpointer user_data)
 {
-       struct bnep_conn *bc = user_data;
+       struct bnep *session = user_data;
 
-       if (bc->attempts == CON_SETUP_RETRIES) {
+       if (session->attempts == CON_SETUP_RETRIES) {
                error("Too many bnep connection attempts");
        } else {
                error("bnep connection setup TO, retrying...");
-               if (bnep_setup_conn_req(bc) == 0)
+               if (bnep_setup_conn_req(session) == 0)
                        return TRUE;
        }
 
-       bc->conn_cb(NULL, NULL, -ETIMEDOUT, bc->data);
-       free_bnep_connect(bc);
+       session->conn_cb(NULL, -ETIMEDOUT, session->conn_data);
 
        return FALSE;
 }
 
-int bnep_connect(int sk, uint16_t src, uint16_t dst, bnep_connect_cb conn_cb,
-                                                               void *data)
+struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role,
+                                                               char *iface)
 {
-       struct bnep_conn *bc;
+       struct bnep *session;
+       int dup_fd;
+
+       dup_fd = dup(sk);
+       if (dup_fd < 0)
+               return NULL;
+
+       session = g_new0(struct bnep, 1);
+       session->io = g_io_channel_unix_new(dup_fd);
+       session->src = local_role;
+       session->dst = remote_role;
+       strncpy(session->iface, iface, 16);
+       session->iface[15] = '\0';
+
+       g_io_channel_set_close_on_unref(session->io, TRUE);
+       session->watch = g_io_add_watch(session->io,
+                               G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       (GIOFunc) bnep_setup_cb, session);
+
+       return session;
+}
+
+void bnep_free(struct bnep *session)
+{
+       if (!session)
+               return;
+
+       if (session->io) {
+               g_io_channel_shutdown(session->io, FALSE, NULL);
+               g_io_channel_unref(session->io);
+               session->io = NULL;
+       }
+
+       if (session->watch > 0) {
+               g_source_remove(session->watch);
+               session->watch = 0;
+       }
+
+       g_free(session);
+}
+
+int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb, void *data)
+{
+       GError *gerr = NULL;
        int err;
 
-       if (!conn_cb)
+       if (!session || !conn_cb)
                return -EINVAL;
 
-       bc = g_new0(struct bnep_conn, 1);
-       bc->io = g_io_channel_unix_new(sk);
-       bc->attempts = 0;
-       bc->src = src;
-       bc->dst = dst;
-       bc->conn_cb = conn_cb;
-       bc->data = data;
+       session->attempts = 0;
+       session->conn_cb = conn_cb;
+       session->conn_data = data;
+
+       bt_io_get(session->io, &gerr, BT_IO_OPT_DEST_BDADDR, &session->dst_addr,
+                                                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               return -EINVAL;
+       }
 
-       err = bnep_setup_conn_req(bc);
+       err = bnep_setup_conn_req(session);
        if (err < 0)
                return err;
 
-       bc->setup_to = g_timeout_add_seconds(CON_SETUP_TO,
-                                                       bnep_conn_req_to, bc);
-       g_io_add_watch(bc->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                                       bnep_setup_cb, bc);
+       session->setup_to = g_timeout_add_seconds(CON_SETUP_TO,
+                                               bnep_conn_req_to, session);
        return 0;
 }
 
-int bnep_add_to_bridge(const char *devname, const char *bridge)
+void bnep_disconnect(struct bnep *session)
+{
+       if (!session)
+               return;
+
+       if (session->watch > 0) {
+               g_source_remove(session->watch);
+               session->watch = 0;
+       }
+
+       if (session->io) {
+               g_io_channel_unref(session->io);
+               session->io = NULL;
+       }
+
+       bnep_if_down(session->iface);
+       bnep_conndel(&session->dst_addr);
+}
+
+void bnep_set_disconnect(struct bnep *session, bnep_disconnect_cb disconn_cb,
+                                                               void *data)
+{
+       if (!session || !disconn_cb)
+               return;
+
+       if (!session->disconn_cb && !session->disconn_data) {
+               session->disconn_cb = disconn_cb;
+               session->disconn_data = data;
+       }
+}
+
+static int bnep_add_to_bridge(const char *devname, const char *bridge)
 {
        int ifindex;
        struct ifreq ifr;
@@ -437,15 +522,17 @@ int bnep_add_to_bridge(const char *devname, const char *bridge)
        return 0;
 }
 
-int bnep_del_from_bridge(const char *devname, const char *bridge)
+static int bnep_del_from_bridge(const char *devname, const char *bridge)
 {
-       int ifindex = if_nametoindex(devname);
+       int ifindex;
        struct ifreq ifr;
        int sk, err;
 
        if (!devname || !bridge)
                return -EINVAL;
 
+       ifindex = if_nametoindex(devname);
+
        sk = socket(AF_INET, SOCK_STREAM, 0);
        if (sk < 0)
                return -1;
@@ -465,3 +552,119 @@ int bnep_del_from_bridge(const char *devname, const char *bridge)
 
        return 0;
 }
+
+int bnep_server_add(int sk, uint16_t dst, char *bridge, char *iface,
+                                               const bdaddr_t *addr)
+{
+       if (!bridge || !bridge || !iface || !addr)
+               return -EINVAL;
+
+       if (bnep_connadd(sk, dst, iface) < 0) {
+               error("Can't add connection to the bridge %s: %s(%d)",
+                                               bridge, strerror(errno), errno);
+               return -errno;
+       }
+
+       if (bnep_add_to_bridge(iface, bridge) < 0) {
+               error("Can't add %s to the bridge %s: %s(%d)",
+                                       iface, bridge, strerror(errno), errno);
+               bnep_conndel(addr);
+               return -errno;
+       }
+
+       if (bnep_if_up(iface) < 0) {
+               error("Can't up the interface %s: %s(%d)",
+                                               iface, strerror(errno), errno);
+               return -errno;
+       }
+
+       return 0;
+}
+
+void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr)
+{
+       if (!bridge || !iface || !addr)
+               return;
+
+       bnep_del_from_bridge(iface, bridge);
+       bnep_if_down(iface);
+       bnep_conndel(addr);
+}
+
+ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp)
+{
+       struct bnep_control_rsp rsp;
+
+       rsp.type = type;
+       rsp.ctrl = ctrl;
+       rsp.resp = htons(resp);
+
+       return send(sk, &rsp, sizeof(rsp), 0);
+}
+
+uint16_t bnep_setup_chk(uint16_t dst, uint16_t src)
+{
+       /* Allowed PAN Profile scenarios */
+       switch (dst) {
+       case BNEP_SVC_NAP:
+       case BNEP_SVC_GN:
+               if (src == BNEP_SVC_PANU)
+                       return 0;
+               return BNEP_CONN_INVALID_SRC;
+       case BNEP_SVC_PANU:
+               if (src == BNEP_SVC_PANU ||  src == BNEP_SVC_GN ||
+                                                       src == BNEP_SVC_NAP)
+                       return 0;
+
+               return BNEP_CONN_INVALID_SRC;
+       }
+
+       return BNEP_CONN_INVALID_DST;
+}
+
+uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
+                                                               uint16_t *src)
+{
+       const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+                                       0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
+       uint8_t *dest, *source;
+       uint32_t val;
+
+       dest = req->service;
+       source = req->service + req->uuid_size;
+
+       switch (req->uuid_size) {
+       case 2: /* UUID16 */
+               *dst = get_be16(dest);
+               *src = get_be16(source);
+               break;
+       case 16: /* UUID128 */
+               /* Check that the bytes in the UUID, except the service ID
+                * itself, are correct. The service ID is checked in
+                * bnep_setup_chk(). */
+               if (memcmp(&dest[4], bt_base, sizeof(bt_base)) != 0)
+                       return BNEP_CONN_INVALID_DST;
+               if (memcmp(&source[4], bt_base, sizeof(bt_base)) != 0)
+                       return BNEP_CONN_INVALID_SRC;
+
+               /* Intentional no-break */
+
+       case 4: /* UUID32 */
+               val = get_be32(dest);
+               if (val > 0xffff)
+                       return BNEP_CONN_INVALID_DST;
+
+               *dst = val;
+
+               val = get_be32(source);
+               if (val > 0xffff)
+                       return BNEP_CONN_INVALID_SRC;
+
+               *src = val;
+               break;
+       default:
+               return BNEP_CONN_INVALID_SVC;
+       }
+
+       return BNEP_SUCCESS;
+}
index 9043e46..bc43d4f 100644 (file)
@@ -21,6 +21,8 @@
  *
  */
 
+struct bnep;
+
 int bnep_init(void);
 int bnep_cleanup(void);
 
@@ -28,15 +30,22 @@ 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(const bdaddr_t *dst);
-
-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);
-int bnep_del_from_bridge(const char *devname, const char *bridge);
+struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role,
+                                                               char *iface);
+void bnep_free(struct bnep *session);
 
-typedef void (*bnep_connect_cb) (GIOChannel *chan, char *iface, int err,
-                                                               void *data);
-int bnep_connect(int sk, uint16_t src, uint16_t dst, bnep_connect_cb conn_cb,
+typedef void (*bnep_connect_cb) (char *iface, int err, void *data);
+int bnep_connect(struct bnep *b, bnep_connect_cb conn_cb, void *data);
+typedef void (*bnep_disconnect_cb) (void *data);
+void bnep_set_disconnect(struct bnep *session, bnep_disconnect_cb disconn_cb,
                                                                void *data);
+void bnep_disconnect(struct bnep *session);
+
+int bnep_server_add(int sk, uint16_t dst, char *bridge, char *iface,
+                                                       const bdaddr_t *addr);
+void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr);
+
+ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp);
+uint16_t bnep_setup_chk(uint16_t dst_role, uint16_t src_role);
+uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
+                                                               uint16_t *src);
index 9aff319..cc73989 100644 (file)
 
 #include <glib.h>
 #include <gdbus/gdbus.h>
-#include <btio/btio.h>
 
-#include "log.h"
-#include "dbus-common.h"
-#include "adapter.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
+#include "btio/btio.h"
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/error.h"
 
-#include "error.h"
 #include "bnep.h"
 #include "connection.h"
 
 #define NETWORK_PEER_INTERFACE "org.bluez.Network1"
+#define BNEP_INTERFACE "bnep%d"
 
 typedef enum {
        CONNECTED,
@@ -72,6 +73,7 @@ struct network_conn {
        guint           dc_id;
        struct network_peer *peer;
        DBusMessage     *connect;
+       struct bnep     *session;
 };
 
 static GSList *peers = NULL;
@@ -106,8 +108,7 @@ static struct network_conn *find_connection_by_state(GSList *list,
        return NULL;
 }
 
-static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
-                               gpointer data)
+static void bnep_disconn_cb(gpointer data)
 {
        struct network_conn *nc = data;
        DBusConnection *conn = btd_get_dbus_connection();
@@ -126,12 +127,13 @@ static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
 
        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");
+       strncpy(nc->dev, BNEP_INTERFACE, 16);
+       nc->dev[15] = '\0';
 
-       return FALSE;
+       bnep_free(nc->session);
+       nc->session = NULL;
 }
 
 static void local_connect_cb(struct network_conn *nc, int err)
@@ -155,12 +157,21 @@ static void local_connect_cb(struct network_conn *nc, int err)
 static void cancel_connection(struct network_conn *nc, int err)
 {
        btd_service_connecting_complete(nc->service, err);
+
        if (nc->connect)
                local_connect_cb(nc, err);
 
-       g_io_channel_shutdown(nc->io, TRUE, NULL);
-       g_io_channel_unref(nc->io);
-       nc->io = NULL;
+       if (nc->io) {
+               g_io_channel_shutdown(nc->io, FALSE, NULL);
+               g_io_channel_unref(nc->io);
+               nc->io = NULL;
+       }
+
+       if (nc->state == CONNECTED)
+               bnep_disconnect(nc->session);
+
+       bnep_free(nc->session);
+       nc->session = NULL;
 
        nc->state = DISCONNECTED;
 }
@@ -169,11 +180,7 @@ 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(device_get_address(nc->peer->device));
-       } else if (nc->io)
-               cancel_connection(nc, -EIO);
+       cancel_connection(nc, -EIO);
 }
 
 static void disconnect_cb(struct btd_device *device, gboolean removal,
@@ -186,7 +193,7 @@ static void disconnect_cb(struct btd_device *device, gboolean removal,
        connection_destroy(NULL, user_data);
 }
 
-static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data)
+static void bnep_conn_cb(char *iface, int err, void *data)
 {
        struct network_conn *nc = data;
        const char *path;
@@ -220,10 +227,6 @@ static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data)
        nc->state = CONNECTED;
        nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb,
                                                                nc, NULL);
-       g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                                       bnep_watchdog_cb, nc);
-       g_io_channel_unref(nc->io);
-       nc->io = NULL;
 
        return;
 
@@ -242,12 +245,23 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
        }
 
        sk = g_io_channel_unix_get_fd(nc->io);
-       perr = bnep_connect(sk, BNEP_SVC_PANU, nc->id, bnep_conn_cb, nc);
+       nc->session = bnep_new(sk, BNEP_SVC_PANU, nc->id, BNEP_INTERFACE);
+       if (!nc->session)
+               goto failed;
+
+       perr = bnep_connect(nc->session, bnep_conn_cb, nc);
        if (perr < 0) {
                error("bnep connect(): %s (%d)", strerror(-perr), -perr);
                goto failed;
        }
 
+       bnep_set_disconnect(nc->session, bnep_disconn_cb, nc);
+
+       if (nc->io) {
+               g_io_channel_unref(nc->io);
+               nc->io = NULL;
+       }
+
        return;
 
 failed:
index 8ac2dec..0fe98a0 100644 (file)
 #include <glib.h>
 #include <gdbus/gdbus.h>
 
-#include "log.h"
-#include "plugin.h"
+#include "src/log.h"
+#include "src/plugin.h"
 
 #include "lib/uuid.h"
-#include "adapter.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+
 #include "bnep.h"
 #include "connection.h"
 #include "server.h"
@@ -200,8 +201,6 @@ static int network_init(void)
 
 static void network_exit(void)
 {
-       server_exit();
-
        btd_profile_unregister(&panu_profile);
        btd_profile_unregister(&gn_profile);
        btd_profile_unregister(&nap_profile);
index b3aab11..3fb031f 100644 (file)
 
 #include <glib.h>
 #include <gdbus/gdbus.h>
-#include <btio/btio.h>
 
+#include "btio/btio.h"
 #include "lib/uuid.h"
-#include "../src/dbus-common.h"
-#include "../src/adapter.h"
-
-#include "log.h"
-#include "error.h"
-#include "sdpd.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/sdpd.h"
 
 #include "bnep.h"
 #include "server.h"
 
 #define NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1"
+#define BNEP_INTERFACE "bnep%d"
 #define SETUP_TIMEOUT          1
 
 /* Pending Authorization */
@@ -251,112 +251,6 @@ static sdp_record_t *server_record_new(const char *name, uint16_t id)
        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;
-
-       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);
-
-       strncpy(session->dev, devname, sizeof(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)
-{
-       const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
-                               0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
-       uint8_t *dest, *source;
-       uint32_t val;
-
-       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 16: /* UUID128 */
-               /* Check that the bytes in the UUID, except the service ID
-                * itself, are correct. The service ID is checked in
-                * bnep_setup_chk(). */
-               if (memcmp(&dest[4], bt_base, sizeof(bt_base)) != 0)
-                       return BNEP_CONN_INVALID_DST;
-               if (memcmp(&source[4], bt_base, sizeof(bt_base)) != 0)
-                       return BNEP_CONN_INVALID_SRC;
-
-               /* Intentional no-break */
-
-       case 4: /* UUID32 */
-               val = bt_get_be32(dest);
-               if (val > 0xffff)
-                       return BNEP_CONN_INVALID_DST;
-               *dst_role = val;
-
-               val = bt_get_be32(source);
-               if (val > 0xffff)
-                       return BNEP_CONN_INVALID_SRC;
-               *src_role = val;
-               break;
-       default:
-               return BNEP_CONN_INVALID_SVC;
-       }
-
-       return BNEP_SUCCESS;
-}
-
 static void session_free(void *data)
 {
        struct network_session *session = data;
@@ -454,7 +348,11 @@ static gboolean bnep_setup(GIOChannel *chan,
                goto reply;
        }
 
-       if (server_connadd(ns, na->setup, dst_role) < 0)
+       strncpy(na->setup->dev, BNEP_INTERFACE, 16);
+       na->setup->dev[15] = '\0';
+
+       if (bnep_server_add(sk, dst_role, ns->bridge, na->setup->dev,
+                                                       &na->setup->dst) < 0)
                goto reply;
 
        na->setup = NULL;
@@ -462,7 +360,7 @@ static gboolean bnep_setup(GIOChannel *chan,
        rsp = BNEP_SUCCESS;
 
 reply:
-       send_bnep_ctrl_rsp(sk, rsp);
+       bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
 
        return FALSE;
 }
@@ -570,10 +468,6 @@ int server_init(gboolean secure)
        return 0;
 }
 
-void server_exit(void)
-{
-}
-
 static uint32_t register_server_record(struct network_server *ns)
 {
        sdp_record_t *record;
@@ -605,10 +499,7 @@ static void server_remove_sessions(struct network_server *ns)
                if (*session->dev == '\0')
                        continue;
 
-               bnep_del_from_bridge(session->dev, ns->bridge);
-               bnep_if_down(session->dev);
-
-               bnep_kill_connection(&session->dst);
+               bnep_server_delete(ns->bridge, session->dev, &session->dst);
        }
 
        g_slist_free_full(ns->sessions, session_free);
index 2edd342..a76e6f7 100644 (file)
@@ -22,6 +22,5 @@
  */
 
 int server_init(gboolean secure);
-void server_exit(void);
 int server_register(struct btd_adapter *adapter, uint16_t id);
 int server_unregister(struct btd_adapter *adapter, uint16_t id);
index 2f8d4e7..3d50b8d 100644 (file)
 #include <stdbool.h>
 
 #include <glib.h>
-#include <adapter.h>
 
 #include <dbus/dbus.h>
 #include <gdbus/gdbus.h>
 
 #include "lib/uuid.h"
-#include "log.h"
+#include "src/log.h"
+#include "src/adapter.h"
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
 #include "attrib/att-database.h"
 #include "attrib/gatt-service.h"
-#include "attrib-server.h"
-#include "device.h"
-#include "profile.h"
-#include "attio.h"
-#include "dbus-common.h"
+#include "src/attrib-server.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
 #include "reporter.h"
 #include "immalert.h"
 
@@ -251,7 +252,7 @@ void imm_alert_register(struct btd_adapter *adapter)
                                /* Alert level characteristic */
                                GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID,
                                GATT_OPT_CHR_PROPS,
-                                       ATT_CHAR_PROPER_WRITE_WITHOUT_RESP,
+                                       GATT_CHR_PROP_WRITE_WITHOUT_RESP,
                                GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
                                        imm_alert_alert_lvl_write, imadapter,
                                GATT_OPT_INVALID);
index a7ed96c..476803a 100644 (file)
 #include <stdbool.h>
 
 #include <glib.h>
-#include <adapter.h>
 
 #include <dbus/dbus.h>
 #include <gdbus/gdbus.h>
 
 #include "lib/uuid.h"
-#include "log.h"
+#include "src/log.h"
+#include "src/adapter.h"
+#include "src/device.h"
 #include "attrib/att-database.h"
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
 #include "attrib/gatt-service.h"
-#include "attrib-server.h"
-#include "device.h"
-#include "profile.h"
-#include "attio.h"
-#include "dbus-common.h"
+#include "src/attrib-server.h"
+#include "src/service.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
 #include "reporter.h"
 #include "linkloss.h"
 
@@ -292,7 +294,7 @@ void link_loss_register(struct btd_adapter *adapter)
                        /* Alert level characteristic */
                        GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID,
                        GATT_OPT_CHR_PROPS,
-                               ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_WRITE,
+                               GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE,
                        GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
                                link_loss_alert_lvl_read, lladapter,
                        GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
index 46468d2..8a350d4 100644 (file)
 #include <glib.h>
 #include <gdbus/gdbus.h>
 
-#include "log.h"
-#include "plugin.h"
+#include "src/log.h"
+#include "src/plugin.h"
 #include "manager.h"
-#include "hcid.h"
 
 static GKeyFile *config = NULL;
 
index 7dab23f..3f0f63c 100644 (file)
 #include <gdbus/gdbus.h>
 
 #include "lib/uuid.h"
-#include "adapter.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
 #include "attrib/att.h"
 #include "attrib/gattrib.h"
 #include "attrib/gatt.h"
index eaa5b0d..f2e0739 100644 (file)
 #include <bluetooth/bluetooth.h>
 
 #include "lib/uuid.h"
-#include "dbus-common.h"
-#include "adapter.h"
-#include "device.h"
-#include "error.h"
-#include "log.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/error.h"
+#include "src/log.h"
 #include "attrib/att.h"
 #include "attrib/gattrib.h"
 #include "attrib/gatt.h"
-#include "attio.h"
+#include "src/attio.h"
+#include "src/textfile.h"
+
 #include "monitor.h"
-#include "textfile.h"
 
 #define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor1"
 
@@ -190,8 +191,8 @@ static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
                                PROXIMITY_INTERFACE, "LinkLossAlertLevel");
 }
 
-static void char_discovered_cb(GSList *characteristics, guint8 status,
-                                                       gpointer user_data)
+static void char_discovered_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
 {
        struct monitor *monitor = user_data;
        struct gatt_char *chr;
@@ -259,8 +260,8 @@ static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
        DBG("Tx Power Level: %02x", (int8_t) value[0]);
 }
 
-static void tx_power_handle_cb(GSList *characteristics, guint8 status,
-                                                       gpointer user_data)
+static void tx_power_handle_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
 {
        struct monitor *monitor = user_data;
        struct gatt_char *chr;
@@ -346,8 +347,8 @@ static void write_immediate_alert(struct monitor *monitor)
                                                immediate_written, monitor);
 }
 
-static void immediate_handle_cb(GSList *characteristics, guint8 status,
-                                                       gpointer user_data)
+static void immediate_handle_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
 {
        struct monitor *monitor = user_data;
        struct gatt_char *chr;
index dbb593a..fb91bc1 100644 (file)
 #include <errno.h>
 
 #include <glib.h>
-#include <adapter.h>
 
 #include <dbus/dbus.h>
 #include <gdbus/gdbus.h>
 
-#include "log.h"
+#include "src/log.h"
 
 #include "lib/uuid.h"
-#include "dbus-common.h"
-#include "error.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
-#include "hcid.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
 #include "attrib/att-database.h"
-#include "attrib-server.h"
+#include "src/attrib-server.h"
+
 #include "reporter.h"
 #include "linkloss.h"
 #include "immalert.h"
@@ -116,19 +116,19 @@ static void register_tx_power(struct btd_adapter *adapter)
 
        /* Primary service definition */
        bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
-       att_put_u16(TX_POWER_SVC_UUID, &atval[0]);
+       put_le16(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]);
+       atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_NOTIFY;
+       put_le16(h + 1, &atval[1]);
+       put_le16(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]);
+       atval[0] = 0x00;
        attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
 
        /* Client characteristic configuration */
index 8cc9533..ad55cd2 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <errno.h>
 #include <gdbus/gdbus.h>
-#include "plugin.h"
+#include "src/plugin.h"
 #include "manager.h"
 
 static int sap_init(void)
index bfb81a5..5c2a0f1 100644 (file)
 
 #include <stdbool.h>
 
-#include "log.h"
-#include "adapter.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
+#include "src/log.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
 
 #include "manager.h"
 #include "server.h"
index 47dedf7..854105e 100644 (file)
@@ -30,9 +30,9 @@
 #include <gdbus/gdbus.h>
 #include <stdint.h>
 
-#include "dbus-common.h"
-#include "error.h"
-#include "log.h"
+#include "src/dbus-common.h"
+#include "src/error.h"
+#include "src/log.h"
 #include "sap.h"
 
 #define SAP_DUMMY_IFACE "org.bluez.SimAccessTest1"
index 39169a0..d043029 100644 (file)
@@ -36,7 +36,7 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
-#include "log.h"
+#include "src/log.h"
 #include "sap.h"
 
 #define STE_SIMD_SOCK  "/dev/socket/catd_a"
index 119862d..20c6cab 100644 (file)
 
 #include "lib/uuid.h"
 #include "btio/btio.h"
-#include "adapter.h"
-#include "sdpd.h"
-#include "log.h"
-#include "error.h"
-#include "dbus-common.h"
+#include "src/adapter.h"
+#include "src/sdpd.h"
+#include "src/log.h"
+#include "src/error.h"
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
 #include "sap.h"
 #include "server.h"
 
@@ -312,7 +313,7 @@ static void connect_req(struct sap_server *server,
 
        stop_guard_timer(server);
 
-       maxmsgsize = bt_get_be16(&param->val);
+       maxmsgsize = get_be16(&param->val);
 
        DBG("Connect MaxMsgSize: 0x%04x", maxmsgsize);
 
@@ -667,7 +668,7 @@ int sap_connect_rsp(void *sap_device, uint8_t status)
                param = (struct sap_parameter *) &buf[size];
                param->id = SAP_PARAM_ID_MAX_MSG_SIZE;
                param->len = htons(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
-               bt_put_be16(SAP_BUF_SIZE, &param->val);
+               put_be16(SAP_BUF_SIZE, &param->val);
                size += PARAMETER_SIZE(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
 
                /* fall */
index 0236af6..25c906e 100644 (file)
 #include <errno.h>
 
 #include "lib/uuid.h"
-#include "log.h"
-#include "plugin.h"
-#include "adapter.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
+#include "src/log.h"
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
 #include "attrib/att.h"
 #include "attrib/gattrib.h"
 #include "attrib/gatt.h"
-#include "attio.h"
+#include "src/attio.h"
 
 #define SCAN_PARAMETERS_UUID           "00001813-0000-1000-8000-00805f9b34fb"
 
@@ -66,8 +67,8 @@ static void write_scan_params(GAttrib *attrib, uint16_t handle)
 {
        uint8_t value[4];
 
-       att_put_u16(SCAN_INTERVAL, &value[0]);
-       att_put_u16(SCAN_WINDOW, &value[2]);
+       put_le16(SCAN_INTERVAL, &value[0]);
+       put_le16(SCAN_WINDOW, &value[2]);
 
        gatt_write_cmd(attrib, handle, value, sizeof(value), NULL, NULL);
 }
@@ -101,43 +102,33 @@ static void ccc_written_cb(guint8 status, const guint8 *pdu,
                                refresh_value_cb, scan, NULL);
 }
 
-static void discover_descriptor_cb(guint8 status, const guint8 *pdu,
-                                       guint16 len, gpointer user_data)
+static void discover_descriptor_cb(uint8_t status, GSList *descs,
+                                                               void *user_data)
 {
        struct scan *scan = user_data;
-       struct att_data_list *list;
-       uint8_t *ptr;
-       uint16_t uuid16, handle;
+       struct gatt_desc *desc;
        uint8_t value[2];
-       uint8_t format;
 
-       list = dec_find_info_resp(pdu, len, &format);
-       if (list == NULL)
+       if (status != 0) {
+               error("Discover descriptors failed: %s", att_ecode2str(status));
                return;
+       }
 
-       if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
-               goto done;
-
-       ptr = list->data[0];
-       handle = att_get_u16(ptr);
-       uuid16 = att_get_u16(&ptr[2]);
-
-       if (uuid16 != GATT_CLIENT_CHARAC_CFG_UUID)
-               goto done;
+       /* There will be only one descriptor on list and it will be CCC */
+       desc = descs->data;
 
-       att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
-       gatt_write_char(scan->attrib, handle, value, sizeof(value),
+       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+       gatt_write_char(scan->attrib, desc->handle, value, sizeof(value),
                                                ccc_written_cb, user_data);
-done:
-       att_data_list_free(list);
 }
 
-static void refresh_discovered_cb(GSList *chars, guint8 status,
-                                               gpointer user_data)
+static void refresh_discovered_cb(uint8_t status, GSList *chars,
+                                                               void *user_data)
 {
        struct scan *scan = user_data;
        struct gatt_char *chr;
        uint16_t start, end;
+       bt_uuid_t uuid;
 
        if (status) {
                error("Scan Refresh %s", att_ecode2str(status));
@@ -161,12 +152,13 @@ static void refresh_discovered_cb(GSList *chars, guint8 status,
 
        scan->refresh_handle = chr->value_handle;
 
-       gatt_discover_char_desc(scan->attrib, start, end,
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+       gatt_discover_desc(scan->attrib, start, end, &uuid,
                                        discover_descriptor_cb, user_data);
 }
 
-static void iwin_discovered_cb(GSList *chars, guint8 status,
-                                               gpointer user_data)
+static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data)
 {
        struct scan *scan = user_data;
        struct gatt_char *chr;
index bd8413d..e3ab923 100644 (file)
 #include <gdbus/gdbus.h>
 
 #include "lib/uuid.h"
-#include "plugin.h"
-#include "dbus-common.h"
-#include "adapter.h"
-#include "device.h"
-#include "profile.h"
-#include "service.h"
-#include "error.h"
-#include "log.h"
+#include "src/plugin.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "src/error.h"
+#include "src/log.h"
 #include "attrib/gattrib.h"
-#include "attio.h"
+#include "src/attio.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
 
@@ -361,7 +362,7 @@ static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
                return;
        }
 
-       raw = att_get_u32(pdu);
+       raw = get_le32(pdu);
        m.mant = raw & 0x00FFFFFF;
        m.exp = ((int32_t) raw) >> 24;
 
@@ -382,7 +383,7 @@ static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
                        return;
                }
 
-               ts.tm_year = att_get_u16(pdu) - 1900;
+               ts.tm_year = get_le16(pdu) - 1900;
                ts.tm_mon = *(pdu + 2) - 1;
                ts.tm_mday = *(pdu + 3);
                ts.tm_hour = *(pdu + 4);
@@ -466,7 +467,7 @@ static void interval_ind_handler(const uint8_t *pdu, uint16_t len,
                return;
        }
 
-       interval = att_get_u16(pdu + 3);
+       interval = get_le16(pdu + 3);
        change_property(t, "Interval", &interval);
 
        opdu = g_attrib_get_buffer(t->attrib, &plen);
@@ -501,8 +502,8 @@ static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
                return;
        }
 
-       min = att_get_u16(&value[0]);
-       max = att_get_u16(&value[2]);
+       min = get_le16(&value[0]);
+       max = get_le16(&value[2]);
 
        if (min == 0 || min > max) {
                DBG("Invalid range");
@@ -568,18 +569,14 @@ static void process_thermometer_desc(struct characteristic *ch, uint16_t uuid,
                return;
        }
 
-       att_put_u16(val, atval);
+       put_le16(val, atval);
        gatt_write_char(ch->t->attrib, handle, atval, sizeof(atval),
                                                        write_ccc_cb, msg);
 }
 
-static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
-                                                       gpointer user_data)
+static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
 {
        struct characteristic *ch = user_data;
-       struct att_data_list *list = NULL;
-       uint8_t format;
-       int i;
 
        if (status != 0) {
                error("Discover all characteristic descriptors failed [%s]: %s",
@@ -587,27 +584,13 @@ static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
                goto done;
        }
 
-       list = dec_find_info_resp(pdu, len, &format);
-       if (list == NULL)
-               goto done;
-
-       if (format != ATT_FIND_INFO_RESP_FMT_16BIT)
-               goto done;
+       for ( ; descs; descs = descs->next) {
+               struct gatt_desc *desc = descs->data;
 
-       for (i = 0; i < list->num; i++) {
-               uint8_t *value;
-               uint16_t handle, uuid;
-
-               value = list->data[i];
-               handle = att_get_u16(value);
-               uuid = att_get_u16(value + 2);
-
-               process_thermometer_desc(ch, uuid, handle);
+               process_thermometer_desc(ch, desc->uuid16, desc->handle);
        }
 
 done:
-       if (list != NULL)
-               att_data_list_free(list);
        g_free(ch);
 }
 
@@ -633,7 +616,7 @@ static void discover_desc(struct thermometer *t, struct gatt_char *c,
        ch->t = t;
        memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
 
-       gatt_discover_char_desc(t->attrib, start, end, discover_desc_cb, ch);
+       gatt_discover_desc(t->attrib, start, end, NULL, discover_desc_cb, ch);
 }
 
 static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
@@ -689,7 +672,7 @@ static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
                return;
        }
 
-       interval = att_get_u16(&value[0]);
+       interval = get_le16(&value[0]);
        change_property(t, "Interval", &interval);
 }
 
@@ -720,12 +703,12 @@ static void process_thermometer_char(struct thermometer *t,
 
                gatt_read_char(t->attrib, c->value_handle, read_interval_cb, t);
 
-               if (c->properties & ATT_CHAR_PROPER_WRITE) {
+               if (c->properties & GATT_CHR_PROP_WRITE) {
                        t->interval_val_handle = c->value_handle;
                        need_desc = true;
                }
 
-               if (c->properties & ATT_CHAR_PROPER_INDICATE) {
+               if (c->properties & GATT_CHR_PROP_INDICATE) {
                        t->attio_interval_id = g_attrib_register(t->attrib,
                                        ATT_OP_HANDLE_IND, c->value_handle,
                                        interval_ind_handler, t, NULL);
@@ -737,8 +720,8 @@ static void process_thermometer_char(struct thermometer *t,
        }
 }
 
-static void configure_thermometer_cb(GSList *characteristics, guint8 status,
-                                                       gpointer user_data)
+static void configure_thermometer_cb(uint8_t status, GSList *characteristics,
+                                                               void *user_data)
 {
        struct thermometer *t = user_data;
        GSList *l;
@@ -789,7 +772,7 @@ static void enable_final_measurement(gpointer data, gpointer user_data)
        if (t->attrib == NULL || !handle)
                return;
 
-       att_put_u16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
+       put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
        msg = g_strdup("Enable Temperature Measurement indications");
 
        gatt_write_char(t->attrib, handle, value, sizeof(value),
@@ -806,7 +789,7 @@ static void enable_intermediate_measurement(gpointer data, gpointer user_data)
        if (t->attrib == NULL || !handle)
                return;
 
-       att_put_u16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+       put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
        msg = g_strdup("Enable Intermediate Temperature notifications");
 
        gatt_write_char(t->attrib, handle, value, sizeof(value),
@@ -823,7 +806,7 @@ static void disable_final_measurement(gpointer data, gpointer user_data)
        if (t->attrib == NULL || !handle)
                return;
 
-       att_put_u16(0x0000, value);
+       put_le16(0x0000, value);
        msg = g_strdup("Disable Temperature Measurement indications");
 
        gatt_write_char(t->attrib, handle, value, sizeof(value),
@@ -840,7 +823,7 @@ static void disable_intermediate_measurement(gpointer data, gpointer user_data)
        if (t->attrib == NULL || !handle)
                return;
 
-       att_put_u16(0x0000, value);
+       put_le16(0x0000, value);
        msg = g_strdup("Disable Intermediate Temperature notifications");
 
        gatt_write_char(t->attrib, handle, value, sizeof(value),
@@ -1070,7 +1053,7 @@ static void property_set_interval(const GDBusPropertyTable *property,
                return;
        }
 
-       att_put_u16(val, &atval[0]);
+       put_le16(val, &atval[0]);
 
        interval_data = g_new0(struct tmp_interval_data, 1);
        interval_data->thermometer = t;
index 178d4d2..1716a5e 100644 (file)
 #include <errno.h>
 #include <stdbool.h>
 
-#include <adapter.h>
-#include <device.h>
-#include <profile.h>
-#include <plugin.h>
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/plugin.h"
 
 #include "lib/uuid.h"
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
 #include "attrib/att-database.h"
-#include "attrib-server.h"
+#include "src/shared/util.h"
+#include "src/attrib-server.h"
 #include "attrib/gatt-service.h"
-#include "log.h"
+#include "src/log.h"
 
 #define CURRENT_TIME_SVC_UUID          0x1805
 #define REF_TIME_UPDATE_SVC_UUID       0x1806
@@ -90,7 +91,7 @@ static int encode_current_time(uint8_t value[10])
                return -EINVAL;
        }
 
-       att_put_u16(1900 + tm.tm_year, &value[0]); /* Year */
+       put_le16(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 */
@@ -153,14 +154,14 @@ static gboolean register_current_time_service(struct btd_adapter *adapter)
        return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
                                /* CT Time characteristic */
                                GATT_OPT_CHR_UUID16, CT_TIME_CHR_UUID,
-                               GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ |
-                                                       ATT_CHAR_PROPER_NOTIFY,
+                               GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+                                                       GATT_CHR_PROP_NOTIFY,
                                GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
                                                current_time_read, adapter,
 
                                /* Local Time Information characteristic */
                                GATT_OPT_CHR_UUID16, LOCAL_TIME_INFO_CHR_UUID,
-                               GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ,
+                               GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
                                GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
                                                local_time_info_read, adapter,
 
@@ -217,13 +218,13 @@ static gboolean register_ref_time_update_service(struct btd_adapter *adapter)
                                /* Time Update control point */
                                GATT_OPT_CHR_UUID16, TIME_UPDATE_CTRL_CHR_UUID,
                                GATT_OPT_CHR_PROPS,
-                                       ATT_CHAR_PROPER_WRITE_WITHOUT_RESP,
+                                       GATT_CHR_PROP_WRITE_WITHOUT_RESP,
                                GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
                                                time_update_control, adapter,
 
                                /* Time Update status */
                                GATT_OPT_CHR_UUID16, TIME_UPDATE_STAT_CHR_UUID,
-                               GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ,
+                               GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
                                GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
                                                time_update_status, adapter,
 
index 9480103..f5f8c8c 100644 (file)
@@ -27,6 +27,7 @@
 #endif
 
 #include <stdio.h>
+#include <inttypes.h>
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -51,6 +52,7 @@
 #include "lib/uuid.h"
 #include "lib/mgmt.h"
 #include "src/shared/mgmt.h"
+#include "src/shared/util.h"
 
 #include "hcid.h"
 #include "sdpd.h"
@@ -59,7 +61,7 @@
 #include "profile.h"
 #include "dbus-common.h"
 #include "error.h"
-#include "glib-helper.h"
+#include "uuid-helper.h"
 #include "agent.h"
 #include "storage.h"
 #include "attrib/gattrib.h"
 
 #define ADAPTER_INTERFACE      "org.bluez.Adapter1"
 
-/* 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 MODE_OFF               0x00
 #define MODE_CONNECTABLE       0x01
 #define MODE_DISCOVERABLE      0x02
@@ -104,6 +97,33 @@ static uint8_t mgmt_revision = 0;
 
 static GSList *adapter_drivers = NULL;
 
+static GSList *disconnect_list = NULL;
+static GSList *conn_fail_list = NULL;
+
+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;
+       bool master;
+       uint8_t enc_size;
+       uint16_t ediv;
+       uint64_t rand;
+       uint8_t val[16];
+};
+
+struct irk_info {
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+       uint8_t val[16];
+};
+
 struct watch_client {
        struct btd_adapter *adapter;
        char *owner;
@@ -112,6 +132,7 @@ struct watch_client {
 
 struct service_auth {
        guint id;
+       unsigned int svc_id;
        service_auth_cb cb;
        void *user_data;
        const char *uuid;
@@ -405,6 +426,7 @@ static void store_adapter_info(struct btd_adapter *adapter)
 static void trigger_pairable_timeout(struct btd_adapter *adapter);
 static void adapter_start(struct btd_adapter *adapter);
 static void adapter_stop(struct btd_adapter *adapter);
+static void trigger_passive_scanning(struct btd_adapter *adapter);
 
 static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
 {
@@ -434,6 +456,12 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
                }
        }
 
+       if (changed_mask & MGMT_SETTING_LE) {
+               if ((adapter->current_settings & MGMT_SETTING_POWERED) &&
+                               (adapter->current_settings & MGMT_SETTING_LE))
+                       trigger_passive_scanning(adapter);
+       }
+
        if (changed_mask & MGMT_SETTING_CONNECTABLE)
                g_dbus_emit_property_changed(dbus_conn, adapter->path,
                                        ADAPTER_INTERFACE, "Connectable");
@@ -464,7 +492,7 @@ static void new_settings_callback(uint16_t index, uint16_t length,
                return;
        }
 
-       settings = bt_get_le32(param);
+       settings = get_le32(param);
 
        if (settings == adapter->current_settings)
                return;
@@ -691,23 +719,36 @@ int adapter_set_name(struct btd_adapter *adapter, const char *name)
 }
 
 struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
-                                                       const bdaddr_t *dst)
+                                                       const bdaddr_t *dst,
+                                                       uint8_t bdaddr_type)
 {
+       struct device_addr_type addr;
        struct btd_device *device;
-       char addr[18];
        GSList *list;
 
        if (!adapter)
                return NULL;
 
-       ba2str(dst, addr);
+       bacpy(&addr.bdaddr, dst);
+       addr.bdaddr_type = bdaddr_type;
 
-       list = g_slist_find_custom(adapter->devices, addr, device_address_cmp);
+       list = g_slist_find_custom(adapter->devices, &addr,
+                                                       device_addr_type_cmp);
        if (!list)
                return NULL;
 
        device = list->data;
 
+       /*
+        * If we're looking up based on public address and the address
+        * was not previously used over this bearer we may need to
+        * update LE or BR/EDR support information.
+        */
+       if (bdaddr_type == BDADDR_BREDR)
+               device_set_bredr_support(device);
+       else
+               device_set_le_support(device, bdaddr_type);
+
        return device;
 }
 
@@ -1023,6 +1064,10 @@ static void service_auth_cancel(struct service_auth *auth)
 {
        DBusError derr;
 
+       if (auth->svc_id > 0)
+               device_remove_svc_complete_callback(auth->device,
+                                                               auth->svc_id);
+
        dbus_error_init(&derr);
        dbus_set_error_const(&derr, ERROR_INTERFACE ".Canceled", NULL);
 
@@ -1031,13 +1076,13 @@ static void service_auth_cancel(struct service_auth *auth)
        dbus_error_free(&derr);
 
        if (auth->agent != NULL)
-               agent_cancel(auth->agent);
+               agent_unref(auth->agent);
 
        g_free(auth);
 }
 
-static void adapter_remove_device(struct btd_adapter *adapter,
-                                               struct btd_device *dev)
+void btd_adapter_remove_device(struct btd_adapter *adapter,
+                               struct btd_device *dev)
 {
        GList *l;
 
@@ -1081,7 +1126,7 @@ struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter,
        if (!adapter)
                return NULL;
 
-       device = btd_adapter_find_device(adapter, addr);
+       device = btd_adapter_find_device(adapter, addr, addr_type);
        if (device)
                return device;
 
@@ -1531,7 +1576,7 @@ static gboolean remove_temp_devices(gpointer user_data)
                next = g_slist_next(l);
 
                if (device_is_temporary(dev))
-                       adapter_remove_device(adapter, dev);
+                       btd_adapter_remove_device(adapter, dev);
        }
 
        return FALSE;
@@ -2099,7 +2144,7 @@ static gboolean property_get_uuids(const GDBusPropertyTable *property,
 
                dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
                                                                &uuid);
-               g_free(uuid);
+               free(uuid);
        }
 
        dbus_message_iter_close_container(iter, &entry);
@@ -2159,7 +2204,7 @@ static DBusMessage *remove_device(DBusConnection *conn,
        btd_device_set_temporary(device, TRUE);
 
        if (!btd_device_is_connected(device)) {
-               adapter_remove_device(adapter, device);
+               btd_adapter_remove_device(adapter, device);
                return dbus_message_new_method_return(msg);
        }
 
@@ -2219,13 +2264,17 @@ static struct link_key_info *get_key_info(GKeyFile *key_file, const char *peer)
        char *str;
 
        str = g_key_file_get_string(key_file, "LinkKey", "Key", NULL);
-       if (!str || strlen(str) != 34)
+       if (!str || strlen(str) < 32)
                goto failed;
 
        info = g_new0(struct link_key_info, 1);
 
        str2ba(peer, &info->bdaddr);
-       str2buf(&str[2], info->key, sizeof(info->key));
+
+       if (!strncmp(str, "0x", 2))
+               str2buf(&str[2], info->key, sizeof(info->key));
+       else
+               str2buf(&str[0], info->key, sizeof(info->key));
 
        info->type = g_key_file_get_integer(key_file, "LinkKey", "Type", NULL);
        info->pin_len = g_key_file_get_integer(key_file, "LinkKey", "PINLength",
@@ -2237,57 +2286,126 @@ failed:
        return info;
 }
 
-static struct smp_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer)
+static struct smp_ltk_info *get_ltk(GKeyFile *key_file, const char *peer,
+                                       uint8_t peer_type, const char *group)
 {
        struct smp_ltk_info *ltk = NULL;
+       GError *gerr = NULL;
+       bool master;
        char *key;
        char *rand = NULL;
-       char *type = NULL;
-       uint8_t bdaddr_type;
 
-       key = g_key_file_get_string(key_file, "LongTermKey", "Key", NULL);
-       if (!key || strlen(key) != 34)
+       key = g_key_file_get_string(key_file, group, "Key", NULL);
+       if (!key || strlen(key) < 32)
                goto failed;
 
-       rand = g_key_file_get_string(key_file, "LongTermKey", "Rand", NULL);
-       if (!rand || strlen(rand) != 18)
+       rand = g_key_file_get_string(key_file, group, "Rand", NULL);
+       if (!rand)
                goto failed;
 
-       type = g_key_file_get_string(key_file, "General", "AddressType", NULL);
-       if (!type)
-               goto failed;
+       ltk = g_new0(struct smp_ltk_info, 1);
 
-       if (g_str_equal(type, "public"))
-               bdaddr_type = BDADDR_LE_PUBLIC;
-       else if (g_str_equal(type, "static"))
-               bdaddr_type = BDADDR_LE_RANDOM;
-       else
+       /* Default to assuming a master key */
+       ltk->master = true;
+
+       str2ba(peer, &ltk->bdaddr);
+       ltk->bdaddr_type = peer_type;
+
+       /*
+        * Long term keys should respond to an identity address which can
+        * either be a public address or a random static address. Keys
+        * stored for resolvable random and unresolvable random addresses
+        * are ignored.
+        *
+        * This is an extra sanity check for older kernel versions or older
+        * daemons that might have been instructed to store long term keys
+        * for these temporary addresses.
+        */
+       if (ltk->bdaddr_type == BDADDR_LE_RANDOM &&
+                                       (ltk->bdaddr.b[5] & 0xc0) != 0xc0) {
+               g_free(ltk);
+               ltk = NULL;
                goto failed;
+       }
 
-       ltk = g_new0(struct smp_ltk_info, 1);
+       if (!strncmp(key, "0x", 2))
+               str2buf(&key[2], ltk->val, sizeof(ltk->val));
+       else
+               str2buf(&key[0], ltk->val, sizeof(ltk->val));
 
-       str2ba(peer, &ltk->bdaddr);
-       ltk->bdaddr_type = bdaddr_type;
-       str2buf(&key[2], ltk->val, sizeof(ltk->val));
-       str2buf(&rand[2], ltk->rand, sizeof(ltk->rand));
+       if (!strncmp(rand, "0x", 2)) {
+               uint64_t rand_le;
+               str2buf(&rand[2], (uint8_t *) &rand_le, sizeof(rand_le));
+               ltk->rand = le64_to_cpu(rand_le);
+       } else {
+               sscanf(rand, "%" PRIu64, &ltk->rand);
+       }
 
-       ltk->authenticated = g_key_file_get_integer(key_file, "LongTermKey",
+       ltk->authenticated = g_key_file_get_integer(key_file, group,
                                                        "Authenticated", NULL);
-       ltk->master = g_key_file_get_integer(key_file, "LongTermKey", "Master",
-                                               NULL);
-       ltk->enc_size = g_key_file_get_integer(key_file, "LongTermKey",
-                                               "EncSize", NULL);
-       ltk->ediv = g_key_file_get_integer(key_file, "LongTermKey", "EDiv",
-                                               NULL);
+       ltk->enc_size = g_key_file_get_integer(key_file, group, "EncSize",
+                                                                       NULL);
+       ltk->ediv = g_key_file_get_integer(key_file, group, "EDiv", NULL);
+
+       master = g_key_file_get_boolean(key_file, group, "Master", &gerr);
+       if (gerr)
+               g_error_free(gerr);
+       else
+               ltk->master = master;
 
 failed:
        g_free(key);
        g_free(rand);
-       g_free(type);
 
        return ltk;
 }
 
+static GSList *get_ltk_info(GKeyFile *key_file, const char *peer,
+                                                       uint8_t bdaddr_type)
+{
+       struct smp_ltk_info *ltk;
+       GSList *l = NULL;
+
+       DBG("%s", peer);
+
+       ltk = get_ltk(key_file, peer, bdaddr_type, "LongTermKey");
+       if (ltk)
+               l = g_slist_append(l, ltk);
+
+       ltk = get_ltk(key_file, peer, bdaddr_type, "SlaveLongTermKey");
+       if (ltk) {
+               ltk->master = false;
+               l = g_slist_append(l, ltk);
+       }
+
+       return l;
+}
+
+static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer,
+                                                       uint8_t bdaddr_type)
+{
+       struct irk_info *irk;
+       char *str;
+
+       str = g_key_file_get_string(key_file, "IdentityResolvingKey", "Key", NULL);
+       if (!str || strlen(str) < 32)
+               return NULL;
+
+       irk = g_new0(struct irk_info, 1);
+
+       str2ba(peer, &irk->bdaddr);
+       irk->bdaddr_type = bdaddr_type;
+
+       if (!strncmp(str, "0x", 2))
+               str2buf(&str[2], irk->val, sizeof(irk->val));
+       else
+               str2buf(&str[0], irk->val, sizeof(irk->val));
+
+       g_free(str);
+
+       return irk;
+}
+
 static void load_link_keys_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
@@ -2442,9 +2560,9 @@ static void load_ltks(struct btd_adapter *adapter, GSList *keys)
                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->rand = cpu_to_le64(info->rand);
+               key->ediv = cpu_to_le16(info->ediv);
+               key->type = info->authenticated;
                key->master = info->master;
                key->enc_size = info->enc_size;
        }
@@ -2470,12 +2588,105 @@ static void load_ltks(struct btd_adapter *adapter, GSList *keys)
                                                load_ltks_timeout, adapter);
 }
 
+static void load_irks_complete(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct btd_adapter *adapter = user_data;
+
+       if (status == MGMT_STATUS_UNKNOWN_COMMAND) {
+               info("Load IRKs failed: Kernel doesn't support LE Privacy");
+               return;
+       }
+
+       if (status != MGMT_STATUS_SUCCESS) {
+               error("Failed to load IRKs for hci%u: %s (0x%02x)",
+                               adapter->dev_id, mgmt_errstr(status), status);
+               return;
+       }
+
+       DBG("IRKs loaded for hci%u", adapter->dev_id);
+}
+
+static void load_irks(struct btd_adapter *adapter, GSList *irks)
+{
+       struct mgmt_cp_load_irks *cp;
+       struct mgmt_irk_info *irk;
+       size_t irk_count, cp_size;
+       unsigned int id;
+       GSList *l;
+
+       /*
+        * If the controller does not support LE Privacy operation,
+        * there is no support for loading identity resolving keys
+        * into the kernel.
+        */
+       if (!(adapter->supported_settings & MGMT_SETTING_PRIVACY))
+               return;
+
+       irk_count = g_slist_length(irks);
+
+       DBG("hci%u irks %zu", adapter->dev_id, irk_count);
+
+       cp_size = sizeof(*cp) + (irk_count * sizeof(*irk));
+
+       cp = g_try_malloc0(cp_size);
+       if (cp == NULL) {
+               error("No memory for IRKs for hci%u", adapter->dev_id);
+               return;
+       }
+
+       /*
+        * Even if the list of stored keys is empty, it is important to
+        * load an empty list into the kernel. That way we tell the
+        * kernel that we are able to handle New IRK events.
+        */
+       cp->irk_count = htobs(irk_count);
+
+       for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++) {
+               struct irk_info *info = l->data;
+
+               bacpy(&irk->addr.bdaddr, &info->bdaddr);
+               irk->addr.type = info->bdaddr_type;
+               memcpy(irk->val, info->val, sizeof(irk->val));
+       }
+
+       id = mgmt_send(adapter->mgmt, MGMT_OP_LOAD_IRKS, adapter->dev_id,
+                       cp_size, cp, load_irks_complete, adapter, NULL);
+
+       g_free(cp);
+
+       if (id == 0)
+               error("Failed to IRKs for hci%u", adapter->dev_id);
+}
+
+static uint8_t get_le_addr_type(GKeyFile *keyfile)
+{
+       uint8_t addr_type;
+       char *type;
+
+       type = g_key_file_get_string(keyfile, "General", "AddressType", NULL);
+       if (!type)
+               return BDADDR_LE_PUBLIC;
+
+       if (g_str_equal(type, "public"))
+               addr_type = BDADDR_LE_PUBLIC;
+       else if (g_str_equal(type, "static"))
+               addr_type = BDADDR_LE_RANDOM;
+       else
+               addr_type = BDADDR_LE_PUBLIC;
+
+       g_free(type);
+
+       return addr_type;
+}
+
 static void load_devices(struct btd_adapter *adapter)
 {
        char filename[PATH_MAX + 1];
        char srcaddr[18];
        GSList *keys = NULL;
        GSList *ltks = NULL;
+       GSList *irks = NULL;
        DIR *dir;
        struct dirent *entry;
 
@@ -2495,8 +2706,9 @@ static void load_devices(struct btd_adapter *adapter)
                char filename[PATH_MAX + 1];
                GKeyFile *key_file;
                struct link_key_info *key_info;
-               struct smp_ltk_info *ltk_info;
-               GSList *list;
+               GSList *list, *ltk_info;
+               struct irk_info *irk_info;
+               uint8_t bdaddr_type;
 
                if (entry->d_type != DT_DIR || bachk(entry->d_name) < 0)
                        continue;
@@ -2511,9 +2723,14 @@ static void load_devices(struct btd_adapter *adapter)
                if (key_info)
                        keys = g_slist_append(keys, key_info);
 
-               ltk_info = get_ltk_info(key_file, entry->d_name);
-               if (ltk_info)
-                       ltks = g_slist_append(ltks, ltk_info);
+               bdaddr_type = get_le_addr_type(key_file);
+
+               ltk_info = get_ltk_info(key_file, entry->d_name, bdaddr_type);
+               ltks = g_slist_concat(ltks, ltk_info);
+
+               irk_info = get_irk_info(key_file, entry->d_name, bdaddr_type);
+               if (irk_info)
+                       irks = g_slist_append(irks, irk_info);
 
                list = g_slist_find_custom(adapter->devices, entry->d_name,
                                                        device_address_cmp);
@@ -2537,9 +2754,14 @@ static void load_devices(struct btd_adapter *adapter)
                        device_probe_profiles(device, list);
 
 device_exist:
-               if (key_info || ltk_info) {
-                       device_set_paired(device, TRUE);
-                       device_set_bonded(device, TRUE);
+               if (key_info) {
+                       device_set_paired(device, BDADDR_BREDR);
+                       device_set_bonded(device, BDADDR_BREDR);
+               }
+
+               if (ltk_info) {
+                       device_set_paired(device, bdaddr_type);
+                       device_set_bonded(device, bdaddr_type);
                }
 
 free:
@@ -2553,6 +2775,8 @@ free:
 
        load_ltks(adapter, ltks);
        g_slist_free_full(ltks, g_free);
+       load_irks(adapter, irks);
+       g_slist_free_full(irks, g_free);
 }
 
 int btd_adapter_block_address(struct btd_adapter *adapter,
@@ -2673,15 +2897,16 @@ void adapter_remove_profile(struct btd_adapter *adapter, gpointer p)
 }
 
 static void adapter_add_connection(struct btd_adapter *adapter,
-                                               struct btd_device *device)
+                                               struct btd_device *device,
+                                               uint8_t bdaddr_type)
 {
+       device_add_connection(device, bdaddr_type);
+
        if (g_slist_find(adapter->connections, device)) {
                error("Device is already marked as connected");
                return;
        }
 
-       device_add_connection(device);
-
        adapter->connections = g_slist_append(adapter->connections, device);
 }
 
@@ -2724,7 +2949,7 @@ static void get_connections_complete(uint8_t status, uint16_t length,
                device = btd_adapter_get_device(adapter, &addr->bdaddr,
                                                                addr->type);
                if (device)
-                       adapter_add_connection(adapter, device);
+                       adapter_add_connection(adapter, device, addr->type);
        }
 }
 
@@ -2958,7 +3183,7 @@ static void adapter_free(gpointer user_data)
        g_free(adapter->system_name);
        g_free(adapter->stored_alias);
        g_free(adapter->current_alias);
-       g_free(adapter->modalias);
+       free(adapter->modalias);
        g_free(adapter);
 }
 
@@ -3283,7 +3508,7 @@ static gboolean record_has_uuid(const sdp_record_t *rec,
 
                ret = strcasecmp(uuid, profile_uuid);
 
-               g_free(uuid);
+               free(uuid);
 
                if (ret == 0)
                        return TRUE;
@@ -3424,8 +3649,8 @@ static void convert_sdp_entry(char *key, char *value, void *user_data)
 
 failed:
        sdp_record_free(rec);
-       g_free(prim_uuid);
-       g_free(att_uuid);
+       free(prim_uuid);
+       free(att_uuid);
 }
 
 static void convert_primaries_entry(char *key, char *value, void *user_data)
@@ -3506,7 +3731,7 @@ static void convert_primaries_entry(char *key, char *value, void *user_data)
 
 end:
        g_free(data);
-       g_free(prim_uuid);
+       free(prim_uuid);
        g_key_file_free(key_file);
 }
 
@@ -3515,16 +3740,16 @@ static void convert_ccc_entry(char *key, char *value, void *user_data)
        char *src_addr = user_data;
        char dst_addr[18];
        char type = BDADDR_BREDR;
-       int handle, ret;
+       uint16_t handle;
+       int ret, err;
        char filename[PATH_MAX + 1];
        GKeyFile *key_file;
        struct stat st;
-       int err;
        char group[6];
        char *data;
        gsize length = 0;
 
-       ret = sscanf(key, "%17s#%hhu#%04X", dst_addr, &type, &handle);
+       ret = sscanf(key, "%17s#%hhu#%04hX", dst_addr, &type, &handle);
        if (ret < 3)
                return;
 
@@ -3565,16 +3790,16 @@ static void convert_gatt_entry(char *key, char *value, void *user_data)
        char *src_addr = user_data;
        char dst_addr[18];
        char type = BDADDR_BREDR;
-       int handle, ret;
+       uint16_t handle;
+       int ret, err;
        char filename[PATH_MAX + 1];
        GKeyFile *key_file;
        struct stat st;
-       int err;
        char group[6];
        char *data;
        gsize length = 0;
 
-       ret = sscanf(key, "%17s#%hhu#%04X", dst_addr, &type, &handle);
+       ret = sscanf(key, "%17s#%hhu#%04hX", dst_addr, &type, &handle);
        if (ret < 3)
                return;
 
@@ -4082,36 +4307,33 @@ static void update_found_devices(struct btd_adapter *adapter,
 {
        struct btd_device *dev;
        struct eir_data eir_data;
+       bool name_known, discoverable;
        char addr[18];
-       GSList *list;
-       bool name_known;
 
        memset(&eir_data, 0, sizeof(eir_data));
        eir_parse(&eir_data, data, data_len);
 
-       /* Avoid creating LE device if it's not discoverable */
-       if (bdaddr_type != BDADDR_BREDR &&
-                       !(eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC))) {
-               eir_data_free(&eir_data);
-               return;
-       }
+       if (bdaddr_type == BDADDR_BREDR)
+               discoverable = true;
+        else
+               discoverable = eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC);
 
        ba2str(bdaddr, addr);
 
-       list = g_slist_find_custom(adapter->devices, bdaddr, device_bdaddr_cmp);
-       if (!list) {
+       dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type);
+       if (!dev) {
                /*
-                * If no client has requested discovery, then do not
-                * create new device objects.
+                * If no client has requested discovery or the device is
+                * not marked as discoverable, then do not create new
+                * device objects.
                 */
-               if (!adapter->discovery_list) {
+               if (!adapter->discovery_list || !discoverable) {
                        eir_data_free(&eir_data);
                        return;
                }
 
                dev = adapter_create_device(adapter, bdaddr, bdaddr_type);
-       } else
-               dev = list->data;
+       }
 
        if (!dev) {
                error("Unable to create object for found device %s", addr);
@@ -4119,6 +4341,18 @@ static void update_found_devices(struct btd_adapter *adapter,
                return;
        }
 
+       device_update_last_seen(dev, bdaddr_type);
+
+       /*
+        * FIXME: We need to check for non-zero flags first because
+        * older kernels send separate adv_ind and scan_rsp. Newer
+        * kernels send them merged, so once we know which mgmt version
+        * supports this we can make the non-zero check conditional.
+        */
+       if (bdaddr_type != BDADDR_BREDR && eir_data.flags &&
+                                       !(eir_data.flags & EIR_BREDR_UNSUP))
+               device_set_bredr_support(dev);
+
        if (eir_data.name != NULL && eir_data.name_complete)
                device_store_cached_name(dev, eir_data.name);
 
@@ -4147,6 +4381,13 @@ static void update_found_devices(struct btd_adapter *adapter,
        if (eir_data.class != 0)
                device_set_class(dev, eir_data.class);
 
+       if (eir_data.did_source || eir_data.did_vendor ||
+                       eir_data.did_product || eir_data.did_version)
+               btd_device_set_pnpid(dev, eir_data.did_source,
+                                                       eir_data.did_vendor,
+                                                       eir_data.did_product,
+                                                       eir_data.did_version);
+
        device_add_eir_uuids(dev, eir_data.services);
 
        eir_data_free(&eir_data);
@@ -4185,7 +4426,7 @@ connect_le:
         * connect_list stop passive scanning so that a connection
         * attempt to it can be made
         */
-       if (device_is_le(dev) && !btd_device_is_connected(dev) &&
+       if (bdaddr_type != BDADDR_BREDR && !btd_device_is_connected(dev) &&
                                g_slist_find(adapter->connect_list, dev)) {
                adapter->connect_le = dev;
                stop_passive_scanning(adapter);
@@ -4241,7 +4482,8 @@ struct agent *adapter_get_agent(struct btd_adapter *adapter)
 }
 
 static void adapter_remove_connection(struct btd_adapter *adapter,
-                                               struct btd_device *device)
+                                               struct btd_device *device,
+                                               uint8_t bdaddr_type)
 {
        DBG("");
 
@@ -4250,18 +4492,22 @@ static void adapter_remove_connection(struct btd_adapter *adapter,
                return;
        }
 
-       device_remove_connection(device);
-
-       adapter->connections = g_slist_remove(adapter->connections, device);
+       device_remove_connection(device, bdaddr_type);
 
        if (device_is_authenticating(device))
                device_cancel_authentication(device, TRUE);
 
+       /* If another bearer is still connected */
+       if (btd_device_is_connected(device))
+               return;
+
+       adapter->connections = g_slist_remove(adapter->connections, device);
+
        if (device_is_temporary(device) && !device_is_retrying(device)) {
                const char *path = device_get_path(device);
 
                DBG("Removing temporary device %s", path);
-               adapter_remove_device(adapter, device);
+               btd_adapter_remove_device(adapter, device);
        }
 }
 
@@ -4288,7 +4534,11 @@ static void adapter_stop(struct btd_adapter *adapter)
 
        while (adapter->connections) {
                struct btd_device *device = adapter->connections->data;
-               adapter_remove_connection(adapter, device);
+               uint8_t addr_type = btd_device_get_bdaddr_type(device);
+
+               adapter_remove_connection(adapter, device, BDADDR_BREDR);
+               if (addr_type != BDADDR_BREDR)
+                       adapter_remove_connection(adapter, device, addr_type);
        }
 
        g_dbus_emit_property_changed(dbus_conn, adapter->path,
@@ -4373,6 +4623,10 @@ static gboolean process_auth_queue(gpointer user_data)
                struct btd_device *device = auth->device;
                const char *dev_path;
 
+               /* Wait services to be resolved before asking authorization */
+               if (auth->svc_id > 0)
+                       return TRUE;
+
                if (device_is_trusted(device) == TRUE) {
                        auth->cb(NULL, auth->user_data);
                        goto next;
@@ -4409,6 +4663,22 @@ next:
        return FALSE;
 }
 
+static void svc_complete(struct btd_device *dev, int err, void *user_data)
+{
+       struct service_auth *auth = user_data;
+       struct btd_adapter *adapter = auth->adapter;
+
+       auth->svc_id = 0;
+
+       if (adapter->auths->length != 1)
+               return;
+
+       if (adapter->auth_idle_id != 0)
+               return;
+
+       adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter);
+}
+
 static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
                                        const char *uuid, service_auth_cb cb,
                                        void *user_data)
@@ -4417,7 +4687,7 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
        struct btd_device *device;
        static guint id = 0;
 
-       device = btd_adapter_find_device(adapter, dst);
+       device = btd_adapter_find_device(adapter, dst, BDADDR_BREDR);
        if (!device)
                return 0;
 
@@ -4435,17 +4705,10 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
        auth->device = device;
        auth->adapter = adapter;
        auth->id = ++id;
+       auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth);
 
        g_queue_push_tail(adapter->auths, auth);
 
-       if (adapter->auths->length != 1)
-               return auth->id;
-
-       if (adapter->auth_idle_id != 0)
-               return auth->id;
-
-       adapter->auth_idle_id = g_idle_add(process_auth_queue, adapter);
-
        return auth->id;
 }
 
@@ -4504,12 +4767,14 @@ int btd_cancel_authorization(guint id)
        if (auth == NULL)
                return -EPERM;
 
+       if (auth->svc_id > 0)
+               device_remove_svc_complete_callback(auth->device,
+                                                               auth->svc_id);
+
        g_queue_remove(auth->adapter->auths, auth);
 
-       if (auth->agent) {
-               agent_cancel(auth->agent);
+       if (auth->agent)
                agent_unref(auth->agent);
-       }
 
        g_free(auth);
 
@@ -4623,7 +4888,7 @@ int btd_adapter_pincode_reply(struct btd_adapter *adapter,
 
                /* Since a pincode was requested, update the starting time to
                 * the point where the pincode is provided. */
-               device = btd_adapter_find_device(adapter, bdaddr);
+               device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
                device_bonding_restart_timer(device);
 
                id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_REPLY,
@@ -4793,7 +5058,7 @@ static void user_passkey_notify_callback(uint16_t index, uint16_t length,
                return;
        }
 
-       passkey = bt_get_le32(&ev->passkey);
+       passkey = get_le32(&ev->passkey);
 
        DBG("passkey %06u entered %u", passkey, ev->entered);
 
@@ -4957,10 +5222,10 @@ static void bonding_complete(struct btd_adapter *adapter,
        if (status == 0)
                device = btd_adapter_get_device(adapter, bdaddr, addr_type);
        else
-               device = btd_adapter_find_device(adapter, bdaddr);
+               device = btd_adapter_find_device(adapter, bdaddr, addr_type);
 
        if (device != NULL)
-               device_bonding_complete(device, status);
+               device_bonding_complete(device, addr_type, status);
 
        resume_discovery(adapter);
 
@@ -4984,7 +5249,7 @@ static void bonding_attempt_complete(struct btd_adapter *adapter,
        if (status == 0)
                device = btd_adapter_get_device(adapter, bdaddr, addr_type);
        else
-               device = btd_adapter_find_device(adapter, bdaddr);
+               device = btd_adapter_find_device(adapter, bdaddr, addr_type);
 
        if (status == MGMT_STATUS_AUTH_FAILED && adapter->pincode_requested) {
                /* On faliure, issue a bonding_retry if possible. */
@@ -5132,6 +5397,16 @@ int adapter_bonding_attempt(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
        return 0;
 }
 
+static void disconnect_notify(struct btd_device *dev, uint8_t reason)
+{
+       GSList *l;
+
+       for (l = disconnect_list; l; l = g_slist_next(l)) {
+               btd_disconnect_cb disconnect_cb = l->data;
+               disconnect_cb(dev, reason);
+       }
+}
+
 static void dev_disconnected(struct btd_adapter *adapter,
                                        const struct mgmt_addr_info *addr,
                                        uint8_t reason)
@@ -5143,21 +5418,35 @@ static void dev_disconnected(struct btd_adapter *adapter,
 
        DBG("Device %s disconnected, reason %u", dst, reason);
 
-       device = btd_adapter_find_device(adapter, &addr->bdaddr);
-       if (device)
-               adapter_remove_connection(adapter, device);
+       device = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type);
+       if (device) {
+               adapter_remove_connection(adapter, device, addr->type);
+               disconnect_notify(device, reason);
+       }
 
        bonding_attempt_complete(adapter, &addr->bdaddr, addr->type,
                                                MGMT_STATUS_DISCONNECTED);
 }
 
+void btd_add_disconnect_cb(btd_disconnect_cb func)
+{
+       disconnect_list = g_slist_append(disconnect_list, func);
+}
+
+void btd_remove_disconnect_cb(btd_disconnect_cb func)
+{
+       disconnect_list = g_slist_remove(disconnect_list, func);
+}
+
 static void disconnect_complete(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
        const struct mgmt_rp_disconnect *rp = param;
        struct btd_adapter *adapter = user_data;
 
-       if (status != MGMT_STATUS_SUCCESS) {
+       if (status == MGMT_STATUS_NOT_CONNECTED) {
+               warn("Disconnecting failed: already disconnected");
+       } else if (status != MGMT_STATUS_SUCCESS) {
                error("Failed to disconnect device: %s (0x%02x)",
                                                mgmt_errstr(status), status);
                return;
@@ -5214,7 +5503,7 @@ static void store_link_key(struct btd_adapter *adapter,
        char filename[PATH_MAX + 1];
        GKeyFile *key_file;
        gsize length = 0;
-       char key_str[35];
+       char key_str[33];
        char *str;
        int i;
 
@@ -5228,10 +5517,8 @@ static void store_link_key(struct btd_adapter *adapter,
        key_file = g_key_file_new();
        g_key_file_load_from_file(key_file, filename, 0, NULL);
 
-       key_str[0] = '0';
-       key_str[1] = 'x';
        for (i = 0; i < 16; i++)
-               sprintf(key_str + 2 + (i * 2), "%2.2X", key[i]);
+               sprintf(key_str + (i * 2), "%2.2X", key[i]);
 
        g_key_file_set_string(key_file, "LinkKey", "Key", key_str);
 
@@ -5284,7 +5571,7 @@ static void new_link_key_callback(uint16_t index, uint16_t length,
                store_link_key(adapter, device, key->val, key->type,
                                                                key->pin_len);
 
-               device_set_bonded(device, TRUE);
+               device_set_bonded(device, BDADDR_BREDR);
 
                if (device_is_temporary(device))
                        btd_device_set_temporary(device, FALSE);
@@ -5297,18 +5584,23 @@ static void store_longtermkey(const bdaddr_t *local, const bdaddr_t *peer,
                                uint8_t bdaddr_type, const unsigned char *key,
                                uint8_t master, uint8_t authenticated,
                                uint8_t enc_size, uint16_t ediv,
-                               const uint8_t rand[8])
+                               uint64_t rand)
 {
+       const char *group = master ? "LongTermKey" : "SlaveLongTermKey";
        char adapter_addr[18];
        char device_addr[18];
        char filename[PATH_MAX + 1];
        GKeyFile *key_file;
-       char key_str[35];
-       char rand_str[19];
+       char key_str[33];
        gsize length = 0;
        char *str;
        int i;
 
+       if (master != 0x00 && master != 0x01) {
+               error("Unsupported LTK type %u", master);
+               return;
+       }
+
        ba2str(local, adapter_addr);
        ba2str(peer, device_addr);
 
@@ -5319,25 +5611,20 @@ static void store_longtermkey(const bdaddr_t *local, const bdaddr_t *peer,
        key_file = g_key_file_new();
        g_key_file_load_from_file(key_file, filename, 0, NULL);
 
-       key_str[0] = '0';
-       key_str[1] = 'x';
-       for (i = 0; i < 16; i++)
-               sprintf(key_str + 2 + (i * 2), "%2.2X", key[i]);
+       /* Old files may contain this so remove it in case it exists */
+       g_key_file_remove_key(key_file, "LongTermKey", "Master", NULL);
 
-       g_key_file_set_string(key_file, "LongTermKey", "Key", key_str);
+       for (i = 0; i < 16; i++)
+               sprintf(key_str + (i * 2), "%2.2X", key[i]);
 
-       g_key_file_set_integer(key_file, "LongTermKey", "Authenticated",
-                               authenticated);
-       g_key_file_set_integer(key_file, "LongTermKey", "Master", master);
-       g_key_file_set_integer(key_file, "LongTermKey", "EncSize", enc_size);
-       g_key_file_set_integer(key_file, "LongTermKey", "EDiv", ediv);
+       g_key_file_set_string(key_file, group, "Key", key_str);
 
-       rand_str[0] = '0';
-       rand_str[1] = 'x';
-       for (i = 0; i < 8; i++)
-               sprintf(rand_str + 2 + (i * 2), "%2.2X", rand[i]);
+       g_key_file_set_integer(key_file, group, "Authenticated",
+                                                       authenticated);
+       g_key_file_set_integer(key_file, group, "EncSize", enc_size);
 
-       g_key_file_set_string(key_file, "LongTermKey", "Rand", rand_str);
+       g_key_file_set_integer(key_file, group, "EDiv", ediv);
+       g_key_file_set_uint64(key_file, group, "Rand", rand);
 
        create_file(filename, S_IRUSR | S_IWUSR);
 
@@ -5355,6 +5642,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
        const struct mgmt_addr_info *addr = &ev->key.addr;
        struct btd_adapter *adapter = user_data;
        struct btd_device *device;
+       bool persistent;
        char dst[18];
 
        if (length < sizeof(*ev)) {
@@ -5364,8 +5652,8 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
 
        ba2str(&addr->bdaddr, dst);
 
-       DBG("hci%u new LTK for %s authenticated %u enc_size %u",
-               adapter->dev_id, dst, ev->key.authenticated, ev->key.enc_size);
+       DBG("hci%u new LTK for %s type %u enc_size %u",
+               adapter->dev_id, dst, ev->key.type, ev->key.enc_size);
 
        device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type);
        if (!device) {
@@ -5373,23 +5661,216 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
                return;
        }
 
-       if (ev->store_hint) {
+       /*
+        * Some older kernel versions set store_hint for long term keys
+        * from resolvable and unresolvable random addresses, but there
+        * is no point in storing these. Next time around the device
+        * address will be invalid.
+        *
+        * So only for identity addresses (public and static random) use
+        * the store_hint as an indication if the long term key should
+        * be persistently stored.
+        *
+        */
+       if (addr->type == BDADDR_LE_RANDOM &&
+                               (addr->bdaddr.b[5] & 0xc0) != 0xc0)
+               persistent = false;
+       else
+               persistent = !!ev->store_hint;
+
+       if (persistent) {
                const struct mgmt_ltk_info *key = &ev->key;
                const bdaddr_t *bdaddr = btd_adapter_get_address(adapter);
+               uint16_t ediv;
+               uint64_t rand;
+
+               ediv = le16_to_cpu(key->ediv);
+               rand = le64_to_cpu(key->rand);
 
                store_longtermkey(bdaddr, &key->addr.bdaddr,
                                        key->addr.type, key->val, key->master,
-                                       key->authenticated, key->enc_size,
-                                       key->ediv, key->rand);
+                                       key->type, key->enc_size, ediv, rand);
 
-               device_set_bonded(device, TRUE);
+               device_set_bonded(device, addr->type);
 
                if (device_is_temporary(device))
                        btd_device_set_temporary(device, FALSE);
        }
 
-       if (ev->key.master)
-               bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
+       bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
+}
+
+static void store_csrk(const bdaddr_t *local, const bdaddr_t *peer,
+                               uint8_t bdaddr_type, const unsigned char *key,
+                               uint8_t master)
+{
+       const char *group;
+       char adapter_addr[18];
+       char device_addr[18];
+       char filename[PATH_MAX + 1];
+       GKeyFile *key_file;
+       char key_str[33];
+       gsize length = 0;
+       char *str;
+       int i;
+
+       if (master == 0x00)
+               group = "LocalSignatureKey";
+       else if (master == 0x01)
+               group = "RemoteSignatureKey";
+       else {
+               warn("Unsupported CSRK type %u", master);
+               return;
+       }
+
+       ba2str(local, adapter_addr);
+       ba2str(peer, device_addr);
+
+       snprintf(filename, sizeof(filename), STORAGEDIR "/%s/%s/info",
+                                               adapter_addr, device_addr);
+
+       key_file = g_key_file_new();
+       g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+       for (i = 0; i < 16; i++)
+               sprintf(key_str + (i * 2), "%2.2X", key[i]);
+
+       g_key_file_set_string(key_file, group, "Key", key_str);
+
+       create_file(filename, S_IRUSR | S_IWUSR);
+
+       str = g_key_file_to_data(key_file, &length, NULL);
+       g_file_set_contents(filename, str, length, NULL);
+       g_free(str);
+
+       g_key_file_free(key_file);
+}
+
+static void new_csrk_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_new_csrk *ev = param;
+       const struct mgmt_addr_info *addr = &ev->key.addr;
+       const struct mgmt_csrk_info *key = &ev->key;
+       struct btd_adapter *adapter = user_data;
+       const bdaddr_t *bdaddr = btd_adapter_get_address(adapter);
+       struct btd_device *device;
+       char dst[18];
+
+       if (length < sizeof(*ev)) {
+               error("Too small CSRK event");
+               return;
+       }
+
+       ba2str(&addr->bdaddr, dst);
+
+       DBG("hci%u new CSRK for %s master %u", adapter->dev_id, dst,
+                                                               ev->key.master);
+
+       device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type);
+       if (!device) {
+               error("Unable to get device object for %s", dst);
+               return;
+       }
+
+       if (!ev->store_hint)
+               return;
+
+       store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val,
+                                                               key->master);
+
+       if (device_is_temporary(device))
+               btd_device_set_temporary(device, FALSE);
+}
+
+static void store_irk(struct btd_adapter *adapter, const bdaddr_t *peer,
+                               uint8_t bdaddr_type, const unsigned char *key)
+{
+       char adapter_addr[18];
+       char device_addr[18];
+       char filename[PATH_MAX + 1];
+       GKeyFile *key_file;
+       char *store_data;
+       char str[33];
+       size_t length = 0;
+       int i;
+
+       ba2str(&adapter->bdaddr, adapter_addr);
+       ba2str(peer, device_addr);
+
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
+                                                               device_addr);
+       filename[PATH_MAX] = '\0';
+
+       key_file = g_key_file_new();
+       g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+       for (i = 0; i < 16; i++)
+               sprintf(str + (i * 2), "%2.2X", key[i]);
+
+       g_key_file_set_string(key_file, "IdentityResolvingKey", "Key", str);
+
+       create_file(filename, S_IRUSR | S_IWUSR);
+
+       store_data = g_key_file_to_data(key_file, &length, NULL);
+       g_file_set_contents(filename, store_data, length, NULL);
+       g_free(store_data);
+
+       g_key_file_free(key_file);
+}
+
+static void new_irk_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_new_irk *ev = param;
+       const struct mgmt_addr_info *addr = &ev->key.addr;
+       const struct mgmt_irk_info *irk = &ev->key;
+       struct btd_adapter *adapter = user_data;
+       struct btd_device *device, *duplicate;
+       bool persistent;
+       char dst[18], rpa[18];
+
+       if (length < sizeof(*ev)) {
+               error("Too small New IRK event");
+               return;
+       }
+
+       ba2str(&addr->bdaddr, dst);
+       ba2str(&ev->rpa, rpa);
+
+       DBG("hci%u new IRK for %s RPA %s", adapter->dev_id, dst, rpa);
+
+       if (bacmp(&ev->rpa, BDADDR_ANY)) {
+               device = btd_adapter_get_device(adapter, &ev->rpa,
+                                                       BDADDR_LE_RANDOM);
+               duplicate = btd_adapter_find_device(adapter, &addr->bdaddr,
+                                                               addr->type);
+               if (duplicate == device)
+                       duplicate = NULL;
+       } else {
+               device = btd_adapter_get_device(adapter, &addr->bdaddr,
+                                                               addr->type);
+               duplicate = NULL;
+       }
+
+       if (!device) {
+               error("Unable to get device object for %s", dst);
+               return;
+       }
+
+       device_update_addr(device, &addr->bdaddr, addr->type);
+
+       if (duplicate)
+               device_merge_duplicate(device, duplicate);
+
+       persistent = !!ev->store_hint;
+       if (!persistent)
+               return;
+
+       store_irk(adapter, &addr->bdaddr, addr->type, irk->val);
+
+       if (device_is_temporary(device))
+               btd_device_set_temporary(device, FALSE);
 }
 
 int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap)
@@ -5732,7 +6213,7 @@ static void connected_callback(uint16_t index, uint16_t length,
        if (eir_data.class != 0)
                device_set_class(device, eir_data.class);
 
-       adapter_add_connection(adapter, device);
+       adapter_add_connection(adapter, device, ev->addr.type);
 
        if (eir_data.name != NULL) {
                device_store_cached_name(device, eir_data.name);
@@ -5758,7 +6239,8 @@ static void device_blocked_callback(uint16_t index, uint16_t length,
        ba2str(&ev->addr.bdaddr, addr);
        DBG("hci%u %s blocked", index, addr);
 
-       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr);
+       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
        if (device)
                device_block(device, TRUE);
 }
@@ -5779,11 +6261,32 @@ static void device_unblocked_callback(uint16_t index, uint16_t length,
        ba2str(&ev->addr.bdaddr, addr);
        DBG("hci%u %s unblocked", index, addr);
 
-       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr);
+       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
        if (device)
                device_unblock(device, FALSE, TRUE);
 }
 
+static void conn_fail_notify(struct btd_device *dev, uint8_t status)
+{
+       GSList *l;
+
+       for (l = conn_fail_list; l; l = g_slist_next(l)) {
+               btd_conn_fail_cb conn_fail_cb = l->data;
+               conn_fail_cb(dev, status);
+       }
+}
+
+void btd_add_conn_fail_cb(btd_conn_fail_cb func)
+{
+       conn_fail_list = g_slist_append(conn_fail_list, func);
+}
+
+void btd_remove_conn_fail_cb(btd_conn_fail_cb func)
+{
+       conn_fail_list = g_slist_remove(conn_fail_list, func);
+}
+
 static void connect_failed_callback(uint16_t index, uint16_t length,
                                        const void *param, void *user_data)
 {
@@ -5801,8 +6304,11 @@ static void connect_failed_callback(uint16_t index, uint16_t length,
 
        DBG("hci%u %s status %u", index, addr, ev->status);
 
-       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr);
+       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
        if (device) {
+               conn_fail_notify(device, ev->status);
+
                /* If the device is in a bonding process cancel any auth request
                 * sent to the agent before proceeding, but keep the bonding
                 * request structure. */
@@ -5827,7 +6333,43 @@ static void connect_failed_callback(uint16_t index, uint16_t length,
         * when it is temporary. */
        if (device && !device_is_bonding(device, NULL)
                                                && device_is_temporary(device))
-               adapter_remove_device(adapter, device);
+               btd_adapter_remove_device(adapter, device);
+}
+
+static void remove_keys(struct btd_adapter *adapter,
+                                       struct btd_device *device, uint8_t type)
+{
+       char adapter_addr[18];
+       char device_addr[18];
+       char filename[PATH_MAX + 1];
+       GKeyFile *key_file;
+       gsize length = 0;
+       char *str;
+
+       ba2str(btd_adapter_get_address(adapter), adapter_addr);
+       ba2str(device_get_address(device), device_addr);
+
+       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
+                                                               device_addr);
+       filename[PATH_MAX] = '\0';
+
+       key_file = g_key_file_new();
+       g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+       if (type == BDADDR_BREDR) {
+               g_key_file_remove_group(key_file, "LinkKey", NULL);
+       } else {
+               g_key_file_remove_group(key_file, "LongTermKey", NULL);
+               g_key_file_remove_group(key_file, "LocalSignatureKey", NULL);
+               g_key_file_remove_group(key_file, "RemoteSignatureKey", NULL);
+               g_key_file_remove_group(key_file, "IdentityResolvingKey", NULL);
+       }
+
+       str = g_key_file_to_data(key_file, &length, NULL);
+       g_file_set_contents(filename, str, length, NULL);
+       g_free(str);
+
+       g_key_file_free(key_file);
 }
 
 static void unpaired_callback(uint16_t index, uint16_t length,
@@ -5847,18 +6389,15 @@ static void unpaired_callback(uint16_t index, uint16_t length,
 
        DBG("hci%u addr %s", index, addr);
 
-       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr);
+       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
        if (!device) {
                warn("No device object for unpaired device %s", addr);
                return;
        }
 
-       btd_device_set_temporary(device, TRUE);
-
-       if (btd_device_is_connected(device))
-               device_request_disconnect(device, NULL);
-       else
-               adapter_remove_device(adapter, device);
+       remove_keys(adapter, device, ev->addr.type);
+       device_set_unpaired(device, ev->addr.type);
 }
 
 static void read_info_complete(uint8_t status, uint16_t length,
@@ -5975,6 +6514,16 @@ static void read_info_complete(uint8_t status, uint16_t length,
                                                new_long_term_key_callback,
                                                adapter, NULL);
 
+       mgmt_register(adapter->mgmt, MGMT_EV_NEW_CSRK,
+                                               adapter->dev_id,
+                                               new_csrk_callback,
+                                               adapter, NULL);
+
+       mgmt_register(adapter->mgmt, MGMT_EV_NEW_IRK,
+                                               adapter->dev_id,
+                                               new_irk_callback,
+                                               adapter, NULL);
+
        mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_BLOCKED,
                                                adapter->dev_id,
                                                device_blocked_callback,
index de5b07d..f88c339 100644 (file)
@@ -36,6 +36,7 @@
 #define INVALID_PASSKEY                0xffffffff
 
 struct btd_adapter;
+struct btd_device;
 
 struct btd_adapter *btd_adapter_get_default(void);
 bool btd_adapter_is_default(struct btd_adapter *adapter);
@@ -58,28 +59,18 @@ struct oob_handler {
        void *user_data;
 };
 
-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];
-};
-
 int adapter_init(void);
 void adapter_cleanup(void);
 void adapter_shutdown(void);
 
+typedef void (*btd_disconnect_cb) (struct btd_device *device, uint8_t reason);
+void btd_add_disconnect_cb(btd_disconnect_cb func);
+void btd_remove_disconnect_cb(btd_disconnect_cb func);
+
+typedef void (*btd_conn_fail_cb) (struct btd_device *device, uint8_t status);
+void btd_add_conn_fail_cb(btd_conn_fail_cb func);
+void btd_remove_conn_fail_cb(btd_conn_fail_cb func);
+
 struct btd_adapter *adapter_find(const bdaddr_t *sba);
 struct btd_adapter *adapter_find_by_id(int id);
 void adapter_foreach(adapter_cb func, gpointer user_data);
@@ -90,13 +81,16 @@ bool btd_adapter_get_connectable(struct btd_adapter *adapter);
 
 uint32_t btd_adapter_get_class(struct btd_adapter *adapter);
 const char *btd_adapter_get_name(struct btd_adapter *adapter);
+void btd_adapter_remove_device(struct btd_adapter *adapter,
+                               struct btd_device *dev);
 struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter,
                                        const bdaddr_t *addr,
                                        uint8_t addr_type);
 sdp_list_t *btd_adapter_get_services(struct btd_adapter *adapter);
 
 struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
-                                                       const bdaddr_t *dst);
+                                                       const bdaddr_t *dst,
+                                                       uint8_t dst_type);
 
 const char *adapter_get_path(struct btd_adapter *adapter);
 const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter);
index 4c63cb9..8c1211c 100644 (file)
@@ -46,6 +46,7 @@
 #include "adapter.h"
 #include "device.h"
 #include "agent.h"
+#include "shared/queue.h"
 
 #define IO_CAPABILITY_DISPLAYONLY      0x00
 #define IO_CAPABILITY_DISPLAYYESNO     0x01
@@ -58,7 +59,7 @@
 #define AGENT_INTERFACE "org.bluez.Agent1"
 
 static GHashTable *agent_list;
-static struct agent *default_agent = NULL;
+struct queue *default_agents = NULL;
 
 typedef enum {
        AGENT_REQUEST_PASSKEY,
@@ -66,7 +67,6 @@ typedef enum {
        AGENT_REQUEST_AUTHORIZATION,
        AGENT_REQUEST_PINCODE,
        AGENT_REQUEST_AUTHORIZE_SERVICE,
-       AGENT_REQUEST_CONFIRM_MODE,
        AGENT_REQUEST_DISPLAY_PINCODE,
 } agent_request_type_t;
 
@@ -153,18 +153,38 @@ static void set_io_cap(struct btd_adapter *adapter, gpointer user_data)
        adapter_set_io_capability(adapter, io_cap);
 }
 
-static void set_default_agent(struct agent *agent)
+static bool add_default_agent(struct agent *agent)
 {
-       if (default_agent == agent)
+       if (queue_peek_head(default_agents) == agent)
+               return true;
+
+       queue_remove(default_agents, agent);
+
+       if (!queue_push_head(default_agents, agent))
+               return false;
+
+       DBG("Default agent set to %s %s", agent->owner, agent->path);
+
+       adapter_foreach(set_io_cap, agent);
+
+       return true;
+}
+
+static void remove_default_agent(struct agent *agent)
+{
+       if (queue_peek_head(default_agents) != agent) {
+               queue_remove(default_agents, agent);
                return;
+       }
 
+       queue_remove(default_agents, agent);
+
+       agent = queue_peek_head(default_agents);
        if (agent)
                DBG("Default agent set to %s %s", agent->owner, agent->path);
        else
                DBG("Default agent cleared");
 
-       default_agent = agent;
-
        adapter_foreach(set_io_cap, agent);
 }
 
@@ -179,8 +199,7 @@ static void agent_disconnect(DBusConnection *conn, void *user_data)
                agent->watch = 0;
        }
 
-       if (agent == default_agent)
-               set_default_agent(NULL);
+       remove_default_agent(agent);
 
        g_hash_table_remove(agent_list, agent->owner);
 }
@@ -248,8 +267,8 @@ struct agent *agent_get(const char *owner)
                        return agent_ref(agent);
        }
 
-       if (default_agent)
-               return agent_ref(default_agent);
+       if (!queue_isempty(default_agents))
+               return agent_ref(queue_peek_head(default_agents));
 
        return NULL;
 }
@@ -330,7 +349,6 @@ static void simple_agent_reply(DBusPendingCall *call, void *user_data)
 
                if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
                        error("Timed out waiting for reply from agent");
-                       agent_cancel(agent);
                        dbus_message_unref(message);
                        dbus_error_free(&err);
                        agent_unref(agent);
@@ -891,6 +909,8 @@ static void agent_destroy(gpointer data)
                agent_release(agent);
        }
 
+       remove_default_agent(agent);
+
        agent_unref(agent);
 }
 
@@ -989,7 +1009,8 @@ static DBusMessage *request_default(DBusConnection *conn, DBusMessage *msg,
        if (g_str_equal(path, agent->path) == FALSE)
                return btd_error_does_not_exist(msg);
 
-       set_default_agent(agent);
+       if (!add_default_agent(agent))
+               return btd_error_failed(msg, "Failed to set as default");
 
        return dbus_message_new_method_return(msg);
 }
@@ -1010,6 +1031,8 @@ void btd_agent_init(void)
        agent_list = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                NULL, agent_destroy);
 
+       default_agents = queue_new();
+
        g_dbus_register_interface(btd_get_dbus_connection(),
                                "/org/bluez", "org.bluez.AgentManager1",
                                methods, NULL, NULL, NULL, NULL);
@@ -1020,6 +1043,6 @@ void btd_agent_cleanup(void)
        g_dbus_unregister_interface(btd_get_dbus_connection(),
                                "/org/bluez", "org.bluez.AgentManager1");
 
-       set_default_agent(NULL);
        g_hash_table_destroy(agent_list);
+       queue_destroy(default_agents, NULL);
 }
index a6f1066..e65fff2 100644 (file)
 #include <bluetooth/sdp_lib.h>
 
 #include "lib/uuid.h"
-#include <gdbus/gdbus.h>
+#include "btio/btio.h"
 #include "log.h"
-#include <btio/btio.h>
-#include "sdpd.h"
-#include "hcid.h"
 #include "adapter.h"
 #include "device.h"
+#include "src/shared/util.h"
 #include "attrib/gattrib.h"
 #include "attrib/att.h"
 #include "attrib/gatt.h"
 #include "attrib/att-database.h"
+#include "textfile.h"
 #include "storage.h"
 
 #include "attrib-server.h"
@@ -69,8 +68,6 @@ struct gatt_server {
 };
 
 struct gatt_channel {
-       bdaddr_t src;
-       bdaddr_t dst;
        GAttrib *attrib;
        guint mtu;
        gboolean le;
@@ -101,6 +98,15 @@ static bt_uuid_t ccc_uuid = {
                        .value.u16 = GATT_CLIENT_CHARAC_CFG_UUID
 };
 
+static inline void put_uuid_le(const bt_uuid_t *src, void *dst)
+{
+       if (src->type == BT_UUID16)
+               put_le16(src->value.u16, dst);
+       else
+               /* Convert from 128-bit BE to LE */
+               bswap_128(&src->value.u128, dst);
+}
+
 static void attrib_free(void *data)
 {
        struct attribute *a = data;
@@ -307,10 +313,14 @@ static uint32_t attrib_create_sdp_new(struct gatt_server *server,
                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
+               sdp_uuid16_create(&svc, get_le16(a->data));
+       else if (a->len == 16) {
+               uint8_t be128[16];
+
+               /* Converting from LE to BE */
+               bswap_128(a->data, be128);
+               sdp_uuid128_create(&svc, be128);
+       } else
                return 0;
 
        record = server_record_new(&svc, handle, end);
@@ -503,8 +513,8 @@ static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start,
 
                value = (void *) adl->data[i];
 
-               att_put_u16(cur->handle, value);
-               att_put_u16(cur->end, &value[2]);
+               put_le16(cur->handle, value);
+               put_le16(cur->end, &value[2]);
                /* Attribute Value */
                memcpy(&value[4], cur->data, cur->len);
        }
@@ -592,7 +602,7 @@ static uint16_t read_by_type(struct gatt_channel *channel, uint16_t start,
 
                value = (void *) adl->data[i];
 
-               att_put_u16(a->handle, value);
+               put_le16(a->handle, value);
 
                /* Attribute Value */
                memcpy(&value[2], a->data, a->len);
@@ -672,10 +682,10 @@ static uint16_t find_info(struct gatt_channel *channel, uint16_t start,
 
                value = (void *) adl->data[i];
 
-               att_put_u16(a->handle, value);
+               put_le16(a->handle, value);
 
                /* Attribute Value */
-               att_put_uuid(a->uuid, &value[2]);
+               put_uuid_le(&a->uuid, &value[2]);
        }
 
        length = enc_find_info_resp(format, adl, pdu, len);
@@ -801,7 +811,7 @@ static uint16_t read_value(struct gatt_channel *channel, uint16_t handle,
                read_device_ccc(channel->device, handle, &cccval) == 0) {
                uint8_t config[2];
 
-               att_put_u16(cccval, config);
+               put_le16(cccval, config);
                return enc_read_resp(config, sizeof(config), pdu, len);
        }
 
@@ -842,7 +852,7 @@ static uint16_t read_blob(struct gatt_channel *channel, uint16_t handle,
                read_device_ccc(channel->device, handle, &cccval) == 0) {
                uint8_t config[2];
 
-               att_put_u16(cccval, config);
+               put_le16(cccval, config);
                return enc_read_blob_resp(config, sizeof(config), offset,
                                                                pdu, len);
        }
@@ -894,7 +904,7 @@ static uint16_t write_value(struct gatt_channel *channel, uint16_t handle,
                                                        status, pdu, len);
                }
        } else {
-               uint16_t cccval = att_get_u16(value);
+               uint16_t cccval = get_le16(value);
                char *filename;
                GKeyFile *key_file;
                char group[6], value[5];
@@ -913,7 +923,7 @@ static uint16_t write_value(struct gatt_channel *channel, uint16_t handle,
                g_key_file_load_from_file(key_file, filename, 0, NULL);
 
                sprintf(group, "%hu", handle);
-               sprintf(value, "%hhX", cccval);
+               sprintf(value, "%hX", cccval);
                g_key_file_set_string(key_file, group, "Value", value);
 
                data = g_key_file_to_data(key_file, &length, NULL);
@@ -944,10 +954,12 @@ static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu,
        io = g_attrib_get_channel(channel->attrib);
 
        bt_io_get(io, &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);
+       if (gerr) {
+               error("bt_io_get: %s", gerr->message);
+               g_error_free(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);
@@ -983,6 +995,12 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
 
        DBG("op 0x%02x", ipdu[0]);
 
+       if (len > vlen) {
+               error("Too much data on ATT socket");
+               status = ATT_ECODE_INVALID_PDU;
+               goto done;
+       }
+
        switch (ipdu[0]) {
        case ATT_OP_READ_BY_GROUP_REQ:
                length = dec_read_by_grp_req(ipdu, len, &start, &end, &uuid);
@@ -1098,53 +1116,70 @@ done:
        g_attrib_send(channel->attrib, 0, opdu, length, NULL, NULL, NULL);
 }
 
+GAttrib *attrib_from_device(struct btd_device *device)
+{
+       struct btd_adapter *adapter = device_get_adapter(device);
+       struct gatt_server *server;
+       GSList *l;
+
+       l = g_slist_find_custom(servers, adapter, adapter_cmp);
+       if (!l)
+               return NULL;
+
+       server = l->data;
+
+       for (l = server->clients; l; l = l->next) {
+               struct gatt_channel *channel = l->data;
+
+               if (channel->device == device)
+                       return g_attrib_ref(channel->attrib);
+       }
+
+       return NULL;
+}
+
 guint attrib_channel_attach(GAttrib *attrib)
 {
        struct gatt_server *server;
        struct btd_device *device;
        struct gatt_channel *channel;
+       bdaddr_t src, dst;
        GIOChannel *io;
        GError *gerr = NULL;
+       uint8_t bdaddr_type;
        uint16_t cid;
        guint mtu = 0;
 
        io = g_attrib_get_channel(attrib);
 
-       channel = g_new0(struct gatt_channel, 1);
-
        bt_io_get(io, &gerr,
-                       BT_IO_OPT_SOURCE_BDADDR, &channel->src,
-                       BT_IO_OPT_DEST_BDADDR, &channel->dst,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_DEST_TYPE, &bdaddr_type,
                        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);
+       server = find_gatt_server(&src);
+       if (server == NULL)
                return 0;
-       }
 
+       channel = g_new0(struct gatt_channel, 1);
        channel->server = server;
 
-       device = btd_adapter_find_device(server->adapter, &channel->dst);
+       device = btd_adapter_find_device(server->adapter, &dst, bdaddr_type);
        if (device == NULL) {
                error("Device object not found for attrib server");
                g_free(channel);
                return 0;
        }
 
-       if (device_is_bonded(device) == FALSE) {
+       if (!device_is_bonded(device, bdaddr_type)) {
                char *filename;
 
                filename = btd_device_get_storage_path(device, "ccc");
@@ -1222,7 +1257,10 @@ gboolean attrib_channel_detach(GAttrib *attrib, guint id)
 
 static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
 {
-       GAttrib *attrib;
+       struct btd_adapter *adapter;
+       struct btd_device *device;
+       uint8_t dst_type;
+       bdaddr_t src, dst;
 
        DBG("");
 
@@ -1231,9 +1269,26 @@ static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
                return;
        }
 
-       attrib = g_attrib_new(io);
-       attrib_channel_attach(attrib);
-       g_attrib_unref(attrib);
+       bt_io_get(io, &gerr,
+                       BT_IO_OPT_SOURCE_BDADDR, &src,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_DEST_TYPE, &dst_type,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("bt_io_get: %s", gerr->message);
+               g_error_free(gerr);
+               return;
+       }
+
+       adapter = adapter_find(&src);
+       if (!adapter)
+               return;
+
+       device = btd_adapter_get_device(adapter, &dst, dst_type);
+       if (!device)
+               return;
+
+       device_attach_attrib(device, io);
 }
 
 static gboolean register_core_services(struct gatt_server *server)
@@ -1244,16 +1299,16 @@ static gboolean register_core_services(struct gatt_server *server)
 
        /* GAP service: primary service definition */
        bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
-       att_put_u16(GENERIC_ACCESS_PROFILE_ID, &atval[0]);
+       put_le16(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]);
+       atval[0] = GATT_CHR_PROP_READ;
+       put_le16(server->name_handle, &atval[1]);
+       put_le16(GATT_CHARAC_DEVICE_NAME, &atval[3]);
        attrib_db_add_new(server, 0x0004, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 5);
 
@@ -1265,15 +1320,15 @@ static gboolean register_core_services(struct gatt_server *server)
        /* 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]);
+       atval[0] = GATT_CHR_PROP_READ;
+       put_le16(server->appearance_handle, &atval[1]);
+       put_le16(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]);
+       put_le16(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,
@@ -1285,7 +1340,7 @@ static gboolean register_core_services(struct gatt_server *server)
 
        /* GATT service: primary service definition */
        bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
-       att_put_u16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]);
+       put_le16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]);
        attrib_db_add_new(server, 0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
                                                                atval, 2);
 
index 90ba17c..063cb66 100644 (file)
@@ -37,5 +37,6 @@ int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid,
 uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle,
                                                        const char *name);
 void attrib_free_sdp(struct btd_adapter *adapter, uint32_t sdp_handle);
+GAttrib *attrib_from_device(struct btd_device *device);
 guint attrib_channel_attach(GAttrib *attrib);
 gboolean attrib_channel_detach(GAttrib *attrib, guint id);
index 0495200..ad8891a 100644 (file)
@@ -18,6 +18,7 @@
     <allow send_interface="org.bluez.Profile1"/>
     <allow send_interface="org.bluez.HeartRateWatcher1"/>
     <allow send_interface="org.bluez.CyclingSpeedWatcher1"/>
+    <allow send_interface="org.freedesktop.DBus.ObjectManager"/>
   </policy>
 
   <policy at_console="true">
index 18543ee..8222610 100644 (file)
 #include <glib.h>
 #include <dbus/dbus.h>
 #include <gdbus/gdbus.h>
-#include <btio/btio.h>
 
 #include "log.h"
 
+#include "btio/btio.h"
 #include "lib/uuid.h"
 #include "lib/mgmt.h"
 #include "attrib/att.h"
 #include "service.h"
 #include "dbus-common.h"
 #include "error.h"
-#include "glib-helper.h"
+#include "uuid-helper.h"
 #include "sdp-client.h"
 #include "attrib/gatt.h"
 #include "agent.h"
+#include "textfile.h"
 #include "storage.h"
 #include "attrib-server.h"
 
@@ -84,6 +85,7 @@ struct bonding_req {
        DBusMessage *msg;
        guint listener_id;
        struct btd_device *device;
+       uint8_t bdaddr_type;
        struct agent *agent;
        struct btd_adapter_pin_cb_iter *cb_iter;
        uint8_t status;
@@ -118,6 +120,7 @@ struct browse_req {
        int search_uuid;
        int reconnect_attempt;
        guint listener_id;
+       uint16_t sdp_flags;
 };
 
 struct included_search {
@@ -150,14 +153,23 @@ struct att_callbacks {
        gpointer user_data;
 };
 
+/* Per-bearer (LE or BR/EDR) device state */
+struct bearer_state {
+       bool paired;
+       bool bonded;
+       bool connected;
+       bool svc_resolved;
+};
+
 struct btd_device {
        int ref_count;
 
        bdaddr_t        bdaddr;
        uint8_t         bdaddr_type;
        char            *path;
+       bool            bredr;
+       bool            le;
        bool            pending_paired;         /* "Paired" waiting for SDP */
-       bool            svc_resolved;
        bool            svc_refreshed;
        GSList          *svc_callbacks;
        GSList          *eir_uuids;
@@ -190,14 +202,16 @@ struct btd_device {
        GSList          *attios_offline;
        guint           attachid;               /* Attrib server attach */
 
-       gboolean        connected;
+       struct bearer_state bredr_state;
+       struct bearer_state le_state;
 
        sdp_list_t      *tmp_records;
 
+       time_t          bredr_seen;
+       time_t          le_seen;
+
        gboolean        trusted;
-       gboolean        paired;
        gboolean        blocked;
-       gboolean        bonded;
        gboolean        auto_connect;
        gboolean        disable_auto_connect;
        gboolean        general_connect;
@@ -220,6 +234,15 @@ static const uint16_t uuid_list[] = {
 static int device_browse_primary(struct btd_device *device, DBusMessage *msg);
 static int device_browse_sdp(struct btd_device *device, DBusMessage *msg);
 
+static struct bearer_state *get_state(struct btd_device *dev,
+                                                       uint8_t bdaddr_type)
+{
+       if (bdaddr_type == BDADDR_BREDR)
+               return &dev->bredr_state;
+       else
+               return &dev->le_state;
+}
+
 static GSList *find_service_with_profile(GSList *list, struct btd_profile *p)
 {
        GSList *l;
@@ -249,6 +272,31 @@ static GSList *find_service_with_state(GSList *list,
        return NULL;
 }
 
+static void update_technologies(GKeyFile *file, struct btd_device *dev)
+{
+       const char *list[2];
+       size_t len = 0;
+
+       if (dev->bredr)
+               list[len++] = "BR/EDR";
+
+       if (dev->le) {
+               const char *type;
+
+               if (dev->bdaddr_type == BDADDR_LE_PUBLIC)
+                       type = "public";
+               else
+                       type = "static";
+
+               g_key_file_set_string(file, "General", "AddressType", type);
+
+               list[len++] = "LE";
+       }
+
+       g_key_file_set_string_list(file, "General", "SupportedTechnologies",
+                                                               list, len);
+}
+
 static gboolean store_device_info_cb(gpointer user_data)
 {
        struct btd_device *device = user_data;
@@ -294,34 +342,7 @@ static gboolean store_device_info_cb(gpointer user_data)
                g_key_file_remove_key(key_file, "General", "Appearance", NULL);
        }
 
-       switch (device->bdaddr_type) {
-       case BDADDR_BREDR:
-               g_key_file_set_string(key_file, "General",
-                                       "SupportedTechnologies", "BR/EDR");
-               g_key_file_remove_key(key_file, "General",
-                                       "AddressType", NULL);
-               break;
-
-       case BDADDR_LE_PUBLIC:
-               g_key_file_set_string(key_file, "General",
-                                       "SupportedTechnologies", "LE");
-               g_key_file_set_string(key_file, "General",
-                                       "AddressType", "public");
-               break;
-
-       case BDADDR_LE_RANDOM:
-               g_key_file_set_string(key_file, "General",
-                                       "SupportedTechnologies", "LE");
-               g_key_file_set_string(key_file, "General",
-                                       "AddressType", "static");
-               break;
-
-       default:
-               g_key_file_remove_key(key_file, "General",
-                                       "SupportedTechnologies", NULL);
-               g_key_file_remove_key(key_file, "General",
-                                       "AddressType", NULL);
-       }
+       update_technologies(key_file, device);
 
        g_key_file_set_boolean(key_file, "General", "Trusted",
                                                        device->trusted);
@@ -532,28 +553,22 @@ static void device_free(gpointer user_data)
 
        g_free(device->path);
        g_free(device->alias);
-       g_free(device->modalias);
+       free(device->modalias);
        g_free(device);
 }
 
-gboolean device_is_bredr(struct btd_device *device)
+bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type)
 {
-       return (device->bdaddr_type == BDADDR_BREDR);
-}
+       struct bearer_state *state = get_state(device, bdaddr_type);
 
-gboolean device_is_le(struct btd_device *device)
-{
-       return (device->bdaddr_type != BDADDR_BREDR);
+       return state->paired;
 }
 
-gboolean device_is_paired(struct btd_device *device)
+bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type)
 {
-       return device->paired;
-}
+       struct bearer_state *state = get_state(device, bdaddr_type);
 
-gboolean device_is_bonded(struct btd_device *device)
-{
-       return device->bonded;
+       return state->bonded;
 }
 
 gboolean device_is_trusted(struct btd_device *device)
@@ -753,8 +768,13 @@ static gboolean dev_property_get_icon(const GDBusPropertyTable *property,
 static gboolean dev_property_get_paired(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *data)
 {
-       struct btd_device *device = data;
-       gboolean val = device_is_paired(device);
+       struct btd_device *dev = data;
+       dbus_bool_t val;
+
+       if (dev->bredr_state.paired || dev->le_state.paired)
+               val = TRUE;
+       else
+               val = FALSE;
 
        dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
 
@@ -890,10 +910,15 @@ static void dev_property_set_blocked(const GDBusPropertyTable *property,
 static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *data)
 {
-       struct btd_device *device = data;
+       struct btd_device *dev = data;
+       dbus_bool_t connected;
 
-       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
-                                                       &device->connected);
+       if (dev->bredr_state.connected || dev->le_state.connected)
+               connected = TRUE;
+       else
+               connected = FALSE;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected);
 
        return TRUE;
 }
@@ -901,19 +926,19 @@ static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
 static gboolean dev_property_get_uuids(const GDBusPropertyTable *property,
                                        DBusMessageIter *iter, void *data)
 {
-       struct btd_device *device = data;
+       struct btd_device *dev = data;
        DBusMessageIter entry;
        GSList *l;
 
        dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
                                DBUS_TYPE_STRING_AS_STRING, &entry);
 
-       if (device->svc_resolved)
-               l = device->uuids;
-       else if (device->eir_uuids)
-               l = device->eir_uuids;
+       if (dev->bredr_state.svc_resolved || dev->le_state.svc_resolved)
+               l = dev->uuids;
+       else if (dev->eir_uuids)
+               l = dev->eir_uuids;
        else
-               l = device->uuids;
+               l = dev->uuids;
 
        for (; l != NULL; l = l->next)
                dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
@@ -957,13 +982,18 @@ static gboolean dev_property_get_adapter(const GDBusPropertyTable *property,
        return TRUE;
 }
 
-static gboolean do_disconnect(gpointer user_data)
+static gboolean disconnect_all(gpointer user_data)
 {
        struct btd_device *device = user_data;
 
        device->disconn_timer = 0;
 
-       btd_adapter_disconnect_device(device->adapter, &device->bdaddr,
+       if (device->bredr_state.connected)
+               btd_adapter_disconnect_device(device->adapter, &device->bdaddr,
+                                                               BDADDR_BREDR);
+
+       if (device->le_state.connected)
+               btd_adapter_disconnect_device(device->adapter, &device->bdaddr,
                                                        device->bdaddr_type);
 
        return FALSE;
@@ -976,8 +1006,7 @@ int device_block(struct btd_device *device, gboolean update_only)
        if (device->blocked)
                return 0;
 
-       if (device->connected)
-               do_disconnect(device);
+       disconnect_all(device);
 
        while (device->services != NULL) {
                struct btd_service *service = device->services->data;
@@ -986,9 +1015,16 @@ int device_block(struct btd_device *device, gboolean update_only)
                service_remove(service);
        }
 
-       if (!update_only)
-               err = btd_adapter_block_address(device->adapter,
-                                       &device->bdaddr, device->bdaddr_type);
+       if (!update_only) {
+               if (device->le)
+                       err = btd_adapter_block_address(device->adapter,
+                                                       &device->bdaddr,
+                                                       device->bdaddr_type);
+               if (!err && device->bredr)
+                       err = btd_adapter_block_address(device->adapter,
+                                                       &device->bdaddr,
+                                                       BDADDR_BREDR);
+       }
 
        if (err < 0)
                return err;
@@ -1071,7 +1107,7 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
                device->connect = NULL;
        }
 
-       if (device->connected && msg)
+       if (btd_device_is_connected(device) && msg)
                device->disconnects = g_slist_append(device->disconnects,
                                                dbus_message_ref(msg));
 
@@ -1099,14 +1135,15 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
                g_free(data);
        }
 
-       if (!device->connected) {
+       if (!btd_device_is_connected(device)) {
                if (msg)
                        g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
                return;
        }
 
        device->disconn_timer = g_timeout_add_seconds(DISCONNECT_TIMER,
-                                               do_disconnect, device);
+                                                       disconnect_all,
+                                                       device);
 }
 
 static DBusMessage *dev_disconnect(DBusConnection *conn, DBusMessage *msg,
@@ -1157,7 +1194,7 @@ static void device_profile_connected(struct btd_device *dev,
        if (dev->pending == NULL)
                return;
 
-       if (!dev->connected) {
+       if (!btd_device_is_connected(dev)) {
                switch (-err) {
                case EHOSTDOWN: /* page timeout */
                case EHOSTUNREACH: /* adapter not powered */
@@ -1199,7 +1236,7 @@ done:
                                btd_error_failed(dev->connect, strerror(-err)));
        else {
                /* Start passive SDP discovery to update known services */
-               if (device_is_bredr(dev) && !dev->svc_refreshed)
+               if (dev->bredr && !dev->svc_refreshed)
                        device_browse_sdp(dev, NULL);
                g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID);
        }
@@ -1216,7 +1253,7 @@ void device_add_eir_uuids(struct btd_device *dev, GSList *uuids)
        GSList *l;
        bool added = false;
 
-       if (dev->svc_resolved)
+       if (dev->bredr_state.svc_resolved || dev->le_state.svc_resolved)
                return;
 
        for (l = uuids; l != NULL; l = l->next) {
@@ -1294,9 +1331,33 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
        return dev->pending;
 }
 
-static DBusMessage *connect_profiles(struct btd_device *dev, DBusMessage *msg,
-                                                       const char *uuid)
+int btd_device_connect_services(struct btd_device *dev, GSList *services)
 {
+       GSList *l;
+
+       if (dev->pending || dev->connect || dev->browse)
+               return -EBUSY;
+
+       if (!btd_adapter_get_powered(dev->adapter))
+               return -ENETDOWN;
+
+       if (!dev->bredr_state.svc_resolved)
+               return -ENOENT;
+
+       for (l = services; l; l = g_slist_next(l)) {
+               struct btd_service *service = l->data;
+
+               dev->pending = g_slist_append(dev->pending,
+                                               btd_service_ref(service));
+       }
+
+       return connect_next(dev);
+}
+
+static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type,
+                                       DBusMessage *msg, const char *uuid)
+{
+       struct bearer_state *state = get_state(dev, bdaddr_type);
        int err;
 
        DBG("%s %s, client %s", dev->path, uuid ? uuid : "(all)",
@@ -1310,7 +1371,7 @@ static DBusMessage *connect_profiles(struct btd_device *dev, DBusMessage *msg,
 
        btd_device_set_temporary(dev, FALSE);
 
-       if (!dev->svc_resolved)
+       if (!state->svc_resolved)
                goto resolve_services;
 
        dev->pending = create_pending_list(dev, uuid);
@@ -1337,7 +1398,7 @@ static DBusMessage *connect_profiles(struct btd_device *dev, DBusMessage *msg,
 resolve_services:
        DBG("Resolving services for %s", dev->path);
 
-       if (device_is_bredr(dev))
+       if (bdaddr_type == BDADDR_BREDR)
                err = device_browse_sdp(dev, msg);
        else
                err = device_browse_primary(dev, msg);
@@ -1347,15 +1408,55 @@ resolve_services:
        return NULL;
 }
 
+#define NVAL_TIME ((time_t) -1)
+#define SEEN_TRESHHOLD 300
+
+static uint8_t select_conn_bearer(struct btd_device *dev)
+{
+       time_t bredr_last = NVAL_TIME, le_last = NVAL_TIME;
+       time_t current = time(NULL);
+
+       if (dev->bredr_seen) {
+               bredr_last = current - dev->bredr_seen;
+               if (bredr_last > SEEN_TRESHHOLD)
+                       bredr_last = NVAL_TIME;
+       }
+
+       if (dev->le_seen) {
+               le_last = current - dev->le_seen;
+               if (le_last > SEEN_TRESHHOLD)
+                       le_last = NVAL_TIME;
+       }
+
+       if (dev->bredr && (!dev->le || le_last == NVAL_TIME))
+               return BDADDR_BREDR;
+
+       if (dev->le && (!dev->bredr || bredr_last == NVAL_TIME))
+               return dev->bdaddr_type;
+
+       if (bredr_last < le_last)
+               return BDADDR_BREDR;
+
+       return dev->bdaddr_type;
+}
+
 static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
                                                        void *user_data)
 {
        struct btd_device *dev = user_data;
+       uint8_t bdaddr_type;
 
-       if (device_is_le(dev)) {
+       if (dev->bredr_state.connected)
+               bdaddr_type = dev->bdaddr_type;
+       else if (dev->le_state.connected && dev->bredr)
+               bdaddr_type = BDADDR_BREDR;
+       else
+               bdaddr_type = select_conn_bearer(dev);
+
+       if (bdaddr_type != BDADDR_BREDR) {
                int err;
 
-               if (btd_device_is_connected(dev))
+               if (dev->le_state.connected)
                        return dbus_message_new_method_return(msg);
 
                btd_device_set_temporary(dev, FALSE);
@@ -1371,7 +1472,7 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
                return NULL;
        }
 
-       return connect_profiles(dev, msg, NULL);
+       return connect_profiles(dev, bdaddr_type, msg, NULL);
 }
 
 static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
@@ -1387,8 +1488,8 @@ static DBusMessage *connect_profile(DBusConnection *conn, DBusMessage *msg,
                return btd_error_invalid_args(msg);
 
        uuid = bt_name2string(pattern);
-       reply = connect_profiles(dev, msg, uuid);
-       g_free(uuid);
+       reply = connect_profiles(dev, BDADDR_BREDR, msg, uuid);
+       free(uuid);
 
        return reply;
 }
@@ -1429,7 +1530,7 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg,
                return btd_error_invalid_args(msg);
 
        service = find_connectable_service(dev, uuid);
-       g_free(uuid);
+       free(uuid);
 
        if (!service)
                return btd_error_invalid_args(msg);
@@ -1452,21 +1553,23 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg,
        return btd_error_failed(msg, strerror(-err));
 }
 
-static void device_svc_resolved(struct btd_device *dev, int err)
+static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
+                                                               int err)
 {
+       struct bearer_state *state = get_state(dev, bdaddr_type);
        DBusMessage *reply;
        struct browse_req *req = dev->browse;
 
        DBG("%s err %d", dev->path, err);
 
-       dev->svc_resolved = true;
+       state->svc_resolved = true;
        dev->browse = NULL;
 
        /* Disconnection notification can happen before this function
         * gets called, so don't set svc_refreshed for a disconnected
         * device.
         */
-       if (dev->connected)
+       if (state->connected)
                dev->svc_refreshed = true;
 
        g_slist_free_full(dev->eir_uuids, g_free);
@@ -1491,6 +1594,9 @@ static void device_svc_resolved(struct btd_device *dev, int err)
                g_free(cb);
        }
 
+       if (!dev->temporary)
+               store_device_info(dev);
+
        if (!req || !req->msg)
                return;
 
@@ -1523,6 +1629,7 @@ static void device_svc_resolved(struct btd_device *dev, int err)
 
 static struct bonding_req *bonding_request_new(DBusMessage *msg,
                                                struct btd_device *device,
+                                               uint8_t bdaddr_type,
                                                struct agent *agent)
 {
        struct bonding_req *bonding;
@@ -1534,6 +1641,7 @@ static struct bonding_req *bonding_request_new(DBusMessage *msg,
        bonding = g_new0(struct bonding_req, 1);
 
        bonding->msg = dbus_message_ref(msg);
+       bonding->bdaddr_type = bdaddr_type;
 
        bonding->cb_iter = btd_adapter_pin_cb_iter_new(device->adapter);
 
@@ -1609,6 +1717,8 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
 {
        struct btd_device *device = data;
        struct btd_adapter *adapter = device->adapter;
+       struct bearer_state *state;
+       uint8_t bdaddr_type;
        const char *sender;
        struct agent *agent;
        struct bonding_req *bonding;
@@ -1623,7 +1733,16 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
        if (device->bonding)
                return btd_error_in_progress(msg);
 
-       if (device_is_bonded(device))
+       if (device->bredr_state.bonded)
+               bdaddr_type = device->bdaddr_type;
+       else if (device->le_state.bonded)
+               bdaddr_type = BDADDR_BREDR;
+       else
+               bdaddr_type = select_conn_bearer(device);
+
+       state = get_state(device, bdaddr_type);
+
+       if (state->bonded)
                return btd_error_already_exists(msg);
 
        sender = dbus_message_get_sender(msg);
@@ -1634,7 +1753,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
        else
                io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
 
-       bonding = bonding_request_new(msg, device, agent);
+       bonding = bonding_request_new(msg, device, bdaddr_type, agent);
 
        if (agent)
                agent_unref(agent);
@@ -1651,11 +1770,17 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
         * channel first and only then start pairing (there's code for
         * this in the ATT connect callback)
         */
-       if (device_is_le(device) && !btd_device_is_connected(device))
-               err = device_connect_le(device);
-       else
+       if (bdaddr_type != BDADDR_BREDR) {
+               if (!state->connected)
+                       err = device_connect_le(device);
+               else
+                       err = adapter_create_bonding(adapter, &device->bdaddr,
+                                                       device->bdaddr_type,
+                                                       io_cap);
+       } else {
                err = adapter_create_bonding(adapter, &device->bdaddr,
-                                               device->bdaddr_type, io_cap);
+                                                       BDADDR_BREDR, io_cap);
+       }
 
        if (err < 0)
                return btd_error_failed(msg, strerror(-err));
@@ -1710,7 +1835,6 @@ static void bonding_request_free(struct bonding_req *bonding)
                g_free(bonding->cb_iter);
 
        if (bonding->agent) {
-               agent_cancel(bonding->agent);
                agent_unref(bonding->agent);
                bonding->agent = NULL;
        }
@@ -1797,38 +1921,54 @@ static const GDBusPropertyTable device_properties[] = {
        { }
 };
 
-gboolean btd_device_is_connected(struct btd_device *device)
+uint8_t btd_device_get_bdaddr_type(struct btd_device *dev)
+{
+       return dev->bdaddr_type;
+}
+
+bool btd_device_is_connected(struct btd_device *dev)
 {
-       return device->connected;
+       return dev->bredr_state.connected || dev->le_state.connected;
 }
 
-void device_add_connection(struct btd_device *device)
+void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type)
 {
-       if (device->connected) {
+       struct bearer_state *state = get_state(dev, bdaddr_type);
+
+       device_update_last_seen(dev, bdaddr_type);
+
+       if (state->connected) {
                char addr[18];
-               ba2str(&device->bdaddr, addr);
+               ba2str(&dev->bdaddr, addr);
                error("Device %s is already connected", addr);
                return;
        }
 
-       device->connected = TRUE;
+       /* If this is the first connection over this bearer */
+       if (bdaddr_type == BDADDR_BREDR)
+               device_set_bredr_support(dev);
+       else
+               device_set_le_support(dev, bdaddr_type);
 
-       g_dbus_emit_property_changed(dbus_conn, device->path,
-                                               DEVICE_INTERFACE, "Connected");
+       state->connected = true;
+
+       if (dev->le_state.connected && dev->bredr_state.connected)
+               return;
+
+       g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE,
+                                                               "Connected");
 }
 
-void device_remove_connection(struct btd_device *device)
+void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type)
 {
-       if (!device->connected) {
-               char addr[18];
-               ba2str(&device->bdaddr, addr);
-               error("Device %s isn't connected", addr);
+       struct bearer_state *state = get_state(device, bdaddr_type);
+
+       if (!state->connected)
                return;
-       }
 
-       device->connected = FALSE;
-       device->general_connect = FALSE;
+       state->connected = false;
        device->svc_refreshed = false;
+       device->general_connect = FALSE;
 
        if (device->disconn_timer > 0) {
                g_source_remove(device->disconn_timer);
@@ -1843,8 +1983,12 @@ void device_remove_connection(struct btd_device *device)
                dbus_message_unref(msg);
        }
 
-       if (device_is_paired(device) && !device_is_bonded(device))
-               device_set_paired(device, FALSE);
+       if (state->paired && !state->bonded)
+               btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
+                                                               bdaddr_type);
+
+       if (device->bredr_state.connected || device->le_state.connected)
+               return;
 
        g_dbus_emit_property_changed(dbus_conn, device->path,
                                                DEVICE_INTERFACE, "Connected");
@@ -1924,8 +2068,6 @@ static void load_info(struct btd_device *device, const char *local,
        char **uuids;
        int source, vendor, product, version;
        char **techno, **t;
-       gboolean bredr = FALSE;
-       gboolean le = FALSE;
 
        /* Load device name from storage info file, if that fails fall back to
         * the cache.
@@ -1971,18 +2113,16 @@ static void load_info(struct btd_device *device, const char *local,
 
        for (t = techno; *t; t++) {
                if (g_str_equal(*t, "BR/EDR"))
-                       bredr = TRUE;
+                       device->bredr = true;
                else if (g_str_equal(*t, "LE"))
-                       le = TRUE;
+                       device->le = true;
                else
                        error("Unknown device technology");
        }
 
-       if (bredr && le) {
-               /* TODO: Add correct type for dual mode device */
-       } else if (bredr) {
+       if (!device->le) {
                device->bdaddr_type = BDADDR_BREDR;
-       } else if (le) {
+       } else {
                str = g_key_file_get_string(key_file, "General",
                                                "AddressType", NULL);
 
@@ -2029,7 +2169,7 @@ next:
                g_strfreev(uuids);
 
                /* Discovered services restored from storage */
-               device->svc_resolved = true;
+               device->bredr_state.svc_resolved = true;
        }
 
        /* Load device id */
@@ -2129,7 +2269,7 @@ static void load_att_info(struct btd_device *device, const char *local,
 
                service_uuid = bt_uuid2string(&uuid);
                memcpy(prim->uuid, service_uuid, MAX_LEN_UUID_STR);
-               g_free(service_uuid);
+               free(service_uuid);
                g_free(str);
 
                device->primaries = g_slist_append(device->primaries, prim);
@@ -2137,7 +2277,7 @@ static void load_att_info(struct btd_device *device, const char *local,
 
        g_strfreev(groups);
        g_key_file_free(key_file);
-       g_free(prim_uuid);
+       free(prim_uuid);
 }
 
 static struct btd_device *device_new(struct btd_adapter *adapter,
@@ -2214,6 +2354,12 @@ struct btd_device *device_create(struct btd_adapter *adapter,
                return NULL;
 
        device->bdaddr_type = bdaddr_type;
+
+       if (bdaddr_type == BDADDR_BREDR)
+               device->bredr = true;
+       else
+               device->le = true;
+
        sba = btd_adapter_get_address(adapter);
        ba2str(sba, src);
 
@@ -2270,7 +2416,10 @@ void btd_device_device_set_name(struct btd_device *device, const char *name)
 
 void device_get_name(struct btd_device *device, char *name, size_t len)
 {
-       strncpy(name, device->name, len);
+       if (name != NULL && len > 0) {
+               strncpy(name, device->name, len - 1);
+               name[len - 1] = '\0';
+       }
 }
 
 bool device_name_known(struct btd_device *device)
@@ -2295,6 +2444,90 @@ void device_set_class(struct btd_device *device, uint32_t class)
                                                DEVICE_INTERFACE, "Icon");
 }
 
+void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type)
+{
+       if (!bacmp(bdaddr, &device->bdaddr) &&
+                                       bdaddr_type == device->bdaddr_type)
+               return;
+
+       /* Since this function is only used for LE SMP Identity
+        * Resolving purposes we can now assume LE is supported.
+        */
+       device->le = true;
+
+       bacpy(&device->bdaddr, bdaddr);
+       device->bdaddr_type = bdaddr_type;
+
+       store_device_info(device);
+
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                               DEVICE_INTERFACE, "Address");
+}
+
+void device_set_bredr_support(struct btd_device *device)
+{
+       if (device->bredr)
+               return;
+
+       device->bredr = true;
+       store_device_info(device);
+}
+
+void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type)
+{
+       if (device->le)
+               return;
+
+       device->le = true;
+       device->bdaddr_type = bdaddr_type;
+
+       store_device_info(device);
+}
+
+void device_update_last_seen(struct btd_device *device, uint8_t bdaddr_type)
+{
+       if (bdaddr_type == BDADDR_BREDR)
+               device->bredr_seen = time(NULL);
+       else
+               device->le_seen = time(NULL);
+}
+
+/* It is possible that we have two device objects for the same device in
+ * case it has first been discovered over BR/EDR and has a private
+ * address when discovered over LE for the first time. In such a case we
+ * need to inherit critical values from the duplicate so that we don't
+ * ovewrite them when writing to storage. The next time bluetoothd
+ * starts the device will show up as a single instance.
+ */
+void device_merge_duplicate(struct btd_device *dev, struct btd_device *dup)
+{
+       GSList *l;
+
+       DBG("");
+
+       dev->bredr = dup->bredr;
+
+       dev->trusted = dup->trusted;
+       dev->blocked = dup->blocked;
+
+       for (l = dup->uuids; l; l = g_slist_next(l))
+               dev->uuids = g_slist_append(dev->uuids, g_strdup(l->data));
+
+       if (dev->name[0] == '\0')
+               strcpy(dev->name, dup->name);
+
+       if (!dev->alias)
+               dev->alias = g_strdup(dup->alias);
+
+       dev->class = dup->class;
+
+       dev->vendor_src = dup->vendor_src;
+       dev->vendor = dup->vendor;
+       dev->product = dup->product;
+       dev->version = dup->version;
+}
+
 uint32_t btd_device_get_class(struct btd_device *device)
 {
        return device->class;
@@ -2351,7 +2584,6 @@ static void delete_folder_tree(const char *dirname)
 static void device_remove_stored(struct btd_device *device)
 {
        const bdaddr_t *src = btd_adapter_get_address(device->adapter);
-       uint8_t dst_type = device->bdaddr_type;
        char adapter_addr[18];
        char device_addr[18];
        char filename[PATH_MAX + 1];
@@ -2359,13 +2591,21 @@ static void device_remove_stored(struct btd_device *device)
        char *data;
        gsize length = 0;
 
-       if (device_is_bonded(device)) {
-               device_set_bonded(device, FALSE);
-               device->paired = FALSE;
+       if (device->bredr_state.bonded) {
+               device->bredr_state.bonded = false;
                btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
-                                                               dst_type);
+                                                               BDADDR_BREDR);
+       }
+
+       if (device->le_state.bonded) {
+               device->le_state.bonded = false;
+               btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
+                                                       device->bdaddr_type);
        }
 
+       device->bredr_state.paired = false;
+       device->le_state.paired = false;
+
        if (device->blocked)
                device_unblock(device, TRUE, FALSE);
 
@@ -2402,7 +2642,7 @@ void device_remove(struct btd_device *device, gboolean remove_stored)
        if (device->bonding) {
                uint8_t status;
 
-               if (device->connected)
+               if (device->bredr_state.connected)
                        status = MGMT_STATUS_DISCONNECTED;
                else
                        status = MGMT_STATUS_CONNECT_FAILED;
@@ -2423,8 +2663,8 @@ void device_remove(struct btd_device *device, gboolean remove_stored)
        g_slist_free(device->pending);
        device->pending = NULL;
 
-       if (device->connected)
-               do_disconnect(device);
+       if (btd_device_is_connected(device))
+               disconnect_all(device);
 
        if (device->store_id > 0) {
                g_source_remove(device->store_id);
@@ -2458,6 +2698,47 @@ int device_bdaddr_cmp(gconstpointer a, gconstpointer b)
        return bacmp(&device->bdaddr, bdaddr);
 }
 
+static bool addr_is_public(uint8_t addr_type)
+{
+       if (addr_type == BDADDR_BREDR || addr_type == BDADDR_LE_PUBLIC)
+               return true;
+
+       return false;
+}
+
+int device_addr_type_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct btd_device *dev = a;
+       const struct device_addr_type *addr = b;
+       int cmp;
+
+       cmp = bacmp(&dev->bdaddr, &addr->bdaddr);
+
+       /*
+        * Address matches and both old and new are public addresses
+        * (doesn't matter whether LE or BR/EDR, then consider this a
+        * match.
+        */
+       if (!cmp && addr_is_public(addr->bdaddr_type) &&
+                                       addr_is_public(dev->bdaddr_type))
+               return 0;
+
+       if (addr->bdaddr_type == BDADDR_BREDR) {
+               if (!dev->bredr)
+                       return -1;
+
+               return cmp;
+       }
+
+       if (!dev->le)
+              return -1;
+
+       if (addr->bdaddr_type != dev->bdaddr_type)
+               return -1;
+
+       return cmp;
+}
+
 static gboolean record_has_uuid(const sdp_record_t *rec,
                                const char *profile_uuid)
 {
@@ -2473,7 +2754,7 @@ static gboolean record_has_uuid(const sdp_record_t *rec,
 
                ret = strcasecmp(uuid, profile_uuid);
 
-               g_free(uuid);
+               free(uuid);
 
                if (ret == 0)
                        return TRUE;
@@ -2504,7 +2785,6 @@ static bool device_match_profile(struct btd_device *device,
 struct probe_data {
        struct btd_device *dev;
        GSList *uuids;
-       char addr[18];
 };
 
 static void dev_probe(struct btd_profile *p, void *user_data)
@@ -2579,15 +2859,16 @@ void device_probe_profiles(struct btd_device *device, GSList *uuids)
 {
        struct probe_data d = { device, uuids };
        GSList *l;
+       char addr[18];
 
-       ba2str(&device->bdaddr, d.addr);
+       ba2str(&device->bdaddr, addr);
 
        if (device->blocked) {
-               DBG("Skipping profiles for blocked device %s", d.addr);
+               DBG("Skipping profiles for blocked device %s", addr);
                goto add_uuids;
        }
 
-       DBG("Probing profiles for device %s", d.addr);
+       DBG("Probing profiles for device %s", addr);
 
        btd_profile_foreach(dev_probe, &d);
 
@@ -2675,8 +2956,8 @@ static void store_primaries_from_sdp_record(GKeyFile *key_file,
        g_key_file_set_integer(key_file, handle, "EndGroupHandle", end);
 
 done:
-       g_free(prim_uuid);
-       g_free(att_uuid);
+       free(prim_uuid);
+       free(att_uuid);
 }
 
 static int rec_cmp(const void *a, const void *b)
@@ -2799,7 +3080,7 @@ static void update_bredr_services(struct browse_req *req, sdp_list_t *recs)
                        store_primaries_from_sdp_record(att_key_file, rec);
 
 next:
-               g_free(profile_uuid);
+               free(profile_uuid);
                sdp_list_free(svcclass, free);
        }
 
@@ -2888,7 +3169,7 @@ static GSList *device_services_from_record(struct btd_device *device,
                prim_list = g_slist_append(prim_list, prim);
        }
 
-       g_free(att_uuid);
+       free(att_uuid);
 
        return prim_list;
 }
@@ -2939,11 +3220,7 @@ static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
                                                DEVICE_INTERFACE, "UUIDs");
 
 send_reply:
-       device_svc_resolved(device, err);
-
-       if (!device->temporary)
-               store_device_info(device);
-
+       device_svc_resolved(device, BDADDR_BREDR, err);
        browse_request_free(req);
 }
 
@@ -2971,7 +3248,8 @@ static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
                sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
                bt_search_service(btd_adapter_get_address(adapter),
                                                &device->bdaddr, &uuid,
-                                               browse_cb, user_data, NULL);
+                                               browse_cb, user_data, NULL,
+                                               req->sdp_flags);
                return;
        }
 
@@ -3049,7 +3327,7 @@ static void store_services(struct btd_device *device)
                g_file_set_contents(filename, data, length, NULL);
        }
 
-       g_free(prim_uuid);
+       free(prim_uuid);
        g_free(data);
        g_key_file_free(key_file);
 }
@@ -3126,7 +3404,7 @@ static void register_all_services(struct browse_req *req, GSList *services)
        g_slist_free_full(device->primaries, g_free);
        device->primaries = NULL;
 
-       device_register_primaries(device, g_slist_copy(services), -1);
+       device_register_primaries(device, services, -1);
 
        device_probe_profiles(device, req->profiles_added);
 
@@ -3136,7 +3414,7 @@ static void register_all_services(struct browse_req *req, GSList *services)
        g_dbus_emit_property_changed(dbus_conn, device->path,
                                                DEVICE_INTERFACE, "UUIDs");
 
-       device_svc_resolved(device, 0);
+       device_svc_resolved(device, device->bdaddr_type, 0);
 
        store_services(device);
 
@@ -3151,29 +3429,66 @@ static int service_by_range_cmp(gconstpointer a, gconstpointer b)
        return memcmp(&prim->range, range, sizeof(*range));
 }
 
-static void find_included_cb(GSList *includes, uint8_t status,
-                                               gpointer user_data)
+static void send_le_browse_response(struct browse_req *req)
+{
+       struct btd_device *dev = req->device;
+       struct bearer_state *state = &dev->le_state;
+       DBusMessage *reply, *msg = req->msg;
+
+       if (!msg)
+               return;
+
+       if (!dbus_message_is_method_call(msg, DEVICE_INTERFACE, "Pair")) {
+               reply = btd_error_failed(msg, "Service discovery failed");
+               g_dbus_send_message(dbus_conn, reply);
+               return;
+       }
+
+       if (!state->paired) {
+               reply = btd_error_failed(msg, "Not paired");
+               g_dbus_send_message(dbus_conn, reply);
+               return;
+       }
+
+       if (!dev->bredr_state.paired && dev->pending_paired) {
+               g_dbus_emit_property_changed(dbus_conn, dev->path,
+                                               DEVICE_INTERFACE, "Paired");
+               dev->pending_paired = false;
+       }
+
+       g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
+}
+
+static void find_included_cb(uint8_t status, GSList *includes, void *user_data)
 {
        struct included_search *search = user_data;
        struct btd_device *device = search->req->device;
        struct gatt_primary *prim;
        GSList *l;
 
-       if (device->attrib == NULL) {
-               error("Disconnected while doing included discovery");
-               g_slist_free(search->services);
-               g_free(search);
-               return;
-       }
+       DBG("status %u", status);
+
+       if (device->attrib == NULL || status) {
+               struct browse_req *req = device->browse;
 
-       if (status != 0) {
-               error("Find included services failed: %s (%d)",
+               if (status)
+                       error("Find included services failed: %s (%d)",
                                        att_ecode2str(status), status);
-               goto done;
+               else
+                       error("Disconnected while doing included discovery");
+
+               if (!req)
+                       goto complete;
+
+               send_le_browse_response(req);
+               device->browse = NULL;
+               browse_request_free(req);
+
+               goto complete;
        }
 
        if (includes == NULL)
-               goto done;
+               goto next;
 
        for (l = includes; l; l = l->next) {
                struct gatt_included *incl = l->data;
@@ -3189,18 +3504,22 @@ static void find_included_cb(GSList *includes, uint8_t status,
                search->services = g_slist_append(search->services, prim);
        }
 
-done:
+next:
        search->current = search->current->next;
        if (search->current == NULL) {
                register_all_services(search->req, search->services);
-               g_slist_free(search->services);
-               g_free(search);
-               return;
+               search->services = NULL;
+               goto complete;
        }
 
        prim = search->current->data;
        gatt_find_included(device->attrib, prim->range.start, prim->range.end,
                                        find_included_cb, search);
+       return;
+
+complete:
+       g_slist_free_full(search->services, g_free);
+       g_free(search);
 }
 
 static void find_included_services(struct browse_req *req, GSList *services)
@@ -3208,13 +3527,31 @@ static void find_included_services(struct browse_req *req, GSList *services)
        struct btd_device *device = req->device;
        struct included_search *search;
        struct gatt_primary *prim;
+       GSList *l;
+
+       DBG("service count %u", g_slist_length(services));
 
-       if (services == NULL)
+       if (services == NULL) {
+               DBG("No services found");
+               register_all_services(req, NULL);
                return;
+       }
 
        search = g_new0(struct included_search, 1);
        search->req = req;
-       search->services = g_slist_copy(services);
+
+       /* We have to completely duplicate the data in order to have a
+        * clearly defined responsibility of freeing regardless of
+        * failure or success. Otherwise memory leaks are inevitable.
+        */
+       for (l = services; l; l = g_slist_next(l)) {
+               struct gatt_primary *dup;
+
+               dup = g_memdup(l->data, sizeof(struct gatt_primary));
+
+               search->services = g_slist_append(search->services, dup);
+       }
+
        search->current = search->services;
 
        prim = search->current->data;
@@ -3222,20 +3559,16 @@ static void find_included_services(struct browse_req *req, GSList *services)
                                        find_included_cb, search);
 }
 
-static void primary_cb(GSList *services, guint8 status, gpointer user_data)
+static void primary_cb(uint8_t status, GSList *services, void *user_data)
 {
        struct browse_req *req = user_data;
 
+       DBG("status %u", status);
+
        if (status) {
                struct btd_device *device = req->device;
 
-               if (req->msg) {
-                       DBusMessage *reply;
-                       reply = btd_error_failed(req->msg,
-                                                       att_ecode2str(status));
-                       g_dbus_send_message(dbus_conn, reply);
-               }
-
+               send_le_browse_response(req);
                device->browse = NULL;
                browse_request_free(req);
                return;
@@ -3244,13 +3577,36 @@ static void primary_cb(GSList *services, guint8 status, gpointer user_data)
        find_included_services(req, services);
 }
 
+bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
+{
+       GAttrib *attrib;
+
+       attrib = g_attrib_new(io);
+       if (!attrib) {
+               error("Unable to create new GAttrib instance");
+               return false;
+       }
+
+       dev->attachid = attrib_channel_attach(attrib);
+       if (dev->attachid == 0) {
+               g_attrib_unref(attrib);
+               error("Attribute server attach failure!");
+               return false;
+       }
+
+       dev->attrib = attrib;
+       dev->cleanup_id = g_io_add_watch(io, G_IO_HUP,
+                                       attrib_disconnected_cb, dev);
+
+       return true;
+}
+
 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;
        DBusMessage *reply;
        uint8_t io_cap;
-       GAttrib *attrib;
        int err = 0;
 
        g_io_channel_unref(device->att_io);
@@ -3266,14 +3622,8 @@ static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer 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 (!device_attach_attrib(device, io))
+               goto done;
 
        if (attcb->success)
                attcb->success(user_data);
@@ -3297,7 +3647,7 @@ done:
        }
 
        if (device->connect) {
-               if (!device->svc_resolved)
+               if (!device->le_state.svc_resolved)
                        device_browse_primary(device, NULL);
 
                if (err < 0)
@@ -3368,7 +3718,7 @@ int device_connect_le(struct btd_device *dev)
        attcb->success = att_success_cb;
        attcb->user_data = dev;
 
-       if (dev->paired)
+       if (dev->le_state.paired)
                sec_level = BT_IO_SEC_MEDIUM;
        else
                sec_level = BT_IO_SEC_LOW;
@@ -3415,13 +3765,7 @@ static void att_browse_error_cb(const GError *gerr, gpointer 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(dbus_conn, reply);
-       }
-
+       send_le_browse_response(req);
        device->browse = NULL;
        browse_request_free(req);
 }
@@ -3494,6 +3838,31 @@ done:
        return 0;
 }
 
+static uint16_t get_sdp_flags(struct btd_device *device)
+{
+       uint16_t vid, pid;
+
+       vid = btd_device_get_vendor(device);
+       pid = btd_device_get_product(device);
+
+       /* Sony DualShock 4 is not respecting negotiated L2CAP MTU. This might
+        * results in SDP response being dropped by kernel. Workaround this by
+        * forcing SDP code to use bigger MTU while connecting.
+        */
+       if (vid == 0x054c && pid == 0x05c4)
+               return SDP_LARGE_MTU;
+
+       if (btd_adapter_ssp_enabled(device->adapter))
+               return 0;
+
+       /* if no EIR try matching Sony DualShock 4 with name and class */
+       if (!strncmp(device->name, "Wireless Controller", MAX_NAME_LENGTH) &&
+                       device->class == 0x2508)
+               return SDP_LARGE_MTU;
+
+       return 0;
+}
+
 static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
 {
        struct btd_adapter *adapter = device->adapter;
@@ -3508,8 +3877,11 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
        req->device = device;
        sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
 
+       req->sdp_flags = get_sdp_flags(device);
+
        err = bt_search_service(btd_adapter_get_address(adapter),
-                               &device->bdaddr, &uuid, browse_cb, req, NULL);
+                               &device->bdaddr, &uuid, browse_cb, req, NULL,
+                               req->sdp_flags);
        if (err < 0) {
                browse_request_free(req);
                return err;
@@ -3536,7 +3908,7 @@ int device_discover_services(struct btd_device *device)
 {
        int err;
 
-       if (device_is_bredr(device))
+       if (device->bredr)
                err = device_browse_sdp(device, NULL);
        else
                err = device_browse_primary(device, NULL);
@@ -3609,14 +3981,17 @@ void btd_device_set_trusted(struct btd_device *device, gboolean trusted)
                                        DEVICE_INTERFACE, "Trusted");
 }
 
-void device_set_bonded(struct btd_device *device, gboolean bonded)
+void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type)
 {
        if (!device)
                return;
 
-       DBG("bonded %d", bonded);
+       DBG("");
 
-       device->bonded = bonded;
+       if (bdaddr_type == BDADDR_BREDR)
+               device->bredr_state.bonded = true;
+       else
+               device->le_state.bonded = true;
 }
 
 void device_set_legacy(struct btd_device *device, bool legacy)
@@ -3672,7 +4047,7 @@ static void device_set_auto_connect(struct btd_device *device, gboolean enable)
 {
        char addr[18];
 
-       if (!device)
+       if (!device || !device->le)
                return;
 
        ba2str(&device->bdaddr, addr);
@@ -3700,7 +4075,7 @@ static gboolean start_discovery(gpointer user_data)
 {
        struct btd_device *device = user_data;
 
-       if (device_is_bredr(device))
+       if (device->bredr)
                device_browse_sdp(device, NULL);
        else
                device_browse_primary(device, NULL);
@@ -3710,29 +4085,75 @@ static gboolean start_discovery(gpointer user_data)
        return FALSE;
 }
 
-void device_set_paired(struct btd_device *device, gboolean value)
+void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type)
 {
-       if (device->paired == value)
+       struct bearer_state *state = get_state(dev, bdaddr_type);
+
+       if (state->paired)
                return;
 
-       if (!value)
-               btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
-                                                       device->bdaddr_type);
+       state->paired = true;
+
+       /* If the other bearer state was alraedy true we don't need to
+        * send any property signals.
+        */
+       if (dev->bredr_state.paired == dev->le_state.paired)
+               return;
 
-       device->paired = value;
+       if (!state->svc_resolved) {
+               dev->pending_paired = true;
+               return;
+       }
 
-       if (device->paired && !device->svc_resolved)
-               device->pending_paired = true;
-       else
-               g_dbus_emit_property_changed(dbus_conn, device->path,
+       g_dbus_emit_property_changed(dbus_conn, dev->path,
                                                DEVICE_INTERFACE, "Paired");
 }
 
+void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type)
+{
+       struct bearer_state *state = get_state(dev, bdaddr_type);
+
+       if (!state->paired)
+               return;
+
+       state->paired = false;
+
+       /*
+        * If the other bearer state is still true we don't need to
+        * send any property signals or remove device.
+        */
+       if (dev->bredr_state.paired != dev->le_state.paired) {
+               /* TODO disconnect only unpaired bearer */
+               if (state->connected)
+                       device_request_disconnect(dev, NULL);
+
+               return;
+       }
+
+       g_dbus_emit_property_changed(dbus_conn, dev->path,
+                                               DEVICE_INTERFACE, "Paired");
+
+       btd_device_set_temporary(dev, TRUE);
+
+       if (btd_device_is_connected(dev))
+               device_request_disconnect(dev, NULL);
+       else
+               btd_adapter_remove_device(dev->adapter, dev);
+}
+
 static void device_auth_req_free(struct btd_device *device)
 {
-       if (device->authr)
-               g_free(device->authr->pincode);
-       g_free(device->authr);
+       struct authentication_req *authr = device->authr;
+
+       if (!authr)
+               return;
+
+       if (authr->agent)
+               agent_unref(authr->agent);
+
+       g_free(authr->pincode);
+       g_free(authr);
+
        device->authr = NULL;
 }
 
@@ -3743,10 +4164,12 @@ bool device_is_retrying(struct btd_device *device)
        return bonding && bonding->retry_timer > 0;
 }
 
-void device_bonding_complete(struct btd_device *device, uint8_t status)
+void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
+                                                               uint8_t status)
 {
        struct bonding_req *bonding = device->bonding;
        struct authentication_req *auth = device->authr;
+       struct bearer_state *state = get_state(device, bdaddr_type);
 
        DBG("bonding %p status 0x%02x", bonding, status);
 
@@ -3762,15 +4185,15 @@ void device_bonding_complete(struct btd_device *device, uint8_t status)
        device_auth_req_free(device);
 
        /* If we're already paired nothing more is needed */
-       if (device->paired)
+       if (state->paired)
                return;
 
-       device_set_paired(device, TRUE);
+       device_set_paired(device, bdaddr_type);
 
        /* If services are already resolved just reply to the pairing
         * request
         */
-       if (device->svc_resolved && bonding) {
+       if (state->svc_resolved && bonding) {
                g_dbus_send_reply(dbus_conn, bonding->msg, DBUS_TYPE_INVALID);
                bonding_request_free(bonding);
                return;
@@ -3789,13 +4212,13 @@ void device_bonding_complete(struct btd_device *device, uint8_t status)
                        device->discov_timer = 0;
                }
 
-               if (device_is_bredr(device))
+               if (bdaddr_type == BDADDR_BREDR)
                        device_browse_sdp(device, bonding->msg);
                else
                        device_browse_primary(device, bonding->msg);
 
                bonding_request_free(bonding);
-       } else if (!device->svc_resolved) {
+       } else if (!state->svc_resolved) {
                if (!device->browse && !device->discov_timer &&
                                main_opts.reverse_sdp) {
                        /* If we are not initiators and there is no currently
@@ -3828,6 +4251,8 @@ unsigned int device_wait_for_svc_complete(struct btd_device *dev,
                                                        device_svc_cb_t func,
                                                        void *user_data)
 {
+       /* This API is only used for BR/EDR (for now) */
+       struct bearer_state *state = &dev->bredr_state;
        static unsigned int id = 0;
        struct svc_callback *cb;
 
@@ -3839,7 +4264,7 @@ unsigned int device_wait_for_svc_complete(struct btd_device *dev,
 
        dev->svc_callbacks = g_slist_prepend(dev->svc_callbacks, cb);
 
-       if (dev->svc_resolved || !main_opts.reverse_sdp)
+       if (state->svc_resolved || !main_opts.reverse_sdp)
                cb->idle_id = g_idle_add(svc_idle_cb, cb);
        else if (dev->discov_timer > 0) {
                g_source_remove(dev->discov_timer);
@@ -3913,7 +4338,8 @@ static gboolean device_bonding_retry(gpointer data)
        err = adapter_bonding_attempt(adapter, &device->bdaddr,
                                device->bdaddr_type, io_cap);
        if (err < 0)
-               device_bonding_complete(device, bonding->status);
+               device_bonding_complete(device, bonding->bdaddr_type,
+                                                       bonding->status);
 
        return FALSE;
 }
@@ -4392,6 +4818,9 @@ void device_set_appearance(struct btd_device *device, uint16_t value)
 {
        const char *icon = gap_appearance_to_icon(value);
 
+       if (device->appearance == value)
+               return;
+
        g_dbus_emit_property_changed(dbus_conn, device->path,
                                        DEVICE_INTERFACE, "Appearance");
 
@@ -4435,6 +4864,11 @@ guint btd_device_add_attio_callback(struct btd_device *device,
 
        device_set_auto_connect(device, TRUE);
 
+       /* Check if there is no GAttrib associated to the device created by a
+        * incoming connection */
+       if (!device->attrib)
+               device->attrib = attrib_from_device(device);
+
        if (device->attrib && cfunc) {
                device->attios_offline = g_slist_append(device->attios_offline,
                                                                        attio);
@@ -4489,12 +4923,16 @@ gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id)
 void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
                        uint16_t vendor, uint16_t product, uint16_t version)
 {
+       if (device->vendor_src == source && device->version == version &&
+                       device->vendor == vendor && device->product == product)
+               return;
+
        device->vendor_src = source;
        device->vendor = vendor;
        device->product = product;
        device->version = version;
 
-       g_free(device->modalias);
+       free(device->modalias);
        device->modalias = bt_modalias(source, vendor, product, version);
 
        g_dbus_emit_property_changed(dbus_conn, device->path,
index 3a33cb2..2e0473e 100644 (file)
@@ -38,6 +38,12 @@ void device_store_cached_name(struct btd_device *dev, const char *name);
 void device_get_name(struct btd_device *device, char *name, size_t len);
 bool device_name_known(struct btd_device *device);
 void device_set_class(struct btd_device *device, uint32_t class);
+void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr,
+                                                       uint8_t bdaddr_type);
+void device_set_bredr_support(struct btd_device *device);
+void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type);
+void device_update_last_seen(struct btd_device *device, uint8_t bdaddr_type);
+void device_merge_duplicate(struct btd_device *dev, struct btd_device *dup);
 uint32_t btd_device_get_class(struct btd_device *device);
 uint16_t btd_device_get_vendor(struct btd_device *device);
 uint16_t btd_device_get_vendor_src(struct btd_device *device);
@@ -46,6 +52,14 @@ uint16_t btd_device_get_version(struct btd_device *device);
 void device_remove(struct btd_device *device, gboolean remove_stored);
 int device_address_cmp(gconstpointer a, gconstpointer b);
 int device_bdaddr_cmp(gconstpointer a, gconstpointer b);
+
+/* Struct used by device_addr_type_cmp() */
+struct device_addr_type {
+       bdaddr_t bdaddr;
+       uint8_t bdaddr_type;
+};
+
+int device_addr_type_cmp(gconstpointer a, gconstpointer b);
 GSList *btd_device_get_uuids(struct btd_device *device);
 void device_probe_profiles(struct btd_device *device, GSList *profiles);
 const sdp_record_t *btd_device_get_record(struct btd_device *device,
@@ -55,6 +69,7 @@ struct gatt_primary *btd_device_get_primary(struct btd_device *device,
 GSList *btd_device_get_primaries(struct btd_device *device);
 void btd_device_gatt_set_service_changed(struct btd_device *device,
                                                uint16_t start, uint16_t end);
+bool device_attach_attrib(struct btd_device *dev, GIOChannel *io);
 void btd_device_add_uuid(struct btd_device *device, const char *uuid);
 void device_add_eir_uuids(struct btd_device *dev, GSList *uuids);
 void device_probe_profile(gpointer a, gpointer b);
@@ -62,21 +77,22 @@ void device_remove_profile(gpointer a, gpointer b);
 struct btd_adapter *device_get_adapter(struct btd_device *device);
 const bdaddr_t *device_get_address(struct btd_device *device);
 const char *device_get_path(const struct btd_device *device);
-gboolean device_is_bredr(struct btd_device *device);
-gboolean device_is_le(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);
+bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type);
+bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type);
 gboolean device_is_trusted(struct btd_device *device);
-void device_set_paired(struct btd_device *device, gboolean paired);
+void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type);
+void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type);
 void btd_device_set_temporary(struct btd_device *device, gboolean temporary);
 void btd_device_set_trusted(struct btd_device *device, gboolean trusted);
-void device_set_bonded(struct btd_device *device, gboolean bonded);
+void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type);
 void device_set_legacy(struct btd_device *device, bool legacy);
 void device_set_rssi(struct btd_device *device, int8_t rssi);
-gboolean btd_device_is_connected(struct btd_device *device);
+bool btd_device_is_connected(struct btd_device *dev);
+uint8_t btd_device_get_bdaddr_type(struct btd_device *dev);
 bool device_is_retrying(struct btd_device *device);
-void device_bonding_complete(struct btd_device *device, uint8_t status);
+void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
+                                                       uint8_t status);
 gboolean device_is_bonding(struct btd_device *device, const char *sender);
 void device_bonding_attempt_failed(struct btd_device *device, uint8_t status);
 void device_bonding_failed(struct btd_device *device, uint8_t status);
@@ -94,8 +110,8 @@ int device_notify_pincode(struct btd_device *device, gboolean secure,
                                                        const char *pincode);
 void device_cancel_authentication(struct btd_device *device, gboolean aborted);
 gboolean device_is_authenticating(struct btd_device *device);
-void device_add_connection(struct btd_device *device);
-void device_remove_connection(struct btd_device *device);
+void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type);
+void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type);
 void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
 
 typedef void (*disconnect_watch) (struct btd_device *device, gboolean removal,
@@ -132,6 +148,7 @@ struct btd_service *btd_device_get_service(struct btd_device *dev,
                                                const char *remote_uuid);
 
 int device_discover_services(struct btd_device *device);
+int btd_device_connect_services(struct btd_device *dev, GSList *services);
 
 void btd_device_init(void);
 void btd_device_cleanup(void);
index 7745ff3..d22ad91 100644 (file)
--- a/src/eir.c
+++ b/src/eir.c
 #include <ctype.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <stdbool.h>
 #include <glib.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/sdp.h>
 
-#include "glib-helper.h"
+#include "src/shared/util.h"
+#include "uuid-helper.h"
 #include "eir.h"
 
 #define EIR_OOB_MIN (2 + 6)
 
 void eir_data_free(struct eir_data *eir)
 {
-       g_slist_free_full(eir->services, g_free);
+       g_slist_free_full(eir->services, free);
        eir->services = NULL;
        g_free(eir->name);
        eir->name = NULL;
@@ -63,9 +65,11 @@ static void eir_parse_uuid16(struct eir_data *eir, const void *data,
 
        service.type = SDP_UUID16;
        for (i = 0; i < len / 2; i++, uuid16++) {
-               service.value.uuid16 = bt_get_le16(uuid16);
+               service.value.uuid16 = get_le16(uuid16);
 
                uuid_str = bt_uuid2string(&service);
+               if (!uuid_str)
+                       continue;
                eir->services = g_slist_append(eir->services, uuid_str);
        }
 }
@@ -80,9 +84,11 @@ static void eir_parse_uuid32(struct eir_data *eir, const void *data,
 
        service.type = SDP_UUID32;
        for (i = 0; i < len / 4; i++, uuid32++) {
-               service.value.uuid32 = bt_get_le32(uuid32);
+               service.value.uuid32 = get_le32(uuid32);
 
                uuid_str = bt_uuid2string(&service);
+               if (!uuid_str)
+                       continue;
                eir->services = g_slist_append(eir->services, uuid_str);
        }
 }
@@ -101,6 +107,8 @@ static void eir_parse_uuid128(struct eir_data *eir, const uint8_t *data,
                for (k = 0; k < 16; k++)
                        service.value.uuid128.data[k] = uuid_ptr[16 - k - 1];
                uuid_str = bt_uuid2string(&service);
+               if (!uuid_str)
+                       continue;
                eir->services = g_slist_append(eir->services, uuid_str);
                uuid_ptr += 16;
        }
@@ -133,7 +141,7 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
 {
        uint16_t len = 0;
 
-       eir->flags = -1;
+       eir->flags = 0;
        eir->tx_power = 127;
 
        /* No EIR data to parse */
@@ -208,7 +216,7 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
                case EIR_GAP_APPEARANCE:
                        if (data_len < 2)
                                break;
-                       eir->appearance = bt_get_le16(data);
+                       eir->appearance = get_le16(data);
                        break;
 
                case EIR_SSP_HASH:
@@ -222,6 +230,16 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
                                break;
                        eir->randomizer = g_memdup(data, 16);
                        break;
+
+               case EIR_DEVICE_ID:
+                       if (data_len < 8)
+                               break;
+
+                       eir->did_source = data[0] | (data[1] << 8);
+                       eir->did_vendor = data[2] | (data[3] << 8);
+                       eir->did_product = data[4] | (data[5] << 8);
+                       eir->did_version = data[6] | (data[7] << 8);
+                       break;
                }
 
                eir_data += field_len + 1;
@@ -234,7 +252,7 @@ int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len)
        if (eir_len < EIR_OOB_MIN)
                return -1;
 
-       if (eir_len != bt_get_le16(eir_data))
+       if (eir_len != get_le16(eir_data))
                return -1;
 
        eir_data += sizeof(uint16_t);
@@ -259,7 +277,7 @@ static void eir_generate_uuid128(sdp_list_t *list, uint8_t *ptr,
        int i, k, uuid_count = 0;
        uint16_t len = *eir_len;
        uint8_t *uuid128;
-       gboolean truncated = FALSE;
+       bool truncated = false;
 
        /* Store UUIDs in place, skip 2 bytes to write type and length later */
        uuid128 = ptr + 2;
@@ -274,7 +292,7 @@ static void eir_generate_uuid128(sdp_list_t *list, uint8_t *ptr,
 
                /* Stop if not enough space to put next UUID128 */
                if ((len + 2 + SIZEOF_UUID128) > HCI_MAX_EIR_LENGTH) {
-                       truncated = TRUE;
+                       truncated = true;
                        break;
                }
 
@@ -323,7 +341,7 @@ int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod,
        uint16_t eir_total_len;
        uint16_t uuid16[HCI_MAX_EIR_LENGTH / 2];
        int i, uuid_count = 0;
-       gboolean truncated = FALSE;
+       bool truncated = false;
        size_t name_len;
 
        eir_total_len =  sizeof(uint16_t) + sizeof(bdaddr_t);
@@ -418,7 +436,7 @@ int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod,
                /* Stop if not enough space to put next UUID16 */
                if ((eir_optional_len + 2 + sizeof(uint16_t)) >
                                HCI_MAX_EIR_LENGTH) {
-                       truncated = TRUE;
+                       truncated = true;
                        break;
                }
 
@@ -456,7 +474,7 @@ int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod,
        eir_total_len += eir_optional_len;
 
        /* store total length */
-       bt_put_le16(eir_total_len, data);
+       put_le16(eir_total_len, data);
 
        return eir_total_len;
 }
index 411986e..e486fa2 100644 (file)
--- a/src/eir.h
+++ b/src/eir.h
 #define EIR_DEVICE_ID               0x10  /* device ID */
 #define EIR_GAP_APPEARANCE          0x19  /* GAP appearance */
 
+/* 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_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) */
+
 struct eir_data {
        GSList *services;
-       int flags;
+       unsigned int flags;
        char *name;
        uint32_t class;
        uint16_t appearance;
-       gboolean name_complete;
+       bool name_complete;
        int8_t tx_power;
        uint8_t *hash;
        uint8_t *randomizer;
        bdaddr_t addr;
+       uint16_t did_vendor;
+       uint16_t did_product;
+       uint16_t did_version;
+       uint16_t did_source;
 };
 
 void eir_data_free(struct eir_data *eir);
diff --git a/src/gatt-dbus.c b/src/gatt-dbus.c
new file mode 100644 (file)
index 0000000..c22e8af
--- /dev/null
@@ -0,0 +1,658 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <stdint.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "lib/uuid.h"
+#include "dbus-common.h"
+#include "log.h"
+
+#include "error.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "gatt.h"
+#include "gatt-dbus.h"
+
+#define GATT_MGR_IFACE                 "org.bluez.GattManager1"
+#define GATT_SERVICE_IFACE             "org.bluez.GattService1"
+#define GATT_CHR_IFACE                 "org.bluez.GattCharacteristic1"
+#define GATT_DESCRIPTOR_IFACE          "org.bluez.GattDescriptor1"
+
+struct external_service {
+       char *owner;
+       char *path;
+       DBusMessage *reg;
+       GDBusClient *client;
+       GSList *proxies;
+       struct btd_attribute *service;
+};
+
+struct proxy_write_data {
+       btd_attr_write_result_t result_cb;
+       void *user_data;
+};
+
+/*
+ * Attribute to Proxy hash table. Used to map incoming
+ * ATT operations to its external characteristic proxy.
+ */
+static GHashTable *proxy_hash;
+
+static GSList *external_services;
+
+static int external_service_path_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct external_service *esvc = a;
+       const char *path = b;
+
+       return g_strcmp0(esvc->path, path);
+}
+
+static gboolean external_service_destroy(void *user_data)
+{
+       struct external_service *esvc = user_data;
+
+       g_dbus_client_unref(esvc->client);
+
+       if (esvc->reg)
+               dbus_message_unref(esvc->reg);
+
+       g_free(esvc->owner);
+       g_free(esvc->path);
+       g_free(esvc);
+
+       return FALSE;
+}
+
+static void external_service_free(void *user_data)
+{
+       struct external_service *esvc = user_data;
+
+       /*
+        * Set callback to NULL to avoid potential race condition
+        * when calling remove_service and GDBusClient unref.
+        */
+       g_dbus_client_set_disconnect_watch(esvc->client, NULL, NULL);
+
+       external_service_destroy(user_data);
+}
+
+static void remove_service(DBusConnection *conn, void *user_data)
+{
+       struct external_service *esvc = user_data;
+
+       external_services = g_slist_remove(external_services, esvc);
+
+       if (esvc->service)
+               btd_gatt_remove_service(esvc->service);
+
+       /*
+        * Do not run in the same loop, this may be a disconnect
+        * watch call and GDBusClient should not be destroyed.
+        */
+       g_idle_add(external_service_destroy, esvc);
+}
+
+static int proxy_path_cmp(gconstpointer a, gconstpointer b)
+{
+       GDBusProxy *proxy1 = (GDBusProxy *) a;
+       GDBusProxy *proxy2 = (GDBusProxy *) b;
+       const char *path1 = g_dbus_proxy_get_path(proxy1);
+       const char *path2 = g_dbus_proxy_get_path(proxy2);
+
+       return g_strcmp0(path1, path2);
+}
+
+static uint8_t flags_string2int(const char *proper)
+{
+       uint8_t value;
+
+       /* Regular Properties: See core spec 4.1 page 2183 */
+       if (!strcmp("broadcast", proper))
+               value = GATT_CHR_PROP_BROADCAST;
+       else if (!strcmp("read", proper))
+               value = GATT_CHR_PROP_READ;
+       else if (!strcmp("write-without-response", proper))
+               value = GATT_CHR_PROP_WRITE_WITHOUT_RESP;
+       else if (!strcmp("write", proper))
+               value = GATT_CHR_PROP_WRITE;
+       else if (!strcmp("notify", proper))
+               value = GATT_CHR_PROP_NOTIFY;
+       else if (!strcmp("indicate", proper))
+               value = GATT_CHR_PROP_INDICATE;
+       else if (!strcmp("authenticated-signed-writes", proper))
+               value = GATT_CHR_PROP_AUTH;
+       else
+               value = 0;
+
+       /* TODO: Extended properties. Ref core spec 4.1 page 2185  */
+
+       return value;
+}
+
+static uint8_t flags_get_bitmask(DBusMessageIter *iter)
+{
+       DBusMessageIter istr;
+       uint8_t propmask = 0, prop;
+       const char *str;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+               goto fail;
+
+       dbus_message_iter_recurse(iter, &istr);
+
+       do {
+               if (dbus_message_iter_get_arg_type(&istr) != DBUS_TYPE_STRING)
+                       goto fail;
+
+               dbus_message_iter_get_basic(&istr, &str);
+               prop = flags_string2int(str);
+               if (!prop)
+                       goto fail;
+
+               propmask |= prop;
+       } while (dbus_message_iter_next(&istr));
+
+       return propmask;
+
+fail:
+       error("Characteristic Flags: Invalid argument!");
+
+       return 0;
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+       struct external_service *esvc = user_data;
+       const char *interface, *path;
+
+       interface = g_dbus_proxy_get_interface(proxy);
+       path = g_dbus_proxy_get_path(proxy);
+
+       if (!g_str_has_prefix(path, esvc->path))
+               return;
+
+       if (g_strcmp0(interface, GATT_CHR_IFACE) != 0 &&
+                       g_strcmp0(interface, GATT_SERVICE_IFACE) != 0 &&
+                       g_strcmp0(interface, GATT_DESCRIPTOR_IFACE) != 0)
+               return;
+
+       DBG("path %s iface %s", path, interface);
+
+       /*
+        * Object path follows a hierarchical organization. Add the
+        * proxies sorted by path helps the logic to register the
+        * object path later.
+        */
+       esvc->proxies = g_slist_insert_sorted(esvc->proxies, proxy,
+                                                       proxy_path_cmp);
+}
+
+static void proxy_removed(GDBusProxy *proxy, void *user_data)
+{
+       struct external_service *esvc = user_data;
+       const char *interface, *path;
+
+       interface = g_dbus_proxy_get_interface(proxy);
+       path = g_dbus_proxy_get_path(proxy);
+
+       DBG("path %s iface %s", path, interface);
+
+       esvc->proxies = g_slist_remove(esvc->proxies, proxy);
+}
+
+static void proxy_read_cb(struct btd_attribute *attr,
+                               btd_attr_read_result_t result, void *user_data)
+{
+       DBusMessageIter iter, array;
+       GDBusProxy *proxy;
+       uint8_t *value;
+       int len;
+
+       /*
+        * Remote device is trying to read the informed attribute,
+        * "Value" should be read from the proxy. GDBusProxy tracks
+        * properties changes automatically, it is not necessary to
+        * get the value directly from the GATT server.
+        */
+       proxy = g_hash_table_lookup(proxy_hash, attr);
+       if (!proxy) {
+               result(-ENOENT, NULL, 0, user_data);
+               return;
+       }
+
+       if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
+               /* Unusual situation, read property will checked earlier */
+               result(-EPERM, NULL, 0, user_data);
+               return;
+       }
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+               DBG("External service inconsistent!");
+               result(-EPERM, NULL, 0, user_data);
+               return;
+       }
+
+       dbus_message_iter_recurse(&iter, &array);
+       dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+       DBG("attribute: %p read %d bytes", attr, len);
+
+       result(0, value, len, user_data);
+}
+
+static void proxy_write_reply(const DBusError *derr, void *user_data)
+{
+       struct proxy_write_data *wdata = user_data;
+       int err;
+
+       /*
+        * Security requirements shall be handled by the core. If external
+        * applications returns an error, the reasons will be restricted to
+        * invalid argument or application specific errors.
+        */
+
+       if (!dbus_error_is_set(derr)) {
+               err = 0;
+               goto done;
+       }
+
+       DBG("Write reply: %s", derr->message);
+
+       if (dbus_error_has_name(derr, DBUS_ERROR_NO_REPLY))
+               err = -ETIMEDOUT;
+       else if (dbus_error_has_name(derr, ERROR_INTERFACE ".InvalidArguments"))
+               err = -EINVAL;
+       else
+               err = -EPROTO;
+
+done:
+       if (wdata && wdata->result_cb)
+               wdata->result_cb(err, wdata->user_data);
+}
+
+static void proxy_write_cb(struct btd_attribute *attr,
+                                       const uint8_t *value, size_t len,
+                                       btd_attr_write_result_t result,
+                                       void *user_data)
+{
+       GDBusProxy *proxy;
+
+       proxy = g_hash_table_lookup(proxy_hash, attr);
+       if (!proxy) {
+               result(-ENOENT, user_data);
+               return;
+       }
+
+       /*
+        * "result" callback defines if the core wants to receive the
+        * operation result, allowing to select ATT Write Request or Write
+        * Command. Descriptors requires Write Request operation. For
+        * Characteristics, the implementation will define which operations
+        * are allowed based on the properties/flags.
+        * TODO: Write Long Characteristics/Descriptors.
+        */
+
+       if (result) {
+               struct proxy_write_data *wdata;
+
+               wdata = g_new0(struct proxy_write_data, 1);
+               wdata->result_cb = result;
+               wdata->user_data = user_data;
+
+               if (!g_dbus_proxy_set_property_array(proxy, "Value",
+                                               DBUS_TYPE_BYTE, value, len,
+                                               proxy_write_reply,
+                                               wdata, g_free)) {
+                       g_free(wdata);
+                       result(-ENOENT, user_data);
+               }
+       } else {
+               /*
+                * Caller is not interested in the Set method call result.
+                * This flow implements the ATT Write Command scenario, where
+                * the remote doesn't receive ATT response.
+                */
+               g_dbus_proxy_set_property_array(proxy, "Value", DBUS_TYPE_BYTE,
+                                               value, len, proxy_write_reply,
+                                               NULL, NULL);
+       }
+
+       DBG("Server: Write attribute callback %s",
+                                       g_dbus_proxy_get_path(proxy));
+
+}
+
+static int register_external_service(struct external_service *esvc,
+                                                       GDBusProxy *proxy)
+{
+       DBusMessageIter iter;
+       const char *str, *path, *iface;
+       bt_uuid_t uuid;
+
+       path = g_dbus_proxy_get_path(proxy);
+       iface = g_dbus_proxy_get_interface(proxy);
+       if (g_strcmp0(esvc->path, path) != 0 ||
+                       g_strcmp0(iface, GATT_SERVICE_IFACE) != 0)
+               return -EINVAL;
+
+       if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+               return -EINVAL;
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return -EINVAL;
+
+       dbus_message_iter_get_basic(&iter, &str);
+
+       if (bt_string_to_uuid(&uuid, str) < 0)
+               return -EINVAL;
+
+       esvc->service = btd_gatt_add_service(&uuid);
+       if (!esvc->service)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int add_char(GDBusProxy *proxy, const bt_uuid_t *uuid)
+{
+       DBusMessageIter iter;
+       struct btd_attribute *attr;
+       btd_attr_write_t write_cb;
+       btd_attr_read_t read_cb;
+       uint8_t propmask = 0;
+
+       /*
+        * Optional property. If is not informed, read and write
+        * procedures will be allowed. Upper-layer should handle
+        * characteristic requirements.
+        */
+       if (g_dbus_proxy_get_property(proxy, "Flags", &iter))
+               propmask = flags_get_bitmask(&iter);
+       else
+               propmask = GATT_CHR_PROP_WRITE_WITHOUT_RESP
+                                               | GATT_CHR_PROP_WRITE
+                                               | GATT_CHR_PROP_READ;
+       if (!propmask)
+               return -EINVAL;
+
+       if (propmask & GATT_CHR_PROP_READ)
+               read_cb = proxy_read_cb;
+       else
+               read_cb = NULL;
+
+       if (propmask & (GATT_CHR_PROP_WRITE | GATT_CHR_PROP_WRITE_WITHOUT_RESP))
+               write_cb = proxy_write_cb;
+       else
+               write_cb = NULL;
+
+       attr = btd_gatt_add_char(uuid, propmask, read_cb, write_cb);
+       if (!attr)
+               return -ENOMEM;
+
+       g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy));
+
+       return 0;
+}
+
+static int add_char_desc(GDBusProxy *proxy, const bt_uuid_t *uuid)
+{
+       struct btd_attribute *attr;
+
+       attr = btd_gatt_add_char_desc(uuid, proxy_read_cb, proxy_write_cb);
+       if (!attr)
+               return -ENOMEM;
+
+       g_hash_table_insert(proxy_hash, attr, g_dbus_proxy_ref(proxy));
+
+       return 0;
+}
+
+static int register_external_characteristics(GSList *proxies)
+
+{
+       GSList *list;
+
+       for (list = proxies; list; list = g_slist_next(list)) {
+               GDBusProxy *proxy = list->data;
+               DBusMessageIter iter;
+               bt_uuid_t uuid;
+               const char *path, *iface, *str;
+               int ret;
+
+               /* Mandatory property */
+               if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+                       return -EINVAL;
+
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+                       return -EINVAL;
+
+               dbus_message_iter_get_basic(&iter, &str);
+
+               if (bt_string_to_uuid(&uuid, str) < 0)
+                       return -EINVAL;
+
+               iface = g_dbus_proxy_get_interface(proxy);
+               path = g_dbus_proxy_get_path(proxy);
+
+               if (!strcmp(GATT_CHR_IFACE, iface))
+                       ret = add_char(proxy, &uuid);
+               else
+                       ret = add_char_desc(proxy, &uuid);
+
+               if (ret < 0)
+                       return ret;
+
+               DBG("Added GATT: %s (%s)", path, str);
+       }
+
+       return 0;
+}
+
+static void client_ready(GDBusClient *client, void *user_data)
+{
+       struct external_service *esvc = user_data;
+       GDBusProxy *proxy;
+       DBusConnection *conn = btd_get_dbus_connection();
+       DBusMessage *reply;
+
+       if (!esvc->proxies)
+               goto fail;
+
+       proxy = esvc->proxies->data;
+       if (register_external_service(esvc, proxy) < 0)
+               goto fail;
+
+       if (register_external_characteristics(g_slist_next(esvc->proxies)) < 0)
+               goto fail;
+
+       DBG("Added GATT service %s", esvc->path);
+
+       reply = dbus_message_new_method_return(esvc->reg);
+       g_dbus_send_message(conn, reply);
+
+       dbus_message_unref(esvc->reg);
+       esvc->reg = NULL;
+
+       return;
+
+fail:
+       error("Could not register external service: %s", esvc->path);
+
+       /*
+        * Set callback to NULL to avoid potential race condition
+        * when calling remove_service and GDBusClient unref.
+        */
+       g_dbus_client_set_disconnect_watch(esvc->client, NULL, NULL);
+
+       remove_service(conn, esvc);
+
+       reply = btd_error_invalid_args(esvc->reg);
+       g_dbus_send_message(conn, reply);
+}
+
+static struct external_service *external_service_new(DBusConnection *conn,
+                                       DBusMessage *msg, const char *path)
+{
+       struct external_service *esvc;
+       GDBusClient *client;
+       const char *sender = dbus_message_get_sender(msg);
+
+       client = g_dbus_client_new(conn, sender, "/");
+       if (!client)
+               return NULL;
+
+       esvc = g_new0(struct external_service, 1);
+       esvc->owner = g_strdup(sender);
+       esvc->reg = dbus_message_ref(msg);
+       esvc->client = client;
+       esvc->path = g_strdup(path);
+
+       g_dbus_client_set_disconnect_watch(client, remove_service, esvc);
+
+       g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+                                                               NULL, esvc);
+
+       g_dbus_client_set_ready_watch(client, client_ready, esvc);
+
+       return esvc;
+}
+
+static DBusMessage *register_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct external_service *esvc;
+       DBusMessageIter iter;
+       const char *path;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return btd_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       if (g_slist_find_custom(external_services, path,
+                                               external_service_path_cmp))
+               return btd_error_already_exists(msg);
+
+       esvc = external_service_new(conn, msg, path);
+       if (!esvc)
+               return btd_error_failed(msg, "Not enough resources");
+
+       external_services = g_slist_prepend(external_services, esvc);
+
+       DBG("New service %p: %s", esvc, path);
+
+       return NULL;
+}
+
+static DBusMessage *unregister_service(DBusConnection *conn,
+                                       DBusMessage *msg, void *user_data)
+{
+       struct external_service *esvc;
+       DBusMessageIter iter;
+       const char *path;
+       GSList *list;
+
+       if (!dbus_message_iter_init(msg, &iter))
+               return btd_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+               return btd_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &path);
+
+       list = g_slist_find_custom(external_services, path,
+                                               external_service_path_cmp);
+       if (!list)
+               return btd_error_does_not_exist(msg);
+
+       esvc = list->data;
+       if (!strcmp(dbus_message_get_sender(msg), esvc->owner))
+               return btd_error_does_not_exist(msg);
+
+       /*
+        * Set callback to NULL to avoid potential race condition
+        * when calling remove_service and GDBusClient unref.
+        */
+       g_dbus_client_set_disconnect_watch(esvc->client, NULL, NULL);
+
+       remove_service(conn, esvc);
+
+       return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+       { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterService",
+                               GDBUS_ARGS({ "service", "o"},
+                                               { "options", "a{sv}"}),
+                               NULL, register_service) },
+       { GDBUS_EXPERIMENTAL_METHOD("UnregisterService",
+                               GDBUS_ARGS({"service", "o"}),
+                               NULL, unregister_service) },
+       { }
+};
+
+gboolean gatt_dbus_manager_register(void)
+{
+       if (!g_dbus_register_interface(btd_get_dbus_connection(),
+                               "/org/bluez", GATT_MGR_IFACE,
+                               methods, NULL, NULL, NULL, NULL))
+               return FALSE;
+
+       proxy_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                               NULL, (GDestroyNotify) g_dbus_proxy_unref);
+
+       return TRUE;
+}
+
+void gatt_dbus_manager_unregister(void)
+{
+       /* We might not have initialized if experimental features are
+        * not enabled.
+        */
+       if (!proxy_hash)
+               return;
+
+       g_hash_table_destroy(proxy_hash);
+       proxy_hash = NULL;
+
+       g_slist_free_full(external_services, external_service_free);
+
+       g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez",
+                                                       GATT_MGR_IFACE);
+}
diff --git a/src/gatt-dbus.h b/src/gatt-dbus.h
new file mode 100644 (file)
index 0000000..310cfa9
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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
+ *
+ */
+
+gboolean gatt_dbus_manager_register(void);
+void gatt_dbus_manager_unregister(void);
diff --git a/src/gatt.c b/src/gatt.c
new file mode 100644 (file)
index 0000000..3060462
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <glib.h>
+#include <stdbool.h>
+
+#include "log.h"
+#include "lib/uuid.h"
+#include "attrib/att.h"
+#include "src/shared/util.h"
+
+#include "gatt-dbus.h"
+#include "gatt.h"
+
+/* Common GATT UUIDs */
+static const bt_uuid_t primary_uuid  = { .type = BT_UUID16,
+                                       .value.u16 = GATT_PRIM_SVC_UUID };
+
+static const bt_uuid_t chr_uuid = { .type = BT_UUID16,
+                                       .value.u16 = GATT_CHARAC_UUID };
+
+struct btd_attribute {
+       uint16_t handle;
+       bt_uuid_t type;
+       btd_attr_read_t read_cb;
+       btd_attr_write_t write_cb;
+       uint16_t value_len;
+       uint8_t value[0];
+};
+
+static GList *local_attribute_db;
+static uint16_t next_handle = 0x0001;
+
+static inline void put_uuid_le(const bt_uuid_t *src, void *dst)
+{
+       if (src->type == BT_UUID16)
+               put_le16(src->value.u16, dst);
+       else if (src->type == BT_UUID32)
+               put_le32(src->value.u32, dst);
+       else
+               /* Convert from 128-bit BE to LE */
+               bswap_128(&src->value.u128, dst);
+}
+
+/*
+ * Helper function to create new attributes containing constant/static values.
+ * eg: declaration of services/characteristics, and characteristics with
+ * fixed values.
+ */
+static struct btd_attribute *new_const_attribute(const bt_uuid_t *type,
+                                                       const uint8_t *value,
+                                                       uint16_t len)
+{
+       struct btd_attribute *attr;
+
+       attr = malloc0(sizeof(struct btd_attribute) + len);
+       if (!attr)
+               return NULL;
+
+       attr->type = *type;
+       memcpy(&attr->value, value, len);
+       attr->value_len = len;
+
+       return attr;
+}
+
+static struct btd_attribute *new_attribute(const bt_uuid_t *type,
+                                               btd_attr_read_t read_cb,
+                                               btd_attr_write_t write_cb)
+{
+       struct btd_attribute *attr;
+
+       attr = new0(struct btd_attribute, 1);
+       if (!attr)
+               return NULL;
+
+       attr->type = *type;
+       attr->read_cb = read_cb;
+       attr->write_cb = write_cb;
+
+       return attr;
+}
+
+static bool is_service(const struct btd_attribute *attr)
+{
+       if (attr->type.type != BT_UUID16)
+               return false;
+
+       if (attr->type.value.u16 == GATT_PRIM_SVC_UUID ||
+                       attr->type.value.u16 == GATT_SND_SVC_UUID)
+               return true;
+
+       return false;
+}
+
+static int local_database_add(uint16_t handle, struct btd_attribute *attr)
+{
+       attr->handle = handle;
+
+       local_attribute_db = g_list_append(local_attribute_db, attr);
+
+       return 0;
+}
+
+struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid)
+{
+       struct btd_attribute *attr;
+       uint16_t len = bt_uuid_len(uuid);
+       uint8_t value[len];
+
+       /*
+        * Service DECLARATION
+        *
+        *   TYPE         ATTRIBUTE VALUE
+        * +-------+---------------------------------+
+        * |0x2800 | 0xYYYY...                       |
+        * | (1)   | (2)                             |
+        * +------+----------------------------------+
+        * (1) - 2 octets: Primary/Secondary Service UUID
+        * (2) - 2 or 16 octets: Service UUID
+        */
+
+       /* Set attribute value */
+       put_uuid_le(uuid, value);
+
+       attr = new_const_attribute(&primary_uuid, value, len);
+       if (!attr)
+               return NULL;
+
+       if (local_database_add(next_handle, attr) < 0) {
+               free(attr);
+               return NULL;
+       }
+
+       /* TODO: missing overflow checking */
+       next_handle = next_handle + 1;
+
+       return attr;
+}
+
+void btd_gatt_remove_service(struct btd_attribute *service)
+{
+       GList *list = g_list_find(local_attribute_db, service);
+       bool first_node;
+
+       if (!list)
+               return;
+
+       first_node = local_attribute_db == list;
+
+       /* Remove service declaration attribute */
+       free(list->data);
+       list = g_list_delete_link(list, list);
+
+       /* Remove all characteristics until next service declaration */
+       while (list && !is_service(list->data)) {
+               free(list->data);
+               list = g_list_delete_link(list, list);
+       }
+
+       /*
+        * When removing the first node, local attribute database head
+        * needs to be updated. Node removed from middle doesn't change
+        * the list head address.
+        */
+       if (first_node)
+               local_attribute_db = list;
+}
+
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+                                               uint8_t properties,
+                                               btd_attr_read_t read_cb,
+                                               btd_attr_write_t write_cb)
+{
+       struct btd_attribute *char_decl, *char_value = NULL;
+
+       /* Attribute value length */
+       uint16_t len = 1 + 2 + bt_uuid_len(uuid);
+       uint8_t value[len];
+
+       /*
+        * Characteristic DECLARATION
+        *
+        *   TYPE         ATTRIBUTE VALUE
+        * +-------+---------------------------------+
+        * |0x2803 | 0xXX 0xYYYY 0xZZZZ...           |
+        * | (1)   |  (2)   (3)   (4)                |
+        * +------+----------------------------------+
+        * (1) - 2 octets: Characteristic declaration UUID
+        * (2) - 1 octet : Properties
+        * (3) - 2 octets: Handle of the characteristic Value
+        * (4) - 2 or 16 octets: Characteristic UUID
+        */
+
+       value[0] = properties;
+
+       /*
+        * Since we don't know yet the characteristic value attribute
+        * handle, we skip and set it later.
+        */
+
+       put_uuid_le(uuid, &value[3]);
+
+       char_decl = new_const_attribute(&chr_uuid, value, len);
+       if (!char_decl)
+               goto fail;
+
+       char_value = new_attribute(uuid, read_cb, write_cb);
+       if (!char_value)
+               goto fail;
+
+       if (local_database_add(next_handle, char_decl) < 0)
+               goto fail;
+
+       next_handle = next_handle + 1;
+
+       /*
+        * Characteristic VALUE
+        *
+        *   TYPE         ATTRIBUTE VALUE
+        * +----------+---------------------------------+
+        * |0xZZZZ... | 0x...                           |
+        * |  (1)     |  (2)                            |
+        * +----------+---------------------------------+
+        * (1) - 2 or 16 octets: Characteristic UUID
+        * (2) - N octets: Value is read dynamically from the service
+        * implementation (external entity).
+        */
+
+       if (local_database_add(next_handle, char_value) < 0)
+               /* TODO: remove declaration */
+               goto fail;
+
+       next_handle = next_handle + 1;
+
+       /*
+        * Update characteristic value handle in characteristic declaration
+        * attribute. For local attributes, we can assume that the handle
+        * representing the characteristic value will get the next available
+        * handle. However, for remote attribute this assumption is not valid.
+        */
+       put_le16(char_value->handle, &char_decl->value[1]);
+
+       return char_value;
+
+fail:
+       free(char_decl);
+       free(char_value);
+
+       return NULL;
+}
+
+struct btd_attribute *btd_gatt_add_char_desc(const bt_uuid_t *uuid,
+                                               btd_attr_read_t read_cb,
+                                               btd_attr_write_t write_cb)
+{
+       struct btd_attribute *attr;
+
+       /*
+        * From Core SPEC 4.1 page 2184:
+        * "Characteristic descriptor declaration permissions are defined by a
+        * higher layer profile or are implementation specific. A client shall
+        * not assume all characteristic descriptor declarations are readable."
+        *
+        * The read/write callbacks presence will define the descriptor
+        * permissions managed directly by the core. The upper layer can define
+        * additional permissions constraints.
+        */
+
+       attr = new_attribute(uuid, read_cb, write_cb);
+       if (!attr)
+               return NULL;
+
+       if (local_database_add(next_handle, attr) < 0) {
+               free(attr);
+               return NULL;
+       }
+
+       next_handle = next_handle + 1;
+
+       return attr;
+}
+
+void gatt_init(void)
+{
+       DBG("Starting GATT server");
+
+       gatt_dbus_manager_register();
+}
+
+void gatt_cleanup(void)
+{
+       DBG("Stopping GATT server");
+
+       gatt_dbus_manager_unregister();
+}
diff --git a/src/gatt.h b/src/gatt.h
new file mode 100644 (file)
index 0000000..f16541e
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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
+ *
+ */
+
+struct btd_attribute;
+
+void gatt_init(void);
+
+void gatt_cleanup(void);
+
+/*
+ * Read operation result callback. Called from the service implementation
+ * informing the core (ATT layer) the result of read operation.
+ * @err:       error in -errno format.
+ * @value:     value of the attribute read.
+ * @len:       length of value.
+ * @user_data: user_data passed in btd_attr_read_t callback.
+ */
+typedef void (*btd_attr_read_result_t) (int err, uint8_t *value, size_t len,
+                                                       void *user_data);
+/*
+ * Service implementation callback passed to core (ATT layer). It manages read
+ * operations received from remote devices.
+ * @attr:      reference of the attribute to be read.
+ * @result:    callback called from the service implementation informing the
+ *             value of attribute read.
+ * @user_data: user_data passed in btd_attr_read_result_t callback.
+ */
+typedef void (*btd_attr_read_t) (struct btd_attribute *attr,
+                                               btd_attr_read_result_t result,
+                                               void *user_data);
+
+/*
+ * Write operation result callback. Called from the service implementation
+ * informing the core (ATT layer) the result of the write operation. It is used
+ * to manage Write Request operations.
+ * @err:       error in -errno format.
+ * @user_data: user_data passed in btd_attr_write_t callback.
+ */
+typedef void (*btd_attr_write_result_t) (int err, void *user_data);
+/*
+ * Service implementation callback passed to core (ATT layer). It manages write
+ * operations received from remote devices.
+ * @attr:      reference of the attribute to be changed.
+ * @value:     new attribute value.
+ * @len:       length of value.
+ * @result:    callback called from the service implementation informing the
+ *             result of the write operation.
+ * @user_data: user_data passed in btd_attr_write_result_t callback.
+ */
+typedef void (*btd_attr_write_t) (struct btd_attribute *attr,
+                                       const uint8_t *value, size_t len,
+                                       btd_attr_write_result_t result,
+                                       void *user_data);
+
+/* btd_gatt_add_service - Add a service declaration to local attribute database.
+ * @uuid:      Service UUID.
+ *
+ * Returns a reference to service declaration attribute. In case of error,
+ * NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid);
+
+/*
+ * btd_gatt_remove_service - Remove a service (along with all its
+ * characteristics) from the local attribute database.
+ * @service:   Service declaration attribute.
+ */
+void btd_gatt_remove_service(struct btd_attribute *service);
+
+/*
+ * btd_gatt_add_char - Add a characteristic (declaration and value attributes)
+ * to local attribute database.
+ * @uuid:      Characteristic UUID (16-bits or 128-bits).
+ * @properties:        Characteristic properties. See Core SPEC 4.1 page 2183.
+ * @read_cb:   Callback used to provide the characteristic value.
+ * @write_cb:  Callback called to notify the implementation that a new value
+ *              is available.
+ *
+ * Returns a reference to characteristic value attribute. In case of error,
+ * NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_char(const bt_uuid_t *uuid,
+                                               uint8_t properties,
+                                               btd_attr_read_t read_cb,
+                                               btd_attr_write_t write_cb);
+
+/*
+ * btd_gatt_add_char_desc - Add a characteristic descriptor to the local
+ * attribute database.
+ * @uuid:      Characteristic Descriptor UUID (16-bits or 128-bits).
+ * @read_cb:   Callback that should be called once the characteristic
+ *             descriptor attribute is read.
+ * @write_cb:  Callback that should be called once the characteristic
+ *             descriptor attribute is written.
+ *
+ * Returns a reference to characteristic descriptor attribute. In case of
+ * error, NULL is returned.
+ */
+struct btd_attribute *btd_gatt_add_char_desc(const bt_uuid_t *uuid,
+                                               btd_attr_read_t read_cb,
+                                               btd_attr_write_t write_cb);
index ea67cc2..6040c71 100644 (file)
@@ -47,4 +47,6 @@ void plugin_cleanup(void);
 void rfkill_init(void);
 void rfkill_exit(void);
 
+GKeyFile *btd_get_main_conf(void);
+
 void btd_exit(void);
index 91d90b4..15f98cf 100644 (file)
@@ -55,6 +55,7 @@
 #include "dbus-common.h"
 #include "agent.h"
 #include "profile.h"
+#include "gatt.h"
 #include "systemd.h"
 
 #define BLUEZ_NAME "org.bluez"
@@ -65,6 +66,7 @@
 #define SHUTDOWN_GRACE_SECONDS 10
 
 struct main_opts main_opts;
+static GKeyFile *main_conf;
 
 static const char * const supported_options[] = {
        "Name",
@@ -78,6 +80,11 @@ static const char * const supported_options[] = {
        "DebugKeys",
 };
 
+GKeyFile *btd_get_main_conf(void)
+{
+       return main_conf;
+}
+
 static GKeyFile *load_config(const char *file)
 {
        GError *err = NULL;
@@ -132,6 +139,7 @@ done:
 
 static void check_config(GKeyFile *config)
 {
+       const char *valid_groups[] = { "General", "Policy", NULL };
        char **keys;
        int i;
 
@@ -141,7 +149,17 @@ static void check_config(GKeyFile *config)
        keys = g_key_file_get_groups(config, NULL);
 
        for (i = 0; keys != NULL && keys[i] != NULL; i++) {
-               if (!g_str_equal(keys[i], "General"))
+               const char **group;
+               bool match = false;
+
+               for (group = valid_groups; *group; group++) {
+                       if (g_str_equal(keys[i], *group)) {
+                               match = true;
+                               break;
+                       }
+               }
+
+               if (!match)
                        warn("Unknown group %s in main.conf", keys[i]);
        }
 
@@ -487,7 +505,6 @@ int main(int argc, char *argv[])
        uint16_t sdp_mtu = 0;
        uint32_t sdp_flags = 0;
        int gdbus_flags = 0;
-       GKeyFile *config;
        guint signal, watchdog;
        const char *watchdog_usec;
 
@@ -522,15 +539,22 @@ int main(int argc, char *argv[])
 
        sd_notify(0, "STATUS=Starting up");
 
-       config = load_config(CONFIGDIR "/main.conf");
+       main_conf = load_config(CONFIGDIR "/main.conf");
 
-       parse_config(config);
+       parse_config(main_conf);
 
        if (connect_dbus() < 0) {
                error("Unable to get on D-Bus");
                exit(1);
        }
 
+       if (option_experimental)
+               gdbus_flags = G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
+
+       g_dbus_set_flags(gdbus_flags);
+
+       gatt_init();
+
        if (adapter_init() < 0) {
                error("Adapter handling initialization failed");
                exit(1);
@@ -540,11 +564,6 @@ int main(int argc, char *argv[])
        btd_agent_init();
        btd_profile_init();
 
-       if (option_experimental)
-               gdbus_flags = G_DBUS_FLAG_ENABLE_EXPERIMENTAL;
-
-       g_dbus_set_flags(gdbus_flags);
-
        if (option_compat == TRUE)
                sdp_flags |= SDP_SERVER_COMPAT;
 
@@ -598,14 +617,16 @@ int main(int argc, char *argv[])
 
        adapter_cleanup();
 
+       gatt_cleanup();
+
        rfkill_exit();
 
        stop_sdp_server();
 
        g_main_loop_unref(event_loop);
 
-       if (config)
-               g_key_file_free(config);
+       if (main_conf)
+               g_key_file_free(main_conf);
 
        disconnect_dbus();
 
index a94274a..3ebadde 100644 (file)
 # makes debug link keys valid only for the duration of the connection
 # that they were created for.
 #DebugKeys = false
+
+#[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# timeout). The policy plugin should contain a sane set of values by
+# default, but this list can be overridden here. By setting the list to
+# empty the reconnection feature gets disabled.
+#ReconnectUUIDs=
index 085e7a5..edb47db 100644 (file)
 #include <bluetooth/bluetooth.h>
 
 #include <glib.h>
-#include <btio/btio.h>
 
-#include "plugin.h"
-#include "log.h"
-#include "hcid.h"
+#include "btio/btio.h"
+#include "src/plugin.h"
+#include "src/log.h"
+#include "src/hcid.h"
 
 static GSList *plugins = NULL;
 
@@ -110,7 +110,7 @@ static gboolean enable_plugin(const char *name, char **cli_enable,
        return TRUE;
 }
 
-#include "builtin.h"
+#include "src/builtin.h"
 
 gboolean plugin_init(const char *enable, const char *disable)
 {
index 3c0d27c..f30f4f6 100644 (file)
@@ -43,7 +43,7 @@
 #include "sdpd.h"
 #include "log.h"
 #include "error.h"
-#include "glib-helper.h"
+#include "uuid-helper.h"
 #include "dbus-common.h"
 #include "sdp-client.h"
 #include "sdp-xml.h"
                <attribute id=\"0x0200\">                               \
                        <uint16 value=\"%u\" name=\"psm\"/>             \
                </attribute>                                            \
+               <attribute id=\"0x0317\">                               \
+                       <uint32 value=\"0x0000007f\"/>                  \
+               </attribute>                                            \
        </record>"
 
 #define SYNC_RECORD                                                    \
@@ -619,7 +622,6 @@ struct ext_io {
        uint8_t chan;
 
        guint auth_id;
-       unsigned int svc_id;
        DBusPendingCall *pending;
 };
 
@@ -708,10 +710,6 @@ static void ext_io_destroy(gpointer p)
        if (ext_io->auth_id != 0)
                btd_cancel_authorization(ext_io->auth_id);
 
-       if (ext_io->svc_id != 0)
-               device_remove_svc_complete_callback(ext_io->device,
-                                                       ext_io->svc_id);
-
        if (ext_io->pending) {
                dbus_pending_call_cancel(ext_io->pending);
                dbus_pending_call_unref(ext_io->pending);
@@ -754,7 +752,9 @@ static gboolean ext_io_disconnected(GIOChannel *io, GIOCondition cond,
 
        DBG("%s disconnected from %s", ext->name, addr);
 drop:
-       btd_service_disconnecting_complete(conn->service, 0);
+       if (conn->service)
+               btd_service_disconnecting_complete(conn->service, 0);
+
        ext->conns = g_slist_remove(ext->conns, conn);
        ext_io_destroy(conn);
        return FALSE;
@@ -776,7 +776,9 @@ static void new_conn_reply(DBusPendingCall *call, void *user_data)
        conn->pending = NULL;
 
        if (!dbus_error_is_set(&err)) {
-               btd_service_connecting_complete(conn->service, 0);
+               if (conn->service)
+                       btd_service_connecting_complete(conn->service, 0);
+
                conn->connected = true;
                return;
        }
@@ -784,7 +786,8 @@ static void new_conn_reply(DBusPendingCall *call, void *user_data)
        error("%s replied with an error: %s, %s", ext->name,
                                                err.name, err.message);
 
-       btd_service_connecting_complete(conn->service, -ECONNREFUSED);
+       if (conn->service)
+               btd_service_connecting_complete(conn->service, -ECONNREFUSED);
 
        dbus_error_free(&err);
 
@@ -808,14 +811,18 @@ static void disconn_reply(DBusPendingCall *call, void *user_data)
        conn->pending = NULL;
 
        if (!dbus_error_is_set(&err)) {
-               btd_service_disconnecting_complete(conn->service, 0);
+               if (conn->service)
+                       btd_service_disconnecting_complete(conn->service, 0);
+
                goto disconnect;
        }
 
        error("%s replied with an error: %s, %s", ext->name,
                                                err.name, err.message);
 
-       btd_service_disconnecting_complete(conn->service, -ECONNREFUSED);
+       if (conn->service)
+               btd_service_disconnecting_complete(conn->service,
+                                                               -ECONNREFUSED);
 
        dbus_error_free(&err);
 
@@ -988,9 +995,13 @@ static void ext_connect(GIOChannel *io, GError *err, gpointer user_data)
                return;
 
 drop:
-       btd_service_connecting_complete(conn->service, err ? -err->code : -EIO);
+       if (conn->service)
+               btd_service_connecting_complete(conn->service,
+                                               err ? -err->code : -EIO);
+
        if (io_err)
                g_error_free(io_err);
+
        ext->conns = g_slist_remove(ext->conns, conn);
        ext_io_destroy(conn);
 }
@@ -1017,12 +1028,6 @@ static void ext_auth(DBusError *err, void *user_data)
                goto drop;
        }
 
-       if (conn->svc_id > 0) {
-               DBG("Connection from %s authorized but still waiting for SDP",
-                                                                       addr);
-               return;
-       }
-
        if (!bt_io_accept(conn->io, ext_connect, conn, NULL, &gerr)) {
                error("bt_io_accept: %s", gerr->message);
                g_error_free(gerr);
@@ -1047,13 +1052,19 @@ static struct ext_io *create_conn(struct ext_io *server, GIOChannel *io,
        GIOCondition cond;
        char addr[18];
 
-       device = btd_adapter_find_device(server->adapter, dst);
+       device = btd_adapter_find_device(server->adapter, dst, BDADDR_BREDR);
        if (device == NULL) {
                ba2str(dst, addr);
                error("%s device %s not found", server->ext->name, addr);
                return NULL;
        }
 
+       /* Do not add UUID if client role is not enabled */
+       if (!server->ext->enable_client) {
+               service = NULL;
+               goto done;
+       }
+
        btd_device_add_uuid(device, server->ext->remote_uuid);
        service = btd_device_get_service(device, server->ext->remote_uuid);
        if (service == NULL) {
@@ -1063,13 +1074,16 @@ static struct ext_io *create_conn(struct ext_io *server, GIOChannel *io,
                return NULL;
        }
 
+done:
        conn = g_new0(struct ext_io, 1);
        conn->io = g_io_channel_ref(io);
        conn->proto = server->proto;
        conn->ext = server->ext;
        conn->adapter = btd_adapter_ref(server->adapter);
        conn->device = btd_device_ref(device);
-       conn->service = btd_service_ref(service);
+
+       if (service)
+               conn->service = btd_service_ref(service);
 
        cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
        conn->io_id = g_io_add_watch(io, cond, ext_io_disconnected, conn);
@@ -1077,47 +1091,6 @@ static struct ext_io *create_conn(struct ext_io *server, GIOChannel *io,
        return conn;
 }
 
-static void ext_svc_complete(struct btd_device *dev, int err, void *user_data)
-{
-       struct ext_io *conn = user_data;
-       struct ext_profile *ext = conn->ext;
-       const bdaddr_t *bdaddr;
-       GError *gerr = NULL;
-       char addr[18];
-
-       conn->svc_id = 0;
-
-       bdaddr = device_get_address(dev);
-       ba2str(bdaddr, addr);
-
-       if (err < 0) {
-               error("Service resolving failed for %s: %s (%d)",
-                                               addr, strerror(-err), -err);
-               goto drop;
-       }
-
-       DBG("Services resolved for %s", addr);
-
-       if (conn->auth_id > 0) {
-               DBG("Services resolved but still waiting for authorization");
-               return;
-       }
-
-       if (!bt_io_accept(conn->io, ext_connect, conn, NULL, &gerr)) {
-               error("bt_io_accept: %s", gerr->message);
-               g_error_free(gerr);
-               goto drop;
-       }
-
-       DBG("%s authorized to connect to %s", addr, ext->name);
-
-       return;
-
-drop:
-       ext->conns = g_slist_remove(ext->conns, conn);
-       ext_io_destroy(conn);
-}
-
 static void ext_confirm(GIOChannel *io, gpointer user_data)
 {
        struct ext_io *server = user_data;
@@ -1156,10 +1129,6 @@ static void ext_confirm(GIOChannel *io, gpointer user_data)
 
        ext->conns = g_slist_append(ext->conns, conn);
 
-       conn->svc_id = device_wait_for_svc_complete(conn->device,
-                                                       ext_svc_complete,
-                                                       conn);
-
        DBG("%s authorizing connection from %s", ext->name, addr);
 }
 
@@ -1590,7 +1559,9 @@ static void record_cb(sdp_list_t *recs, int err, gpointer user_data)
        return;
 
 failed:
-       btd_service_connecting_complete(conn->service, err);
+       if (conn->service)
+               btd_service_connecting_complete(conn->service, err);
+
        ext->conns = g_slist_remove(ext->conns, conn);
        ext_io_destroy(conn);
 }
@@ -1605,7 +1576,7 @@ static int resolve_service(struct ext_io *conn, const bdaddr_t *src,
        bt_string2uuid(&uuid, ext->remote_uuid);
        sdp_uuid128_to_uuid(&uuid);
 
-       err = bt_search_service(src, dst, &uuid, record_cb, conn, NULL);
+       err = bt_search_service(src, dst, &uuid, record_cb, conn, NULL, 0);
        if (err == 0)
                conn->resolving = true;
 
@@ -2146,7 +2117,7 @@ static int parse_ext_opt(struct ext_profile *ext, const char *key,
                if (type != DBUS_TYPE_STRING)
                        return -EINVAL;
                dbus_message_iter_get_basic(value, &str);
-               g_free(ext->service);
+               free(ext->service);
                ext->service = bt_name2string(str);
        }
 
@@ -2156,27 +2127,27 @@ static int parse_ext_opt(struct ext_profile *ext, const char *key,
 static void set_service(struct ext_profile *ext)
 {
        if (strcasecmp(ext->uuid, HSP_HS_UUID) == 0) {
-               ext->service = g_strdup(ext->uuid);
+               ext->service = strdup(ext->uuid);
        } else if (strcasecmp(ext->uuid, HSP_AG_UUID) == 0) {
                ext->service = ext->uuid;
-               ext->uuid = g_strdup(HSP_HS_UUID);
+               ext->uuid = strdup(HSP_HS_UUID);
        } else if (strcasecmp(ext->uuid, HFP_HS_UUID) == 0) {
-               ext->service = g_strdup(ext->uuid);
+               ext->service = strdup(ext->uuid);
        } else if (strcasecmp(ext->uuid, HFP_AG_UUID) == 0) {
                ext->service = ext->uuid;
-               ext->uuid = g_strdup(HFP_HS_UUID);
+               ext->uuid = strdup(HFP_HS_UUID);
        } else if (strcasecmp(ext->uuid, OBEX_SYNC_UUID) == 0 ||
                        strcasecmp(ext->uuid, OBEX_OPP_UUID) == 0 ||
                        strcasecmp(ext->uuid, OBEX_FTP_UUID) == 0) {
-               ext->service = g_strdup(ext->uuid);
+               ext->service = strdup(ext->uuid);
        } else if (strcasecmp(ext->uuid, OBEX_PSE_UUID) == 0 ||
                        strcasecmp(ext->uuid, OBEX_PCE_UUID) ==  0) {
                ext->service = ext->uuid;
-               ext->uuid = g_strdup(OBEX_PBAP_UUID);
+               ext->uuid = strdup(OBEX_PBAP_UUID);
        } else if (strcasecmp(ext->uuid, OBEX_MAS_UUID) == 0 ||
                        strcasecmp(ext->uuid, OBEX_MNS_UUID) == 0) {
                ext->service = ext->uuid;
-               ext->uuid = g_strdup(OBEX_MAP_UUID);
+               ext->uuid = strdup(OBEX_MAP_UUID);
        }
 }
 
@@ -2238,7 +2209,7 @@ static struct ext_profile *create_ext(const char *owner, const char *path,
        p->local_uuid = ext->service ? ext->service : ext->uuid;
        p->remote_uuid = ext->remote_uuid;
 
-       if (ext->enable_server || ext->record || ext->get_record) {
+       if (ext->enable_server) {
                p->adapter_probe = ext_adapter_probe;
                p->adapter_remove = ext_adapter_remove;
        }
@@ -2275,8 +2246,8 @@ static void remove_ext(struct ext_profile *ext)
        g_free(ext->remote_uuid);
        g_free(ext->name);
        g_free(ext->owner);
-       g_free(ext->uuid);
-       g_free(ext->service);
+       free(ext->uuid);
+       free(ext->service);
        g_free(ext->role);
        g_free(ext->path);
        g_free(ext->record);
@@ -2397,7 +2368,7 @@ bool btd_profile_add_custom_prop(const char *uuid, const char *type,
 
        prop = g_new0(struct btd_profile_custom_property, 1);
 
-       prop->uuid = g_strdup(uuid);
+       prop->uuid = strdup(uuid);
        prop->type = g_strdup(type);
        prop->name = g_strdup(name);
        prop->exists = exists;
index 51f3048..edbdaec 100644 (file)
@@ -33,7 +33,7 @@
 
 #include <glib.h>
 
-#include <btio/btio.h>
+#include "btio/btio.h"
 #include "log.h"
 #include "sdp-client.h"
 
@@ -86,17 +86,6 @@ static sdp_session_t *get_cached_sdp_session(const bdaddr_t *src, const bdaddr_t
        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)
 {
@@ -275,7 +264,7 @@ failed:
 static int create_search_context(struct search_context **ctxt,
                                        const bdaddr_t *src,
                                        const bdaddr_t *dst,
-                                       uuid_t *uuid)
+                                       uuid_t *uuid, uint16_t flags)
 {
        sdp_session_t *s;
        GIOChannel *chan;
@@ -285,7 +274,10 @@ static int create_search_context(struct search_context **ctxt,
        if (!ctxt)
                return -EINVAL;
 
-       s = get_sdp_session(src, dst);
+       s = get_cached_sdp_session(src, dst);
+       if (!s)
+               s = sdp_connect(src, dst, SDP_NON_BLOCKING | flags);
+
        if (!s)
                return -errno;
 
@@ -319,7 +311,7 @@ static int create_search_context(struct search_context **ctxt,
 
 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)
+                       bt_destroy_t destroy, uint16_t flags)
 {
        struct search_context *ctxt = NULL;
        int err;
@@ -327,7 +319,7 @@ int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
        if (!cb)
                return -EINVAL;
 
-       err = create_search_context(&ctxt, src, dst, uuid);
+       err = create_search_context(&ctxt, src, dst, uuid, flags);
        if (err < 0)
                return err;
 
index 9191594..9aa5a4d 100644 (file)
@@ -26,6 +26,6 @@ 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);
+                       bt_destroy_t destroy, uint16_t flags);
 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);
index fbeb488..3e58c22 100644 (file)
@@ -37,6 +37,8 @@
 #include <bluetooth/sdp.h>
 #include <bluetooth/sdp_lib.h>
 
+#include "src/shared/util.h"
+
 #include "sdpd.h"
 #include "log.h"
 
@@ -174,7 +176,7 @@ static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *p
                                struct attrid *aid;
                                aid = malloc(sizeof(struct attrid));
                                aid->dtd = dataType;
-                               aid->uint16 = bt_get_be16(p);
+                               aid->uint16 = get_be16(p);
                                pElem = (char *) aid;
                        } else {
                                uint16_t tmp;
@@ -182,7 +184,7 @@ static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *p
                                memcpy(&tmp, p, sizeof(tmp));
 
                                pElem = malloc(sizeof(uint16_t));
-                               bt_put_be16(tmp, pElem);
+                               put_be16(tmp, pElem);
                        }
                        p += sizeof(uint16_t);
                        seqlen += sizeof(uint16_t);
@@ -201,7 +203,7 @@ static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *p
                                struct attrid *aid;
                                aid = malloc(sizeof(struct attrid));
                                aid->dtd = dataType;
-                               aid->uint32 = bt_get_be32(p);
+                               aid->uint32 = get_be32(p);
 
                                pElem = (char *) aid;
                        } else {
@@ -210,7 +212,7 @@ static int extract_des(uint8_t *buf, int len, sdp_list_t **svcReqSeq, uint8_t *p
                                memcpy(&tmp, p, sizeof(tmp));
 
                                pElem = malloc(sizeof(uint32_t));
-                               bt_put_be32(tmp, pElem);
+                               put_be32(tmp, pElem);
                        }
                        p += sizeof(uint32_t);
                        seqlen += sizeof(uint32_t);
@@ -392,7 +394,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
                goto done;
        }
 
-       expected = bt_get_be16(pdata);
+       expected = get_be16(pdata);
 
        SDPDBG("Expected count: %d", expected);
        SDPDBG("Bytes scanned : %d", scanned);
@@ -417,13 +419,13 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
 
        /* total service record count = 0 */
        pTotalRecordCount = pdata;
-       bt_put_be16(0, pdata);
+       put_be16(0, pdata);
        pdata += sizeof(uint16_t);
        buf->data_size += sizeof(uint16_t);
 
        /* current service record count = 0 */
        pCurrentRecordCount = pdata;
-       bt_put_be16(0, pdata);
+       put_be16(0, pdata);
        pdata += sizeof(uint16_t);
        buf->data_size += sizeof(uint16_t);
 
@@ -440,7 +442,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
                        if (sdp_match_uuid(pattern, rec->pattern) > 0 &&
                                        sdp_check_access(rec->handle, &req->device)) {
                                rsp_count++;
-                               bt_put_be32(rec->handle, pdata);
+                               put_be32(rec->handle, pdata);
                                pdata += sizeof(uint32_t);
                                handleSize += sizeof(uint32_t);
                        }
@@ -449,8 +451,8 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
                SDPDBG("Match count: %d", rsp_count);
 
                buf->data_size += handleSize;
-               bt_put_be16(rsp_count, pTotalRecordCount);
-               bt_put_be16(rsp_count, pCurrentRecordCount);
+               put_be16(rsp_count, pTotalRecordCount);
+               put_be16(rsp_count, pCurrentRecordCount);
 
                if (rsp_count > actual) {
                        /* cache the rsp and generate a continuation state */
@@ -479,7 +481,7 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
                        if (pCache) {
                                pCacheBuffer = pCache->data;
                                /* get the rsp_count from the cached buffer */
-                               rsp_count = bt_get_be16(pCacheBuffer);
+                               rsp_count = get_be16(pCacheBuffer);
 
                                /* get index of the last sdp_record_t sent */
                                lastIndex = cstate->cStateValue.lastIndexSent;
@@ -515,8 +517,8 @@ static int service_search_req(sdp_req_t *req, sdp_buf_t *buf)
                }
 
                buf->data_size += handleSize;
-               bt_put_be16(rsp_count, pTotalRecordCount);
-               bt_put_be16(i - lastIndex, pCurrentRecordCount);
+               put_be16(rsp_count, pTotalRecordCount);
+               put_be16(i - lastIndex, pCurrentRecordCount);
 
                if (i == rsp_count) {
                        /* set "null" continuationState */
@@ -646,7 +648,7 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
                goto done;
        }
 
-       handle = bt_get_be32(pdata);
+       handle = get_be32(pdata);
 
        pdata += sizeof(uint32_t);
        data_left -= sizeof(uint32_t);
@@ -656,7 +658,7 @@ static int service_attr_req(sdp_req_t *req, sdp_buf_t *buf)
                goto done;
        }
 
-       max_rsp_size = bt_get_be16(pdata);
+       max_rsp_size = get_be16(pdata);
 
        pdata += sizeof(uint16_t);
        data_left -= sizeof(uint16_t);
@@ -772,7 +774,7 @@ done:
                return status;
 
        /* set attribute list byte count */
-       bt_put_be16(buf->data_size - cstate_size, buf->data);
+       put_be16(buf->data_size - cstate_size, buf->data);
        buf->data_size += sizeof(uint16_t);
        return 0;
 }
@@ -813,7 +815,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
                goto done;
        }
 
-       max = bt_get_be16(pdata);
+       max = get_be16(pdata);
 
        pdata += sizeof(uint16_t);
        data_left -= sizeof(uint16_t);
@@ -943,7 +945,7 @@ static int service_search_attr_req(sdp_req_t *req, sdp_buf_t *buf)
 
        if (!status) {
                /* set attribute list byte count */
-               bt_put_be16(buf->data_size - cstate_size, buf->data);
+               put_be16(buf->data_size - cstate_size, buf->data);
                buf->data_size += sizeof(uint16_t);
        }
 
@@ -1027,7 +1029,7 @@ static void process_request(sdp_req_t *req)
 send_rsp:
        if (status) {
                rsphdr->pdu_id = SDP_ERROR_RSP;
-               bt_put_be16(status, rsp.data);
+               put_be16(status, rsp.data);
                rsp.data_size = sizeof(uint16_t);
        }
 
index 38bf808..a90b299 100644 (file)
@@ -39,6 +39,7 @@
 
 #include <glib.h>
 
+#include "src/shared/util.h"
 #include "sdpd.h"
 #include "log.h"
 
@@ -320,7 +321,7 @@ static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p,
                return NULL;
        }
 
-       lookAheadAttrId = bt_get_be16(p + sizeof(uint8_t));
+       lookAheadAttrId = get_be16(p + sizeof(uint8_t));
 
        SDPDBG("Look ahead attr id : %d", lookAheadAttrId);
 
@@ -330,7 +331,7 @@ static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p,
                        SDPDBG("Unexpected end of packet");
                        return NULL;
                }
-               handle = bt_get_be32(p + sizeof(uint8_t) + sizeof(uint16_t) +
+               handle = get_be32(p + sizeof(uint8_t) + sizeof(uint16_t) +
                                                        sizeof(uint8_t));
                SDPDBG("SvcRecHandle : 0x%x", handle);
                rec = sdp_record_find(handle);
@@ -365,7 +366,7 @@ static sdp_record_t *extract_pdu_server(bdaddr_t *device, uint8_t *p,
                                                        seqlen, localExtractedLength);
                dtd = *(uint8_t *) p;
 
-               attrId = bt_get_be16(p + attrSize);
+               attrId = get_be16(p + attrSize);
                attrSize += sizeof(uint16_t);
 
                SDPDBG("DTD of attrId : %d Attr id : 0x%x", dtd, attrId);
@@ -456,13 +457,13 @@ success:
        update_db_timestamp();
 
        /* Build a rsp buffer */
-       bt_put_be32(rec->handle, rsp->data);
+       put_be32(rec->handle, rsp->data);
        rsp->data_size = sizeof(uint32_t);
 
        return 0;
 
 invalid:
-       bt_put_be16(SDP_INVALID_SYNTAX, rsp->data);
+       put_be16(SDP_INVALID_SYNTAX, rsp->data);
        rsp->data_size = sizeof(uint16_t);
 
        return -1;
@@ -477,7 +478,7 @@ int service_update_req(sdp_req_t *req, sdp_buf_t *rsp)
        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 = bt_get_be32(p);
+       uint32_t handle = get_be32(p);
 
        SDPDBG("Svc Rec Handle: 0x%x", handle);
 
@@ -505,7 +506,7 @@ int service_update_req(sdp_req_t *req, sdp_buf_t *rsp)
 
 done:
        p = rsp->data;
-       bt_put_be16(status, p);
+       put_be16(status, p);
        rsp->data_size = sizeof(uint16_t);
        return status;
 }
@@ -516,7 +517,7 @@ done:
 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 = bt_get_be32(p);
+       uint32_t handle = get_be32(p);
        sdp_record_t *rec;
        int status = 0;
 
@@ -535,7 +536,7 @@ int service_remove_req(sdp_req_t *req, sdp_buf_t *rsp)
        }
 
        p = rsp->data;
-       bt_put_be16(status, p);
+       put_be16(status, p);
        rsp->data_size = sizeof(uint16_t);
 
        return status;
index 58c9f1d..17a872c 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
 #include <config.h>
 #endif
 
+#include <endian.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <arpa/inet.h>
+#include <sys/stat.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)
+#include "src/shared/btsnoop.h"
 
 struct btsnoop_hdr {
        uint8_t         id[8];          /* Identification Pattern */
@@ -68,13 +57,24 @@ static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
 
 static const uint32_t btsnoop_version = 1;
 
+struct pklg_pkt {
+       uint32_t        len;
+       uint64_t        ts;
+       uint8_t         type;
+} __attribute__ ((packed));
+#define PKLG_PKT_SIZE (sizeof(struct pklg_pkt))
+
 struct btsnoop {
        int ref_count;
        int fd;
+       unsigned long flags;
        uint32_t type;
+       uint16_t index;
+       bool aborted;
+       bool pklg_format;
 };
 
-struct btsnoop *btsnoop_open(const char *path)
+struct btsnoop *btsnoop_open(const char *path, unsigned long flags)
 {
        struct btsnoop *btsnoop;
        struct btsnoop_hdr hdr;
@@ -90,17 +90,34 @@ struct btsnoop *btsnoop_open(const char *path)
                return NULL;
        }
 
+       btsnoop->flags = flags;
+
        len = read(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
        if (len < 0 || len != BTSNOOP_HDR_SIZE)
                goto failed;
 
-       if (memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id)))
-               goto failed;
+       if (!memcmp(hdr.id, btsnoop_id, sizeof(btsnoop_id))) {
+               /* Check for BTSnoop version 1 format */
+               if (be32toh(hdr.version) != btsnoop_version)
+                       goto failed;
 
-       if (ntohl(hdr.version) != btsnoop_version)
-               goto failed;
+               btsnoop->type = be32toh(hdr.type);
+               btsnoop->index = 0xffff;
+       } else {
+               if (!(btsnoop->flags & BTSNOOP_FLAG_PKLG_SUPPORT))
+                       goto failed;
 
-       btsnoop->type = ntohl(hdr.type);
+               /* Check for Apple Packet Logger format */
+               if (hdr.id[0] != 0x00 || hdr.id[1] != 0x00)
+                       goto failed;
+
+               btsnoop->type = BTSNOOP_TYPE_MONITOR;
+               btsnoop->index = 0xffff;
+               btsnoop->pklg_format = true;
+
+               /* Apple Packet Logger format has no header */
+               lseek(btsnoop->fd, 0, SEEK_SET);
+       }
 
        return btsnoop_ref(btsnoop);
 
@@ -129,10 +146,11 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t type)
        }
 
        btsnoop->type = type;
+       btsnoop->index = 0xffff;
 
        memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
-       hdr.version = htonl(btsnoop_version);
-       hdr.type = htonl(btsnoop->type);
+       hdr.version = htobe32(btsnoop_version);
+       hdr.type = htobe32(btsnoop->type);
 
        written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
        if (written < 0) {
@@ -188,11 +206,11 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
 
        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);
+       pkt.size  = htobe32(size);
+       pkt.len   = htobe32(size);
+       pkt.flags = htobe32(flags);
+       pkt.drops = htobe32(0);
+       pkt.ts    = htobe64(ts + 0x00E03AB44A676000ll);
 
        written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
        if (written < 0)
@@ -207,6 +225,61 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
        return true;
 }
 
+static uint32_t get_flags_from_opcode(uint16_t opcode)
+{
+       switch (opcode) {
+       case BTSNOOP_OPCODE_NEW_INDEX:
+       case BTSNOOP_OPCODE_DEL_INDEX:
+               break;
+       case BTSNOOP_OPCODE_COMMAND_PKT:
+               return 0x02;
+       case BTSNOOP_OPCODE_EVENT_PKT:
+               return 0x03;
+       case BTSNOOP_OPCODE_ACL_TX_PKT:
+               return 0x00;
+       case BTSNOOP_OPCODE_ACL_RX_PKT:
+               return 0x01;
+       case BTSNOOP_OPCODE_SCO_TX_PKT:
+       case BTSNOOP_OPCODE_SCO_RX_PKT:
+               break;
+       }
+
+       return 0xff;
+}
+
+bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv,
+                                       uint16_t index, uint16_t opcode,
+                                       const void *data, uint16_t size)
+{
+       uint32_t flags;
+
+       if (!btsnoop)
+               return false;
+
+       switch (btsnoop->type) {
+       case BTSNOOP_TYPE_HCI:
+               if (btsnoop->index == 0xffff)
+                       btsnoop->index = index;
+
+               if (index != btsnoop->index)
+                       return false;
+
+               flags = get_flags_from_opcode(opcode);
+               if (flags == 0xff)
+                       return false;
+               break;
+
+       case BTSNOOP_TYPE_MONITOR:
+               flags = (index << 16) | opcode;
+               break;
+
+       default:
+               return false;
+       }
+
+       return btsnoop_write(btsnoop, tv, flags, data, size);
+}
+
 bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
                        uint16_t frequency, const void *data, uint16_t size)
 {
@@ -226,3 +299,169 @@ bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
 
        return btsnoop_write(btsnoop, tv, flags, data, size);
 }
+
+static uint16_t get_opcode_from_pklg(uint8_t type)
+{
+       switch (type) {
+       case 0x00:
+               return BTSNOOP_OPCODE_COMMAND_PKT;
+       case 0x01:
+               return BTSNOOP_OPCODE_EVENT_PKT;
+       case 0x02:
+               return BTSNOOP_OPCODE_ACL_TX_PKT;
+       case 0x03:
+               return BTSNOOP_OPCODE_ACL_RX_PKT;
+       }
+
+       return 0xffff;
+}
+
+static bool pklg_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
+                                       uint16_t *index, uint16_t *opcode,
+                                       void *data, uint16_t *size)
+{
+       struct pklg_pkt pkt;
+       uint32_t toread;
+       uint64_t ts;
+       ssize_t len;
+
+       len = read(btsnoop->fd, &pkt, PKLG_PKT_SIZE);
+       if (len == 0)
+               return false;
+
+       if (len < 0 || len != PKLG_PKT_SIZE) {
+               btsnoop->aborted = true;
+               return false;
+       }
+
+       toread = be32toh(pkt.len) - 9;
+
+       ts = be64toh(pkt.ts);
+       tv->tv_sec = ts >> 32;
+       tv->tv_usec = ts & 0xffffffff;
+
+       *index = 0;
+       *opcode = get_opcode_from_pklg(pkt.type);
+
+       len = read(btsnoop->fd, data, toread);
+       if (len < 0) {
+               btsnoop->aborted = true;
+               return false;
+       }
+
+       *size = toread;
+
+       return true;
+}
+
+static uint16_t get_opcode_from_flags(uint8_t type, uint32_t flags)
+{
+       switch (type) {
+       case 0x01:
+               return BTSNOOP_OPCODE_COMMAND_PKT;
+       case 0x02:
+               if (flags & 0x01)
+                       return BTSNOOP_OPCODE_ACL_RX_PKT;
+               else
+                       return BTSNOOP_OPCODE_ACL_TX_PKT;
+       case 0x03:
+               if (flags & 0x01)
+                       return BTSNOOP_OPCODE_SCO_RX_PKT;
+               else
+                       return BTSNOOP_OPCODE_SCO_TX_PKT;
+       case 0x04:
+               return BTSNOOP_OPCODE_EVENT_PKT;
+       case 0xff:
+               if (flags & 0x02) {
+                       if (flags & 0x01)
+                               return BTSNOOP_OPCODE_EVENT_PKT;
+                       else
+                               return BTSNOOP_OPCODE_COMMAND_PKT;
+               } else {
+                       if (flags & 0x01)
+                               return BTSNOOP_OPCODE_ACL_RX_PKT;
+                       else
+                               return BTSNOOP_OPCODE_ACL_TX_PKT;
+               }
+               break;
+       }
+
+       return 0xffff;
+}
+
+bool btsnoop_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
+                                       uint16_t *index, uint16_t *opcode,
+                                       void *data, uint16_t *size)
+{
+       struct btsnoop_pkt pkt;
+       uint32_t toread, flags;
+       uint64_t ts;
+       uint8_t pkt_type;
+       ssize_t len;
+
+       if (!btsnoop || btsnoop->aborted)
+               return false;
+
+       if (btsnoop->pklg_format)
+               return pklg_read_hci(btsnoop, tv, index, opcode, data, size);
+
+       len = read(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
+       if (len == 0)
+               return false;
+
+       if (len < 0 || len != BTSNOOP_PKT_SIZE) {
+               btsnoop->aborted = true;
+               return false;
+       }
+
+       toread = be32toh(pkt.size);
+       flags = be32toh(pkt.flags);
+
+       ts = be64toh(pkt.ts) - 0x00E03AB44A676000ll;
+       tv->tv_sec = (ts / 1000000ll) + 946684800ll;
+       tv->tv_usec = ts % 1000000ll;
+
+       switch (btsnoop->type) {
+       case BTSNOOP_TYPE_HCI:
+               *index = 0;
+               *opcode = get_opcode_from_flags(0xff, flags);
+               break;
+
+       case BTSNOOP_TYPE_UART:
+               len = read(btsnoop->fd, &pkt_type, 1);
+               if (len < 0) {
+                       btsnoop->aborted = true;
+                       return false;
+               }
+               toread--;
+
+               *index = 0;
+               *opcode = get_opcode_from_flags(pkt_type, flags);
+               break;
+
+       case BTSNOOP_TYPE_MONITOR:
+               *index = flags >> 16;
+               *opcode = flags & 0xffff;
+               break;
+
+       default:
+               btsnoop->aborted = true;
+               return false;
+       }
+
+       len = read(btsnoop->fd, data, toread);
+       if (len < 0) {
+               btsnoop->aborted = true;
+               return false;
+       }
+
+       *size = toread;
+
+       return true;
+}
+
+bool btsnoop_read_phy(struct btsnoop *btsnoop, struct timeval *tv,
+                       uint16_t *frequency, void *data, uint16_t *size)
+{
+       return false;
+}
index 24580d4..2c55d02 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
@@ -33,6 +33,8 @@
 #define BTSNOOP_TYPE_MONITOR           2001
 #define BTSNOOP_TYPE_SIMULATOR         2002
 
+#define BTSNOOP_FLAG_PKLG_SUPPORT      (1 << 0)
+
 #define BTSNOOP_OPCODE_NEW_INDEX       0
 #define BTSNOOP_OPCODE_DEL_INDEX       1
 #define BTSNOOP_OPCODE_COMMAND_PKT     2
 #define BTSNOOP_OPCODE_SCO_TX_PKT      6
 #define BTSNOOP_OPCODE_SCO_RX_PKT      7
 
+struct btsnoop_opcode_new_index {
+       uint8_t  type;
+       uint8_t  bus;
+       uint8_t  bdaddr[6];
+       char     name[8];
+} __attribute__((packed));
+
 struct btsnoop;
 
-struct btsnoop *btsnoop_open(const char *path);
+struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
 struct btsnoop *btsnoop_create(const char *path, uint32_t type);
 
 struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop);
@@ -54,5 +63,14 @@ uint32_t btsnoop_get_type(struct btsnoop *btsnoop);
 
 bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
                        uint32_t flags, const void *data, uint16_t size);
+bool btsnoop_write_hci(struct btsnoop *btsnoop, struct timeval *tv,
+                                       uint16_t index, uint16_t opcode,
+                                       const void *data, uint16_t size);
 bool btsnoop_write_phy(struct btsnoop *btsnoop, struct timeval *tv,
                        uint16_t frequency, const void *data, uint16_t size);
+
+bool btsnoop_read_hci(struct btsnoop *btsnoop, struct timeval *tv,
+                                       uint16_t *index, uint16_t *opcode,
+                                       void *data, uint16_t *size);
+bool btsnoop_read_phy(struct btsnoop *btsnoop, struct timeval *tv,
+                       uint16_t *frequency, void *data, uint16_t *size);
diff --git a/src/shared/crypto.c b/src/shared/crypto.c
new file mode 100644 (file)
index 0000000..cc7536a
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "src/shared/util.h"
+#include "src/shared/crypto.h"
+
+#ifndef PF_ALG
+#include <linux/types.h>
+
+struct sockaddr_alg {
+       __u16   salg_family;
+       __u8    salg_type[14];
+       __u32   salg_feat;
+       __u32   salg_mask;
+       __u8    salg_name[64];
+};
+
+struct af_alg_iv {
+       __u32   ivlen;
+       __u8    iv[0];
+};
+
+#define ALG_SET_KEY                     1
+#define ALG_SET_IV                      2
+#define ALG_SET_OP                      3
+
+#define ALG_OP_DECRYPT                  0
+#define ALG_OP_ENCRYPT                  1
+
+#define PF_ALG         38      /* Algorithm sockets.  */
+#define AF_ALG         PF_ALG
+#else
+#include <linux/if_alg.h>
+#endif
+
+#ifndef SOL_ALG
+#define SOL_ALG                279
+#endif
+
+struct bt_crypto {
+       int ref_count;
+       int ecb_aes;
+       int urandom;
+};
+
+static int urandom_setup(void)
+{
+       int fd;
+
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd < 0)
+               return -1;
+
+       return fd;
+}
+
+static int ecb_aes_setup(void)
+{
+       struct sockaddr_alg salg;
+       int fd;
+
+       fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+       if (fd < 0)
+               return -1;
+
+       memset(&salg, 0, sizeof(salg));
+       salg.salg_family = AF_ALG;
+       strcpy((char *) salg.salg_type, "skcipher");
+       strcpy((char *) salg.salg_name, "ecb(aes)");
+
+       if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+struct bt_crypto *bt_crypto_new(void)
+{
+       struct bt_crypto *crypto;
+
+       crypto = new0(struct bt_crypto, 1);
+       if (!crypto)
+               return NULL;
+
+       crypto->ecb_aes = ecb_aes_setup();
+       if (crypto->ecb_aes < 0) {
+               free(crypto);
+               return NULL;
+       }
+
+       crypto->urandom = urandom_setup();
+       if (crypto->urandom < 0) {
+               close(crypto->ecb_aes);
+               free(crypto);
+               return NULL;
+       }
+
+       return bt_crypto_ref(crypto);
+}
+
+struct bt_crypto *bt_crypto_ref(struct bt_crypto *crypto)
+{
+       if (!crypto)
+               return NULL;
+
+       __sync_fetch_and_add(&crypto->ref_count, 1);
+
+       return crypto;
+}
+
+void bt_crypto_unref(struct bt_crypto *crypto)
+{
+       if (!crypto)
+               return;
+
+       if (__sync_sub_and_fetch(&crypto->ref_count, 1))
+               return;
+
+       close(crypto->urandom);
+       close(crypto->ecb_aes);
+
+       free(crypto);
+}
+
+bool bt_crypto_random_bytes(struct bt_crypto *crypto,
+                                       uint8_t *buf, uint8_t num_bytes)
+{
+       ssize_t len;
+
+       if (!crypto)
+               return false;
+
+       len = read(crypto->urandom, buf, num_bytes);
+       if (len < num_bytes)
+               return false;
+
+       return true;
+}
+
+static int alg_new(int fd, const void *keyval, socklen_t keylen)
+{
+       if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, keyval, keylen) < 0)
+               return -1;
+
+       /* FIXME: This should use accept4() with SOCK_CLOEXEC */
+       return accept(fd, NULL, 0);
+}
+
+static bool alg_encrypt(int fd, const void *inbuf, size_t inlen,
+                                               void *outbuf, size_t outlen)
+{
+       __u32 alg_op = ALG_OP_ENCRYPT;
+       char cbuf[CMSG_SPACE(sizeof(alg_op))];
+       struct cmsghdr *cmsg;
+       struct msghdr msg;
+       struct iovec iov;
+       ssize_t len;
+
+       memset(cbuf, 0, sizeof(cbuf));
+       memset(&msg, 0, sizeof(msg));
+
+       msg.msg_control = cbuf;
+       msg.msg_controllen = sizeof(cbuf);
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+       cmsg->cmsg_level = SOL_ALG;
+       cmsg->cmsg_type = ALG_SET_OP;
+       cmsg->cmsg_len = CMSG_LEN(sizeof(alg_op));
+       memcpy(CMSG_DATA(cmsg), &alg_op, sizeof(alg_op));
+
+       iov.iov_base = (void *) inbuf;
+       iov.iov_len = inlen;
+
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       len = sendmsg(fd, &msg, 0);
+       if (len < 0)
+               return false;
+
+       len = read(fd, outbuf, outlen);
+       if (len < 0)
+               return false;
+
+       return true;
+}
+
+static inline void swap128(const uint8_t src[16], uint8_t dst[16])
+{
+       int i;
+
+       for (i = 0; i < 16; i++)
+               dst[15 - i] = src[i];
+}
+
+/*
+ * Security function e
+ *
+ * Security function e generates 128-bit encryptedData from a 128-bit key
+ * and 128-bit plaintextData using the AES-128-bit block cypher:
+ *
+ *   encryptedData = e(key, plaintextData)
+ *
+ * The most significant octet of key corresponds to key[0], the most
+ * significant octet of plaintextData corresponds to in[0] and the
+ * most significant octet of encryptedData corresponds to out[0].
+ *
+ */
+bool bt_crypto_e(struct bt_crypto *crypto, const uint8_t key[16],
+                       const uint8_t plaintext[16], uint8_t encrypted[16])
+{
+       uint8_t tmp[16], in[16], out[16];
+       int fd;
+
+       if (!crypto)
+               return false;
+
+       /* The most significant octet of key corresponds to key[0] */
+       swap128(key, tmp);
+
+       fd = alg_new(crypto->ecb_aes, tmp, 16);
+       if (fd < 0)
+               return false;
+
+
+       /* Most significant octet of plaintextData corresponds to in[0] */
+       swap128(plaintext, in);
+
+       if (!alg_encrypt(fd, in, 16, out, 16)) {
+               close(fd);
+               return false;
+       }
+
+       /* Most significant octet of encryptedData corresponds to out[0] */
+       swap128(out, encrypted);
+
+       close(fd);
+
+       return true;
+}
+
+/*
+ * Random Address Hash function ah
+ *
+ * The random address hash function ah is used to generate a hash value
+ * that is used in resolvable private addresses.
+ *
+ * The following are inputs to the random address hash function ah:
+ *
+ *   k is 128 bits
+ *   r is 24 bits
+ *   padding is 104 bits
+ *
+ * r is concatenated with padding to generate r' which is used as the
+ * 128-bit input parameter plaintextData to security function e:
+ *
+ *   r' = padding || r
+ *
+ * The least significant octet of r becomes the least significant octet
+ * of r’ and the most significant octet of padding becomes the most
+ * significant octet of r'.
+ *
+ * For example, if the 24-bit value r is 0x423456 then r' is
+ * 0x00000000000000000000000000423456.
+ *
+ * The output of the random address function ah is:
+ *
+ *   ah(k, r) = e(k, r') mod 2^24
+ *
+ * The output of the security function e is then truncated to 24 bits by
+ * taking the least significant 24 bits of the output of e as the result
+ * of ah.
+ */
+bool bt_crypto_ah(struct bt_crypto *crypto, const uint8_t k[16],
+                                       const uint8_t r[3], uint8_t hash[3])
+{
+       uint8_t rp[16];
+       uint8_t encrypted[16];
+
+       if (!crypto)
+               return false;
+
+       /* r' = padding || r */
+       memcpy(rp, r, 3);
+       memset(rp + 3, 0, 13);
+
+       /* e(k, r') */
+       if (!bt_crypto_e(crypto, k, rp, encrypted))
+               return false;
+
+       /* ah(k, r) = e(k, r') mod 2^24 */
+       memcpy(hash, encrypted, 3);
+
+       return true;
+}
+
+typedef struct {
+       uint64_t a, b;
+} u128;
+
+static inline void u128_xor(const uint8_t p[16], const uint8_t q[16],
+                                                               uint8_t r[16])
+{
+       u128 pp, qq, rr;
+
+       memcpy(&pp, p, 16);
+       memcpy(&qq, q, 16);
+
+       rr.a = pp.a ^ qq.a;
+       rr.b = pp.b ^ qq.b;
+
+       memcpy(r, &rr, 16);
+}
+
+/*
+ * Confirm value generation function c1
+ *
+ * During the pairing process confirm values are exchanged. This confirm
+ * value generation function c1 is used to generate the confirm values.
+ *
+ * The following are inputs to the confirm value generation function c1:
+ *
+ *   k is 128 bits
+ *   r is 128 bits
+ *   pres is 56 bits
+ *   preq is 56 bits
+ *   iat is 1 bit
+ *   ia is 48 bits
+ *   rat is 1 bit
+ *   ra is 48 bits
+ *   padding is 32 bits of 0
+ *
+ * iat is concatenated with 7-bits of 0 to create iat' which is 8 bits
+ * in length. iat is the least significant bit of iat'
+ *
+ * rat is concatenated with 7-bits of 0 to create rat' which is 8 bits
+ * in length. rat is the least significant bit of rat'
+ *
+ * pres, preq, rat' and iat' are concatenated to generate p1 which is
+ * XORed with r and used as 128-bit input parameter plaintextData to
+ * security function e:
+ *
+ *   p1 = pres || preq || rat' || iat'
+ *
+ * The octet of iat' becomes the least significant octet of p1 and the
+ * most significant octet of pres becomes the most significant octet of
+ * p1.
+ *
+ * ra is concatenated with ia and padding to generate p2 which is XORed
+ * with the result of the security function e using p1 as the input
+ * paremter plaintextData and is then used as the 128-bit input
+ * parameter plaintextData to security function e:
+ *
+ *   p2 = padding || ia || ra
+ *
+ * The least significant octet of ra becomes the least significant octet
+ * of p2 and the most significant octet of padding becomes the most
+ * significant octet of p2.
+ *
+ * The output of the confirm value generation function c1 is:
+ *
+ *   c1(k, r, preq, pres, iat, rat, ia, ra) = e(k, e(k, r XOR p1) XOR p2)
+ *
+ * The 128-bit output of the security function e is used as the result
+ * of confirm value generation function c1.
+ */
+bool bt_crypto_c1(struct bt_crypto *crypto, const uint8_t k[16],
+                       const uint8_t r[16], const uint8_t pres[7],
+                       const uint8_t preq[7], uint8_t iat,
+                       const uint8_t ia[6], uint8_t rat,
+                       const uint8_t ra[6], uint8_t res[16])
+{
+       uint8_t p1[16], p2[16];
+
+       /* p1 = pres || preq || _rat || _iat */
+       p1[0] = iat;
+       p1[1] = rat;
+       memcpy(p1 + 2, preq, 7);
+       memcpy(p1 + 9, pres, 7);
+
+       /* p2 = padding || ia || ra */
+       memcpy(p2, ra, 6);
+       memcpy(p2 + 6, ia, 6);
+       memset(p2 + 12, 0, 4);
+
+       /* res = r XOR p1 */
+       u128_xor(r, p1, res);
+
+       /* res = e(k, res) */
+       if (!bt_crypto_e(crypto, k, res, res))
+               return false;
+
+       /* res = res XOR p2 */
+       u128_xor(res, p2, res);
+
+       /* res = e(k, res) */
+       return bt_crypto_e(crypto, k, res, res);
+}
+
+/*
+ * Key generation function s1
+ *
+ * The key generation function s1 is used to generate the STK during the
+ * pairing process.
+ *
+ * The following are inputs to the key generation function s1:
+ *
+ *   k is 128 bits
+ *   r1 is 128 bits
+ *   r2 is 128 bits
+ *
+ * The most significant 64-bits of r1 are discarded to generate r1' and
+ * the most significant 64-bits of r2 are discarded to generate r2'.
+ *
+ * r1' is concatenated with r2' to generate r' which is used as the
+ * 128-bit input parameter plaintextData to security function e:
+ *
+ *   r' = r1' || r2'
+ *
+ * The least significant octet of r2' becomes the least significant
+ * octet of r' and the most significant octet of r1' becomes the most
+ * significant octet of r'.
+ *
+ * The output of the key generation function s1 is:
+ *
+ *   s1(k, r1, r2) = e(k, r')
+ *
+ * The 128-bit output of the security function e is used as the result
+ * of key generation function s1.
+ */
+bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16],
+                       const uint8_t r1[16], const uint8_t r2[16],
+                       uint8_t res[16])
+{
+       memcpy(res, r2, 8);
+       memcpy(res + 8, r1, 8);
+
+       return bt_crypto_e(crypto, k, res, res);
+}
diff --git a/src/shared/crypto.h b/src/shared/crypto.h
new file mode 100644 (file)
index 0000000..cae8daa
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct bt_crypto;
+
+struct bt_crypto *bt_crypto_new(void);
+
+struct bt_crypto *bt_crypto_ref(struct bt_crypto *crypto);
+void bt_crypto_unref(struct bt_crypto *crypto);
+
+bool bt_crypto_random_bytes(struct bt_crypto *crypto,
+                                       uint8_t *buf, uint8_t num_bytes);
+
+bool bt_crypto_e(struct bt_crypto *crypto, const uint8_t key[16],
+                       const uint8_t plaintext[16], uint8_t encrypted[16]);
+bool bt_crypto_ah(struct bt_crypto *crypto, const uint8_t k[16],
+                                       const uint8_t r[3], uint8_t hash[3]);
+bool bt_crypto_c1(struct bt_crypto *crypto, const uint8_t k[16],
+                       const uint8_t r[16], const uint8_t pres[7],
+                       const uint8_t preq[7], uint8_t iat,
+                       const uint8_t ia[6], uint8_t rat,
+                       const uint8_t ra[6], uint8_t res[16]);
+bool bt_crypto_s1(struct bt_crypto *crypto, const uint8_t k[16],
+                       const uint8_t r1[16], const uint8_t r2[16],
+                       uint8_t res[16]);
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
new file mode 100644 (file)
index 0000000..36316af
--- /dev/null
@@ -0,0 +1,735 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdbool.h>
+
+#include "lib/uuid.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+
+#define MAX_CHAR_DECL_VALUE_LEN 19
+#define MAX_INCLUDED_VALUE_LEN 6
+
+static const bt_uuid_t primary_service_uuid = { .type = BT_UUID16,
+                                       .value.u16 = GATT_PRIM_SVC_UUID };
+static const bt_uuid_t secondary_service_uuid = { .type = BT_UUID16,
+                                       .value.u16 = GATT_SND_SVC_UUID };
+static const bt_uuid_t characteristic_uuid = { .type = BT_UUID16,
+                                       .value.u16 = GATT_CHARAC_UUID };
+static const bt_uuid_t included_service_uuid = { .type = BT_UUID16,
+                                       .value.u16 = GATT_INCLUDE_UUID };
+
+struct gatt_db {
+       uint16_t next_handle;
+       struct queue *services;
+};
+
+struct gatt_db_attribute {
+       uint16_t handle;
+       bt_uuid_t uuid;
+       uint8_t permissions;
+       uint16_t value_len;
+       uint8_t *value;
+
+       gatt_db_read_t read_func;
+       gatt_db_write_t write_func;
+       void *user_data;
+};
+
+struct gatt_db_service {
+       bool active;
+       uint16_t num_handles;
+       struct gatt_db_attribute **attributes;
+};
+
+static bool match_service_by_handle(const void *data, const void *user_data)
+{
+       const struct gatt_db_service *service = data;
+
+       return service->attributes[0]->handle == PTR_TO_INT(user_data);
+}
+
+static struct gatt_db_attribute *new_attribute(const bt_uuid_t *type,
+                                                       const uint8_t *val,
+                                                       uint16_t len)
+{
+       struct gatt_db_attribute *attribute;
+
+       attribute = new0(struct gatt_db_attribute, 1);
+       if (!attribute)
+               return NULL;
+
+       attribute->uuid = *type;
+       attribute->value_len = len;
+       if (len) {
+               attribute->value = malloc0(len);
+               if (!attribute->value) {
+                       free(attribute);
+                       return NULL;
+               }
+
+               memcpy(attribute->value, val, len);
+       }
+
+       return attribute;
+}
+
+static void attribute_destroy(void *data)
+{
+       struct gatt_db_attribute *attribute = data;
+
+       /* Attribute was not initialized by user */
+       if (!attribute)
+               return;
+
+       free(attribute->value);
+       free(attribute);
+}
+
+struct gatt_db *gatt_db_new(void)
+{
+       struct gatt_db *db;
+
+       db = new0(struct gatt_db, 1);
+       if (!db)
+               return NULL;
+
+       db->services = queue_new();
+       if (!db->services) {
+               free(db);
+               return NULL;
+       }
+
+       db->next_handle = 0x0001;
+
+       return db;
+}
+
+static void gatt_db_service_destroy(void *data)
+{
+       struct gatt_db_service *service = data;
+       int i;
+
+       for (i = 0; i < service->num_handles; i++)
+               attribute_destroy(service->attributes[i]);
+
+       free(service->attributes);
+       free(service);
+}
+
+void gatt_db_destroy(struct gatt_db *db)
+{
+       queue_destroy(db->services, gatt_db_service_destroy);
+       free(db);
+}
+
+static int uuid_to_le(const bt_uuid_t *uuid, uint8_t *dst)
+{
+       bt_uuid_t uuid128;
+
+       if (uuid->type == BT_UUID16) {
+               put_le16(uuid->value.u16, dst);
+               return bt_uuid_len(uuid);
+       }
+
+       bt_uuid_to_uuid128(uuid, &uuid128);
+       bswap_128(&uuid128.value.u128, dst);
+       return bt_uuid_len(&uuid128);
+}
+
+uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid,
+                                       bool primary, uint16_t num_handles)
+{
+       struct gatt_db_service *service;
+       const bt_uuid_t *type;
+       uint8_t value[16];
+       uint16_t len;
+
+       if (num_handles < 1 || (num_handles + db->next_handle) > UINT16_MAX)
+               return 0;
+
+       service = new0(struct gatt_db_service, 1);
+       if (!service)
+               return 0;
+
+       service->attributes = new0(struct gatt_db_attribute *, num_handles);
+       if (!service->attributes) {
+               free(service);
+               return 0;
+       }
+
+       if (primary)
+               type = &primary_service_uuid;
+       else
+               type = &secondary_service_uuid;
+
+       len = uuid_to_le(uuid, value);
+
+       service->attributes[0] = new_attribute(type, value, len);
+       if (!service->attributes[0]) {
+               gatt_db_service_destroy(service);
+               return 0;
+       }
+
+       if (!queue_push_tail(db->services, service)) {
+               gatt_db_service_destroy(service);
+               return 0;
+       }
+
+       /* TODO now we get next handle from database. We should first look
+        * for 'holes' between existing services first, and assign next_handle
+        * only if enough space was not found.
+        */
+       service->attributes[0]->handle = db->next_handle;
+       db->next_handle += num_handles;
+       service->num_handles = num_handles;
+
+       return service->attributes[0]->handle;
+}
+
+bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle)
+{
+       struct gatt_db_service *service;
+
+       service = queue_remove_if(db->services, match_service_by_handle,
+                                                       INT_TO_PTR(handle));
+       if (!service)
+               return false;
+
+       gatt_db_service_destroy(service);
+
+       return true;
+}
+
+static uint16_t get_attribute_index(struct gatt_db_service *service,
+                                                       int end_offset)
+{
+       int i = 0;
+
+       /* Here we look for first free attribute index with given offset */
+       while (i < (service->num_handles - end_offset) &&
+                                               service->attributes[i])
+               i++;
+
+       return i == (service->num_handles - end_offset) ? 0 : i;
+}
+
+static uint16_t get_handle_at_index(struct gatt_db_service *service,
+                                                               int index)
+{
+       return service->attributes[index]->handle;
+}
+
+static uint16_t update_attribute_handle(struct gatt_db_service *service,
+                                                               int index)
+{
+       uint16_t previous_handle;
+
+       /* We call this function with index > 0, because index 0 is reserved
+        * for service declaration, and is set in add_service()
+        */
+       previous_handle = service->attributes[index - 1]->handle;
+       service->attributes[index]->handle = previous_handle + 1;
+
+       return service->attributes[index]->handle;
+}
+
+static void set_attribute_data(struct gatt_db_attribute *attribute,
+                                               gatt_db_read_t read_func,
+                                               gatt_db_write_t write_func,
+                                               uint8_t permissions,
+                                               void *user_data)
+{
+       attribute->permissions = permissions;
+       attribute->read_func = read_func;
+       attribute->write_func = write_func;
+       attribute->user_data = user_data;
+}
+
+uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle,
+                                               const bt_uuid_t *uuid,
+                                               uint8_t permissions,
+                                               uint8_t properties,
+                                               gatt_db_read_t read_func,
+                                               gatt_db_write_t write_func,
+                                               void *user_data)
+{
+       uint8_t value[MAX_CHAR_DECL_VALUE_LEN];
+       struct gatt_db_service *service;
+       uint16_t len = 0;
+       int i;
+
+       service = queue_find(db->services, match_service_by_handle,
+                                                       INT_TO_PTR(handle));
+       if (!service)
+               return 0;
+
+       i = get_attribute_index(service, 1);
+       if (!i)
+               return 0;
+
+       value[0] = properties;
+       len += sizeof(properties);
+       /* We set handle of characteristic value, which will be added next */
+       put_le16(get_handle_at_index(service, i - 1) + 2, &value[1]);
+       len += sizeof(uint16_t);
+       len += uuid_to_le(uuid, &value[3]);
+
+       service->attributes[i] = new_attribute(&characteristic_uuid, value,
+                                                                       len);
+       if (!service->attributes[i])
+               return 0;
+
+       update_attribute_handle(service, i++);
+
+       service->attributes[i] = new_attribute(uuid, NULL, 0);
+       if (!service->attributes[i]) {
+               free(service->attributes[i - 1]);
+               return 0;
+       }
+
+       set_attribute_data(service->attributes[i], read_func, write_func,
+                                                       permissions, user_data);
+
+       return update_attribute_handle(service, i);
+}
+
+uint16_t gatt_db_add_char_descriptor(struct gatt_db *db, uint16_t handle,
+                                               const bt_uuid_t *uuid,
+                                               uint8_t permissions,
+                                               gatt_db_read_t read_func,
+                                               gatt_db_write_t write_func,
+                                               void *user_data)
+{
+       struct gatt_db_service *service;
+       int i;
+
+       service = queue_find(db->services, match_service_by_handle,
+                                                       INT_TO_PTR(handle));
+       if (!service)
+               return 0;
+
+       i = get_attribute_index(service, 0);
+       if (!i)
+               return 0;
+
+       service->attributes[i] = new_attribute(uuid, NULL, 0);
+       if (!service->attributes[i])
+               return 0;
+
+       set_attribute_data(service->attributes[i], read_func, write_func,
+                                                       permissions, user_data);
+
+       return update_attribute_handle(service, i);
+}
+
+uint16_t gatt_db_add_included_service(struct gatt_db *db, uint16_t handle,
+                                               uint16_t included_handle)
+{
+       struct gatt_db_service *included_service;
+       uint8_t value[MAX_INCLUDED_VALUE_LEN];
+       uint16_t len = 0;
+       struct gatt_db_service *service;
+       int index;
+
+       service = queue_find(db->services, match_service_by_handle,
+                                                       INT_TO_PTR(handle));
+       if (!service)
+               return 0;
+
+       included_service = queue_find(db->services, match_service_by_handle,
+                                               INT_TO_PTR(included_handle));
+
+       if (!included_service)
+               return 0;
+
+       put_le16(included_handle, &value[len]);
+       len += sizeof(uint16_t);
+
+       put_le16(included_handle + included_service->num_handles - 1,
+                                                               &value[len]);
+       len += sizeof(uint16_t);
+
+       /* The Service UUID shall only be present when the UUID is a 16-bit
+        * Bluetooth UUID. Vol 2. Part G. 3.2
+        */
+       if (included_service->attributes[0]->value_len == sizeof(uint16_t)) {
+               memcpy(&value[len], included_service->attributes[0]->value,
+                               included_service->attributes[0]->value_len);
+               len += included_service->attributes[0]->value_len;
+       }
+
+       index = get_attribute_index(service, 0);
+       if (!index)
+               return 0;
+
+       service->attributes[index] = new_attribute(&included_service_uuid,
+                                                               value, len);
+       if (!service->attributes[index])
+               return 0;
+
+       /* The Attribute Permissions shall be read only and not require
+        * authentication or authorization. Vol 2. Part G. 3.2
+        *
+        * TODO handle permissions
+        */
+       set_attribute_data(service->attributes[index], NULL, NULL, 0, NULL);
+
+       return update_attribute_handle(service, index);
+}
+
+bool gatt_db_service_set_active(struct gatt_db *db, uint16_t handle,
+                                                               bool active)
+{
+       struct gatt_db_service *service;
+
+       service = queue_find(db->services, match_service_by_handle,
+                                                       INT_TO_PTR(handle));
+       if (!service)
+               return false;
+
+       service->active = active;
+
+       return true;
+}
+
+struct read_by_group_type_data {
+       struct queue *queue;
+       bt_uuid_t uuid;
+       uint16_t start_handle;
+       uint16_t end_handle;
+       uint16_t uuid_size;
+       bool stop_search;
+};
+
+static void read_by_group_type(void *data, void *user_data)
+{
+       struct read_by_group_type_data *search_data = user_data;
+       struct gatt_db_service *service = data;
+
+       if (!service->active)
+               return;
+
+       /* Don't want more results as they have different size */
+       if (search_data->stop_search)
+               return;
+
+       if (bt_uuid_cmp(&search_data->uuid, &service->attributes[0]->uuid))
+               return;
+
+       if (service->attributes[0]->handle < search_data->start_handle)
+               return;
+
+       /* Remember size of uuid */
+       if (!search_data->uuid_size) {
+               search_data->uuid_size = service->attributes[0]->value_len;
+       } else if (search_data->uuid_size !=
+                                       service->attributes[0]->value_len) {
+               /* Don't want more results. This is last */
+               search_data->stop_search = true;
+               return;
+       }
+
+       queue_push_tail(search_data->queue,
+                       UINT_TO_PTR(service->attributes[0]->handle));
+}
+
+void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,
+                                                       uint16_t end_handle,
+                                                       const bt_uuid_t type,
+                                                       struct queue *queue)
+{
+       struct read_by_group_type_data data;
+
+       data.uuid = type;
+       data.start_handle = start_handle;
+       data.end_handle = end_handle;
+       data.queue = queue;
+       data.uuid_size = 0;
+       data.stop_search = false;
+
+       queue_foreach(db->services, read_by_group_type, &data);
+}
+
+struct find_by_type_value_data {
+       struct queue *queue;
+       bt_uuid_t uuid;
+       uint16_t start_handle;
+       uint16_t end_handle;
+};
+
+static void find_by_type(void *data, void *user_data)
+{
+       struct find_by_type_value_data *search_data = user_data;
+       struct gatt_db_service *service = data;
+       struct gatt_db_attribute *attribute;
+       int i;
+
+       if (!service->active)
+               return;
+
+       for (i = 0; i < service->num_handles; i++) {
+               attribute = service->attributes[i];
+
+               if (!attribute)
+                       continue;
+
+               if ((attribute->handle < search_data->start_handle) ||
+                               (attribute->handle > search_data->end_handle))
+                       continue;
+
+               if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid))
+                       continue;
+
+               queue_push_tail(search_data->queue,
+                                               UINT_TO_PTR(attribute->handle));
+       }
+}
+
+void gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle,
+                                                       uint16_t end_handle,
+                                                       const bt_uuid_t *type,
+                                                       struct queue *queue)
+{
+       struct find_by_type_value_data data;
+
+       data.uuid = *type;
+       data.start_handle = start_handle;
+       data.end_handle = end_handle;
+       data.queue = queue;
+
+       queue_foreach(db->services, find_by_type, &data);
+}
+
+struct read_by_type_data {
+       struct queue *queue;
+       bt_uuid_t uuid;
+       uint16_t start_handle;
+       uint16_t end_handle;
+};
+
+static void read_by_type(void *data, void *user_data)
+{
+       struct read_by_type_data *search_data = user_data;
+       struct gatt_db_service *service = data;
+       struct gatt_db_attribute *attribute;
+       int i;
+
+       if (!service->active)
+               return;
+
+       for (i = 0; i < service->num_handles; i++) {
+               attribute = service->attributes[i];
+               if (!attribute)
+                       continue;
+
+               if (attribute->handle < search_data->start_handle)
+                       continue;
+
+               if (attribute->handle > search_data->end_handle)
+                       return;
+
+               if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid))
+                       continue;
+
+               queue_push_tail(search_data->queue,
+                                               UINT_TO_PTR(attribute->handle));
+       }
+}
+
+void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle,
+                                               uint16_t end_handle,
+                                               const bt_uuid_t type,
+                                               struct queue *queue)
+{
+       struct read_by_type_data data;
+       data.uuid = type;
+       data.start_handle = start_handle;
+       data.end_handle = end_handle;
+       data.queue = queue;
+
+       queue_foreach(db->services, read_by_type, &data);
+}
+
+
+struct find_information_data {
+       struct queue *queue;
+       uint16_t start_handle;
+       uint16_t end_handle;
+};
+
+static void find_information(void *data, void *user_data)
+{
+       struct find_information_data *search_data = user_data;
+       struct gatt_db_service *service = data;
+       struct gatt_db_attribute *attribute;
+       int i;
+
+       if (!service->active)
+               return;
+
+       /* Check if service is in range */
+       if ((service->attributes[0]->handle + service->num_handles - 1) <
+                                               search_data->start_handle)
+               return;
+
+       for (i = 0; i < service->num_handles; i++) {
+               attribute = service->attributes[i];
+               if (!attribute)
+                       continue;
+
+               if (attribute->handle < search_data->start_handle)
+                       continue;
+
+               if (attribute->handle > search_data->end_handle)
+                       return;
+
+               queue_push_tail(search_data->queue,
+                                               UINT_TO_PTR(attribute->handle));
+       }
+}
+
+void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
+                                                       uint16_t end_handle,
+                                                       struct queue *queue)
+{
+       struct find_information_data data;
+
+       data.start_handle = start_handle;
+       data.end_handle = end_handle;
+       data.queue = queue;
+
+       queue_foreach(db->services, find_information, &data);
+}
+
+static bool find_service_for_handle(const void *data, const void *user_data)
+{
+       const struct gatt_db_service *service = data;
+       uint16_t handle = PTR_TO_INT(user_data);
+       uint16_t start, end;
+
+       start = service->attributes[0]->handle;
+       end = start + service->num_handles;
+
+       return (start <= handle) && (handle < end);
+}
+
+bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset,
+                               uint8_t att_opcode, bdaddr_t *bdaddr,
+                               uint8_t **value, int *length)
+{
+       struct gatt_db_service *service;
+       uint16_t service_handle;
+       struct gatt_db_attribute *a;
+
+       if (!value || !length)
+               return false;
+
+       service = queue_find(db->services, find_service_for_handle,
+                                               INT_TO_PTR(handle));
+       if (!service)
+               return false;
+
+       service_handle = service->attributes[0]->handle;
+
+       a = service->attributes[handle - service_handle];
+       if (!a)
+               return false;
+
+       /*
+        * We call callback, and set length to -1, to notify user that callback
+        * has been called. Otherwise we set length to value length in database.
+        */
+       if (a->read_func) {
+               *value = NULL;
+               *length = -1;
+               a->read_func(handle, offset, att_opcode, bdaddr, a->user_data);
+       } else {
+               if (offset > a->value_len)
+                       return false;
+
+               *value = &a->value[offset];
+               *length = a->value_len - offset;
+       }
+
+       return true;
+}
+
+bool gatt_db_write(struct gatt_db *db, uint16_t handle, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t att_opcode, bdaddr_t *bdaddr)
+{
+       struct gatt_db_service *service;
+       uint16_t service_handle;
+       struct gatt_db_attribute *a;
+
+       service = queue_find(db->services, find_service_for_handle,
+                                               INT_TO_PTR(handle));
+       if (!service)
+               return false;
+
+       service_handle = service->attributes[0]->handle;
+
+       a = service->attributes[handle - service_handle];
+       if (!a || !a->write_func)
+               return false;
+
+       a->write_func(handle, offset, value, len, att_opcode, bdaddr,
+                                                               a->user_data);
+
+       return true;
+}
+
+const bt_uuid_t *gatt_db_get_attribute_type(struct gatt_db *db,
+                                                       uint16_t handle)
+{
+       struct gatt_db_service *service;
+       struct gatt_db_attribute *attribute;
+       uint16_t service_handle;
+
+       service = queue_find(db->services, find_service_for_handle,
+                                               INT_TO_PTR(handle));
+       if (!service)
+               return NULL;
+
+       service_handle = service->attributes[0]->handle;
+
+       attribute = service->attributes[handle - service_handle];
+       if (!attribute)
+               return NULL;
+
+       return &attribute->uuid;
+}
+
+uint16_t gatt_db_get_end_handle(struct gatt_db *db, uint16_t handle)
+{
+       struct gatt_db_service *service;
+
+       service = queue_find(db->services, find_service_for_handle,
+                                               INT_TO_PTR(handle));
+       if (!service)
+               return 0;
+
+       return service->attributes[0]->handle + service->num_handles - 1;
+}
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
new file mode 100644 (file)
index 0000000..3d46730
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct gatt_db;
+
+struct gatt_db *gatt_db_new(void);
+void gatt_db_destroy(struct gatt_db *db);
+
+uint16_t gatt_db_add_service(struct gatt_db *db, const bt_uuid_t *uuid,
+                                       bool primary, uint16_t num_handles);
+bool gatt_db_remove_service(struct gatt_db *db, uint16_t handle);
+
+typedef void (*gatt_db_read_t) (uint16_t handle, uint16_t offset,
+                                       uint8_t att_opcode, bdaddr_t *bdaddr,
+                                       void *user_data);
+
+typedef void (*gatt_db_write_t) (uint16_t handle, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t att_opcode, bdaddr_t *bdaddr,
+                                       void *user_data);
+
+uint16_t gatt_db_add_characteristic(struct gatt_db *db, uint16_t handle,
+                                               const bt_uuid_t *uuid,
+                                               uint8_t permissions,
+                                               uint8_t properties,
+                                               gatt_db_read_t read_func,
+                                               gatt_db_write_t write_func,
+                                               void *user_data);
+
+uint16_t gatt_db_add_char_descriptor(struct gatt_db *db, uint16_t handle,
+                                               const bt_uuid_t *uuid,
+                                               uint8_t permissions,
+                                               gatt_db_read_t read_func,
+                                               gatt_db_write_t write_func,
+                                               void *user_data);
+
+uint16_t gatt_db_add_included_service(struct gatt_db *db, uint16_t handle,
+                                               uint16_t included_handle);
+
+bool gatt_db_service_set_active(struct gatt_db *db, uint16_t handle,
+                                                               bool active);
+
+void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,
+                                                       uint16_t end_handle,
+                                                       const bt_uuid_t type,
+                                                       struct queue *queue);
+
+void gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle,
+                                                       uint16_t end_handle,
+                                                       const bt_uuid_t *type,
+                                                       struct queue *queue);
+
+void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle,
+                                                       uint16_t end_handle,
+                                                       const bt_uuid_t type,
+                                                       struct queue *queue);
+
+void gatt_db_find_information(struct gatt_db *db, uint16_t start_handle,
+                                                       uint16_t end_handle,
+                                                       struct queue *queue);
+
+bool gatt_db_read(struct gatt_db *db, uint16_t handle, uint16_t offset,
+                                       uint8_t att_opcode, bdaddr_t *bdaddr,
+                                       uint8_t **value, int *length);
+
+bool gatt_db_write(struct gatt_db *db, uint16_t handle, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t att_opcode, bdaddr_t *bdaddr);
+
+const bt_uuid_t *gatt_db_get_attribute_type(struct gatt_db *db,
+                                                       uint16_t handle);
+
+uint16_t gatt_db_get_end_handle(struct gatt_db *db, uint16_t handle);
diff --git a/src/shared/hci.c b/src/shared/hci.c
new file mode 100644 (file)
index 0000000..6d0a5f8
--- /dev/null
@@ -0,0 +1,618 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "monitor/bt.h"
+#include "monitor/mainloop.h"
+#include "src/shared/io.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/hci.h"
+
+#define BTPROTO_HCI    1
+struct sockaddr_hci {
+       sa_family_t     hci_family;
+       unsigned short  hci_dev;
+       unsigned short  hci_channel;
+};
+#define HCI_CHANNEL_RAW                0
+#define HCI_CHANNEL_USER       1
+
+#define SOL_HCI                0
+#define HCI_FILTER     2
+struct hci_filter {
+       uint32_t type_mask;
+       uint32_t event_mask[2];
+       uint16_t opcode;
+};
+
+struct bt_hci {
+       int ref_count;
+       struct io *io;
+       bool is_stream;
+       bool writer_active;
+       uint8_t num_cmds;
+       unsigned int next_cmd_id;
+       unsigned int next_evt_id;
+       struct queue *cmd_queue;
+       struct queue *rsp_queue;
+       struct queue *evt_list;
+};
+
+struct cmd {
+       unsigned int id;
+       uint16_t opcode;
+       void *data;
+       uint8_t size;
+       bt_hci_callback_func_t callback;
+       bt_hci_destroy_func_t destroy;
+       void *user_data;
+};
+
+struct evt {
+       unsigned int id;
+       uint8_t event;
+       bt_hci_callback_func_t callback;
+       bt_hci_destroy_func_t destroy;
+       void *user_data;
+};
+
+static void cmd_free(void *data)
+{
+       struct cmd *cmd = data;
+
+       if (cmd->destroy)
+               cmd->destroy(cmd->user_data);
+
+       free(cmd->data);
+       free(cmd);
+}
+
+static void evt_free(void *data)
+{
+       struct evt *evt = data;
+
+       if (evt->destroy)
+               evt->destroy(evt->user_data);
+
+       free(evt);
+}
+
+static void send_command(struct bt_hci *hci, uint16_t opcode,
+                                               void *data, uint8_t size)
+{
+       uint8_t type = BT_H4_CMD_PKT;
+       struct bt_hci_cmd_hdr hdr;
+       struct iovec iov[3];
+       int fd, iovcnt;
+
+       if (hci->num_cmds < 1)
+               return;
+
+       hdr.opcode = cpu_to_le16(opcode);
+       hdr.plen = size;
+
+       iov[0].iov_base = &type;
+       iov[0].iov_len  = 1;
+       iov[1].iov_base = &hdr;
+       iov[1].iov_len  = sizeof(hdr);
+
+       if (size > 0) {
+               iov[2].iov_base = data;
+               iov[2].iov_len  = size;
+               iovcnt = 3;
+       } else
+               iovcnt = 2;
+
+       fd = io_get_fd(hci->io);
+       if (fd < 0)
+               return;
+
+       if (writev(fd, iov, iovcnt) < 0)
+               return;
+
+       hci->num_cmds--;
+}
+
+static bool io_write_callback(struct io *io, void *user_data)
+{
+       struct bt_hci *hci = user_data;
+       struct cmd *cmd;
+
+       cmd = queue_pop_head(hci->cmd_queue);
+       if (cmd) {
+               send_command(hci, cmd->opcode, cmd->data, cmd->size);
+               queue_push_tail(hci->rsp_queue, cmd);
+       }
+
+       hci->writer_active = false;
+
+       return false;
+}
+
+static void wakeup_writer(struct bt_hci *hci)
+{
+       if (hci->writer_active)
+               return;
+
+       if (hci->num_cmds < 1)
+               return;
+
+       if (queue_isempty(hci->cmd_queue))
+               return;
+
+       if (!io_set_write_handler(hci->io, io_write_callback, hci, NULL))
+               return;
+
+       hci->writer_active = true;
+}
+
+static bool match_cmd_opcode(const void *a, const void *b)
+{
+       const struct cmd *cmd = a;
+       uint16_t opcode = PTR_TO_UINT(b);
+
+       return cmd->opcode == opcode;
+}
+
+static void process_response(struct bt_hci *hci, uint16_t opcode,
+                                       const void *data, size_t size)
+{
+       struct cmd *cmd;
+
+       if (opcode == BT_HCI_CMD_NOP)
+               goto done;
+
+       cmd = queue_remove_if(hci->rsp_queue, match_cmd_opcode,
+                                               UINT_TO_PTR(opcode));
+       if (!cmd)
+               return;
+
+       if (cmd->callback)
+               cmd->callback(data, size, cmd->user_data);
+
+       cmd_free(cmd);
+
+done:
+       wakeup_writer(hci);
+}
+
+static void process_notify(void *data, void *user_data)
+{
+       struct bt_hci_evt_hdr *hdr = user_data;
+       struct evt *evt = data;
+
+       if (evt->event == hdr->evt)
+               evt->callback(user_data + sizeof(struct bt_hci_evt_hdr),
+                                               hdr->plen, evt->user_data);
+}
+
+static void process_event(struct bt_hci *hci, const void *data, size_t size)
+{
+       const struct bt_hci_evt_hdr *hdr = data;
+       const struct bt_hci_evt_cmd_complete *cc;
+       const struct bt_hci_evt_cmd_status *cs;
+
+       if (size < sizeof(struct bt_hci_evt_hdr))
+               return;
+
+       data += sizeof(struct bt_hci_evt_hdr);
+       size -= sizeof(struct bt_hci_evt_hdr);
+
+       if (hdr->plen != size)
+               return;
+
+       switch (hdr->evt) {
+       case BT_HCI_EVT_CMD_COMPLETE:
+               if (size < sizeof(*cc))
+                       return;
+               cc = data;
+               hci->num_cmds = cc->ncmd;
+               process_response(hci, le16_to_cpu(cc->opcode),
+                                               data + sizeof(*cc),
+                                               size - sizeof(*cc));
+               break;
+
+       case BT_HCI_EVT_CMD_STATUS:
+               if (size < sizeof(*cs))
+                       return;
+               cs = data;
+               hci->num_cmds = cs->ncmd;
+               process_response(hci, le16_to_cpu(cs->opcode), &cs->status, 1);
+               break;
+
+       default:
+               queue_foreach(hci->evt_list, process_notify, (void *) hdr);
+               break;
+       }
+}
+
+static bool io_read_callback(struct io *io, void *user_data)
+{
+       struct bt_hci *hci = user_data;
+       uint8_t buf[512];
+       ssize_t len;
+       int fd;
+
+       fd = io_get_fd(hci->io);
+       if (fd < 0)
+               return false;
+
+       if (hci->is_stream)
+               return false;
+
+       len = read(fd, buf, sizeof(buf));
+       if (len < 0)
+               return false;
+
+       if (len < 1)
+               return true;
+
+       switch (buf[0]) {
+       case BT_H4_EVT_PKT:
+               process_event(hci, buf + 1, len - 1);
+               break;
+       }
+
+       return true;
+}
+
+static struct bt_hci *create_hci(int fd)
+{
+       struct bt_hci *hci;
+
+       if (fd < 0)
+               return NULL;
+
+       hci = new0(struct bt_hci, 1);
+       if (!hci)
+               return NULL;
+
+       hci->io = io_new(fd);
+       if (!hci->io) {
+               free(hci);
+               return NULL;
+       }
+
+       hci->is_stream = true;
+       hci->writer_active = false;
+       hci->num_cmds = 1;
+       hci->next_cmd_id = 1;
+       hci->next_evt_id = 1;
+
+       hci->cmd_queue = queue_new();
+       if (!hci->cmd_queue) {
+               io_destroy(hci->io);
+               free(hci);
+               return NULL;
+       }
+
+       hci->rsp_queue = queue_new();
+       if (!hci->rsp_queue) {
+               queue_destroy(hci->cmd_queue, NULL);
+               io_destroy(hci->io);
+               free(hci);
+               return NULL;
+       }
+
+       hci->evt_list = queue_new();
+       if (!hci->evt_list) {
+               queue_destroy(hci->rsp_queue, NULL);
+               queue_destroy(hci->cmd_queue, NULL);
+               io_destroy(hci->io);
+               free(hci);
+               return NULL;
+       }
+
+       if (!io_set_read_handler(hci->io, io_read_callback, hci, NULL)) {
+               queue_destroy(hci->evt_list, NULL);
+               queue_destroy(hci->rsp_queue, NULL);
+               queue_destroy(hci->cmd_queue, NULL);
+               io_destroy(hci->io);
+               free(hci);
+               return NULL;
+       }
+
+       return bt_hci_ref(hci);
+}
+
+struct bt_hci *bt_hci_new(int fd)
+{
+       struct bt_hci *hci;
+
+       hci = create_hci(fd);
+       if (!hci)
+               return NULL;
+
+       return hci;
+}
+
+static int create_socket(uint16_t index, uint16_t channel)
+{
+       struct sockaddr_hci addr;
+       int fd;
+
+       fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                                                               BTPROTO_HCI);
+       if (fd < 0)
+               return -1;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.hci_family = AF_BLUETOOTH;
+       addr.hci_dev = index;
+       addr.hci_channel = channel;
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+struct bt_hci *bt_hci_new_user_channel(uint16_t index)
+{
+       struct bt_hci *hci;
+       int fd;
+
+       fd = create_socket(index, HCI_CHANNEL_USER);
+       if (fd < 0)
+               return NULL;
+
+       hci = create_hci(fd);
+       if (!hci) {
+               close(fd);
+               return NULL;
+       }
+
+       hci->is_stream = false;
+
+       bt_hci_set_close_on_unref(hci, true);
+
+       return hci;
+}
+
+struct bt_hci *bt_hci_new_raw_device(uint16_t index)
+{
+       struct bt_hci *hci;
+       struct hci_filter flt;
+       int fd;
+
+       fd = create_socket(index, HCI_CHANNEL_RAW);
+       if (fd < 0)
+               return NULL;
+
+       memset(&flt, 0, sizeof(flt));
+       flt.type_mask = 1 << BT_H4_EVT_PKT;
+       flt.event_mask[0] = 0xffffffff;
+       flt.event_mask[1] = 0xffffffff;
+
+       if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+               close(fd);
+               return NULL;
+       }
+
+       hci = create_hci(fd);
+       if (!hci) {
+               close(fd);
+               return NULL;
+       }
+
+       hci->is_stream = false;
+
+       bt_hci_set_close_on_unref(hci, true);
+
+       return hci;
+}
+
+struct bt_hci *bt_hci_ref(struct bt_hci *hci)
+{
+       if (!hci)
+               return NULL;
+
+       __sync_fetch_and_add(&hci->ref_count, 1);
+
+       return hci;
+}
+
+void bt_hci_unref(struct bt_hci *hci)
+{
+       if (!hci)
+               return;
+
+       if (__sync_sub_and_fetch(&hci->ref_count, 1))
+               return;
+
+       queue_destroy(hci->evt_list, evt_free);
+       queue_destroy(hci->cmd_queue, cmd_free);
+       queue_destroy(hci->rsp_queue, cmd_free);
+
+       io_destroy(hci->io);
+
+       free(hci);
+}
+
+bool bt_hci_set_close_on_unref(struct bt_hci *hci, bool do_close)
+{
+       if (!hci)
+               return false;
+
+       return io_set_close_on_destroy(hci->io, do_close);
+}
+
+unsigned int bt_hci_send(struct bt_hci *hci, uint16_t opcode,
+                               const void *data, uint8_t size,
+                               bt_hci_callback_func_t callback,
+                               void *user_data, bt_hci_destroy_func_t destroy)
+{
+       struct cmd *cmd;
+
+       if (!hci)
+               return 0;
+
+       cmd = new0(struct cmd, 1);
+       if (!cmd)
+               return 0;
+
+       cmd->opcode = opcode;
+       cmd->size = size;
+
+       if (cmd->size > 0) {
+               cmd->data = malloc(cmd->size);
+               if (!cmd->data) {
+                       free(cmd);
+                       return 0;
+               }
+
+               memcpy(cmd->data, data, cmd->size);
+       }
+
+       if (hci->next_cmd_id < 1)
+               hci->next_cmd_id = 1;
+
+       cmd->id = hci->next_cmd_id++;
+
+       cmd->callback = callback;
+       cmd->destroy = destroy;
+       cmd->user_data = user_data;
+
+       if (!queue_push_tail(hci->cmd_queue, cmd)) {
+               free(cmd->data);
+               free(cmd);
+               return 0;
+       }
+
+       wakeup_writer(hci);
+
+       return cmd->id;
+}
+
+static bool match_cmd_id(const void *a, const void *b)
+{
+       const struct cmd *cmd = a;
+       unsigned int id = PTR_TO_UINT(b);
+
+       return cmd->id == id;
+}
+
+bool bt_hci_cancel(struct bt_hci *hci, unsigned int id)
+{
+       struct cmd *cmd;
+
+       if (!hci || !id)
+               return false;
+
+       cmd = queue_remove_if(hci->cmd_queue, match_cmd_id, UINT_TO_PTR(id));
+       if (!cmd) {
+               cmd = queue_remove_if(hci->rsp_queue, match_cmd_id,
+                                                       UINT_TO_PTR(id));
+               if (!cmd)
+                       return false;
+       }
+
+       cmd_free(cmd);
+
+       wakeup_writer(hci);
+
+       return true;
+}
+
+bool bt_hci_flush(struct bt_hci *hci)
+{
+       if (!hci)
+               return false;
+
+       if (hci->writer_active) {
+               io_set_write_handler(hci->io, NULL, NULL, NULL);
+               hci->writer_active = false;
+       }
+
+       queue_remove_all(hci->cmd_queue, NULL, NULL, cmd_free);
+       queue_remove_all(hci->rsp_queue, NULL, NULL, cmd_free);
+
+       return true;
+}
+
+unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event,
+                               bt_hci_callback_func_t callback,
+                               void *user_data, bt_hci_destroy_func_t destroy)
+{
+       struct evt *evt;
+
+       if (!hci)
+               return 0;
+
+       evt = new0(struct evt, 1);
+       if (!evt)
+               return 0;
+
+       evt->event = event;
+
+       if (hci->next_evt_id < 1)
+               hci->next_evt_id = 1;
+
+       evt->id = hci->next_evt_id++;
+
+       evt->callback = callback;
+       evt->destroy = destroy;
+       evt->user_data = user_data;
+
+       if (!queue_push_tail(hci->evt_list, evt)) {
+               free(evt);
+               return 0;
+       }
+
+       return evt->id;
+}
+
+static bool match_evt_id(const void *a, const void *b)
+{
+       const struct evt *evt = a;
+       unsigned int id = PTR_TO_UINT(b);
+
+       return evt->id == id;
+}
+
+bool bt_hci_unregister(struct bt_hci *hci, unsigned int id)
+{
+       struct evt *evt;
+
+       if (!hci || !id)
+               return false;
+
+       evt = queue_remove_if(hci->evt_list, match_evt_id, UINT_TO_PTR(id));
+       if (!evt)
+               return false;
+
+       evt_free(evt);
+
+       return true;
+}
diff --git a/src/shared/hci.h b/src/shared/hci.h
new file mode 100644 (file)
index 0000000..dba0f11
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+
+typedef void (*bt_hci_destroy_func_t)(void *user_data);
+
+struct bt_hci;
+
+struct bt_hci *bt_hci_new(int fd);
+struct bt_hci *bt_hci_new_user_channel(uint16_t index);
+struct bt_hci *bt_hci_new_raw_device(uint16_t index);
+
+struct bt_hci *bt_hci_ref(struct bt_hci *hci);
+void bt_hci_unref(struct bt_hci *hci);
+
+bool bt_hci_set_close_on_unref(struct bt_hci *hci, bool do_close);
+
+typedef void (*bt_hci_callback_func_t)(const void *data, uint8_t size,
+                                                       void *user_data);
+
+unsigned int bt_hci_send(struct bt_hci *hci, uint16_t opcode,
+                               const void *data, uint8_t size,
+                               bt_hci_callback_func_t callback,
+                               void *user_data, bt_hci_destroy_func_t destroy);
+bool bt_hci_cancel(struct bt_hci *hci, unsigned int id);
+bool bt_hci_flush(struct bt_hci *hci);
+
+unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event,
+                               bt_hci_callback_func_t callback,
+                               void *user_data, bt_hci_destroy_func_t destroy);
+bool bt_hci_unregister(struct bt_hci *hci, unsigned int id);
index 0ea191f..6c93005 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
@@ -31,6 +31,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdbool.h>
+#include <errno.h>
 #include <sys/socket.h>
 
 #include <glib.h>
@@ -41,8 +42,9 @@
 #include "monitor/bt.h"
 #include "emulator/btdev.h"
 #include "emulator/bthost.h"
-
-#include "hciemu.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/hciemu.h"
 
 struct hciemu {
        int ref_count;
@@ -53,7 +55,7 @@ struct hciemu {
        guint host_source;
        guint master_source;
        guint client_source;
-       GList *post_command_hooks;
+       struct queue *post_command_hooks;
        char bdaddr_str[18];
 };
 
@@ -62,11 +64,27 @@ struct hciemu_command_hook {
        void *user_data;
 };
 
-static void destroy_command_hook(gpointer data, gpointer user_data)
+static void destroy_command_hook(void *data)
+{
+       struct hciemu_command_hook *hook = data;
+
+       free(hook);
+}
+
+struct run_data {
+       uint16_t opcode;
+       const void *data;
+       uint8_t len;
+};
+
+static void run_command_hook(void *data, void *user_data)
 {
        struct hciemu_command_hook *hook = data;
+       struct run_data *run_data = user_data;
 
-       g_free(hook);
+       if (hook->function)
+               hook->function(run_data->opcode, run_data->data,
+                                       run_data->len, hook->user_data);
 }
 
 static void master_command_callback(uint16_t opcode,
@@ -74,17 +92,12 @@ static void master_command_callback(uint16_t opcode,
                                btdev_callback callback, void *user_data)
 {
        struct hciemu *hciemu = user_data;
-       GList *list;
+       struct run_data run_data = { .opcode = opcode,
+                                               .data = data, .len = len };
 
        btdev_command_default(callback);
 
-       for (list = g_list_first(hciemu->post_command_hooks); list;
-                                               list = g_list_next(list)) {
-               struct hciemu_command_hook *hook = list->data;
-
-               if (hook->function)
-                       hook->function(opcode, data, len, hook->user_data);
-       }
+       queue_foreach(hciemu->post_command_hooks, run_command_hook, &run_data);
 }
 
 static void client_command_callback(uint16_t opcode,
@@ -216,6 +229,7 @@ static bool create_vhci(struct hciemu *hciemu)
 
        fd = open("/dev/vhci", O_RDWR | O_NONBLOCK | O_CLOEXEC);
        if (fd < 0) {
+               perror("Opening /dev/vhci failed");
                btdev_destroy(btdev);
                return false;
        }
@@ -291,7 +305,7 @@ struct hciemu *hciemu_new(enum hciemu_type type)
 {
        struct hciemu *hciemu;
 
-       hciemu = g_try_new0(struct hciemu, 1);
+       hciemu = new0(struct hciemu, 1);
        if (!hciemu)
                return NULL;
 
@@ -309,15 +323,23 @@ struct hciemu *hciemu_new(enum hciemu_type type)
                return NULL;
        }
 
+       hciemu->post_command_hooks = queue_new();
+       if (!hciemu->post_command_hooks) {
+               free(hciemu);
+               return NULL;
+       }
+
        if (!create_vhci(hciemu)) {
-               g_free(hciemu);
+               queue_destroy(hciemu->post_command_hooks, NULL);
+               free(hciemu);
                return NULL;
        }
 
        if (!create_stack(hciemu)) {
                g_source_remove(hciemu->master_source);
                btdev_destroy(hciemu->master_dev);
-               g_free(hciemu);
+               queue_destroy(hciemu->post_command_hooks, NULL);
+               free(hciemu);
                return NULL;
        }
 
@@ -341,11 +363,10 @@ void hciemu_unref(struct hciemu *hciemu)
        if (!hciemu)
                return;
 
-       if (__sync_sub_and_fetch(&hciemu->ref_count, 1) > 0)
+       if (__sync_sub_and_fetch(&hciemu->ref_count, 1))
                return;
 
-       g_list_foreach(hciemu->post_command_hooks, destroy_command_hook, NULL);
-       g_list_free(hciemu->post_command_hooks);
+       queue_destroy(hciemu->post_command_hooks, destroy_command_hook);
 
        bthost_stop(hciemu->host_stack);
 
@@ -357,7 +378,7 @@ void hciemu_unref(struct hciemu *hciemu)
        btdev_destroy(hciemu->client_dev);
        btdev_destroy(hciemu->master_dev);
 
-       g_free(hciemu);
+       free(hciemu);
 }
 
 const char *hciemu_get_address(struct hciemu *hciemu)
@@ -405,15 +426,17 @@ bool hciemu_add_master_post_command_hook(struct hciemu *hciemu,
        if (!hciemu)
                return false;
 
-       hook = g_try_new0(struct hciemu_command_hook, 1);
+       hook = new0(struct hciemu_command_hook, 1);
        if (!hook)
                return false;
 
        hook->function = function;
        hook->user_data = user_data;
 
-       hciemu->post_command_hooks = g_list_append(hciemu->post_command_hooks,
-                                                                       hook);
+       if (!queue_push_tail(hciemu->post_command_hooks, hook)) {
+               free(hook);
+               return false;
+       }
 
        return true;
 }
index d17eaf7..d948867 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/shared/hfp.c b/src/shared/hfp.c
new file mode 100644 (file)
index 0000000..36c8c3e
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "src/shared/util.h"
+#include "src/shared/ringbuf.h"
+#include "src/shared/queue.h"
+#include "src/shared/io.h"
+#include "src/shared/hfp.h"
+
+struct hfp_gw {
+       int ref_count;
+       int fd;
+       bool close_on_unref;
+       struct io *io;
+       struct ringbuf *read_buf;
+       struct ringbuf *write_buf;
+       struct queue *cmd_handlers;
+       bool writer_active;
+       bool permissive_syntax;
+       bool result_pending;
+       hfp_command_func_t command_callback;
+       hfp_destroy_func_t command_destroy;
+       void *command_data;
+       hfp_debug_func_t debug_callback;
+       hfp_destroy_func_t debug_destroy;
+       void *debug_data;
+
+       hfp_disconnect_func_t disconnect_callback;
+       hfp_destroy_func_t disconnect_destroy;
+       void *disconnect_data;
+
+       bool in_disconnect;
+       bool destroyed;
+};
+
+struct cmd_handler {
+       char *prefix;
+       void *user_data;
+       hfp_destroy_func_t destroy;
+       hfp_result_func_t callback;
+};
+
+struct hfp_gw_result {
+       const char *data;
+       unsigned int offset;
+};
+
+static void destroy_cmd_handler(void *data)
+{
+       struct cmd_handler *handler = data;
+
+       if (handler->destroy)
+               handler->destroy(handler->user_data);
+
+       free(handler->prefix);
+
+       free(handler);
+}
+
+static bool match_handler_prefix(const void *a, const void *b)
+{
+       const struct cmd_handler *handler = a;
+       const char *prefix = b;
+
+       if (strlen(handler->prefix) != strlen(prefix))
+               return false;
+
+       if (memcmp(handler->prefix, prefix, strlen(prefix)))
+               return false;
+
+       return true;
+}
+
+static void write_watch_destroy(void *user_data)
+{
+       struct hfp_gw *hfp = user_data;
+
+       hfp->writer_active = false;
+}
+
+static bool can_write_data(struct io *io, void *user_data)
+{
+       struct hfp_gw *hfp = user_data;
+       ssize_t bytes_written;
+
+       bytes_written = ringbuf_write(hfp->write_buf, hfp->fd);
+       if (bytes_written < 0)
+               return false;
+
+       if (ringbuf_len(hfp->write_buf) > 0)
+               return true;
+
+       return false;
+}
+
+static void wakeup_writer(struct hfp_gw *hfp)
+{
+       if (hfp->writer_active)
+               return;
+
+       if (!ringbuf_len(hfp->write_buf))
+               return;
+
+       if (!io_set_write_handler(hfp->io, can_write_data,
+                                       hfp, write_watch_destroy))
+               return;
+
+       hfp->writer_active = true;
+}
+
+static void skip_whitespace(struct hfp_gw_result *result)
+{
+       while (result->data[result->offset] == ' ')
+               result->offset++;
+}
+
+static bool call_prefix_handler(struct hfp_gw *hfp, const char *data)
+{
+       struct cmd_handler *handler;
+       const char *separators = ";?=\0";
+       struct hfp_gw_result result;
+       enum hfp_gw_cmd_type type;
+       char lookup_prefix[18];
+       uint8_t pref_len = 0;
+       const char *prefix;
+       int i;
+
+       result.offset = 0;
+       result.data = data;
+
+       skip_whitespace(&result);
+
+       if (strlen(data + result.offset) < 3)
+               return false;
+
+       if (strncmp(data + result.offset, "AT", 2))
+               if (strncmp(data + result.offset, "at", 2))
+                       return false;
+
+       result.offset += 2;
+       prefix = data + result.offset;
+
+       if (isalpha(prefix[0])) {
+               lookup_prefix[pref_len++] = toupper(prefix[0]);
+       } else {
+               pref_len = strcspn(prefix, separators);
+               if (pref_len > 17 || pref_len < 2)
+                       return false;
+
+               for (i = 0; i < pref_len; i++)
+                       lookup_prefix[i] = toupper(prefix[i]);
+       }
+
+       lookup_prefix[pref_len] = '\0';
+       result.offset += pref_len;
+
+       if (lookup_prefix[0] == 'D') {
+               type = HFP_GW_CMD_TYPE_SET;
+               goto done;
+       }
+
+       if (data[result.offset] == '=') {
+               result.offset++;
+               if (data[result.offset] == '?') {
+                       result.offset++;
+                       type = HFP_GW_CMD_TYPE_TEST;
+               } else {
+                       type = HFP_GW_CMD_TYPE_SET;
+               }
+               goto done;
+       }
+
+       if (data[result.offset] == '?') {
+               result.offset++;
+               type = HFP_GW_CMD_TYPE_READ;
+               goto done;
+       }
+
+       type = HFP_GW_CMD_TYPE_COMMAND;
+
+done:
+
+       handler = queue_find(hfp->cmd_handlers, match_handler_prefix,
+                                                               lookup_prefix);
+       if (!handler)
+               return false;
+
+       handler->callback(&result, type, handler->user_data);
+
+       return true;
+}
+
+static void next_field(struct hfp_gw_result *result)
+{
+       if (result->data[result->offset] == ',')
+               result->offset++;
+}
+
+bool hfp_gw_result_get_number_default(struct hfp_gw_result *result,
+                                               unsigned int *val,
+                                               unsigned int default_val)
+{
+       skip_whitespace(result);
+
+       if (result->data[result->offset] == ',') {
+               if (val)
+                       *val = default_val;
+
+               result->offset++;
+               return true;
+       }
+
+       return hfp_gw_result_get_number(result, val);
+}
+
+bool hfp_gw_result_get_number(struct hfp_gw_result *result, unsigned int *val)
+{
+       unsigned int i;
+       int tmp = 0;
+
+       skip_whitespace(result);
+
+       i = result->offset;
+
+       while (result->data[i] >= '0' && result->data[i] <= '9')
+               tmp = tmp * 10 + result->data[i++] - '0';
+
+       if (i == result->offset)
+               return false;
+
+       if (val)
+               *val = tmp;
+       result->offset = i;
+
+       skip_whitespace(result);
+       next_field(result);
+
+       return true;
+}
+
+bool hfp_gw_result_open_container(struct hfp_gw_result *result)
+{
+       skip_whitespace(result);
+
+       /* The list shall be preceded by a left parenthesis "(") */
+       if (result->data[result->offset] != '(')
+               return false;
+
+       result->offset++;
+
+       return true;
+}
+
+bool hfp_gw_result_close_container(struct hfp_gw_result *result)
+{
+       skip_whitespace(result);
+
+       /* The list shall be followed by a right parenthesis (")" V250 5.7.3.1*/
+       if (result->data[result->offset] != ')')
+               return false;
+
+       result->offset++;
+
+       return true;
+}
+
+bool hfp_gw_result_get_string(struct hfp_gw_result *result, char *buf,
+                                                               uint8_t len)
+{
+       int i = 0;
+       const char *data = result->data;
+       unsigned int offset;
+
+       skip_whitespace(result);
+
+       if (data[result->offset] != '"')
+               return false;
+
+       offset = result->offset;
+       offset++;
+
+       while (data[offset] != '\0' && data[offset] != '"') {
+               if (i == len)
+                       return false;
+
+               buf[i++] = data[offset];
+               offset++;
+       }
+
+       if (i == len)
+               return false;
+
+       buf[i] = '\0';
+
+       if (data[offset] == '"')
+               offset++;
+       else
+               return false;
+
+       result->offset = offset;
+
+       skip_whitespace(result);
+       next_field(result);
+
+       return true;
+}
+
+bool hfp_gw_result_get_unquoted_string(struct hfp_gw_result *result, char *buf,
+                                                               uint8_t len)
+{
+       const char *data = result->data;
+       unsigned int offset;
+       int i = 0;
+       char c;
+
+       skip_whitespace(result);
+
+       c = data[result->offset];
+       if (c == '"' || c == ')' || c == '(')
+               return false;
+
+       offset = result->offset;
+
+       while (data[offset] != '\0' && data[offset] != ',' &&
+                                                       data[offset] != ')') {
+               if (i == len)
+                       return false;
+
+               buf[i++] = data[offset];
+               offset++;
+       }
+
+       if (i == len)
+               return false;
+
+       buf[i] = '\0';
+
+       result->offset = offset;
+
+       next_field(result);
+
+       return true;
+}
+
+bool hfp_gw_result_has_next(struct hfp_gw_result *result)
+{
+       return result->data[result->offset] != '\0';
+}
+
+static void process_input(struct hfp_gw *hfp)
+{
+       char *str, *ptr;
+       size_t len, count;
+
+       str = ringbuf_peek(hfp->read_buf, 0, &len);
+       if (!str)
+               return;
+
+       ptr = memchr(str, '\r', len);
+       if (!ptr) {
+               char *str2;
+               size_t len2;
+
+               /* If there is no more data in ringbuffer,
+                * it's just an incomplete command.
+                */
+               if (len == ringbuf_len(hfp->read_buf))
+                       return;
+
+               str2 = ringbuf_peek(hfp->read_buf, len, &len2);
+               if (!str2)
+                       return;
+
+               ptr = memchr(str2, '\r', len2);
+               if (!ptr)
+                       return;
+
+               *ptr = '\0';
+               count = asprintf(&ptr, "%s%s", str, str2);
+               str = ptr;
+       } else {
+               count = ptr - str;
+               *ptr = '\0';
+       }
+
+       hfp->result_pending = true;
+
+       if (!call_prefix_handler(hfp, str)) {
+               if (hfp->command_callback)
+                       hfp->command_callback(str, hfp->command_data);
+               else
+                       hfp_gw_send_result(hfp, HFP_RESULT_ERROR);
+       }
+
+       len = ringbuf_drain(hfp->read_buf, count + 1);
+
+       if (str == ptr)
+               free(ptr);
+}
+
+static void read_watch_destroy(void *user_data)
+{
+}
+
+static bool can_read_data(struct io *io, void *user_data)
+{
+       struct hfp_gw *hfp = user_data;
+       ssize_t bytes_read;
+
+       bytes_read = ringbuf_read(hfp->read_buf, hfp->fd);
+       if (bytes_read < 0)
+               return false;
+
+       if (hfp->result_pending)
+               return true;
+
+       process_input(hfp);
+
+       return true;
+}
+
+struct hfp_gw *hfp_gw_new(int fd)
+{
+       struct hfp_gw *hfp;
+
+       if (fd < 0)
+               return NULL;
+
+       hfp = new0(struct hfp_gw, 1);
+       if (!hfp)
+               return NULL;
+
+       hfp->fd = fd;
+       hfp->close_on_unref = false;
+
+       hfp->read_buf = ringbuf_new(4096);
+       if (!hfp->read_buf) {
+               free(hfp);
+               return NULL;
+       }
+
+       hfp->write_buf = ringbuf_new(4096);
+       if (!hfp->write_buf) {
+               ringbuf_free(hfp->read_buf);
+               free(hfp);
+               return NULL;
+       }
+
+       hfp->io = io_new(fd);
+       if (!hfp->io) {
+               ringbuf_free(hfp->write_buf);
+               ringbuf_free(hfp->read_buf);
+               free(hfp);
+               return NULL;
+       }
+
+       hfp->cmd_handlers = queue_new();
+       if (!hfp->cmd_handlers) {
+               io_destroy(hfp->io);
+               ringbuf_free(hfp->write_buf);
+               ringbuf_free(hfp->read_buf);
+               free(hfp);
+               return NULL;
+       }
+
+       if (!io_set_read_handler(hfp->io, can_read_data,
+                                       hfp, read_watch_destroy)) {
+               queue_destroy(hfp->cmd_handlers,
+                                               destroy_cmd_handler);
+               io_destroy(hfp->io);
+               ringbuf_free(hfp->write_buf);
+               ringbuf_free(hfp->read_buf);
+               free(hfp);
+               return NULL;
+       }
+
+       hfp->writer_active = false;
+       hfp->permissive_syntax = false;
+       hfp->result_pending = false;
+
+       return hfp_gw_ref(hfp);
+}
+
+struct hfp_gw *hfp_gw_ref(struct hfp_gw *hfp)
+{
+       if (!hfp)
+               return NULL;
+
+       __sync_fetch_and_add(&hfp->ref_count, 1);
+
+       return hfp;
+}
+
+void hfp_gw_unref(struct hfp_gw *hfp)
+{
+       if (!hfp)
+               return;
+
+       if (__sync_sub_and_fetch(&hfp->ref_count, 1))
+               return;
+
+       hfp_gw_set_command_handler(hfp, NULL, NULL, NULL);
+
+       io_set_write_handler(hfp->io, NULL, NULL, NULL);
+       io_set_read_handler(hfp->io, NULL, NULL, NULL);
+       io_set_disconnect_handler(hfp->io, NULL, NULL, NULL);
+
+       io_destroy(hfp->io);
+       hfp->io = NULL;
+
+       if (hfp->close_on_unref)
+               close(hfp->fd);
+
+       hfp_gw_set_debug(hfp, NULL, NULL, NULL);
+
+       ringbuf_free(hfp->read_buf);
+       hfp->read_buf = NULL;
+
+       ringbuf_free(hfp->write_buf);
+       hfp->write_buf = NULL;
+
+       queue_destroy(hfp->cmd_handlers, destroy_cmd_handler);
+       hfp->cmd_handlers = NULL;
+
+       if (!hfp->in_disconnect) {
+               free(hfp);
+               return;
+       }
+
+       hfp->destroyed = true;
+}
+
+static void read_tracing(const void *buf, size_t count, void *user_data)
+{
+       struct hfp_gw *hfp = user_data;
+
+       util_hexdump('>', buf, count, hfp->debug_callback, hfp->debug_data);
+}
+
+static void write_tracing(const void *buf, size_t count, void *user_data)
+{
+       struct hfp_gw *hfp = user_data;
+
+       util_hexdump('<', buf, count, hfp->debug_callback, hfp->debug_data);
+}
+
+bool hfp_gw_set_debug(struct hfp_gw *hfp, hfp_debug_func_t callback,
+                               void *user_data, hfp_destroy_func_t destroy)
+{
+       if (!hfp)
+               return false;
+
+       if (hfp->debug_destroy)
+               hfp->debug_destroy(hfp->debug_data);
+
+       hfp->debug_callback = callback;
+       hfp->debug_destroy = destroy;
+       hfp->debug_data = user_data;
+
+       if (hfp->debug_callback) {
+               ringbuf_set_input_tracing(hfp->read_buf, read_tracing, hfp);
+               ringbuf_set_input_tracing(hfp->write_buf, write_tracing, hfp);
+       } else {
+               ringbuf_set_input_tracing(hfp->read_buf, NULL, NULL);
+               ringbuf_set_input_tracing(hfp->write_buf, NULL, NULL);
+       }
+
+       return true;
+}
+
+bool hfp_gw_set_close_on_unref(struct hfp_gw *hfp, bool do_close)
+{
+       if (!hfp)
+               return false;
+
+       hfp->close_on_unref = do_close;
+
+       return true;
+}
+
+bool hfp_gw_set_permissive_syntax(struct hfp_gw *hfp, bool permissive)
+{
+       if (!hfp)
+               return false;
+
+       hfp->permissive_syntax = permissive;
+
+       return true;
+}
+
+bool hfp_gw_send_result(struct hfp_gw *hfp, enum hfp_result result)
+{
+       const char *str;
+
+       if (!hfp)
+               return false;
+
+       switch (result) {
+       case HFP_RESULT_OK:
+               str = "OK";
+               break;
+       case HFP_RESULT_ERROR:
+               str = "ERROR";
+               break;
+       default:
+               return false;
+       }
+
+       if (ringbuf_printf(hfp->write_buf, "\r\n%s\r\n", str) < 0)
+               return false;
+
+       wakeup_writer(hfp);
+
+       hfp->result_pending = false;
+
+       return true;
+}
+
+bool hfp_gw_send_error(struct hfp_gw *hfp, enum hfp_error error)
+{
+       if (!hfp)
+               return false;
+
+       if (ringbuf_printf(hfp->write_buf, "\r\n+CME ERROR: %u\r\n", error) < 0)
+               return false;
+
+       wakeup_writer(hfp);
+
+       hfp->result_pending = false;
+
+       return true;
+}
+
+bool hfp_gw_send_info(struct hfp_gw *hfp, const char *format, ...)
+{
+       va_list ap;
+       char *fmt;
+       int len;
+
+       if (!hfp || !format)
+               return false;
+
+       if (asprintf(&fmt, "\r\n%s\r\n", format) < 0)
+               return false;
+
+       va_start(ap, format);
+       len = ringbuf_vprintf(hfp->write_buf, fmt, ap);
+       va_end(ap);
+
+       free(fmt);
+
+       if (len < 0)
+               return false;
+
+       if (hfp->result_pending)
+               return true;
+
+       wakeup_writer(hfp);
+
+       return true;
+}
+
+bool hfp_gw_set_command_handler(struct hfp_gw *hfp,
+                               hfp_command_func_t callback,
+                               void *user_data, hfp_destroy_func_t destroy)
+{
+       if (!hfp)
+               return false;
+
+       if (hfp->command_destroy)
+               hfp->command_destroy(hfp->command_data);
+
+       hfp->command_callback = callback;
+       hfp->command_destroy = destroy;
+       hfp->command_data = user_data;
+
+       return true;
+}
+
+bool hfp_gw_register(struct hfp_gw *hfp, hfp_result_func_t callback,
+                                               const char *prefix,
+                                               void *user_data,
+                                               hfp_destroy_func_t destroy)
+{
+       struct cmd_handler *handler;
+
+       handler = new0(struct cmd_handler, 1);
+       if (!handler)
+               return false;
+
+       handler->callback = callback;
+       handler->user_data = user_data;
+
+       handler->prefix = strdup(prefix);
+       if (!handler->prefix) {
+               free(handler);
+               return false;
+       }
+
+       if (queue_find(hfp->cmd_handlers, match_handler_prefix,
+                                                       handler->prefix)) {
+               destroy_cmd_handler(handler);
+               return false;
+       }
+
+       handler->destroy = destroy;
+
+       return queue_push_tail(hfp->cmd_handlers, handler);
+}
+
+bool hfp_gw_unregister(struct hfp_gw *hfp, const char *prefix)
+{
+       struct cmd_handler *handler;
+       char *lookup_prefix;
+
+       lookup_prefix = strdup(prefix);
+       if (!lookup_prefix)
+               return false;
+
+       handler = queue_remove_if(hfp->cmd_handlers, match_handler_prefix,
+                                                               lookup_prefix);
+       free(lookup_prefix);
+
+       if (!handler)
+               return false;
+
+       destroy_cmd_handler(handler);
+
+       return true;
+}
+
+static void disconnect_watch_destroy(void *user_data)
+{
+       struct hfp_gw *hfp = user_data;
+
+       if (hfp->disconnect_destroy)
+               hfp->disconnect_destroy(hfp->disconnect_data);
+
+       if (hfp->destroyed)
+               free(hfp);
+}
+
+static bool io_disconnected(struct io *io, void *user_data)
+{
+       struct hfp_gw *hfp = user_data;
+
+       hfp->in_disconnect = true;
+
+       if (hfp->disconnect_callback)
+               hfp->disconnect_callback(hfp->disconnect_data);
+
+       hfp->in_disconnect = false;
+
+       return false;
+}
+
+bool hfp_gw_set_disconnect_handler(struct hfp_gw *hfp,
+                                       hfp_disconnect_func_t callback,
+                                       void *user_data,
+                                       hfp_destroy_func_t destroy)
+{
+       if (!hfp)
+               return false;
+
+       if (hfp->disconnect_destroy)
+               hfp->disconnect_destroy(hfp->disconnect_data);
+
+       if (!io_set_disconnect_handler(hfp->io, io_disconnected, hfp,
+                                               disconnect_watch_destroy)) {
+               hfp->disconnect_callback = NULL;
+               hfp->disconnect_destroy = NULL;
+               hfp->disconnect_data = NULL;
+               return false;
+       }
+
+       hfp->disconnect_callback = callback;
+       hfp->disconnect_destroy = destroy;
+       hfp->disconnect_data = user_data;
+
+       return true;
+}
+
+bool hfp_gw_disconnect(struct hfp_gw *hfp)
+{
+       if (!hfp)
+               return false;
+
+       return io_shutdown(hfp->io);
+}
diff --git a/src/shared/hfp.h b/src/shared/hfp.h
new file mode 100644 (file)
index 0000000..743db65
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdbool.h>
+
+enum hfp_result {
+       HFP_RESULT_OK           = 0,
+       HFP_RESULT_CONNECT      = 1,
+       HFP_RESULT_RING         = 2,
+       HFP_RESULT_NO_CARRIER   = 3,
+       HFP_RESULT_ERROR        = 4,
+       HFP_RESULT_NO_DIALTONE  = 6,
+       HFP_RESULT_BUSY         = 7,
+       HFP_RESULT_NO_ANSWER    = 8,
+};
+
+enum hfp_error {
+       HFP_ERROR_AG_FAILURE                    = 0,
+       HFP_ERROR_NO_CONNECTION_TO_PHONE        = 1,
+       HFP_ERROR_OPERATION_NOT_ALLOWED         = 3,
+       HFP_ERROR_OPERATION_NOT_SUPPORTED       = 4,
+       HFP_ERROR_PH_SIM_PIN_REQUIRED           = 5,
+       HFP_ERROR_SIM_NOT_INSERTED              = 10,
+       HFP_ERROR_SIM_PIN_REQUIRED              = 11,
+       HFP_ERROR_SIM_PUK_REQUIRED              = 12,
+       HFP_ERROR_SIM_FAILURE                   = 13,
+       HFP_ERROR_SIM_BUSY                      = 14,
+       HFP_ERROR_INCORRECT_PASSWORD            = 16,
+       HFP_ERROR_SIM_PIN2_REQUIRED             = 17,
+       HFP_ERROR_SIM_PUK2_REQUIRED             = 18,
+       HFP_ERROR_MEMORY_FULL                   = 20,
+       HFP_ERROR_INVALID_INDEX                 = 21,
+       HFP_ERROR_MEMORY_FAILURE                = 23,
+       HFP_ERROR_TEXT_STRING_TOO_LONG          = 24,
+       HFP_ERROR_INVALID_CHARS_IN_TEXT_STRING  = 25,
+       HFP_ERROR_DIAL_STRING_TO_LONG           = 26,
+       HFP_ERROR_INVALID_CHARS_IN_DIAL_STRING  = 27,
+       HFP_ERROR_NO_NETWORK_SERVICE            = 30,
+       HFP_ERROR_NETWORK_TIMEOUT               = 31,
+       HFP_ERROR_NETWORK_NOT_ALLOWED           = 32,
+};
+
+enum hfp_gw_cmd_type {
+       HFP_GW_CMD_TYPE_READ,
+       HFP_GW_CMD_TYPE_SET,
+       HFP_GW_CMD_TYPE_TEST,
+       HFP_GW_CMD_TYPE_COMMAND
+};
+
+struct hfp_gw_result;
+
+typedef void (*hfp_result_func_t)(struct hfp_gw_result *result,
+                               enum hfp_gw_cmd_type type, void *user_data);
+
+typedef void (*hfp_destroy_func_t)(void *user_data);
+typedef void (*hfp_debug_func_t)(const char *str, void *user_data);
+
+typedef void (*hfp_command_func_t)(const char *command, void *user_data);
+typedef void (*hfp_disconnect_func_t)(void *user_data);
+
+struct hfp_gw;
+
+struct hfp_gw *hfp_gw_new(int fd);
+
+struct hfp_gw *hfp_gw_ref(struct hfp_gw *hfp);
+void hfp_gw_unref(struct hfp_gw *hfp);
+
+bool hfp_gw_set_debug(struct hfp_gw *hfp, hfp_debug_func_t callback,
+                               void *user_data, hfp_destroy_func_t destroy);
+
+bool hfp_gw_set_close_on_unref(struct hfp_gw *hfp, bool do_close);
+bool hfp_gw_set_permissive_syntax(struct hfp_gw *hfp, bool permissive);
+
+bool hfp_gw_send_result(struct hfp_gw *hfp, enum hfp_result result);
+bool hfp_gw_send_error(struct hfp_gw *hfp, enum hfp_error error);
+bool hfp_gw_send_info(struct hfp_gw *hfp, const char *format, ...)
+                                       __attribute__((format(printf, 2, 3)));
+
+bool hfp_gw_set_command_handler(struct hfp_gw *hfp,
+                               hfp_command_func_t callback,
+                               void *user_data, hfp_destroy_func_t destroy);
+
+bool hfp_gw_set_disconnect_handler(struct hfp_gw *hfp,
+                                       hfp_disconnect_func_t callback,
+                                       void *user_data,
+                                       hfp_destroy_func_t destroy);
+
+bool hfp_gw_disconnect(struct hfp_gw *hfp);
+
+bool hfp_gw_register(struct hfp_gw *hfp, hfp_result_func_t callback,
+                                               const char *prefix,
+                                               void *user_data,
+                                               hfp_destroy_func_t destroy);
+bool hfp_gw_unregister(struct hfp_gw *hfp, const char *prefix);
+
+bool hfp_gw_result_get_number(struct hfp_gw_result *result, unsigned int *val);
+bool hfp_gw_result_get_number_default(struct hfp_gw_result *result,
+                                               unsigned int *val,
+                                               unsigned int default_val);
+bool hfp_gw_result_open_container(struct hfp_gw_result *result);
+bool hfp_gw_result_close_container(struct hfp_gw_result *result);
+bool hfp_gw_result_get_string(struct hfp_gw_result *result, char *buf,
+                                                               uint8_t len);
+bool hfp_gw_result_get_unquoted_string(struct hfp_gw_result *result, char *buf,
+                                                               uint8_t len);
+bool hfp_gw_result_has_next(struct hfp_gw_result *result);
diff --git a/src/shared/io-glib.c b/src/shared/io-glib.c
new file mode 100644 (file)
index 0000000..6316037
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "src/shared/io.h"
+
+struct io {
+       int ref_count;
+       GIOChannel *channel;
+       guint read_watch;
+       io_callback_func_t read_callback;
+       io_destroy_func_t read_destroy;
+       void *read_data;
+       guint write_watch;
+       io_callback_func_t write_callback;
+       io_destroy_func_t write_destroy;
+       void *write_data;
+       guint disconnect_watch;
+       io_callback_func_t disconnect_callback;
+       io_destroy_func_t disconnect_destroy;
+       void *disconnect_data;
+};
+
+static struct io *io_ref(struct io *io)
+{
+       if (!io)
+               return NULL;
+
+       __sync_fetch_and_add(&io->ref_count, 1);
+
+       return io;
+}
+
+static void io_unref(struct io *io)
+{
+       if (!io)
+               return;
+
+       if (__sync_sub_and_fetch(&io->ref_count, 1))
+               return;
+
+       g_free(io);
+}
+
+struct io *io_new(int fd)
+{
+       struct io *io;
+
+       if (fd < 0)
+               return NULL;
+
+       io = g_try_new0(struct io, 1);
+       if (!io)
+               return NULL;
+
+       io->channel = g_io_channel_unix_new(fd);
+
+       g_io_channel_set_encoding(io->channel, NULL, NULL);
+       g_io_channel_set_buffered(io->channel, FALSE);
+
+       g_io_channel_set_close_on_unref(io->channel, FALSE);
+
+       io->read_watch = 0;
+       io->read_callback = NULL;
+       io->read_destroy = NULL;
+       io->read_data = NULL;
+
+       io->write_watch = 0;
+       io->write_callback = NULL;
+       io->write_destroy = NULL;
+       io->write_data = NULL;
+
+       return io_ref(io);
+}
+
+void io_destroy(struct io *io)
+{
+       if (!io)
+               return;
+
+       if (io->read_watch > 0) {
+               g_source_remove(io->read_watch);
+               io->read_watch = 0;
+       }
+
+       if (io->write_watch > 0) {
+               g_source_remove(io->write_watch);
+               io->write_watch = 0;
+       }
+
+       g_io_channel_unref(io->channel);
+       io->channel = NULL;
+
+       io_unref(io);
+}
+
+int io_get_fd(struct io *io)
+{
+       if (!io)
+               return -1;
+
+       return g_io_channel_unix_get_fd(io->channel);
+}
+
+bool io_set_close_on_destroy(struct io *io, bool do_close)
+{
+       if (!io)
+               return false;
+
+       if (do_close)
+               g_io_channel_set_close_on_unref(io->channel, TRUE);
+       else
+               g_io_channel_set_close_on_unref(io->channel, FALSE);
+
+       return true;
+}
+
+static void read_watch_destroy(gpointer user_data)
+{
+       struct io *io = user_data;
+
+       if (io->read_destroy)
+               io->read_destroy(io->read_data);
+
+       io->read_watch = 0;
+       io->read_callback = NULL;
+       io->read_destroy = NULL;
+       io->read_data = NULL;
+
+       io_unref(io);
+}
+
+static gboolean read_callback(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct io *io = user_data;
+       bool result;
+
+       if (cond & (G_IO_ERR | G_IO_NVAL))
+               return FALSE;
+
+       if (io->read_callback)
+               result = io->read_callback(io, io->read_data);
+       else
+               result = false;
+
+       return result ? TRUE : FALSE;
+}
+
+bool io_set_read_handler(struct io *io, io_callback_func_t callback,
+                               void *user_data, io_destroy_func_t destroy)
+{
+       if (!io)
+               return false;
+
+       if (io->read_watch > 0) {
+               g_source_remove(io->read_watch);
+               io->read_watch = 0;
+       }
+
+       if (!callback)
+               goto done;
+
+       io->read_watch = g_io_add_watch_full(io->channel, G_PRIORITY_DEFAULT,
+                                               G_IO_IN | G_IO_ERR | G_IO_NVAL,
+                                               read_callback, io_ref(io),
+                                               read_watch_destroy);
+       if (io->read_watch == 0)
+               return false;
+
+       io->read_destroy = destroy;
+       io->read_data = user_data;
+
+done:
+       io->read_callback = callback;
+
+       return true;
+}
+
+static void write_watch_destroy(gpointer user_data)
+{
+       struct io *io = user_data;
+
+       if (io->write_destroy)
+               io->write_destroy(io->write_data);
+
+       io->write_watch = 0;
+       io->write_callback = NULL;
+       io->write_destroy = NULL;
+       io->write_data = NULL;
+
+       io_unref(io);
+}
+
+static gboolean write_callback(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct io *io = user_data;
+       bool result;
+
+       if (cond & (G_IO_ERR | G_IO_NVAL))
+               return FALSE;
+
+       if (io->write_callback)
+               result = io->write_callback(io, io->write_data);
+       else
+               result = false;
+
+       return result ? TRUE : FALSE;
+}
+
+bool io_set_write_handler(struct io *io, io_callback_func_t callback,
+                               void *user_data, io_destroy_func_t destroy)
+{
+       if (!io)
+               return false;
+
+       if (io->write_watch > 0) {
+               g_source_remove(io->write_watch);
+               io->write_watch = 0;
+       }
+
+       if (!callback)
+               goto done;
+
+       io->write_watch = g_io_add_watch_full(io->channel, G_PRIORITY_DEFAULT,
+                                               G_IO_OUT | G_IO_ERR | G_IO_NVAL,
+                                               write_callback, io_ref(io),
+                                               write_watch_destroy);
+       if (io->write_watch == 0)
+               return false;
+
+       io->write_destroy = destroy;
+       io->write_data = user_data;
+
+done:
+       io->write_callback = callback;
+
+       return true;
+}
+
+static void disconnect_watch_destroy(gpointer user_data)
+{
+       struct io *io = user_data;
+
+       if (io->disconnect_destroy)
+               io->disconnect_destroy(io->disconnect_data);
+
+       io->disconnect_watch = 0;
+       io->disconnect_callback = NULL;
+       io->disconnect_destroy = NULL;
+       io->disconnect_data = NULL;
+
+       io_unref(io);
+}
+
+static gboolean disconnect_callback(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct io *io = user_data;
+       bool result;
+
+       if (io->disconnect_callback)
+               result = io->disconnect_callback(io, io->disconnect_data);
+       else
+               result = false;
+
+       return result ? TRUE : FALSE;
+}
+
+bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
+                               void *user_data, io_destroy_func_t destroy)
+{
+       if (!io)
+               return false;
+
+       if (io->disconnect_watch > 0) {
+               g_source_remove(io->disconnect_watch);
+               io->disconnect_watch = 0;
+       }
+
+       if (!callback)
+               goto done;
+
+       io->disconnect_watch = g_io_add_watch_full(io->channel,
+                                               G_PRIORITY_DEFAULT,
+                                               G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                               disconnect_callback, io_ref(io),
+                                               disconnect_watch_destroy);
+       if (io->disconnect_watch == 0)
+               return false;
+
+       io->disconnect_destroy = destroy;
+       io->disconnect_data = user_data;
+
+done:
+       io->disconnect_callback = callback;
+
+       return true;
+}
+
+bool io_shutdown(struct io *io)
+{
+       if (!io || !io->channel)
+               return false;
+
+       return g_io_channel_shutdown(io->channel, TRUE, NULL)
+                                                       == G_IO_STATUS_NORMAL;
+}
diff --git a/src/shared/io-mainloop.c b/src/shared/io-mainloop.c
new file mode 100644 (file)
index 0000000..3e33d88
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include "monitor/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/io.h"
+
+struct io {
+       int ref_count;
+       int fd;
+       uint32_t events;
+       bool close_on_destroy;
+       io_callback_func_t read_callback;
+       io_destroy_func_t read_destroy;
+       void *read_data;
+       io_callback_func_t write_callback;
+       io_destroy_func_t write_destroy;
+       void *write_data;
+       io_callback_func_t disconnect_callback;
+       io_destroy_func_t disconnect_destroy;
+       void *disconnect_data;
+};
+
+static struct io *io_ref(struct io *io)
+{
+       if (!io)
+               return NULL;
+
+       __sync_fetch_and_add(&io->ref_count, 1);
+
+       return io;
+}
+
+static void io_unref(struct io *io)
+{
+       if (!io)
+               return;
+
+       if (__sync_sub_and_fetch(&io->ref_count, 1))
+               return;
+
+       free(io);
+}
+
+static void io_cleanup(void *user_data)
+{
+       struct io *io = user_data;
+
+       if (io->write_destroy)
+               io->write_destroy(io->write_data);
+
+       if (io->read_destroy)
+               io->read_destroy(io->read_data);
+
+       if (io->disconnect_destroy)
+               io->disconnect_destroy(io->disconnect_data);
+
+       if (io->close_on_destroy)
+               close(io->fd);
+
+       io->fd = -1;
+}
+
+static void io_callback(int fd, uint32_t events, void *user_data)
+{
+       struct io *io = user_data;
+
+       if ((events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR))) {
+               io->read_callback = NULL;
+               io->write_callback = NULL;
+
+               if (!io->disconnect_callback) {
+                       mainloop_remove_fd(io->fd);
+                       return;
+               }
+
+               if (!io->disconnect_callback(io, io->disconnect_data)) {
+                       if (io->disconnect_destroy)
+                               io->disconnect_destroy(io->disconnect_data);
+
+                       io->disconnect_callback = NULL;
+                       io->disconnect_destroy = NULL;
+                       io->disconnect_data = NULL;
+
+                       io->events &= ~EPOLLRDHUP;
+
+                       mainloop_modify_fd(io->fd, io->events);
+               }
+       }
+
+       if ((events & EPOLLIN) && io->read_callback) {
+               if (!io->read_callback(io, io->read_data)) {
+                       if (io->read_destroy)
+                               io->read_destroy(io->read_data);
+
+                       io->read_callback = NULL;
+                       io->read_destroy = NULL;
+                       io->read_data = NULL;
+
+                       io->events &= ~EPOLLIN;
+
+                       mainloop_modify_fd(io->fd, io->events);
+               }
+       }
+
+       if ((events & EPOLLOUT) && io->write_callback) {
+               if (!io->write_callback(io, io->write_data)) {
+                       if (io->write_destroy)
+                               io->write_destroy(io->write_data);
+
+                       io->write_callback = NULL;
+                       io->write_destroy = NULL;
+                       io->write_data = NULL;
+
+                       io->events &= ~EPOLLOUT;
+
+                       mainloop_modify_fd(io->fd, io->events);
+               }
+       }
+}
+
+struct io *io_new(int fd)
+{
+       struct io *io;
+
+       if (fd < 0)
+               return NULL;
+
+       io = new0(struct io, 1);
+       if (!io)
+               return NULL;
+
+       io->fd = fd;
+       io->events = 0;
+       io->close_on_destroy = false;
+
+       if (mainloop_add_fd(io->fd, io->events, io_callback,
+                                               io, io_cleanup) < 0) {
+               free(io);
+               return NULL;
+       }
+
+       return io_ref(io);
+}
+
+void io_destroy(struct io *io)
+{
+       if (!io)
+               return;
+
+       io->read_callback = NULL;
+       io->write_callback = NULL;
+       io->disconnect_callback = NULL;
+
+       mainloop_remove_fd(io->fd);
+
+       io_unref(io);
+}
+
+int io_get_fd(struct io *io)
+{
+       if (!io)
+               return -1;
+
+       return io->fd;
+}
+
+bool io_set_close_on_destroy(struct io *io, bool do_close)
+{
+       if (!io)
+               return false;
+
+       io->close_on_destroy = do_close;
+
+       return true;
+}
+
+bool io_set_read_handler(struct io *io, io_callback_func_t callback,
+                               void *user_data, io_destroy_func_t destroy)
+{
+       uint32_t events;
+
+       if (!io || io->fd < 0)
+               return false;
+
+       if (io->read_destroy)
+               io->read_destroy(io->read_data);
+
+       if (callback)
+               events = io->events | EPOLLIN;
+       else
+               events = io->events & ~EPOLLIN;
+
+       io->read_callback = callback;
+       io->read_destroy = destroy;
+       io->read_data = user_data;
+
+       if (events == io->events)
+               return true;
+
+       if (mainloop_modify_fd(io->fd, events) < 0)
+               return false;
+
+       io->events = events;
+
+       return true;
+}
+
+bool io_set_write_handler(struct io *io, io_callback_func_t callback,
+                               void *user_data, io_destroy_func_t destroy)
+{
+       uint32_t events;
+
+       if (!io || io->fd < 0)
+               return false;
+
+       if (io->write_destroy)
+               io->write_destroy(io->write_data);
+
+       if (callback)
+               events = io->events | EPOLLOUT;
+       else
+               events = io->events & ~EPOLLOUT;
+
+       io->write_callback = callback;
+       io->write_destroy = destroy;
+       io->write_data = user_data;
+
+       if (events == io->events)
+               return true;
+
+       if (mainloop_modify_fd(io->fd, events) < 0)
+               return false;
+
+       io->events = events;
+
+       return true;
+}
+
+bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
+                               void *user_data, io_destroy_func_t destroy)
+{
+       uint32_t events;
+
+       if (!io || io->fd < 0)
+               return false;
+
+       if (io->disconnect_destroy)
+               io->disconnect_destroy(io->disconnect_data);
+
+       if (callback)
+               events = io->events | EPOLLRDHUP;
+       else
+               events = io->events & ~EPOLLRDHUP;
+
+       io->disconnect_callback = callback;
+       io->disconnect_destroy = destroy;
+       io->disconnect_data = user_data;
+
+       if (events == io->events)
+               return true;
+
+       if (mainloop_modify_fd(io->fd, events) < 0)
+               return false;
+
+       io->events = events;
+
+       return true;
+}
+
+bool io_shutdown(struct io *io)
+{
+       if (!io || io->fd < 0)
+               return false;
+
+       return shutdown(io->fd, SHUT_RDWR) == 0;
+}
diff --git a/src/shared/io.h b/src/shared/io.h
new file mode 100644 (file)
index 0000000..8897964
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdbool.h>
+
+typedef void (*io_destroy_func_t)(void *data);
+
+struct io;
+
+struct io *io_new(int fd);
+void io_destroy(struct io *io);
+
+int io_get_fd(struct io *io);
+bool io_set_close_on_destroy(struct io *io, bool do_close);
+
+bool io_shutdown(struct io *io);
+
+typedef bool (*io_callback_func_t)(struct io *io, void *user_data);
+
+bool io_set_read_handler(struct io *io, io_callback_func_t callback,
+                               void *user_data, io_destroy_func_t destroy);
+bool io_set_write_handler(struct io *io, io_callback_func_t callback,
+                               void *user_data, io_destroy_func_t destroy);
+bool io_set_disconnect_handler(struct io *io, io_callback_func_t callback,
+                               void *user_data, io_destroy_func_t destroy);
index 2c79886..ae90b89 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
 #include <string.h>
 #include <errno.h>
 
-#include <glib.h>
-
 #include "lib/bluetooth.h"
 #include "lib/mgmt.h"
 #include "lib/hci.h"
 
+#include "src/shared/io.h"
+#include "src/shared/queue.h"
 #include "src/shared/util.h"
 #include "src/shared/mgmt.h"
 
@@ -43,16 +43,15 @@ struct mgmt {
        int ref_count;
        int fd;
        bool close_on_unref;
-       GIOChannel *io;
-       guint read_watch;
-       guint write_watch;
-       GQueue *request_queue;
-       GQueue *reply_queue;
-       GList *pending_list;
-       GList *notify_list;
-       GList *notify_destroyed;
+       struct io *io;
+       bool writer_active;
+       struct queue *request_queue;
+       struct queue *reply_queue;
+       struct queue *pending_list;
+       struct queue *notify_list;
        unsigned int next_request_id;
        unsigned int next_notify_id;
+       bool need_notify_cleanup;
        bool in_notify;
        bool destroyed;
        void *buf;
@@ -77,75 +76,103 @@ struct mgmt_notify {
        unsigned int id;
        uint16_t event;
        uint16_t index;
-       bool destroyed;
+       bool removed;
        mgmt_notify_func_t callback;
        mgmt_destroy_func_t destroy;
        void *user_data;
 };
 
-static void destroy_request(gpointer data, gpointer user_data)
+static void destroy_request(void *data)
 {
        struct mgmt_request *request = data;
 
        if (request->destroy)
                request->destroy(request->user_data);
 
-       g_free(request->buf);
-       g_free(request);
+       free(request->buf);
+       free(request);
 }
 
-static int compare_request_id(gconstpointer a, gconstpointer b)
+static bool match_request_id(const void *a, const void *b)
 {
        const struct mgmt_request *request = a;
-       unsigned int id = GPOINTER_TO_UINT(b);
+       unsigned int id = PTR_TO_UINT(b);
 
-       return request->id - id;
+       return request->id == id;
 }
 
-static void destroy_notify(gpointer data, gpointer user_data)
+static bool match_request_index(const void *a, const void *b)
+{
+       const struct mgmt_request *request = a;
+       uint16_t index = PTR_TO_UINT(b);
+
+       return request->index == index;
+}
+
+static void destroy_notify(void *data)
 {
        struct mgmt_notify *notify = data;
 
        if (notify->destroy)
                notify->destroy(notify->user_data);
 
-       g_free(notify);
+       free(notify);
+}
+
+static bool match_notify_id(const void *a, const void *b)
+{
+       const struct mgmt_notify *notify = a;
+       unsigned int id = PTR_TO_UINT(b);
+
+       return notify->id == id;
+}
+
+static bool match_notify_index(const void *a, const void *b)
+{
+       const struct mgmt_notify *notify = a;
+       uint16_t index = PTR_TO_UINT(b);
+
+       return notify->index == index;
 }
 
-static int compare_notify_id(gconstpointer a, gconstpointer b)
+static bool match_notify_removed(const void *a, const void *b)
 {
        const struct mgmt_notify *notify = a;
-       unsigned int id = GPOINTER_TO_UINT(b);
 
-       return notify->id - id;
+       return notify->removed;
 }
 
-static void write_watch_destroy(gpointer user_data)
+static void mark_notify_removed(void *data , void *user_data)
+{
+       struct mgmt_notify *notify = data;
+       uint16_t index = PTR_TO_UINT(user_data);
+
+       if (notify->index == index || index == MGMT_INDEX_NONE)
+               notify->removed = true;
+}
+
+static void write_watch_destroy(void *user_data)
 {
        struct mgmt *mgmt = user_data;
 
-       mgmt->write_watch = 0;
+       mgmt->writer_active = false;
 }
 
-static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
-                                                       gpointer user_data)
+static bool can_write_data(struct io *io, void *user_data)
 {
        struct mgmt *mgmt = user_data;
        struct mgmt_request *request;
        ssize_t bytes_written;
 
-       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
-               return FALSE;
-
-       request = g_queue_pop_head(mgmt->reply_queue);
+       request = queue_pop_head(mgmt->reply_queue);
        if (!request) {
                /* only reply commands can jump the queue */
-               if (mgmt->pending_list)
-                       return FALSE;
+               if (!queue_isempty(mgmt->pending_list))
+                       return false;
 
-               request = g_queue_pop_head(mgmt->request_queue);
+               request = queue_pop_head(mgmt->request_queue);
                if (!request)
-                       return FALSE;
+                       return false;
        }
 
        bytes_written = write(mgmt->fd, request->buf, request->len);
@@ -155,8 +182,8 @@ static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
                if (request->callback)
                        request->callback(MGMT_STATUS_FAILED, 0, NULL,
                                                        request->user_data);
-               destroy_request(request, NULL);
-               return TRUE;
+               destroy_request(request);
+               return true;
        }
 
        util_debug(mgmt->debug_callback, mgmt->debug_data,
@@ -166,61 +193,56 @@ static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
        util_hexdump('<', request->buf, bytes_written,
                                mgmt->debug_callback, mgmt->debug_data);
 
-       mgmt->pending_list = g_list_append(mgmt->pending_list, request);
+       queue_push_tail(mgmt->pending_list, request);
 
-       return FALSE;
+       return false;
 }
 
 static void wakeup_writer(struct mgmt *mgmt)
 {
-       if (mgmt->pending_list) {
+       if (!queue_isempty(mgmt->pending_list)) {
                /* only queued reply commands trigger wakeup */
-               if (g_queue_get_length(mgmt->reply_queue) == 0)
+               if (queue_isempty(mgmt->reply_queue))
                        return;
        }
 
-       if (mgmt->write_watch > 0)
+       if (mgmt->writer_active)
                return;
 
-       mgmt->write_watch = g_io_add_watch_full(mgmt->io, G_PRIORITY_HIGH,
-                               G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                               can_write_data, mgmt, write_watch_destroy);
+       io_set_write_handler(mgmt->io, can_write_data, mgmt,
+                                               write_watch_destroy);
 }
 
-static GList *lookup_pending(struct mgmt *mgmt, uint16_t opcode, uint16_t index)
-{
-       GList *list;
-
-       for (list = g_list_first(mgmt->pending_list); list;
-                                               list = g_list_next(list)) {
-               struct mgmt_request *request = list->data;
+struct opcode_index {
+       uint16_t opcode;
+       uint16_t index;
+};
 
-               if (request->opcode == opcode && request->index == index)
-                       return list;
-       }
+static bool match_request_opcode_index(const void *a, const void *b)
+{
+       const struct mgmt_request *request = a;
+       const struct opcode_index *match = b;
 
-       return NULL;
+       return request->opcode == match->opcode &&
+                                       request->index == match->index;
 }
 
 static void request_complete(struct mgmt *mgmt, uint8_t status,
                                        uint16_t opcode, uint16_t index,
                                        uint16_t length, const void *param)
 {
+       struct opcode_index match = { .opcode = opcode, .index = index };
        struct mgmt_request *request;
-       GList *list;
-
-       list = lookup_pending(mgmt, opcode, index);
-       if (!list)
-               return;
 
-       request = list->data;
-
-       mgmt->pending_list = g_list_delete_link(mgmt->pending_list, list);
-
-       if (request->callback)
-               request->callback(status, length, param, request->user_data);
+       request = queue_remove_if(mgmt->pending_list,
+                                       match_request_opcode_index, &match);
+       if (request) {
+               if (request->callback)
+                       request->callback(status, length, param,
+                                                       request->user_data);
 
-       destroy_request(request, NULL);
+               destroy_request(request);
+       }
 
        if (mgmt->destroyed)
                return;
@@ -228,56 +250,63 @@ static void request_complete(struct mgmt *mgmt, uint8_t status,
        wakeup_writer(mgmt);
 }
 
-static void process_notify(struct mgmt *mgmt, uint16_t event, uint16_t index,
-                                       uint16_t length, const void *param)
+struct event_index {
+       uint16_t event;
+       uint16_t index;
+       uint16_t length;
+       const void *param;
+};
+
+static void notify_handler(void *data, void *user_data)
 {
-       GList *list;
+       struct mgmt_notify *notify = data;
+       struct event_index *match = user_data;
 
-       mgmt->in_notify = true;
+       if (notify->removed)
+               return;
 
-       for (list = g_list_first(mgmt->notify_list); list;
-                                               list = g_list_next(list)) {
-               struct mgmt_notify *notify = list->data;
+       if (notify->event != match->event)
+               return;
 
-               if (notify->destroyed)
-                       continue;
+       if (notify->index != match->index && notify->index != MGMT_INDEX_NONE)
+               return;
 
-               if (notify->event != event)
-                       continue;
+       if (notify->callback)
+               notify->callback(match->index, match->length, match->param,
+                                                       notify->user_data);
+}
 
-               if (notify->index != index && notify->index != MGMT_INDEX_NONE)
-                       continue;
+static void process_notify(struct mgmt *mgmt, uint16_t event, uint16_t index,
+                                       uint16_t length, const void *param)
+{
+       struct event_index match = { .event = event, .index = index,
+                                       .length = length, .param = param };
 
-               if (notify->callback)
-                       notify->callback(index, length, param,
-                                                       notify->user_data);
+       mgmt->in_notify = true;
 
-               if (mgmt->destroyed)
-                       break;
-       }
+       queue_foreach(mgmt->notify_list, notify_handler, &match);
 
        mgmt->in_notify = false;
 
-       g_list_foreach(mgmt->notify_destroyed, destroy_notify, NULL);
-       g_list_free(mgmt->notify_destroyed);
-
-       mgmt->notify_destroyed = NULL;
+       if (mgmt->need_notify_cleanup) {
+               queue_remove_all(mgmt->notify_list, match_notify_removed,
+                                                       NULL, destroy_notify);
+               mgmt->need_notify_cleanup = false;
+       }
 }
 
-static void read_watch_destroy(gpointer user_data)
+static void read_watch_destroy(void *user_data)
 {
        struct mgmt *mgmt = user_data;
 
        if (mgmt->destroyed) {
-               g_free(mgmt);
-               return;
+               queue_destroy(mgmt->notify_list, NULL);
+               queue_destroy(mgmt->pending_list, NULL);
+               free(mgmt);
        }
-
-       mgmt->read_watch = 0;
 }
 
-static gboolean received_data(GIOChannel *channel, GIOCondition cond,
-                                                       gpointer user_data)
+static bool can_read_data(struct io *io, void *user_data)
 {
        struct mgmt *mgmt = user_data;
        struct mgmt_hdr *hdr;
@@ -286,18 +315,15 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
        ssize_t bytes_read;
        uint16_t opcode, event, index, length;
 
-       if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
-               return FALSE;
-
        bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len);
        if (bytes_read < 0)
-               return TRUE;
+               return false;
 
        util_hexdump('>', mgmt->buf, bytes_read,
                                mgmt->debug_callback, mgmt->debug_data);
 
        if (bytes_read < MGMT_HDR_SIZE)
-               return TRUE;
+               return true;
 
        hdr = mgmt->buf;
        event = btohs(hdr->opcode);
@@ -305,7 +331,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
        length = btohs(hdr->len);
 
        if (bytes_read < length + MGMT_HDR_SIZE)
-               return TRUE;
+               return true;
 
        switch (event) {
        case MGMT_EV_CMD_COMPLETE:
@@ -339,9 +365,9 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
        }
 
        if (mgmt->destroyed)
-               return FALSE;
+               return false;
 
-       return TRUE;
+       return true;
 }
 
 struct mgmt *mgmt_new(int fd)
@@ -351,7 +377,7 @@ struct mgmt *mgmt_new(int fd)
        if (fd < 0)
                return NULL;
 
-       mgmt = g_try_new0(struct mgmt, 1);
+       mgmt = new0(struct mgmt, 1);
        if (!mgmt)
                return NULL;
 
@@ -359,23 +385,70 @@ struct mgmt *mgmt_new(int fd)
        mgmt->close_on_unref = false;
 
        mgmt->len = 512;
-       mgmt->buf = g_try_malloc(mgmt->len);
+       mgmt->buf = malloc(mgmt->len);
        if (!mgmt->buf) {
-               g_free(mgmt);
+               free(mgmt);
                return NULL;
        }
 
-       mgmt->io = g_io_channel_unix_new(mgmt->fd);
+       mgmt->io = io_new(fd);
+       if (!mgmt->io) {
+               free(mgmt->buf);
+               free(mgmt);
+               return NULL;
+       }
 
-       g_io_channel_set_encoding(mgmt->io, NULL, NULL);
-       g_io_channel_set_buffered(mgmt->io, FALSE);
+       mgmt->request_queue = queue_new();
+       if (!mgmt->request_queue) {
+               io_destroy(mgmt->io);
+               free(mgmt->buf);
+               free(mgmt);
+               return NULL;
+       }
 
-       mgmt->request_queue = g_queue_new();
-       mgmt->reply_queue = g_queue_new();
+       mgmt->reply_queue = queue_new();
+       if (!mgmt->reply_queue) {
+               queue_destroy(mgmt->request_queue, NULL);
+               io_destroy(mgmt->io);
+               free(mgmt->buf);
+               free(mgmt);
+               return NULL;
+       }
 
-       mgmt->read_watch = g_io_add_watch_full(mgmt->io, G_PRIORITY_DEFAULT,
-                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
-                               received_data, mgmt, read_watch_destroy);
+       mgmt->pending_list = queue_new();
+       if (!mgmt->pending_list) {
+               queue_destroy(mgmt->reply_queue, NULL);
+               queue_destroy(mgmt->request_queue, NULL);
+               io_destroy(mgmt->io);
+               free(mgmt->buf);
+               free(mgmt);
+               return NULL;
+       }
+
+       mgmt->notify_list = queue_new();
+       if (!mgmt->notify_list) {
+               queue_destroy(mgmt->pending_list, NULL);
+               queue_destroy(mgmt->reply_queue, NULL);
+               queue_destroy(mgmt->request_queue, NULL);
+               io_destroy(mgmt->io);
+               free(mgmt->buf);
+               free(mgmt);
+               return NULL;
+       }
+
+       if (!io_set_read_handler(mgmt->io, can_read_data, mgmt,
+                                               read_watch_destroy)) {
+               queue_destroy(mgmt->notify_list, NULL);
+               queue_destroy(mgmt->pending_list, NULL);
+               queue_destroy(mgmt->reply_queue, NULL);
+               queue_destroy(mgmt->request_queue, NULL);
+               io_destroy(mgmt->io);
+               free(mgmt->buf);
+               free(mgmt);
+               return NULL;
+       }
+
+       mgmt->writer_active = false;
 
        return mgmt_ref(mgmt);
 }
@@ -383,7 +456,10 @@ struct mgmt *mgmt_new(int fd)
 struct mgmt *mgmt_new_default(void)
 {
        struct mgmt *mgmt;
-       struct sockaddr_hci addr;
+       union {
+               struct sockaddr common;
+               struct sockaddr_hci hci;
+       } addr;
        int fd;
 
        fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
@@ -392,11 +468,11 @@ struct mgmt *mgmt_new_default(void)
                return NULL;
 
        memset(&addr, 0, sizeof(addr));
-       addr.hci_family = AF_BLUETOOTH;
-       addr.hci_dev = HCI_DEV_NONE;
-       addr.hci_channel = HCI_CHANNEL_CONTROL;
+       addr.hci.hci_family = AF_BLUETOOTH;
+       addr.hci.hci_dev = HCI_DEV_NONE;
+       addr.hci.hci_channel = HCI_CHANNEL_CONTROL;
 
-       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+       if (bind(fd, &addr.common, sizeof(addr.hci)) < 0) {
                close(fd);
                return NULL;
        }
@@ -433,16 +509,13 @@ void mgmt_unref(struct mgmt *mgmt)
        mgmt_unregister_all(mgmt);
        mgmt_cancel_all(mgmt);
 
-       g_queue_free(mgmt->reply_queue);
-       g_queue_free(mgmt->request_queue);
+       queue_destroy(mgmt->reply_queue, NULL);
+       queue_destroy(mgmt->request_queue, NULL);
 
-       if (mgmt->write_watch > 0)
-               g_source_remove(mgmt->write_watch);
+       io_set_write_handler(mgmt->io, NULL, NULL, NULL);
+       io_set_read_handler(mgmt->io, NULL, NULL, NULL);
 
-       if (mgmt->read_watch > 0)
-               g_source_remove(mgmt->read_watch);
-
-       g_io_channel_unref(mgmt->io);
+       io_destroy(mgmt->io);
        mgmt->io = NULL;
 
        if (mgmt->close_on_unref)
@@ -451,11 +524,13 @@ void mgmt_unref(struct mgmt *mgmt)
        if (mgmt->debug_destroy)
                mgmt->debug_destroy(mgmt->debug_data);
 
-       g_free(mgmt->buf);
+       free(mgmt->buf);
        mgmt->buf = NULL;
 
        if (!mgmt->in_notify) {
-               g_free(mgmt);
+               queue_destroy(mgmt->notify_list, NULL);
+               queue_destroy(mgmt->pending_list, NULL);
+               free(mgmt);
                return;
        }
 
@@ -502,14 +577,14 @@ static struct mgmt_request *create_request(uint16_t opcode, uint16_t index,
        if (length > 0 && !param)
                return NULL;
 
-       request = g_try_new0(struct mgmt_request, 1);
+       request = new0(struct mgmt_request, 1);
        if (!request)
                return NULL;
 
        request->len = length + MGMT_HDR_SIZE;
-       request->buf = g_try_malloc(request->len);
+       request->buf = malloc(request->len);
        if (!request->buf) {
-               g_free(request);
+               free(request);
                return NULL;
        }
 
@@ -551,7 +626,11 @@ unsigned int mgmt_send(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
 
        request->id = mgmt->next_request_id++;
 
-       g_queue_push_tail(mgmt->request_queue, request);
+       if (!queue_push_tail(mgmt->request_queue, request)) {
+               free(request->buf);
+               free(request);
+               return 0;
+       }
 
        wakeup_writer(mgmt);
 
@@ -578,7 +657,11 @@ unsigned int mgmt_reply(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
 
        request->id = mgmt->next_request_id++;
 
-       g_queue_push_tail(mgmt->reply_queue, request);
+       if (!queue_push_tail(mgmt->reply_queue, request)) {
+               free(request->buf);
+               free(request);
+               return 0;
+       }
 
        wakeup_writer(mgmt);
 
@@ -588,38 +671,27 @@ unsigned int mgmt_reply(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
 bool mgmt_cancel(struct mgmt *mgmt, unsigned int id)
 {
        struct mgmt_request *request;
-       GList *list;
 
        if (!mgmt || !id)
                return false;
 
-       list = g_queue_find_custom(mgmt->request_queue, GUINT_TO_POINTER(id),
-                                                       compare_request_id);
-       if (list) {
-               request = list->data;
-               g_queue_delete_link(mgmt->request_queue, list);
+       request = queue_remove_if(mgmt->request_queue, match_request_id,
+                                                       UINT_TO_PTR(id));
+       if (request)
                goto done;
-       }
 
-       list = g_queue_find_custom(mgmt->reply_queue, GUINT_TO_POINTER(id),
-                                                       compare_request_id);
-       if (list) {
-               request = list->data;
-               g_queue_delete_link(mgmt->reply_queue, list);
+       request = queue_remove_if(mgmt->reply_queue, match_request_id,
+                                                       UINT_TO_PTR(id));
+       if (request)
                goto done;
-       }
 
-       list = g_list_find_custom(mgmt->pending_list, GUINT_TO_POINTER(id),
-                                                       compare_request_id);
-       if (!list)
+       request = queue_remove_if(mgmt->pending_list, match_request_id,
+                                                       UINT_TO_PTR(id));
+       if (!request)
                return false;
 
-       request = list->data;
-
-       mgmt->pending_list = g_list_delete_link(mgmt->pending_list, list);
-
 done:
-       destroy_request(request, NULL);
+       destroy_request(request);
 
        wakeup_writer(mgmt);
 
@@ -628,52 +700,15 @@ done:
 
 bool mgmt_cancel_index(struct mgmt *mgmt, uint16_t index)
 {
-       GList *list, *next;
-
        if (!mgmt)
                return false;
 
-       for (list = g_queue_peek_head_link(mgmt->request_queue); list;
-                                                               list = next) {
-               struct mgmt_request *request = list->data;
-
-               next = g_list_next(list);
-
-               if (request->index != index)
-                       continue;
-
-               g_queue_delete_link(mgmt->request_queue, list);
-
-               destroy_request(request, NULL);
-       }
-
-       for (list = g_queue_peek_head_link(mgmt->reply_queue); list;
-                                                               list = next) {
-               struct mgmt_request *request = list->data;
-
-               next = g_list_next(list);
-
-               if (request->index != index)
-                       continue;
-
-               g_queue_delete_link(mgmt->reply_queue, list);
-
-               destroy_request(request, NULL);
-       }
-
-       for (list = g_list_first(mgmt->pending_list); list; list = next) {
-               struct mgmt_request *request = list->data;
-
-               next = g_list_next(list);
-
-               if (request->index != index)
-                       continue;
-
-               mgmt->pending_list = g_list_delete_link(mgmt->pending_list,
-                                                                       list);
-
-               destroy_request(request, NULL);
-       }
+       queue_remove_all(mgmt->request_queue, match_request_index,
+                                       UINT_TO_PTR(index), destroy_request);
+       queue_remove_all(mgmt->reply_queue, match_request_index,
+                                       UINT_TO_PTR(index), destroy_request);
+       queue_remove_all(mgmt->pending_list, match_request_index,
+                                       UINT_TO_PTR(index), destroy_request);
 
        return true;
 }
@@ -683,15 +718,9 @@ bool mgmt_cancel_all(struct mgmt *mgmt)
        if (!mgmt)
                return false;
 
-       g_list_foreach(mgmt->pending_list, destroy_request, NULL);
-       g_list_free(mgmt->pending_list);
-       mgmt->pending_list = NULL;
-
-       g_queue_foreach(mgmt->reply_queue, destroy_request, NULL);
-       g_queue_clear(mgmt->reply_queue);
-
-       g_queue_foreach(mgmt->request_queue, destroy_request, NULL);
-       g_queue_clear(mgmt->request_queue);
+       queue_remove_all(mgmt->pending_list, NULL, NULL, destroy_request);
+       queue_remove_all(mgmt->reply_queue, NULL, NULL, destroy_request);
+       queue_remove_all(mgmt->request_queue, NULL, NULL, destroy_request);
 
        return true;
 }
@@ -705,7 +734,7 @@ unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index,
        if (!mgmt || !event)
                return 0;
 
-       notify = g_try_new0(struct mgmt_notify, 1);
+       notify = new0(struct mgmt_notify, 1);
        if (!notify)
                return 0;
 
@@ -721,7 +750,10 @@ unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index,
 
        notify->id = mgmt->next_notify_id++;
 
-       mgmt->notify_list = g_list_append(mgmt->notify_list, notify);
+       if (!queue_push_tail(mgmt->notify_list, notify)) {
+               free(notify);
+               return 0;
+       }
 
        return notify->id;
 }
@@ -729,87 +761,53 @@ unsigned int mgmt_register(struct mgmt *mgmt, uint16_t event, uint16_t index,
 bool mgmt_unregister(struct mgmt *mgmt, unsigned int id)
 {
        struct mgmt_notify *notify;
-       GList *list;
 
        if (!mgmt || !id)
                return false;
 
-       list = g_list_find_custom(mgmt->notify_list,
-                               GUINT_TO_POINTER(id), compare_notify_id);
-       if (!list)
+       notify = queue_remove_if(mgmt->notify_list, match_notify_id,
+                                                       UINT_TO_PTR(id));
+       if (!notify)
                return false;
 
-       notify = list->data;
-
-       mgmt->notify_list = g_list_remove_link(mgmt->notify_list, list);
-
        if (!mgmt->in_notify) {
-               g_list_free_1(list);
-               destroy_notify(notify, NULL);
+               destroy_notify(notify);
                return true;
        }
 
-       notify->destroyed = true;
-
-       mgmt->notify_destroyed = g_list_concat(mgmt->notify_destroyed, list);
+       notify->removed = true;
+       mgmt->need_notify_cleanup = true;
 
        return true;
 }
 
 bool mgmt_unregister_index(struct mgmt *mgmt, uint16_t index)
 {
-       GList *list, *next;
-
        if (!mgmt)
                return false;
 
-       for (list = g_list_first(mgmt->notify_list); list; list = next) {
-               struct mgmt_notify *notify = list->data;
-
-               next = g_list_next(list);
-
-               if (notify->index != index)
-                       continue;
-
-               mgmt->notify_list = g_list_remove_link(mgmt->notify_list, list);
-
-               if (!mgmt->in_notify) {
-                       g_list_free_1(list);
-                       destroy_notify(notify, NULL);
-                       continue;
-               }
-
-               notify->destroyed = true;
-
-               mgmt->notify_destroyed = g_list_concat(mgmt->notify_destroyed,
-                                                                       list);
-       }
+       if (mgmt->in_notify) {
+               queue_foreach(mgmt->notify_list, mark_notify_removed,
+                                                       UINT_TO_PTR(index));
+               mgmt->need_notify_cleanup = true;
+       } else
+               queue_remove_all(mgmt->notify_list, match_notify_index,
+                                       UINT_TO_PTR(index), destroy_notify);
 
        return true;
 }
 
-static void mark_notify(gpointer data, gpointer user_data)
-{
-       struct mgmt_notify *notify = data;
-
-       notify->destroyed = true;
-}
-
 bool mgmt_unregister_all(struct mgmt *mgmt)
 {
        if (!mgmt)
                return false;
 
-       if (!mgmt->in_notify) {
-               g_list_foreach(mgmt->notify_list, destroy_notify, NULL);
-               g_list_free(mgmt->notify_list);
-       } else {
-               g_list_foreach(mgmt->notify_list, mark_notify, NULL);
-               mgmt->notify_destroyed = g_list_concat(mgmt->notify_destroyed,
-                                                       mgmt->notify_list);
-       }
-
-       mgmt->notify_list = NULL;
+       if (mgmt->in_notify) {
+               queue_foreach(mgmt->notify_list, mark_notify_removed,
+                                               UINT_TO_PTR(MGMT_INDEX_NONE));
+               mgmt->need_notify_cleanup = true;
+       } else
+               queue_remove_all(mgmt->notify_list, NULL, NULL, destroy_notify);
 
        return true;
 }
index e23cc7d..626a699 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
index c722db2..bd7675f 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
 #include <unistd.h>
 #include <stdlib.h>
 
-#include "pcap.h"
-
-#define le16_to_cpu(val) (val)
-#define le32_to_cpu(val) (val)
-#define cpu_to_le16(val) (val)
-#define cpu_to_le32(val) (val)
+#include "src/shared/util.h"
+#include "src/shared/pcap.h"
 
 struct pcap_hdr {
        uint32_t magic_number;  /* magic number */
index f333bfc..b47de62 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/shared/queue.c b/src/shared/queue.c
new file mode 100644 (file)
index 0000000..8a69729
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+
+struct queue_entry {
+       void *data;
+       struct queue_entry *next;
+};
+
+struct queue {
+       struct queue_entry *head;
+       struct queue_entry *tail;
+       unsigned int entries;
+};
+
+struct queue *queue_new(void)
+{
+       struct queue *queue;
+
+       queue = new0(struct queue, 1);
+       if (!queue)
+               return NULL;
+
+       queue->head = NULL;
+       queue->tail = NULL;
+       queue->entries = 0;
+
+       return queue;
+}
+
+void queue_destroy(struct queue *queue, queue_destroy_func_t destroy)
+{
+       struct queue_entry *entry;
+
+       if (!queue)
+               return;
+
+       entry = queue->head;
+
+       while (entry) {
+               struct queue_entry *tmp = entry;
+
+               if (destroy)
+                       destroy(entry->data);
+
+               entry = entry->next;
+
+               free(tmp);
+       }
+
+       free(queue);
+}
+
+bool queue_push_tail(struct queue *queue, void *data)
+{
+       struct queue_entry *entry;
+
+       if (!queue)
+               return false;
+
+       entry = new0(struct queue_entry, 1);
+       if (!entry)
+               return false;
+
+       entry->data = data;
+       entry->next = NULL;
+
+       if (queue->tail)
+               queue->tail->next = entry;
+
+       queue->tail = entry;
+
+       if (!queue->head)
+               queue->head = entry;
+
+       queue->entries++;
+
+       return true;
+}
+
+bool queue_push_head(struct queue *queue, void *data)
+{
+       struct queue_entry *entry;
+
+       if (!queue)
+               return false;
+
+       entry = new0(struct queue_entry, 1);
+       if (!entry)
+               return false;
+
+       entry->data = data;
+       entry->next = queue->head;
+
+       queue->head = entry;
+
+       if (!queue->tail)
+               queue->tail = entry;
+
+       queue->entries++;
+
+       return true;
+}
+
+void *queue_pop_head(struct queue *queue)
+{
+       struct queue_entry *entry;
+       void *data;
+
+       if (!queue || !queue->head)
+               return NULL;
+
+       entry = queue->head;
+
+       if (!queue->head->next) {
+               queue->head = NULL;
+               queue->tail = NULL;
+       } else
+               queue->head = queue->head->next;
+
+       data = entry->data;
+
+       free(entry);
+       queue->entries--;
+
+       return data;
+}
+
+void *queue_peek_head(struct queue *queue)
+{
+       if (!queue || !queue->head)
+               return NULL;
+
+       return queue->head->data;
+}
+
+void *queue_peek_tail(struct queue *queue)
+{
+       if (!queue || !queue->tail)
+               return NULL;
+
+       return queue->tail->data;
+}
+
+void queue_foreach(struct queue *queue, queue_foreach_func_t function,
+                                                       void *user_data)
+{
+       struct queue_entry *entry;
+
+       if (!queue || !function)
+               return;
+
+       entry = queue->head;
+
+       while (entry) {
+               struct queue_entry *tmp = entry;
+
+               entry = tmp->next;
+
+               function(tmp->data, user_data);
+       }
+}
+
+void *queue_find(struct queue *queue, queue_match_func_t function,
+                                                       void *user_data)
+{
+       struct queue_entry *entry;
+
+       if (!queue || !function)
+               return NULL;
+
+       for (entry = queue->head; entry; entry = entry->next)
+               if (function(entry->data, user_data))
+                       return entry->data;
+
+       return NULL;
+}
+
+bool queue_remove(struct queue *queue, void *data)
+{
+       struct queue_entry *entry, *prev;
+
+       if (!queue || !data)
+               return false;
+
+       for (entry = queue->head, prev = NULL; entry;
+                                       prev = entry, entry = entry->next) {
+               if (entry->data != data)
+                       continue;
+
+               if (prev)
+                       prev->next = entry->next;
+               else
+                       queue->head = entry->next;
+
+               if (!entry->next)
+                       queue->tail = prev;
+
+               free(entry);
+               queue->entries--;
+
+               return true;
+       }
+
+       return false;
+}
+
+void *queue_remove_if(struct queue *queue, queue_match_func_t function,
+                                                       void *user_data)
+{
+       struct queue_entry *entry, *prev = NULL;
+
+       if (!queue || !function)
+               return NULL;
+
+       entry = queue->head;
+
+       while (entry) {
+               if (function(entry->data, user_data)) {
+                       void *data;
+
+                       if (prev)
+                               prev->next = entry->next;
+                       else
+                               queue->head = entry->next;
+
+                       if (!entry->next)
+                               queue->tail = prev;
+
+                       data = entry->data;
+
+                       free(entry);
+                       queue->entries--;
+
+                       return data;
+               } else {
+                       prev = entry;
+                       entry = entry->next;
+               }
+       }
+
+       return NULL;
+}
+
+unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function,
+                               void *user_data, queue_destroy_func_t destroy)
+{
+       struct queue_entry *entry;
+       unsigned int count = 0;
+
+       if (!queue)
+               return 0;
+
+       entry = queue->head;
+
+       if (function) {
+               struct queue_entry *prev = NULL;
+
+               while (entry) {
+                       if (function(entry->data, user_data)) {
+                               struct queue_entry *tmp = entry;
+
+                               if (prev)
+                                       prev->next = entry->next;
+                               else
+                                       queue->head = entry->next;
+
+                               if (!entry->next)
+                                       queue->tail = prev;
+
+                               entry = entry->next;
+
+                               if (destroy)
+                                       destroy(tmp->data);
+
+                               free(tmp);
+                               count++;
+                       } else {
+                               prev = entry;
+                               entry = entry->next;
+                       }
+               }
+
+               queue->entries -= count;
+       } else {
+               while (entry) {
+                       struct queue_entry *tmp = entry;
+
+                       entry = entry->next;
+
+                       if (destroy)
+                               destroy(tmp->data);
+
+                       free(tmp);
+                       count++;
+               }
+
+               queue->head = NULL;
+               queue->tail = NULL;
+               queue->entries = 0;
+       }
+
+       return count;
+}
+
+unsigned int queue_length(struct queue *queue)
+{
+       if (!queue)
+               return 0;
+
+       return queue->entries;
+}
+
+bool queue_isempty(struct queue *queue)
+{
+       if (!queue)
+               return true;
+
+       return queue->entries == 0;
+}
diff --git a/src/shared/queue.h b/src/shared/queue.h
new file mode 100644 (file)
index 0000000..8201ff8
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdbool.h>
+
+typedef void (*queue_destroy_func_t)(void *data);
+
+struct queue;
+
+struct queue *queue_new(void);
+void queue_destroy(struct queue *queue, queue_destroy_func_t destroy);
+
+bool queue_push_tail(struct queue *queue, void *data);
+bool queue_push_head(struct queue *queue, void *data);
+void *queue_pop_head(struct queue *queue);
+void *queue_peek_head(struct queue *queue);
+void *queue_peek_tail(struct queue *queue);
+
+typedef void (*queue_foreach_func_t)(void *data, void *user_data);
+
+void queue_foreach(struct queue *queue, queue_foreach_func_t function,
+                                                       void *user_data);
+
+typedef bool (*queue_match_func_t)(const void *a, const void *b);
+
+void *queue_find(struct queue *queue, queue_match_func_t function,
+                                                       void *user_data);
+
+bool queue_remove(struct queue *queue, void *data);
+void *queue_remove_if(struct queue *queue, queue_match_func_t function,
+                                                       void *user_data);
+unsigned int queue_remove_all(struct queue *queue, queue_match_func_t function,
+                               void *user_data, queue_destroy_func_t destroy);
+
+unsigned int queue_length(struct queue *queue);
+bool queue_isempty(struct queue *queue);
diff --git a/src/shared/ringbuf.c b/src/shared/ringbuf.c
new file mode 100644 (file)
index 0000000..a11d2dc
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+
+#include "src/shared/util.h"
+#include "src/shared/ringbuf.h"
+
+#ifndef MIN
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+struct ringbuf {
+       void *buffer;
+       size_t size;
+       size_t in;
+       size_t out;
+       ringbuf_tracing_func_t in_tracing;
+       void *in_data;
+};
+
+#define RINGBUF_RESET 0
+
+/* Find last (most siginificant) set bit */
+static inline unsigned int fls(unsigned int x)
+{
+       return x ? sizeof(x) * 8 - __builtin_clz(x) : 0;
+}
+
+/* Round up to nearest power of two */
+static inline unsigned int align_power2(unsigned int u)
+{
+       return 1 << fls(u - 1);
+}
+
+struct ringbuf *ringbuf_new(size_t size)
+{
+       struct ringbuf *ringbuf;
+       size_t real_size;
+
+       if (size < 2 || size > UINT_MAX)
+               return NULL;
+
+       /* Find the next power of two for size */
+       real_size = align_power2(size);
+
+       ringbuf = new0(struct ringbuf, 1);
+       if (!ringbuf)
+               return NULL;
+
+       ringbuf->buffer = malloc(real_size);
+       if (!ringbuf->buffer) {
+               free(ringbuf);
+               return NULL;
+       }
+
+       ringbuf->size = real_size;
+       ringbuf->in = RINGBUF_RESET;
+       ringbuf->out = RINGBUF_RESET;
+
+       return ringbuf;
+}
+
+void ringbuf_free(struct ringbuf *ringbuf)
+{
+       if (!ringbuf)
+               return;
+
+       free(ringbuf->buffer);
+       free(ringbuf);
+}
+
+bool ringbuf_set_input_tracing(struct ringbuf *ringbuf,
+                       ringbuf_tracing_func_t callback, void *user_data)
+{
+       if (!ringbuf)
+               return false;
+
+       ringbuf->in_tracing = callback;
+       ringbuf->in_data = user_data;
+
+       return true;
+}
+
+size_t ringbuf_capacity(struct ringbuf *ringbuf)
+{
+       if (!ringbuf)
+               return 0;
+
+       return ringbuf->size;
+}
+
+size_t ringbuf_len(struct ringbuf *ringbuf)
+{
+       if (!ringbuf)
+               return 0;
+
+       return ringbuf->in - ringbuf->out;
+}
+
+size_t ringbuf_drain(struct ringbuf *ringbuf, size_t count)
+{
+       size_t len;
+
+       if (!ringbuf)
+               return 0;
+
+       len = MIN(count, ringbuf->in - ringbuf->out);
+       if (!len)
+               return 0;
+
+       ringbuf->out += len;
+
+       if (ringbuf->out == ringbuf->in) {
+               ringbuf->in = RINGBUF_RESET;
+               ringbuf->out = RINGBUF_RESET;
+       }
+
+       return len;
+}
+
+void *ringbuf_peek(struct ringbuf *ringbuf, size_t offset, size_t *len_nowrap)
+{
+       if (!ringbuf)
+               return NULL;
+
+       offset = (ringbuf->out + offset) & (ringbuf->size - 1);
+
+       if (len_nowrap) {
+               size_t len = ringbuf->in - ringbuf->out;
+               *len_nowrap = MIN(len, ringbuf->size - offset);
+       }
+
+       return ringbuf->buffer + offset;
+}
+
+ssize_t ringbuf_write(struct ringbuf *ringbuf, int fd)
+{
+       size_t len, offset, end;
+       struct iovec iov[2];
+       ssize_t consumed;
+
+       if (!ringbuf || fd < 0)
+               return -1;
+
+       /* Determine how much data is available */
+       len = ringbuf->in - ringbuf->out;
+       if (!len)
+               return 0;
+
+       /* Grab data from buffer starting at offset until the end */
+       offset = ringbuf->out & (ringbuf->size - 1);
+       end = MIN(len, ringbuf->size - offset);
+
+       iov[0].iov_base = ringbuf->buffer + offset;
+       iov[0].iov_len = end;
+
+       /* Use second vector for remainder from the beginning */
+       iov[1].iov_base = ringbuf->buffer;
+       iov[1].iov_len = len - end;
+
+       consumed = writev(fd, iov, 2);
+       if (consumed < 0)
+               return -1;
+
+       ringbuf->out += consumed;
+
+       if (ringbuf->out == ringbuf->in) {
+               ringbuf->in = RINGBUF_RESET;
+               ringbuf->out = RINGBUF_RESET;
+       }
+
+       return consumed;
+}
+
+size_t ringbuf_avail(struct ringbuf *ringbuf)
+{
+       if (!ringbuf)
+               return 0;
+
+       return ringbuf->size - ringbuf->in + ringbuf->out;
+}
+
+int ringbuf_printf(struct ringbuf *ringbuf, const char *format, ...)
+{
+       va_list ap;
+       int len;
+
+       va_start(ap, format);
+       len = ringbuf_vprintf(ringbuf, format, ap);
+       va_end(ap);
+
+       return len;
+}
+
+int ringbuf_vprintf(struct ringbuf *ringbuf, const char *format, va_list ap)
+{
+       size_t avail, offset, end;
+       char *str;
+       int len;
+
+       if (!ringbuf || !format)
+               return -1;
+
+       /* Determine maximum length available for string */
+       avail = ringbuf->size - ringbuf->in + ringbuf->out;
+       if (!avail)
+               return -1;
+
+       len = vasprintf(&str, format, ap);
+       if (len < 0)
+               return -1;
+
+       if ((size_t) len > avail) {
+               free(str);
+               return -1;
+       }
+
+       /* Determine possible length of string before wrapping */
+       offset = ringbuf->in & (ringbuf->size - 1);
+       end = MIN((size_t) len, ringbuf->size - offset);
+       memcpy(ringbuf->buffer + offset, str, end);
+
+       if (ringbuf->in_tracing)
+               ringbuf->in_tracing(ringbuf->buffer + offset, end,
+                                                       ringbuf->in_data);
+
+       if (len - end > 0) {
+               /* Put the remainder of string at the beginning */
+               memcpy(ringbuf->buffer, str + end, len - end);
+
+               if (ringbuf->in_tracing)
+                       ringbuf->in_tracing(ringbuf->buffer, len - end,
+                                                       ringbuf->in_data);
+       }
+
+       free(str);
+
+       ringbuf->in += len;
+
+       return len;
+}
+
+ssize_t ringbuf_read(struct ringbuf *ringbuf, int fd)
+{
+       size_t avail, offset, end;
+       struct iovec iov[2];
+       ssize_t consumed;
+
+       if (!ringbuf || fd < 0)
+               return -1;
+
+       /* Determine how much can actually be consumed */
+       avail = ringbuf->size - ringbuf->in + ringbuf->out;
+       if (!avail)
+               return -1;
+
+       /* Determine how much to consume before wrapping */
+       offset = ringbuf->in & (ringbuf->size - 1);
+       end = MIN(avail, ringbuf->size - offset);
+
+       iov[0].iov_base = ringbuf->buffer + offset;
+       iov[0].iov_len = end;
+
+       /* Now put the remainder into the second vector */
+       iov[1].iov_base = ringbuf->buffer;
+       iov[1].iov_len = avail - end;
+
+       consumed = readv(fd, iov, 2);
+       if (consumed < 0)
+               return -1;
+
+       if (ringbuf->in_tracing) {
+               size_t len = MIN((size_t) consumed, end);
+               ringbuf->in_tracing(ringbuf->buffer + offset, len,
+                                                       ringbuf->in_data);
+               if (consumed - len > 0)
+                       ringbuf->in_tracing(ringbuf->buffer, consumed - len,
+                                                       ringbuf->in_data);
+       }
+
+       ringbuf->in += consumed;
+
+       return consumed;
+}
diff --git a/src/shared/ringbuf.h b/src/shared/ringbuf.h
new file mode 100644 (file)
index 0000000..adf471a
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+typedef void (*ringbuf_tracing_func_t)(const void *buf, size_t count,
+                                                       void *user_data);
+
+struct ringbuf;
+
+struct ringbuf *ringbuf_new(size_t size);
+void ringbuf_free(struct ringbuf *ringbuf);
+
+bool ringbuf_set_input_tracing(struct ringbuf *ringbuf,
+                       ringbuf_tracing_func_t callback, void *user_data);
+
+size_t ringbuf_capacity(struct ringbuf *ringbuf);
+
+size_t ringbuf_len(struct ringbuf *ringbuf);
+size_t ringbuf_drain(struct ringbuf *ringbuf, size_t count);
+void *ringbuf_peek(struct ringbuf *ringbuf, size_t offset, size_t *len_nowrap);
+ssize_t ringbuf_write(struct ringbuf *ringbuf, int fd);
+
+size_t ringbuf_avail(struct ringbuf *ringbuf);
+int ringbuf_printf(struct ringbuf *ringbuf, const char *format, ...)
+                                       __attribute__((format(printf, 2, 3)));
+int ringbuf_vprintf(struct ringbuf *ringbuf, const char *format, va_list ap);
+ssize_t ringbuf_read(struct ringbuf *ringbuf, int fd);
index f3edd74..56e5696 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <string.h>
 #include <signal.h>
 #include <sys/signalfd.h>
 
 #include <glib.h>
 
+#include "src/shared/util.h"
 #include "src/shared/tester.h"
 
 #define COLOR_OFF      "\x1B[0m"
@@ -114,8 +116,8 @@ static void test_destroy(gpointer data)
        if (test->destroy)
                test->destroy(test->user_data);
 
-       g_free(test->name);
-       g_free(test);
+       free(test->name);
+       free(test);
 }
 
 void tester_print(const char *format, ...)
@@ -190,9 +192,14 @@ void tester_add_full(const char *name, const void *test_data,
                return;
        }
 
-       test = g_new0(struct test_case, 1);
+       test = new0(struct test_case, 1);
+       if (!test) {
+               if (destroy)
+                       destroy(user_data);
+               return;
+       }
 
-       test->name = g_strdup(name);
+       test->name = strdup(name);
        test->result = TEST_RESULT_NOT_RUN;
        test->stage = TEST_STAGE_INVALID;
 
@@ -460,6 +467,11 @@ void tester_setup_failed(void)
        if (test->stage != TEST_STAGE_SETUP)
                return;
 
+       if (test->timeout_id > 0) {
+               g_source_remove(test->timeout_id);
+               test->timeout_id = 0;
+       }
+
        print_progress(test->name, COLOR_RED, "setup failed");
 
        g_idle_add(done_callback, test);
@@ -612,7 +624,7 @@ static gboolean wait_callback(gpointer user_data)
 
        wait->func(wait->user_data);
 
-       g_free(wait);
+       free(wait);
 
        return FALSE;
 }
@@ -631,9 +643,9 @@ void tester_wait(unsigned int seconds, tester_wait_func_t func,
 
        test = test_current->data;
 
-       print_progress(test->name, COLOR_BLACK, "waiting %u seconds", seconds);
-
-       wait = g_new0(struct wait_data, 1);
+       wait = new0(struct wait_data, 1);
+       if (!wait)
+               return;
 
        wait->seconds = seconds;
        wait->test = test;
@@ -641,6 +653,8 @@ void tester_wait(unsigned int seconds, tester_wait_func_t func,
        wait->user_data = user_data;
 
        g_timeout_add(1000, wait_callback, wait);
+
+       print_progress(test->name, COLOR_BLACK, "waiting %u seconds", seconds);
 }
 
 static gboolean signal_handler(GIOChannel *channel, GIOCondition condition,
index 775ed1e..85d5e95 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/shared/timeout-glib.c b/src/shared/timeout-glib.c
new file mode 100644 (file)
index 0000000..4163bce
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#include "timeout.h"
+
+#include <glib.h>
+
+struct timeout_data {
+       timeout_func_t func;
+       timeout_destroy_func_t destroy;
+       void *user_data;
+};
+
+static gboolean timeout_callback(gpointer user_data)
+{
+       struct timeout_data *data  = user_data;
+
+       if (data->func(data->user_data))
+               return TRUE;
+
+       return FALSE;
+}
+
+static void timeout_destroy(gpointer user_data)
+{
+       struct timeout_data *data = user_data;
+
+       if (data->destroy)
+               data->destroy(data->user_data);
+
+       g_free(data);
+}
+
+unsigned int timeout_add(unsigned int timeout, timeout_func_t func,
+                       void *user_data, timeout_destroy_func_t destroy)
+{
+       struct timeout_data *data;
+       guint id;
+
+       data = g_try_new0(struct timeout_data, 1);
+       if (!data)
+               return 0;
+
+       data->func = func;
+       data->destroy = destroy;
+       data->user_data = user_data;
+
+       id = g_timeout_add_full(G_PRIORITY_DEFAULT, timeout, timeout_callback,
+                                               data, timeout_destroy);
+       if (!id)
+               g_free(data);
+
+       return id;
+}
+
+void timeout_remove(unsigned int id)
+{
+       GSource *source = g_main_context_find_source_by_id(NULL, id);
+
+       if (source)
+               g_source_destroy(source);
+}
diff --git a/src/shared/timeout-mainloop.c b/src/shared/timeout-mainloop.c
new file mode 100644 (file)
index 0000000..77aaa63
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#include <stdlib.h>
+
+#include "monitor/mainloop.h"
+
+#include "util.h"
+#include "timeout.h"
+
+struct timeout_data {
+       int id;
+       timeout_func_t func;
+       timeout_destroy_func_t destroy;
+       unsigned int timeout;
+       void *user_data;
+};
+
+static void timeout_callback(int id, void *user_data)
+{
+       struct timeout_data *data = user_data;
+
+       if (data->func(data->user_data) &&
+                       !mainloop_modify_timeout(data->id, data->timeout))
+               return;
+
+       mainloop_remove_timeout(data->id);
+}
+
+static void timeout_destroy(void *user_data)
+{
+       struct timeout_data *data = user_data;
+
+       if (data->destroy)
+               data->destroy(data->user_data);
+
+       free(data);
+}
+
+unsigned int timeout_add(unsigned int timeout, timeout_func_t func,
+                       void *user_data, timeout_destroy_func_t destroy)
+{
+       struct timeout_data *data;
+
+       data = new0(struct timeout_data, 1);
+       if (!data)
+               return 0;
+
+       data->func = func;
+       data->user_data = user_data;
+       data->timeout = timeout;
+       data->destroy = destroy;
+
+       data->id = mainloop_add_timeout(timeout, timeout_callback, data,
+                                                       timeout_destroy);
+       if (data->id < 0) {
+               free(data);
+               return 0;
+       }
+
+       return (unsigned int) data->id;
+}
+
+void timeout_remove(unsigned int id)
+{
+       if (!id)
+               return;
+
+       mainloop_remove_timeout((int) id);
+}
diff --git a/src/shared/timeout.h b/src/shared/timeout.h
new file mode 100644 (file)
index 0000000..4930ce1
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ */
+
+#include <stdbool.h>
+
+typedef bool (*timeout_func_t)(void *user_data);
+typedef void (*timeout_destroy_func_t)(void *user_data);
+
+unsigned int timeout_add(unsigned int timeout, timeout_func_t func,
+                       void *user_data, timeout_destroy_func_t destroy);
+void timeout_remove(unsigned int id);
index 5aee69d..eb90ecb 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
index 88e8954..4bc77a1 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012-2014  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
  *
  */
 
+#include <stdint.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <byteswap.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define le16_to_cpu(val) (val)
+#define le32_to_cpu(val) (val)
+#define le64_to_cpu(val) (val)
+#define cpu_to_le16(val) (val)
+#define cpu_to_le32(val) (val)
+#define cpu_to_le64(val) (val)
+#define be16_to_cpu(val) bswap_16(val)
+#define be32_to_cpu(val) bswap_32(val)
+#define be64_to_cpu(val) bswap_64(val)
+#define cpu_to_be16(val) bswap_16(val)
+#define cpu_to_be32(val) bswap_32(val)
+#define cpu_to_be64(val) bswap_64(val)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define le16_to_cpu(val) bswap_16(val)
+#define le32_to_cpu(val) bswap_32(val)
+#define le64_to_cpu(val) bswap_64(val)
+#define cpu_to_le16(val) bswap_16(val)
+#define cpu_to_le32(val) bswap_32(val)
+#define cpu_to_le64(val) bswap_64(val)
+#define be16_to_cpu(val) (val)
+#define be32_to_cpu(val) (val)
+#define be64_to_cpu(val) (val)
+#define cpu_to_be16(val) (val)
+#define cpu_to_be32(val) (val)
+#define cpu_to_be64(val) (val)
+#else
+#error "Unknown byte order"
+#endif
+
+#define get_unaligned(ptr)                     \
+({                                             \
+       struct __attribute__((packed)) {        \
+               typeof(*(ptr)) __v;             \
+       } *__p = (typeof(__p)) (ptr);           \
+       __p->__v;                               \
+})
+
+#define put_unaligned(val, ptr)                        \
+do {                                           \
+       struct __attribute__((packed)) {        \
+               typeof(*(ptr)) __v;             \
+       } *__p = (typeof(__p)) (ptr);           \
+       __p->__v = (val);                       \
+} while (0)
+
+#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u)))
+
+#define PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define INT_TO_PTR(u) ((void *) ((intptr_t) (u)))
+
+#define new0(t, n) ((t*) calloc((n), sizeof(t)))
+#define newa(t, n) ((t*) alloca(sizeof(t)*(n)))
+#define malloc0(n) (calloc((n), 1))
+
 typedef void (*util_debug_func_t)(const char *str, void *user_data);
 
 void util_debug(util_debug_func_t function, void *user_data,
@@ -29,3 +90,73 @@ void util_debug(util_debug_func_t function, void *user_data,
 
 void util_hexdump(const char dir, const unsigned char *buf, size_t len,
                                util_debug_func_t function, void *user_data);
+
+static inline void bswap_128(const void *src, void *dst)
+{
+       const uint8_t *s = src;
+       uint8_t *d = dst;
+       int i;
+
+       for (i = 0; i < 16; i++)
+               d[15 - i] = s[i];
+}
+
+static inline uint16_t get_le16(const void *ptr)
+{
+       return le16_to_cpu(get_unaligned((const uint16_t *) ptr));
+}
+
+static inline uint16_t get_be16(const void *ptr)
+{
+       return be16_to_cpu(get_unaligned((const uint16_t *) ptr));
+}
+
+static inline uint32_t get_le32(const void *ptr)
+{
+       return le32_to_cpu(get_unaligned((const uint32_t *) ptr));
+}
+
+static inline uint32_t get_be32(const void *ptr)
+{
+       return be32_to_cpu(get_unaligned((const uint32_t *) ptr));
+}
+
+static inline uint64_t get_le64(const void *ptr)
+{
+       return le64_to_cpu(get_unaligned((const uint64_t *) ptr));
+}
+
+static inline uint64_t get_be64(const void *ptr)
+{
+       return be64_to_cpu(get_unaligned((const uint64_t *) ptr));
+}
+
+static inline void put_le16(uint16_t val, void *dst)
+{
+       put_unaligned(cpu_to_le16(val), (uint16_t *) dst);
+}
+
+static inline void put_be16(uint16_t val, const void *ptr)
+{
+       put_unaligned(cpu_to_be16(val), (uint16_t *) ptr);
+}
+
+static inline void put_le32(uint32_t val, void *dst)
+{
+       put_unaligned(cpu_to_le32(val), (uint32_t *) dst);
+}
+
+static inline void put_be32(uint32_t val, void *dst)
+{
+       put_unaligned(cpu_to_be32(val), (uint32_t *) dst);
+}
+
+static inline void put_le64(uint64_t val, void *dst)
+{
+       put_unaligned(cpu_to_le64(val), (uint64_t *) dst);
+}
+
+static inline void put_be64(uint64_t val, void *dst)
+{
+       put_unaligned(cpu_to_be64(val), (uint64_t *) dst);
+}
index f7e4db6..b230e1e 100644 (file)
@@ -44,7 +44,7 @@
 
 #include "lib/uuid.h"
 #include "textfile.h"
-#include "glib-helper.h"
+#include "uuid-helper.h"
 #include "storage.h"
 
 /* When all services should trust a remote device */
index 4c5ab50..1c0ad57 100644 (file)
@@ -21,8 +21,6 @@
  *
  */
 
-#include "textfile.h"
-
 int read_discoverable_timeout(const char *src, int *timeout);
 int read_pairable_timeout(const char *src, int *timeout);
 int read_on_mode(const char *src, char *mode, int length);
index b779bd2..f01629e 100644 (file)
@@ -21,9 +21,6 @@
  *
  */
 
-#ifndef __TEXTFILE_H
-#define __TEXTFILE_H
-
 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);
@@ -35,5 +32,3 @@ char *textfile_get(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 */
similarity index 87%
rename from src/glib-helper.c
rename to src/uuid-helper.c
index 4a020e9..bce36b0 100644 (file)
 #endif
 
 #include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
 #include <errno.h>
+#include <arpa/inet.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/sdp.h>
 #include <bluetooth/sdp_lib.h>
 
-#include <glib.h>
-
-#include "glib-helper.h"
+#include "uuid-helper.h"
 
 char *bt_modalias(uint16_t source, uint16_t vendor,
                                        uint16_t product, uint16_t version)
 {
+       char *str;
+       int err;
+
        switch (source) {
        case 0x0001:
-               return g_strdup_printf("%s:v%04Xp%04Xd%04X",
+               err = asprintf(&str, "%s:v%04Xp%04Xd%04X",
                                        "bluetooth", vendor, product, version);
+               break;
        case 0x0002:
-               return g_strdup_printf("%s:v%04Xp%04Xd%04X",
+               err = asprintf(&str, "%s:v%04Xp%04Xd%04X",
                                        "usb", vendor, product, version);
+               break;
+       default:
+               return NULL;
        }
 
-       return NULL;
+       if (err < 0)
+               return NULL;
+
+       return str;
 }
 
 char *bt_uuid2string(uuid_t *uuid)
@@ -61,6 +72,7 @@ char *bt_uuid2string(uuid_t *uuid)
        unsigned short data3;
        unsigned int data4;
        unsigned short data5;
+       int err;
 
        if (!uuid)
                return NULL;
@@ -87,15 +99,13 @@ char *bt_uuid2string(uuid_t *uuid)
        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)
+       err = asprintf(&str, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x",
+                       ntohl(data0), ntohs(data1),
+                       ntohs(data2), ntohs(data3),
+                       ntohl(data4), ntohs(data5));
+       if (err < 0)
                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;
 }
 
@@ -141,7 +151,7 @@ static uint16_t name2class(const char *pattern)
        return 0;
 }
 
-static inline gboolean is_uuid128(const char *string)
+static inline bool is_uuid128(const char *string)
 {
        return (strlen(string) == 36 &&
                        string[8] == '-' &&
@@ -176,7 +186,7 @@ char *bt_name2string(const char *pattern)
 
        /* UUID 128 string format */
        if (is_uuid128(pattern))
-               return g_strdup(pattern);
+               return strdup(pattern);
 
        /* Friendly service name format */
        uuid16 = name2class(pattern);
@@ -208,12 +218,12 @@ int bt_string2uuid(uuid_t *uuid, const char *string)
                                &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);
+               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);
similarity index 100%
rename from src/glib-helper.h
rename to src/uuid-helper.h
diff --git a/test/ftp-client b/test/ftp-client
new file mode 100755 (executable)
index 0000000..78c32b3
--- /dev/null
@@ -0,0 +1,178 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser
+import os.path
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME='org.bluez.obex'
+PATH = '/org/bluez/obex'
+CLIENT_INTERFACE='org.bluez.obex.Client1'
+SESSION_INTERFACE='org.bluez.obex.Session1'
+FILE_TRASNFER_INTERFACE='org.bluez.obex.FileTransfer1'
+TRANSFER_INTERFACE='org.bluez.obex.Transfer1'
+
+def parse_options():
+       parser.add_option("-d", "--device", dest="device",
+                       help="Device to connect", metavar="DEVICE")
+       parser.add_option("-c", "--chdir", dest="new_dir",
+                       help="Change current directory to DIR", metavar="DIR")
+       parser.add_option("-l", "--list", action="store_true", dest="list_dir",
+                       help="List the current directory")
+       parser.add_option("-g", "--get", dest="get_file",
+                       help="Get FILE", metavar="FILE")
+       parser.add_option("-p", "--put", dest="put_file",
+                       help="Put FILE", metavar="FILE")
+       parser.add_option("-y", "--copy", dest="copy_file",
+                       help="Copy FILE", metavar="FILE")
+       parser.add_option("-m", "--move", dest="move_file",
+                       help="Move FILE", metavar="FILE")
+       parser.add_option("-n", "--destname", dest="dest_file",
+                       help="Destination FILE", metavar="FILE")
+       parser.add_option("-r", "--remove", dest="remove_file",
+                       help="Remove FILE", metavar="FILE")
+       parser.add_option("-v", "--verbose", action="store_true",
+                       dest="verbose")
+
+       return parser.parse_args()
+
+class FtpClient:
+       def __init__(self, session_path, verbose=False):
+               self.transferred = 0
+               self.transfer_path = None
+               self.transfer_size = 0
+               self.verbose = verbose
+               bus = dbus.SessionBus()
+               obj = bus.get_object(BUS_NAME, session_path)
+               self.session = dbus.Interface(obj, SESSION_INTERFACE)
+               self.ftp = dbus.Interface(obj, FILE_TRASNFER_INTERFACE)
+               bus.add_signal_receiver(self.properties_changed,
+                       dbus_interface="org.freedesktop.DBus.Properties",
+                       signal_name="PropertiesChanged",
+                       path_keyword="path")
+
+       def create_transfer_reply(self, path, properties):
+               self.transfer_path = path
+               self.transfer_size = properties["Size"]
+               if self.verbose:
+                       print("Transfer created: %s" % path)
+
+       def generic_reply(self):
+               if self.verbose:
+                       print("Operation succeeded")
+
+       def error(self, err):
+               print(err)
+               mainloop.quit()
+
+       def properties_changed(self, interface, properties, invalidated, path):
+               if path != self.transfer_path:
+                       return
+
+               if properties['Status'] == 'complete' or \
+                               properties['Status'] == 'error':
+                       if self.verbose:
+                               print("Transfer %s" % properties['Status'])
+                       mainloop.quit()
+                       return
+
+               if properties["Transferred"] == None:
+                       return
+
+               speed = (value - self.transferred) / 1000
+               print("Transfer progress %d/%d at %d kBps" % (value,
+                                                       self.transfer_size,
+                                                       speed))
+               self.transferred = value
+
+       def change_folder(self, new_dir):
+               for node in new_dir.split("/"):
+                       self.ftp.ChangeFolder(node)
+
+       def list_folder(self):
+               for i in self.ftp.ListFolder():
+                       if i["Type"] == "folder":
+                               print("%s/" % (i["Name"]))
+                       else:
+                               print("%s" % (i["Name"]))
+
+       def put_file(self, filename):
+               self.ftp.PutFile(os.path.abspath(filename),
+                               os.path.basename(filename),
+                               reply_handler=self.create_transfer_reply,
+                               error_handler=self.error)
+
+       def get_file(self, filename):
+               self.ftp.GetFile(os.path.abspath(filename),
+                               os.path.basename(filename),
+                               reply_handler=self.create_transfer_reply,
+                               error_handler=self.error)
+
+       def remove_file(self, filename):
+               self.ftp.Delete(filename,
+                               reply_handler=self.generic_reply,
+                               error_handler=self.error)
+
+       def move_file(self, filename, destname):
+               self.ftp.MoveFile(filename, destname,
+                               reply_handler=self.generic_reply,
+                               error_handler=self.error)
+
+       def copy_file(self, filename, destname):
+               self.ftp.CopyFile(filename, destname,
+                               reply_handler=self.generic_reply,
+                               error_handler=self.error)
+
+if  __name__ == '__main__':
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       parser = OptionParser()
+
+       (options, args) = parse_options()
+
+       if not options.device:
+               parser.print_help()
+               sys.exit(0)
+
+       bus = dbus.SessionBus()
+       mainloop = GObject.MainLoop()
+
+       client = dbus.Interface(bus.get_object(BUS_NAME, PATH,),
+                                                       CLIENT_INTERFACE)
+
+       print("Creating Session")
+       path = client.CreateSession(options.device, { "Target": "ftp" })
+
+       ftp_client = FtpClient(path, options.verbose)
+
+       if options.new_dir:
+               ftp_client.change_folder(options.new_dir)
+
+       if options.list_dir:
+               ftp_client.list_folder()
+
+       if options.get_file:
+               ftp_client.get_file(options.get_file)
+
+       if options.put_file:
+               ftp_client.put_file(options.put_file)
+
+       if options.move_file:
+               ftp_client.move_file(options.move_file, options.dest_file)
+
+       if options.copy_file:
+               ftp_client.copy_file(options.copy_file, options.dest_file)
+
+       if options.remove_file:
+               ftp_client.remove_file(options.remove_file)
+
+       mainloop.run()
diff --git a/test/map-client b/test/map-client
new file mode 100755 (executable)
index 0000000..b9695da
--- /dev/null
@@ -0,0 +1,232 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser
+import os
+from pprint import pformat
+import sys
+import dbus
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME='org.bluez.obex'
+PATH = '/org/bluez/obex'
+CLIENT_INTERFACE = 'org.bluez.obex.Client1'
+SESSION_INTERFACE = 'org.bluez.obex.Session1'
+MESSAGE_ACCESS_INTERFACE = 'org.bluez.obex.MessageAccess1'
+MESSAGE_INTERFACE = 'org.bluez.obex.Message1'
+TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1'
+
+def unwrap(x):
+    """Hack to unwrap D-Bus values, so that they're easier to read when
+    printed. Taken from d-feet """
+
+    if isinstance(x, list):
+        return map(unwrap, x)
+
+    if isinstance(x, tuple):
+        return tuple(map(unwrap, x))
+
+    if isinstance(x, dict):
+        return dict([(unwrap(k), unwrap(v)) for k, v in x.iteritems()])
+
+    for t in [unicode, str, long, int, float, bool]:
+        if isinstance(x, t):
+            return t(x)
+
+    return x
+
+def parse_options():
+       parser.add_option("-d", "--device", dest="device",
+                       help="Device to connect", metavar="DEVICE")
+       parser.add_option("-c", "--chdir", dest="new_dir",
+                       help="Change current directory to DIR", metavar="DIR")
+       parser.add_option("-l", "--lsdir", action="store_true", dest="ls_dir",
+                       help="List folders in current directory")
+       parser.add_option("-v", "--verbose", action="store_true", dest="verbose")
+       parser.add_option("-L", "--lsmsg", action="store", dest="ls_msg",
+                       help="List messages in supplied CWD subdir")
+       parser.add_option("-g", "--get", action="store", dest="get_msg",
+                       help="Get message contents")
+       parser.add_option("-p", "--push", action="store", dest="push_msg",
+                       help="Push message")
+       parser.add_option("--get-properties", action="store", dest="get_msg_properties",
+                       help="Get message properties")
+       parser.add_option("--mark-read", action="store", dest="mark_msg_read",
+                       help="Marks the messages as read")
+       parser.add_option("--mark-unread", action="store", dest="mark_msg_unread",
+                       help="Marks the messages as unread")
+       parser.add_option("--mark-deleted", action="store", dest="mark_msg_deleted",
+                       help="Deletes the message from the folder")
+       parser.add_option("--mark-undeleted", action="store", dest="mark_msg_undeleted",
+                       help="Undeletes the message")
+       parser.add_option("-u", "--update-inbox", action="store_true", dest="update_inbox",
+                       help="Checks for new mails")
+
+       return parser.parse_args()
+
+def set_folder(session, new_dir):
+       session.SetFolder(new_dir)
+
+class MapClient:
+       def __init__(self, session_path, verbose=False):
+               self.progress = 0
+               self.transfer_path = None
+               self.props = dict()
+               self.verbose = verbose
+               self.path = session_path
+               bus = dbus.SessionBus()
+               obj = bus.get_object(BUS_NAME, session_path)
+               self.session = dbus.Interface(obj, SESSION_INTERFACE)
+               self.map = dbus.Interface(obj, MESSAGE_ACCESS_INTERFACE)
+               bus.add_signal_receiver(self.properties_changed,
+                       dbus_interface="org.freedesktop.DBus.Properties",
+                       signal_name="PropertiesChanged",
+                       path_keyword="path")
+
+       def create_transfer_reply(self, path, properties):
+               self.transfer_path = path
+               self.props[path] = properties
+               if self.verbose:
+                       print("Transfer created: %s (file %s)" % (path,
+                                                       properties["Filename"]))
+
+       def generic_reply(self):
+               if self.verbose:
+                       print("Operation succeeded")
+
+       def error(self, err):
+               print(err)
+               mainloop.quit()
+
+       def transfer_complete(self, path):
+               if self.verbose:
+                       print("Transfer finished")
+               properties = self.props.get(path)
+               if properties == None:
+                       return
+               f = open(properties["Filename"], "r")
+               os.remove(properties["Filename"])
+               print(f.readlines())
+
+       def transfer_error(self, path):
+               print("Transfer %s error" % path)
+               mainloop.quit()
+
+       def properties_changed(self, interface, properties, invalidated, path):
+               req = self.props.get(path)
+               if req == None:
+                       return
+
+               if properties['Status'] == 'complete':
+                       self.transfer_complete(path)
+                       return
+
+               if properties['Status'] == 'error':
+                       self.transfer_error(path)
+                       return
+
+       def set_folder(self, new_dir):
+               self.map.SetFolder(new_dir)
+
+       def list_folders(self):
+               for i in self.map.ListFolders(dict()):
+                       print("%s/" % (i["Name"]))
+
+       def list_messages(self, folder):
+               ret = self.map.ListMessages(folder, dict())
+               print(pformat(unwrap(ret)))
+
+       def get_message(self, handle):
+               self.map.ListMessages("", dict())
+               path = self.path + "/message" + handle
+               obj = bus.get_object(BUS_NAME, path)
+               msg = dbus.Interface(obj, MESSAGE_INTERFACE)
+               msg.Get("", True, reply_handler=self.create_transfer_reply,
+                                               error_handler=self.error)
+
+       def push_message(self, filename):
+               self.map.PushMessage(filename, "telecom/msg/outbox", dict(),
+                               reply_handler=self.create_transfer_reply,
+                               error_handler=self.error)
+
+       def get_message_properties(self, handle):
+               self.map.ListMessages("", dict())
+               path = self.path + "/message" + handle
+               obj = bus.get_object(BUS_NAME, path)
+               msg = dbus.Interface(obj, "org.freedesktop.DBus.Properties")
+               ret = msg.GetAll(MESSAGE_INTERFACE)
+               print(pformat(unwrap(ret)))
+
+       def set_message_property(self, handle, prop, flag):
+               self.map.ListMessages("", dict())
+               path = self.path + "/message" + handle
+               obj = bus.get_object(BUS_NAME, path)
+               msg = dbus.Interface(obj, MESSAGE_INTERFACE)
+               msg.SetProperty (prop, flag);
+
+       def update_inbox(self):
+               self.map.UpdateInbox()
+
+
+if  __name__ == '__main__':
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       parser = OptionParser()
+
+       (options, args) = parse_options()
+
+       if not options.device:
+               parser.print_help()
+               exit(0)
+
+       bus = dbus.SessionBus()
+       mainloop = GObject.MainLoop()
+
+       client = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+                                                       CLIENT_INTERFACE)
+
+       print("Creating Session")
+       path = client.CreateSession(options.device, { "Target": "map" })
+
+       map_client = MapClient(path, options.verbose)
+
+       if options.new_dir:
+               map_client.set_folder(options.new_dir)
+
+       if options.ls_dir:
+               map_client.list_folders()
+
+       if options.ls_msg is not None:
+               map_client.list_messages(options.ls_msg)
+
+       if options.get_msg is not None:
+               map_client.get_message(options.get_msg)
+
+       if options.push_msg is not None:
+               map_client.push_message(options.push_msg)
+
+       if options.get_msg_properties is not None:
+               map_client.get_message_properties(options.get_msg_properties)
+
+       if options.mark_msg_read is not None:
+               map_client.set_message_property(options.mark_msg_read, "Read", True)
+
+       if options.mark_msg_unread is not None:
+               map_client.set_message_property(options.mark_msg_unread, "Read", False)
+
+       if options.mark_msg_deleted is not None:
+               map_client.set_message_property(options.mark_msg_deleted, "Deleted", True)
+
+       if options.mark_msg_undeleted is not None:
+               map_client.set_message_property(options.mark_msg_undeleted, "Deleted", False)
+
+       if options.update_inbox:
+               map_client.update_inbox()
+
+       mainloop.run()
index bc5ddaf..d9b5472 100755 (executable)
@@ -2,10 +2,12 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-import gobject
-
 import dbus
 import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 
 relevant_ifaces = [ "org.bluez.Adapter1", "org.bluez.Device1" ]
 
@@ -48,5 +50,5 @@ if __name__ == '__main__':
                        dbus_interface="org.freedesktop.DBus.ObjectManager",
                        signal_name="InterfacesRemoved")
 
-       mainloop = gobject.MainLoop()
+       mainloop = GObject.MainLoop()
        mainloop.run()
diff --git a/test/opp-client b/test/opp-client
new file mode 100755 (executable)
index 0000000..62d5b84
--- /dev/null
@@ -0,0 +1,119 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from optparse import OptionParser
+import os.path
+import sys
+import dbus
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME='org.bluez.obex'
+PATH = '/org/bluez/obex'
+CLIENT_INTERFACE='org.bluez.obex.Client1'
+SESSION_INTERFACE='org.bluez.obex.Session1'
+OBJECT_PUSH_INTERFACE='org.bluez.obex.ObjectPush1'
+TRANSFER_INTERFACE='org.bluez.obex.Transfer1'
+
+def parse_options():
+       parser.add_option("-d", "--device", dest="device",
+                       help="Device to connect", metavar="DEVICE")
+       parser.add_option("-p", "--pull", dest="pull_to_file",
+                       help="Pull vcard and store in FILE", metavar="FILE")
+       parser.add_option("-s", "--send", dest="send_file",
+                       help="Send FILE", metavar="FILE")
+       parser.add_option("-v", "--verbose", action="store_true",
+                       dest="verbose")
+
+       return parser.parse_args()
+
+class OppClient:
+       def __init__(self, session_path, verbose=False):
+               self.transferred = 0
+               self.transfer_path = None
+               self.verbose = verbose
+               bus = dbus.SessionBus()
+               obj = bus.get_object(BUS_NAME, session_path)
+               self.session = dbus.Interface(obj, SESSION_INTERFACE)
+               self.opp = dbus.Interface(obj, OBJECT_PUSH_INTERFACE)
+               bus.add_signal_receiver(self.properties_changed,
+                       dbus_interface="org.freedesktop.DBus.Properties",
+                       signal_name="PropertiesChanged",
+                       path_keyword="path")
+
+       def create_transfer_reply(self, path, properties):
+               self.transfer_path = path
+               self.transfer_size = properties["Size"]
+               if self.verbose:
+                       print("Transfer created: %s" % path)
+
+       def error(self, err):
+               print(err)
+               mainloop.quit()
+
+       def properties_changed(self, interface, properties, invalidated, path):
+               if path != self.transfer_path:
+                       return
+
+               if "Status" in properties and \
+                               (properties["Status"] == "complete" or \
+                               properties["Status"] == "error"):
+                       if self.verbose:
+                               print("Transfer %s" % properties["Status"])
+                       mainloop.quit()
+                       return
+
+               if "Transferred" not in properties:
+                       return
+
+               value = properties["Transferred"]
+               speed = (value - self.transferred) / 1000
+               print("Transfer progress %d/%d at %d kBps" % (value,
+                                                       self.transfer_size,
+                                                       speed))
+               self.transferred = value
+
+       def pull_business_card(self, filename):
+               self.opp.PullBusinessCard(os.path.abspath(filename),
+                               reply_handler=self.create_transfer_reply,
+                               error_handler=self.error)
+
+       def send_file(self, filename):
+               self.opp.SendFile(os.path.abspath(filename),
+                               reply_handler=self.create_transfer_reply,
+                               error_handler=self.error)
+
+if  __name__ == '__main__':
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       parser = OptionParser()
+
+       (options, args) = parse_options()
+
+       if not options.device:
+               parser.print_help()
+               sys.exit(0)
+
+       bus = dbus.SessionBus()
+       mainloop = GObject.MainLoop()
+
+       client = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+                                                       CLIENT_INTERFACE)
+
+       print("Creating Session")
+       path = client.CreateSession(options.device, { "Target": "OPP" })
+
+       opp_client = OppClient(path, options.verbose)
+
+       if options.pull_to_file:
+               opp_client.pull_business_card(options.pull_to_file)
+
+       if options.send_file:
+               opp_client.send_file(options.send_file)
+
+       mainloop.run()
diff --git a/test/pbap-client b/test/pbap-client
new file mode 100755 (executable)
index 0000000..51e26eb
--- /dev/null
@@ -0,0 +1,175 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import os
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+
+BUS_NAME='org.bluez.obex'
+PATH = '/org/bluez/obex'
+CLIENT_INTERFACE = 'org.bluez.obex.Client1'
+SESSION_INTERFACE = 'org.bluez.obex.Session1'
+PHONEBOOK_ACCESS_INTERFACE = 'org.bluez.obex.PhonebookAccess1'
+TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1'
+
+class Transfer:
+       def __init__(self, callback_func):
+               self.callback_func = callback_func
+               self.path = None
+               self.filename = None
+
+class PbapClient:
+       def __init__(self, session_path):
+               self.transfers = 0
+               self.props = dict()
+               self.flush_func = None
+               bus = dbus.SessionBus()
+               obj = bus.get_object(BUS_NAME, session_path)
+               self.session = dbus.Interface(obj, SESSION_INTERFACE)
+               self.pbap = dbus.Interface(obj, PHONEBOOK_ACCESS_INTERFACE)
+               bus.add_signal_receiver(self.properties_changed,
+                       dbus_interface="org.freedesktop.DBus.Properties",
+                       signal_name="PropertiesChanged",
+                       path_keyword="path")
+
+       def register(self, path, properties, transfer):
+               transfer.path = path
+               transfer.filename = properties["Filename"]
+               self.props[path] = transfer
+               print("Transfer created: %s (file %s)" % (path,
+                                                       transfer.filename))
+
+       def error(self, err):
+               print(err)
+               mainloop.quit()
+
+       def transfer_complete(self, path):
+               req = self.props.get(path)
+               if req == None:
+                       return
+               self.transfers -= 1
+               print("Transfer %s complete" % path)
+               try:
+                       f = open(req.filename, "r")
+                       os.remove(req.filename)
+                       lines = f.readlines()
+                       del self.props[path]
+                       req.callback_func(lines)
+               except:
+                       pass
+
+               if (len(self.props) == 0) and (self.transfers == 0):
+                       if self.flush_func != None:
+                               f = self.flush_func
+                               self.flush_func = None
+                               f()
+
+       def transfer_error(self, path):
+               print("Transfer %s error" % path)
+               mainloop.quit()
+
+       def properties_changed(self, interface, properties, invalidated, path):
+               req = self.props.get(path)
+               if req == None:
+                       return
+
+               if properties['Status'] == 'complete':
+                       self.transfer_complete(path)
+                       return
+
+               if properties['Status'] == 'error':
+                       self.transfer_error(path)
+                       return
+
+       def pull(self, vcard, params, func):
+               req = Transfer(func)
+               self.pbap.Pull(vcard, "", params,
+                       reply_handler=lambda o, p: self.register(o, p, req),
+                       error_handler=self.error)
+               self.transfers += 1
+
+       def pull_all(self, params, func):
+               req = Transfer(func)
+               self.pbap.PullAll("", params,
+                       reply_handler=lambda o, p: self.register(o, p, req),
+                       error_handler=self.error)
+               self.transfers += 1
+
+       def flush_transfers(self, func):
+               if (len(self.props) == 0) and (self.transfers == 0):
+                       return
+               self.flush_func = func
+
+       def interface(self):
+               return self.pbap
+
+if  __name__ == '__main__':
+
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+       bus = dbus.SessionBus()
+       mainloop = GObject.MainLoop()
+
+       client = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+                                                       CLIENT_INTERFACE)
+
+       if (len(sys.argv) < 2):
+               print("Usage: %s <device>" % (sys.argv[0]))
+               sys.exit(1)
+
+       print("Creating Session")
+       session_path = client.CreateSession(sys.argv[1], { "Target": "PBAP" })
+
+       pbap_client = PbapClient(session_path)
+
+       def process_result(lines, header):
+               if header != None:
+                       print(header)
+               for line in lines:
+                       print(line),
+               print
+
+       def test_paths(paths):
+               if len(paths) == 0:
+                       print
+                       print("FINISHED")
+                       mainloop.quit()
+                       return
+
+               path = paths[0]
+
+               print("\n--- Select Phonebook %s ---\n" % (path))
+               pbap_client.interface().Select("int", path)
+
+               print("\n--- GetSize ---\n")
+               ret = pbap_client.interface().GetSize()
+               print("Size = %d\n" % (ret))
+
+               print("\n--- List vCard ---\n")
+               try:
+                       ret = pbap_client.interface().List(dbus.Dictionary())
+               except:
+                       ret = []
+
+               params = dbus.Dictionary({ "Format" : "vcard30",
+                                               "Fields" : ["PHOTO"] })
+               for item in ret:
+                       print("%s : %s" % (item[0], item[1]))
+                       pbap_client.pull(item[0], params,
+                                       lambda x: process_result(x, None))
+
+               pbap_client.pull_all(params, lambda x: process_result(x,
+                                                       "\n--- PullAll ---\n"))
+
+               pbap_client.flush_transfers(lambda: test_paths(paths[1:]))
+
+       test_paths(["PB", "ICH", "OCH", "MCH", "CCH"])
+
+       mainloop.run()
index 854e1af..a69299a 100755 (executable)
@@ -2,13 +2,15 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-from gi.repository import GObject
-
+from optparse import OptionParser
 import sys
 import dbus
 import dbus.service
 import dbus.mainloop.glib
-from optparse import OptionParser
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 import bluezutils
 
 BUS_NAME = 'org.bluez'
index 590f83a..0164cff 100755 (executable)
@@ -6,7 +6,10 @@ import sys
 import dbus
 import dbus.service
 import dbus.mainloop.glib
-import gobject
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 import bluezutils
 
 A2DP_SOURCE_UUID = "0000110A-0000-1000-8000-00805F9B34FB"
@@ -94,7 +97,7 @@ if __name__ == '__main__':
 
        path = "/test/endpoint"
        endpoint = Endpoint(bus, path)
-       mainloop = gobject.MainLoop()
+       mainloop = GObject.MainLoop()
 
        properties = dbus.Dictionary({ "UUID" : A2DP_SOURCE_UUID,
                                        "Codec" : SBC_CODEC,
index 01bec06..23e78ad 100755 (executable)
@@ -1,12 +1,16 @@
 #!/usr/bin/python
 
 from __future__ import print_function
+
 import os
 import sys
 import dbus
 import dbus.service
 import dbus.mainloop.glib
-import gobject
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 import bluezutils
 
 class Player(dbus.service.Object):
@@ -56,7 +60,7 @@ class Player(dbus.service.Object):
                                        signature="sv")
 
                        handler = InputHandler(self)
-                       gobject.io_add_watch(sys.stdin, gobject.IO_IN,
+                       GObject.io_add_watch(sys.stdin, GObject.IO_IN,
                                                        handler.handle)
 
        @dbus.service.method("org.freedesktop.DBus.Properties",
@@ -136,7 +140,7 @@ if __name__ == '__main__':
 
        path = "/test/player"
        player = Player(bus, path)
-       mainloop = gobject.MainLoop()
+       mainloop = GObject.MainLoop()
 
        if len(sys.argv) > 2:
                player.set_object(sys.argv[2])
diff --git a/test/simple-service b/test/simple-service
deleted file mode 100755 (executable)
index 02d7648..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/usr/bin/python
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-import sys
-import time
-import dbus
-import bluezutils
-
-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()
-
-if len(sys.argv) > 1:
-       path = bluezutils.find_adapter(sys.argv[1]).object_path
-else:
-       path = bluezutils.find_adapter().object_path
-
-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))
index 5deeda4..959a437 100755 (executable)
@@ -2,10 +2,10 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from optparse import OptionParser, make_option
 import sys
-import dbus
 import time
-from optparse import OptionParser, make_option
+import dbus
 import bluezutils
 
 bus = dbus.SystemBus()
index 066e537..43b3cf3 100755 (executable)
@@ -1,12 +1,17 @@
 #!/usr/bin/python
+
 from __future__ import absolute_import, print_function, unicode_literals
+
+import optparse
+import os
+import sys
 import dbus
 import dbus.service
 import dbus.mainloop.glib
-import gobject
-import optparse
-import sys
-import os
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 
 BUS_NAME = 'org.bluez'
 ALERT_INTERFACE = 'org.bluez.Alert1'
@@ -149,7 +154,7 @@ parser.disable_interspersed_args()
 
 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 bus = dbus.SystemBus()
-mainloop = gobject.MainLoop()
+mainloop = GObject.MainLoop()
 alert = dbus.Interface(bus.get_object(BUS_NAME, BLUEZ_OBJECT_PATH),
                                                                ALERT_INTERFACE)
 alert_agent = AlertAgent(bus, TEST_OBJECT_PATH, alert, mainloop)
index 75bd7d7..393f79c 100755 (executable)
@@ -6,13 +6,16 @@ from __future__ import absolute_import, print_function, unicode_literals
 Cycling Speed and Cadence test script
 '''
 
-import gobject
-
+from optparse import OptionParser, make_option
 import sys
 import dbus
 import dbus.service
 import dbus.mainloop.glib
-from optparse import OptionParser, make_option
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
+import bluezutils
 
 BUS_NAME = 'org.bluez'
 CYCLINGSPEED_MANAGER_INTERFACE = 'org.bluez.CyclingSpeedManager1'
@@ -141,7 +144,7 @@ if __name__ == "__main__":
        device_path = device.object_path
 
        cscmanager = dbus.Interface(bus.get_object(BUS_NAME, adapter_path),
-                                               CYCLINGSPEED_WATCHER_INTERFACE)
+                                               CYCLINGSPEED_MANAGER_INTERFACE)
 
        watcher_path = "/test/watcher"
        watcher = Watcher(bus, watcher_path)
@@ -190,5 +193,5 @@ if __name__ == "__main__":
                        print("Unknown command")
                        sys.exit(1)
 
-       mainloop = gobject.MainLoop()
+       mainloop = GObject.MainLoop()
        mainloop.run()
index 3d7b852..b490d53 100755 (executable)
@@ -2,13 +2,15 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-from gi.repository import GObject
-
+from optparse import OptionParser, make_option
+import re
 import sys
 import dbus
 import dbus.mainloop.glib
-import re
-from optparse import OptionParser, make_option
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 import bluezutils
 
 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
index c13bfac..73b8161 100755 (executable)
@@ -2,11 +2,13 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-from gi.repository import GObject
-
+from optparse import OptionParser, make_option
 import dbus
 import dbus.mainloop.glib
-from optparse import OptionParser, make_option
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 import bluezutils
 
 compact = False
index 052a602..343f29c 100755 (executable)
@@ -3,11 +3,14 @@
 from __future__ import absolute_import, print_function, unicode_literals
 # -*- coding: utf-8 -*-
 
+import sys
 import dbus
 import dbus.service
-import gobject
 from dbus.mainloop.glib import DBusGMainLoop
-import sys
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 
 BUS_NAME = 'org.bluez'
 PATH = '/org/bluez'
@@ -16,7 +19,7 @@ HEALTH_MANAGER_INTERFACE = 'org.bluez.HealthManager1'
 HEALTH_DEVICE_INTERFACE = 'org.bluez.HealthDevice1'
 
 DBusGMainLoop(set_as_default=True)
-loop = gobject.MainLoop()
+loop = GObject.MainLoop()
 
 bus = dbus.SystemBus()
 
@@ -48,7 +51,7 @@ def enter_mainloop():
        try:
                print("Entering main lopp, push Ctrl+C for finish")
 
-               mainloop = gobject.MainLoop()
+               mainloop = GObject.MainLoop()
                mainloop.run()
        except KeyboardInterrupt:
                pass
index 32afd71..52be535 100755 (executable)
@@ -3,11 +3,14 @@
 from __future__ import absolute_import, print_function, unicode_literals
 # -*- coding: utf-8 -*-
 
+import sys
 import dbus
 import dbus.service
-import gobject
 from dbus.mainloop.glib import DBusGMainLoop
-import sys
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 
 BUS_NAME = 'org.bluez'
 PATH = '/org/bluez'
@@ -16,7 +19,7 @@ HEALTH_MANAGER_INTERFACE = 'org.bluez.HealthManager1'
 HEALTH_DEVICE_INTERFACE = 'org.bluez.HealthDevice1'
 
 DBusGMainLoop(set_as_default=True)
-loop = gobject.MainLoop()
+loop = GObject.MainLoop()
 
 bus = dbus.SystemBus()
 
index f26b3db..5e4e7e5 100755 (executable)
@@ -6,13 +6,15 @@ from __future__ import absolute_import, print_function, unicode_literals
 Heart Rate Monitor test script
 '''
 
-import gobject
-
+from optparse import OptionParser, make_option
 import sys
 import dbus
 import dbus.service
 import dbus.mainloop.glib
-from optparse import OptionParser, make_option
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 import bluezutils
 
 BUS_NAME = 'org.bluez'
@@ -102,5 +104,5 @@ if __name__ == "__main__":
                        print("unknown command")
                        sys.exit(1)
 
-       mainloop = gobject.MainLoop()
+       mainloop = GObject.MainLoop()
        mainloop.run()
index 873de0a..a806043 100755 (executable)
@@ -2,16 +2,18 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-from gi.repository import GObject
-
+from optparse import OptionParser, make_option
 import os
+from socket import SOCK_SEQPACKET, socket
 import sys
 import dbus
-import glib
 import dbus.service
 import dbus.mainloop.glib
-from optparse import OptionParser, make_option
-from socket import SOCK_SEQPACKET, socket
+import glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 
 mainloop = None
 audio_supported = True
index 1e3882f..4f5994f 100755 (executable)
@@ -2,10 +2,12 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-from gi.repository import GObject
-
 import dbus
 import dbus.mainloop.glib
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 import bluezutils
 
 def interfaces_added(path, interfaces):
index 197e3c2..00a2585 100755 (executable)
@@ -2,10 +2,10 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from optparse import OptionParser, make_option
 import sys
 import time
 import dbus
-from optparse import OptionParser, make_option
 import bluezutils
 
 bus = dbus.SystemBus()
index 3e8713f..6f09486 100755 (executable)
@@ -2,10 +2,10 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from optparse import OptionParser, make_option
 import sys
 import time
 import dbus
-from optparse import OptionParser, make_option
 import bluezutils
 
 bus = dbus.SystemBus()
index b78d00c..2791580 100755 (executable)
@@ -2,15 +2,17 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
-from gi.repository import GObject
-
+from optparse import OptionParser, make_option
 import os
 import sys
 import uuid
 import dbus
 import dbus.service
 import dbus.mainloop.glib
-from optparse import OptionParser, make_option
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 
 class Profile(dbus.service.Object):
        fd = -1
index 2f47824..66b7bc2 100755 (executable)
@@ -6,12 +6,14 @@ from __future__ import absolute_import, print_function, unicode_literals
 Proximity Monitor test script
 '''
 
-import gobject
-
+from optparse import OptionParser, make_option
 import sys
 import dbus
 import dbus.mainloop.glib
-from optparse import OptionParser, make_option
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 import bluezutils
 
 BUS_NAME = 'org.bluez'
@@ -64,5 +66,5 @@ if __name__ == "__main__":
        print("Proximity SetProperty('%s', '%s')" % (args[0], args[1]))
        device_prop.Set(PROXIMITY_MONITOR_INTERFACE, args[0], args[1])
 
-       mainloop = gobject.MainLoop()
+       mainloop = GObject.MainLoop()
        mainloop.run()
index 6c143be..7e67c23 100755 (executable)
@@ -6,13 +6,15 @@ from __future__ import absolute_import, print_function, unicode_literals
 Thermometer test script
 '''
 
-import gobject
-
+from optparse import OptionParser, make_option
 import sys
 import dbus
 import dbus.service
 import dbus.mainloop.glib
-from optparse import OptionParser, make_option
+try:
+  from gi.repository import GObject
+except ImportError:
+  import gobject as GObject
 import bluezutils
 
 BUS_NAME = 'org.bluez'
@@ -93,5 +95,5 @@ if __name__ == "__main__":
                        print("unknown command")
                        sys.exit(1)
 
-       mainloop = gobject.MainLoop()
+       mainloop = GObject.MainLoop()
        mainloop.run()
diff --git a/tools/3dsp.c b/tools/3dsp.c
new file mode 100644 (file)
index 0000000..68dcbb5
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ *
+ *  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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include "monitor/mainloop.h"
+#include "monitor/bt.h"
+#include "src/shared/timeout.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+
+#define LT_ADDR 0x01
+#define PKT_TYPE 0x0008                /* 0x0008 = EDR + DM1, 0xff1e = BR only */
+#define SERVICE_DATA LT_ADDR
+
+static struct bt_hci *hci_dev;
+
+static bool reset_on_init = false;
+static bool reset_on_shutdown = false;
+
+static bool shutdown_timeout(void *user_data)
+{
+       mainloop_quit();
+
+       return false;
+}
+
+static void shutdown_complete(const void *data, uint8_t size, void *user_data)
+{
+       unsigned int id = PTR_TO_UINT(user_data);
+
+       timeout_remove(id);
+       mainloop_quit();
+}
+
+static void shutdown_device(void)
+{
+       unsigned int id;
+
+       bt_hci_flush(hci_dev);
+
+       if (reset_on_shutdown) {
+               id = timeout_add(5000, shutdown_timeout, NULL, NULL);
+
+               bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+                               shutdown_complete, UINT_TO_PTR(id), NULL);
+       } else
+               mainloop_quit();
+}
+
+static void slave_broadcast_receive(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       printf("Slave broadcast receiption enabled\n");
+}
+
+static void sync_train_received(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_evt_sync_train_received *evt = data;
+       struct bt_hci_cmd_set_slave_broadcast_receive cmd;
+
+       if (evt->status) {
+               printf("Failed to synchronize with 3D display\n");
+               shutdown_device();
+               return;
+       }
+
+       cmd.enable = 0x01;
+       memcpy(cmd.bdaddr, evt->bdaddr, 6);
+       cmd.lt_addr = evt->lt_addr;
+       cmd.interval = evt->interval;
+       cmd.offset = evt->offset;
+       cmd.instant = evt->instant;
+       cmd.timeout = cpu_to_le16(0xfffe);
+       cmd.accuracy = 250;
+       cmd.skip = 20;
+       cmd.pkt_type = cpu_to_le16(PKT_TYPE);
+       memcpy(cmd.map, evt->map, 10);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_RECEIVE,
+                                       &cmd, sizeof(cmd),
+                                       slave_broadcast_receive, NULL, NULL);
+}
+
+static void truncated_page_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_evt_truncated_page_complete *evt = data;
+       struct bt_hci_cmd_receive_sync_train cmd;
+
+       if (evt->status) {
+               printf("Failed to contact 3D display\n");
+               shutdown_device();
+               return;
+       }
+
+       printf("Attempt to synchronize with 3D display\n");
+
+       bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_RECEIVED,
+                                       sync_train_received, NULL, NULL);
+
+       memcpy(cmd.bdaddr, evt->bdaddr, 6);
+       cmd.timeout = cpu_to_le16(0x4000);
+       cmd.window = cpu_to_le16(0x0100);
+       cmd.interval = cpu_to_le16(0x0080);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_RECEIVE_SYNC_TRAIN, &cmd, sizeof(cmd),
+                                                       NULL, NULL, NULL);
+}
+
+static void ext_inquiry_result(const void *data, uint8_t size, void *user_data)
+{
+       const struct bt_hci_evt_ext_inquiry_result *evt = data;
+
+       if (evt->dev_class[0] != 0x3c || evt->dev_class[1] != 0x04
+                                       || evt->dev_class[2] != 0x08)
+               return;
+
+       if (evt->data[0]) {
+               struct bt_hci_cmd_truncated_page cmd;
+
+               printf("Found 3D display\n");
+
+               bt_hci_send(hci_dev, BT_HCI_CMD_INQUIRY_CANCEL, NULL, 0,
+                                                       NULL, NULL, NULL);
+
+               bt_hci_register(hci_dev, BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE,
+                                       truncated_page_complete, NULL, NULL);
+
+               memcpy(cmd.bdaddr, evt->bdaddr, 6);
+               cmd.pscan_rep_mode = evt->pscan_rep_mode;
+               cmd.clock_offset = evt->clock_offset;
+
+               bt_hci_send(hci_dev, BT_HCI_CMD_TRUNCATED_PAGE,
+                                       &cmd, sizeof(cmd), NULL, NULL, NULL);
+       }
+}
+
+static void inquiry_complete(const void *data, uint8_t size, void *user_data)
+{
+       printf("No 3D display found\n");
+
+       shutdown_device();
+}
+
+static void inquiry_started(const void *data, uint8_t size, void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               printf("Failed to search for 3D display\n");
+               shutdown_device();
+               return;
+       }
+
+       printf("Searching for 3D display\n");
+}
+
+static void start_glasses(void)
+{
+       struct bt_hci_cmd_inquiry cmd;
+       uint8_t evtmask1[] = { 0x03, 0xe0, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00 };
+       uint8_t evtmask2[] = { 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 };
+       uint8_t inqmode = 0x02;
+
+       if (reset_on_init) {
+               bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+                                                       NULL, NULL, NULL);
+               bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask1, 8,
+                                                       NULL, NULL, NULL);
+       }
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK_PAGE2, evtmask2, 8,
+                                                       NULL, NULL, NULL);
+       bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_INQUIRY_MODE, &inqmode, 1,
+                                                       NULL, NULL, NULL);
+
+       bt_hci_register(hci_dev, BT_HCI_EVT_INQUIRY_COMPLETE,
+                                               inquiry_complete, NULL, NULL);
+       bt_hci_register(hci_dev, BT_HCI_EVT_EXT_INQUIRY_RESULT,
+                                               ext_inquiry_result, NULL, NULL);
+
+       cmd.lap[0] = 0x33;
+       cmd.lap[1] = 0x8b;
+       cmd.lap[2] = 0x9e;
+       cmd.length = 0x08;
+       cmd.num_resp = 0x00;
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_INQUIRY, &cmd, sizeof(cmd),
+                                               inquiry_started, NULL, NULL);
+}
+
+static void conn_request(const void *data, uint8_t size, void *user_data)
+{
+       const struct bt_hci_evt_conn_request *evt = data;
+       struct bt_hci_cmd_accept_conn_request cmd;
+
+       printf("Incoming connection from 3D glasses\n");
+
+       memcpy(cmd.bdaddr, evt->bdaddr, 6);
+       cmd.role = 0x00;
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_ACCEPT_CONN_REQUEST, &cmd, sizeof(cmd),
+                                                       NULL, NULL, NULL);
+}
+
+static bool sync_train_active = false;
+
+static void sync_train_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       sync_train_active = false;
+}
+
+static void slave_page_response_timeout(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       struct bt_hci_cmd_write_sync_train_params cmd;
+
+       if (sync_train_active)
+               return;
+
+       printf("Starting new synchronization train\n");
+
+       cmd.min_interval = cpu_to_le16(0x0050);
+       cmd.max_interval = cpu_to_le16(0x00a0);
+       cmd.timeout = cpu_to_le32(0x0002ee00);          /* 120 sec */
+       cmd.service_data = SERVICE_DATA;
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SYNC_TRAIN_PARAMS,
+                                       &cmd, sizeof(cmd), NULL, NULL, NULL);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS, NULL, 0,
+                                                       NULL, NULL, NULL);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_START_SYNC_TRAIN, NULL, 0,
+                                                       NULL, NULL, NULL);
+
+       sync_train_active = true;
+}
+
+static void inquiry_resp_tx_power(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_read_inquiry_resp_tx_power *rsp = data;
+       struct bt_hci_cmd_write_ext_inquiry_response cmd;
+       uint8_t inqdata[] = { 0x03, 0x3d, 0x03, 0x43, 0x02, 0x0a, 0x00, 0x00 };
+       uint8_t devclass[] = { 0x3c, 0x04, 0x08 };
+       uint8_t scanmode = 0x03;
+
+       inqdata[6] = (uint8_t) rsp->level;
+
+       cmd.fec = 0x00;
+       memset(cmd.data, 0, sizeof(cmd.data));
+       memcpy(cmd.data, inqdata, sizeof(inqdata));
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_EXT_INQUIRY_RESPONSE,
+                                       &cmd, sizeof(cmd), NULL, NULL, NULL);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_CLASS_OF_DEV, devclass, 3,
+                                                       NULL, NULL, NULL);
+       bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SCAN_ENABLE, &scanmode, 1,
+                                                       NULL, NULL, NULL);
+}
+
+static void start_display(void)
+{
+       struct bt_hci_cmd_set_slave_broadcast cmd;
+       uint8_t bcastdata[20] = { LT_ADDR, 0x03, 0x11, 0x23, 0x42, };
+       uint8_t evtmask1[] = { 0x1c, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+       uint8_t evtmask2[] = { 0x00, 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00 };
+       uint8_t sspmode = 0x01;
+       uint8_t ltaddr = LT_ADDR;
+
+       if (reset_on_init) {
+               bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+                                                       NULL, NULL, NULL);
+               bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK, evtmask1, 8,
+                                                       NULL, NULL, NULL);
+       }
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK_PAGE2, evtmask2, 8,
+                                                       NULL, NULL, NULL);
+       bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE, &sspmode, 1,
+                                                       NULL, NULL, NULL);
+       bt_hci_send(hci_dev, BT_HCI_CMD_SET_RESERVED_LT_ADDR, &ltaddr, 1,
+                                                       NULL, NULL, NULL);
+       bt_hci_send(hci_dev, BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS, NULL, 0,
+                                                       NULL, NULL, NULL);
+
+       bt_hci_register(hci_dev, BT_HCI_EVT_CONN_REQUEST,
+                                               conn_request, NULL, NULL);
+
+       bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_PAGE_RESPONSE_TIMEOUT,
+                               slave_page_response_timeout, NULL, NULL);
+       bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_COMPLETE,
+                                       sync_train_complete, NULL, NULL);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_READ_INQUIRY_RESP_TX_POWER, NULL, 0,
+                                       inquiry_resp_tx_power, NULL, NULL);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_DATA,
+                       bcastdata, sizeof(bcastdata), NULL, NULL, NULL);
+
+       cmd.enable = 0x01;
+       cmd.lt_addr = LT_ADDR;
+       cmd.lpo_allowed = 0x01;
+       cmd.pkt_type = cpu_to_le16(PKT_TYPE);
+       cmd.min_interval = cpu_to_le16(0x0050);         /* 50 ms */
+       cmd.max_interval = cpu_to_le16(0x00a0);         /* 100 ms */
+       cmd.timeout = cpu_to_le16(0xfffe);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST, &cmd, sizeof(cmd),
+                                                       NULL, NULL, NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+       static bool terminated = false;
+
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               if (!terminated) {
+                       shutdown_device();
+                       terminated = true;
+               }
+               break;
+       }
+}
+
+static void usage(void)
+{
+       printf("3dsp - 3D Synchronization Profile testing\n"
+               "Usage:\n");
+       printf("\t3dsp [options]\n");
+       printf("options:\n"
+               "\t-D, --display          Use display role\n"
+               "\t-G, --glasses          Use glasses role\n"
+               "\t-i, --index <num>      Use specified controller\n"
+               "\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+       { "display", no_argument,       NULL, 'D' },
+       { "glasses", no_argument,       NULL, 'G' },
+       { "index",   required_argument, NULL, 'i' },
+       { "raw",     no_argument,       NULL, 'r' },
+       { "version", no_argument,       NULL, 'v' },
+       { "help",    no_argument,       NULL, 'h' },
+       { }
+};
+
+int main(int argc, char *argv[])
+{
+       bool display_role = false, glasses_role = false;
+       uint16_t index = 0;
+       const char *str;
+       bool use_raw = false;
+       sigset_t mask;
+       int exit_status;
+
+       for (;;) {
+               int opt;
+
+               opt = getopt_long(argc, argv, "DGi:rvh", main_options, NULL);
+               if (opt < 0)
+                       break;
+
+               switch (opt) {
+               case 'D':
+                       display_role = true;
+                       break;
+               case 'G':
+                       glasses_role = true;
+                       break;
+               case 'i':
+                       if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+                               str = optarg + 3;
+                       else
+                               str = optarg;
+                       if (!isdigit(*str)) {
+                               usage();
+                               return EXIT_FAILURE;
+                       }
+                       index = atoi(str);
+                       break;
+               case 'r':
+                       use_raw = true;
+                       break;
+               case 'v':
+                       printf("%s\n", VERSION);
+                       return EXIT_SUCCESS;
+               case 'h':
+                       usage();
+                       return EXIT_SUCCESS;
+               default:
+                       return EXIT_FAILURE;
+               }
+       }
+
+       if (argc - optind > 0) {
+               fprintf(stderr, "Invalid command line parameters\n");
+               return EXIT_FAILURE;
+       }
+
+       if (display_role == glasses_role) {
+               fprintf(stderr, "Specify either display or glasses role\n");
+               return EXIT_FAILURE;
+       }
+
+       mainloop_init();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       printf("3D Synchronization Profile testing ver %s\n", VERSION);
+
+       if (use_raw) {
+               hci_dev = bt_hci_new_raw_device(index);
+               if (!hci_dev) {
+                       fprintf(stderr, "Failed to open HCI raw device\n");
+                       return EXIT_FAILURE;
+               }
+       } else {
+               hci_dev = bt_hci_new_user_channel(index);
+               if (!hci_dev) {
+                       fprintf(stderr, "Failed to open HCI user channel\n");
+                       return EXIT_FAILURE;
+               }
+
+               reset_on_init = true;
+               reset_on_shutdown = true;
+       }
+
+       if (display_role)
+               start_display();
+       else if (glasses_role)
+               start_glasses();
+
+       exit_status = mainloop_run();
+
+       bt_hci_unref(hci_dev);
+
+       return exit_status;
+}
index 16f15bc..6192f7e 100644 (file)
@@ -496,6 +496,7 @@ static bool find_amp_controller(void)
        struct hci_dev_list_req *dl;
        struct hci_dev_req *dr;
        int fd, i;
+       bool result;
 
        fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
        if (fd < 0) {
@@ -515,8 +516,8 @@ static bool find_amp_controller(void)
 
        if (ioctl(fd, HCIGETDEVLIST, (void *) dl) < 0) {
                perror("Failed to get HCI device list");
-               close(fd);
-               return false;
+               result = false;
+               goto done;
        }
 
        for (i = 0; i< dl->dev_num; i++) {
@@ -541,9 +542,12 @@ static bool find_amp_controller(void)
                }
        }
 
-       close(fd);
+       result = true;
 
-       return true;
+done:
+       free(dl);
+       close(fd);
+       return result;
 }
 
 int main(int argc ,char *argv[])
index d237742..a4deaac 100644 (file)
@@ -170,6 +170,10 @@ static void print_vendor(a2dp_vendor_codec_t *vendor)
                        vendor->codec_id[0], vendor->codec_id[1]);
 }
 
+static void print_mpeg24(a2dp_mpeg_t *mpeg)
+{
+       printf("\tMedia Codec: MPEG24\n");
+}
 
 static void print_mpeg12(a2dp_mpeg_t *mpeg)
 {
@@ -304,6 +308,9 @@ static void print_media_codec(struct avdtp_media_codec_capability *cap)
        case A2DP_CODEC_MPEG12:
                print_mpeg12((void *) cap->data);
                break;
+       case A2DP_CODEC_MPEG24:
+               print_mpeg24((void *) cap->data);
+               break;
        case A2DP_CODEC_VENDOR:
                print_vendor((void *) cap->data);
                break;
index ff1b307..4649ad5 100644 (file)
@@ -389,7 +389,7 @@ static int cmd_chiprev(int transport, int argc, char *argv[])
 
 static int cmd_buildname(int transport, int argc, char *argv[])
 {
-       uint8_t array[130];
+       uint8_t array[131];
        char name[64];
        unsigned int i;
        int err;
index 73dffce..8356a8d 100644 (file)
@@ -29,6 +29,7 @@
 #include <errno.h>
 #include <stdlib.h>
 #include <getopt.h>
+#include <unistd.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 
@@ -36,7 +37,7 @@
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 
-#include "oui.h"
+#include "src/oui.h"
 
 static int transient = 0;
 
@@ -56,7 +57,6 @@ static int generic_reset_device(int dd)
 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)
 {
@@ -70,7 +70,7 @@ static int ericsson_write_bd_addr(int dd, bdaddr_t *bdaddr)
        rq.ogf    = OGF_VENDOR_CMD;
        rq.ocf    = OCF_ERICSSON_WRITE_BD_ADDR;
        rq.cparam = &cp;
-       rq.clen   = ERICSSON_WRITE_BD_ADDR_CP_SIZE;
+       rq.clen   = sizeof(cp);
        rq.rparam = NULL;
        rq.rlen   = 0;
 
@@ -86,7 +86,6 @@ typedef struct {
        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)
 {
@@ -103,7 +102,7 @@ static int ericsson_store_in_flash(int dd, uint8_t user_id, uint8_t flash_length
        rq.ogf    = OGF_VENDOR_CMD;
        rq.ocf    = OCF_ERICSSON_STORE_IN_FLASH;
        rq.cparam = &cp;
-       rq.clen   = ERICSSON_STORE_IN_FLASH_CP_SIZE;
+       rq.clen   = sizeof(cp);
        rq.rparam = NULL;
        rq.rlen   = 0;
 
@@ -198,7 +197,6 @@ static int csr_reset_device(int dd)
 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)
 {
@@ -212,7 +210,7 @@ static int ti_write_bd_addr(int dd, bdaddr_t *bdaddr)
        rq.ogf    = OGF_VENDOR_CMD;
        rq.ocf    = OCF_TI_WRITE_BD_ADDR;
        rq.cparam = &cp;
-       rq.clen   = TI_WRITE_BD_ADDR_CP_SIZE;
+       rq.clen   = sizeof(cp);
        rq.rparam = NULL;
        rq.rlen   = 0;
 
@@ -226,7 +224,6 @@ static int ti_write_bd_addr(int dd, bdaddr_t *bdaddr)
 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)
 {
@@ -240,7 +237,7 @@ static int bcm_write_bd_addr(int dd, bdaddr_t *bdaddr)
        rq.ogf    = OGF_VENDOR_CMD;
        rq.ocf    = OCF_BCM_WRITE_BD_ADDR;
        rq.cparam = &cp;
-       rq.clen   = BCM_WRITE_BD_ADDR_CP_SIZE;
+       rq.clen   = sizeof(cp);
        rq.rparam = NULL;
        rq.rlen   = 0;
 
@@ -254,7 +251,6 @@ static int bcm_write_bd_addr(int dd, bdaddr_t *bdaddr)
 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)
 {
@@ -268,7 +264,7 @@ static int zeevo_write_bd_addr(int dd, bdaddr_t *bdaddr)
        rq.ogf    = OGF_VENDOR_CMD;
        rq.ocf    = OCF_ZEEVO_WRITE_BD_ADDR;
        rq.cparam = &cp;
-       rq.clen   = ZEEVO_WRITE_BD_ADDR_CP_SIZE;
+       rq.clen   = sizeof(cp);
        rq.rparam = NULL;
        rq.rlen   = 0;
 
@@ -278,6 +274,30 @@ static int zeevo_write_bd_addr(int dd, bdaddr_t *bdaddr)
        return 0;
 }
 
+#define OCF_MRVL_WRITE_BD_ADDR         0x0022
+typedef struct {
+       uint8_t         parameter_id;
+       uint8_t         bdaddr_len;
+       bdaddr_t        bdaddr;
+} __attribute__ ((packed)) mrvl_write_bd_addr_cp;
+
+static int mrvl_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+       mrvl_write_bd_addr_cp cp;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.parameter_id = 0xFE;
+       cp.bdaddr_len = 6;
+       bacpy(&cp.bdaddr, bdaddr);
+
+       if (hci_send_cmd(dd, OGF_VENDOR_CMD, OCF_MRVL_WRITE_BD_ADDR,
+                                                       sizeof(cp), &cp) < 0)
+               return -1;
+
+       sleep(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);
@@ -295,6 +315,7 @@ static struct {
        { 18,           zeevo_write_bd_addr,    NULL                    },
        { 48,           st_write_bd_addr,       generic_reset_device    },
        { 57,           ericsson_write_bd_addr, generic_reset_device    },
+       { 72,           mrvl_write_bd_addr,     generic_reset_device    },
        { 65535,        NULL,                   NULL                    },
 };
 
diff --git a/tools/bluemoon.c b/tools/bluemoon.c
new file mode 100644 (file)
index 0000000..67d6d25
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ *
+ *  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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include "monitor/mainloop.h"
+#include "monitor/bt.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+
+#define CMD_READ_VERSION       0xfc05
+struct rsp_read_version {
+       uint8_t  status;
+       uint8_t  hw_platform;
+       uint8_t  hw_variant;
+       uint8_t  hw_revision;
+       uint8_t  fw_variant;
+       uint8_t  fw_revision;
+       uint8_t  fw_build_nn;
+       uint8_t  fw_build_cw;
+       uint8_t  fw_build_yy;
+       uint8_t  fw_patch;
+} __attribute__ ((packed));
+
+#define CMD_MANUFACTURER_MODE  0xfc11
+struct cmd_manufacturer_mode {
+       uint8_t  mode_switch;
+       uint8_t  reset;
+} __attribute__ ((packed));
+
+#define CMD_WRITE_BD_DATA      0xfc2f
+struct cmd_write_bd_data {
+       uint8_t  bdaddr[6];
+       uint8_t  reserved1[6];
+       uint8_t  features[8];
+       uint8_t  le_features;
+       uint8_t  reserved2[32];
+       uint8_t  lmp_version;
+       uint8_t  reserved3[26];
+} __attribute__ ((packed));
+
+#define CMD_READ_BD_DATA       0xfc30
+struct rsp_read_bd_data {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+       uint8_t  reserved1[6];
+       uint8_t  features[8];
+       uint8_t  le_features;
+       uint8_t  reserved2[32];
+       uint8_t  lmp_version;
+       uint8_t  reserved3[26];
+} __attribute__ ((packed));
+
+#define CMD_WRITE_BD_ADDRESS   0xfc31
+struct cmd_write_bd_address {
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define CMD_ACT_DEACT_TRACES   0xfc43
+struct cmd_act_deact_traces {
+       uint8_t  tx_trace;
+       uint8_t  tx_arq;
+       uint8_t  rx_trace;
+} __attribute__ ((packed));
+
+static struct bt_hci *hci_dev;
+static uint16_t hci_index = 0;
+
+static bool set_bdaddr = false;
+static const char *set_bdaddr_value = NULL;
+
+static bool reset_on_exit = false;
+static bool use_manufacturer_mode = false;
+static bool get_bddata = false;
+static bool set_traces = false;
+
+static void reset_complete(const void *data, uint8_t size, void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               fprintf(stderr, "Failed to reset (0x%02x)\n", status);
+               mainloop_quit();
+               return;
+       }
+
+       mainloop_quit();
+}
+
+static void leave_manufacturer_mode_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               fprintf(stderr, "Failed to leave manufacturer mode (0x%02x)\n",
+                                                                       status);
+               mainloop_quit();
+               return;
+       }
+
+       if (reset_on_exit) {
+               bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+                                               reset_complete, NULL, NULL);
+               return;
+       }
+
+       mainloop_quit();
+}
+
+static void shutdown_device(void)
+{
+       bt_hci_flush(hci_dev);
+
+       if (use_manufacturer_mode) {
+               struct cmd_manufacturer_mode cmd;
+
+               cmd.mode_switch = 0x00;
+               cmd.reset = 0x00;
+
+               bt_hci_send(hci_dev, CMD_MANUFACTURER_MODE, &cmd, sizeof(cmd),
+                               leave_manufacturer_mode_complete, NULL, NULL);
+               return;
+       }
+
+       if (reset_on_exit) {
+               bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+                                               reset_complete, NULL, NULL);
+               return;
+       }
+
+       mainloop_quit();
+}
+
+static void write_bd_address_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               fprintf(stderr, "Failed to write address (0x%02x)\n", status);
+               mainloop_quit();
+               return;
+       }
+
+       shutdown_device();
+}
+
+static void read_bd_addr_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_read_bd_addr *rsp = data;
+       struct cmd_write_bd_address cmd;
+
+       if (rsp->status) {
+               fprintf(stderr, "Failed to read address (0x%02x)\n",
+                                                       rsp->status);
+               mainloop_quit();
+               shutdown_device();
+               return;
+       }
+
+       if (set_bdaddr_value) {
+               fprintf(stderr, "Setting address is not supported\n");
+               mainloop_quit();
+               return;
+       }
+
+       printf("Controller Address\n");
+       printf("\tOld BD_ADDR: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+                                       rsp->bdaddr[5], rsp->bdaddr[4],
+                                       rsp->bdaddr[3], rsp->bdaddr[2],
+                                       rsp->bdaddr[1], rsp->bdaddr[0]);
+
+       memcpy(cmd.bdaddr, rsp->bdaddr, 6);
+       cmd.bdaddr[0] = (hci_index & 0xff);
+
+       printf("\tNew BD_ADDR: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+                                       cmd.bdaddr[5], cmd.bdaddr[4],
+                                       cmd.bdaddr[3], cmd.bdaddr[2],
+                                       cmd.bdaddr[1], cmd.bdaddr[0]);
+
+       bt_hci_send(hci_dev, CMD_WRITE_BD_ADDRESS, &cmd, sizeof(cmd),
+                                       write_bd_address_complete, NULL, NULL);
+}
+
+static void act_deact_traces_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               fprintf(stderr, "Failed to activate traces (0x%02x)\n", status);
+               shutdown_device();
+               return;
+       }
+
+       shutdown_device();
+}
+
+static void act_deact_traces(void)
+{
+       struct cmd_act_deact_traces cmd;
+
+       cmd.tx_trace = 0x03;
+       cmd.tx_arq = 0x03;
+       cmd.rx_trace = 0x03;
+
+       bt_hci_send(hci_dev, CMD_ACT_DEACT_TRACES, &cmd, sizeof(cmd),
+                                       act_deact_traces_complete, NULL, NULL);
+}
+
+static void write_bd_data_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               fprintf(stderr, "Failed to write data (0x%02x)\n", status);
+               shutdown_device();
+               return;
+       }
+
+       if (set_traces) {
+               act_deact_traces();
+               return;
+       }
+
+       shutdown_device();
+}
+
+static void read_bd_data_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct rsp_read_bd_data *rsp = data;
+
+       if (rsp->status) {
+               fprintf(stderr, "Failed to read data (0x%02x)\n", rsp->status);
+               shutdown_device();
+               return;
+       }
+
+       printf("Controller Data\n");
+       printf("\tBD_ADDR: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+                                       rsp->bdaddr[5], rsp->bdaddr[4],
+                                       rsp->bdaddr[3], rsp->bdaddr[2],
+                                       rsp->bdaddr[1], rsp->bdaddr[0]);
+
+       printf("\tLMP Version: %u\n", rsp->lmp_version);
+       printf("\tLMP Features: 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",
+                                       rsp->features[0], rsp->features[1],
+                                       rsp->features[2], rsp->features[3],
+                                       rsp->features[4], rsp->features[5],
+                                       rsp->features[6], rsp->features[7]);
+       printf("\tLE Features: 0x%2.2x\n", rsp->le_features);
+
+       if (set_bdaddr) {
+               struct cmd_write_bd_data cmd;
+
+               memcpy(cmd.bdaddr, rsp->bdaddr, 6);
+               cmd.bdaddr[0] = (hci_index & 0xff);
+               cmd.lmp_version = 0x07;
+               memcpy(cmd.features, rsp->features, 8);
+               cmd.le_features = rsp->le_features;
+               cmd.le_features |= 0x1e;
+               memcpy(cmd.reserved1, rsp->reserved1, sizeof(cmd.reserved1));
+               memcpy(cmd.reserved2, rsp->reserved2, sizeof(cmd.reserved2));
+               memcpy(cmd.reserved3, rsp->reserved3, sizeof(cmd.reserved3));
+
+               bt_hci_send(hci_dev, CMD_WRITE_BD_DATA, &cmd, sizeof(cmd),
+                                       write_bd_data_complete, NULL, NULL);
+               return;
+       }
+
+       shutdown_device();
+}
+
+static void enter_manufacturer_mode_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               fprintf(stderr, "Failed to enter manufacturer mode (0x%02x)\n",
+                                                                       status);
+               mainloop_quit();
+               return;
+       }
+
+       if (get_bddata || set_bdaddr) {
+               bt_hci_send(hci_dev, CMD_READ_BD_DATA, NULL, 0,
+                                       read_bd_data_complete, NULL, NULL);
+               return;
+       }
+
+       if (set_traces) {
+               act_deact_traces();
+               return;
+       }
+
+       shutdown_device();
+}
+
+static void read_version_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct rsp_read_version *rsp = data;
+       const char *str;
+
+       if (rsp->status) {
+               fprintf(stderr, "Failed to read version (0x%02x)\n",
+                                                       rsp->status);
+               mainloop_quit();
+               return;
+       }
+
+       if (use_manufacturer_mode) {
+               struct cmd_manufacturer_mode cmd;
+
+               cmd.mode_switch = 0x01;
+               cmd.reset = 0x00;
+
+               bt_hci_send(hci_dev, CMD_MANUFACTURER_MODE, &cmd, sizeof(cmd),
+                               enter_manufacturer_mode_complete, NULL, NULL);
+               return;
+       }
+
+       if (set_bdaddr) {
+               bt_hci_send(hci_dev, BT_HCI_CMD_READ_BD_ADDR, NULL, 0,
+                                       read_bd_addr_complete, NULL, NULL);
+               return;
+       }
+
+       printf("Controller Version Information\n");
+       printf("\tHardware Platform:\t%u\n", rsp->hw_platform);
+
+       switch (rsp->hw_variant) {
+       case 0x07:
+               str = "iBT 2.0";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       printf("\tHardware Variant:\t%s (0x%02x)\n", str, rsp->hw_variant);
+       printf("\tHardware Revision:\t%u.%u\n", rsp->hw_revision >> 4,
+                                               rsp->hw_revision & 0x0f);
+
+       switch (rsp->fw_variant) {
+       case 0x01:
+               str = "BT IP 4.0";
+               break;
+       case 0x06:
+               str = "iBT Bootloader";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       printf("\tFirmware Variant:\t%s (0x%02x)\n", str, rsp->fw_variant);
+       printf("\tFirmware Revision:\t%u.%u\n", rsp->fw_revision >> 4,
+                                               rsp->fw_revision & 0x0f);
+       printf("\tFirmware Build Number:\t%u-%u.%u\n", rsp->fw_build_nn,
+                               rsp->fw_build_cw, 2000 + rsp->fw_build_yy);
+       printf("\tFirmware Patch Number:\t%u\n", rsp->fw_patch);
+
+       mainloop_quit();
+}
+
+static void read_local_version_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_read_local_version *rsp = data;
+       uint16_t manufacturer;
+
+       if (rsp->status) {
+               fprintf(stderr, "Failed to read local version (0x%02x)\n",
+                                                               rsp->status);
+               mainloop_quit();
+               return;
+       }
+
+       manufacturer = le16_to_cpu(rsp->manufacturer);
+
+       if (manufacturer != 2) {
+               fprintf(stderr, "Unsupported manufacturer (%u)\n",
+                                                       manufacturer);
+               mainloop_quit();
+               return;
+       }
+
+       bt_hci_send(hci_dev, CMD_READ_VERSION, NULL, 0,
+                                       read_version_complete, NULL, NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               mainloop_quit();
+               break;
+       }
+}
+
+static void usage(void)
+{
+       printf("bluemoon - Bluemoon configuration utility\n"
+               "Usage:\n");
+       printf("\tbluemoon [options]\n");
+       printf("Options:\n"
+               "\t-B, --bdaddr [addr]    Set Bluetooth address\n"
+               "\t-R, --reset            Reset controller\n"
+               "\t-i, --index <num>      Use specified controller\n"
+               "\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+       { "bdaddr",  optional_argument, NULL, 'A' },
+       { "bddata",  no_argument,       NULL, 'D' },
+       { "traces",  no_argument,       NULL, 'T' },
+       { "reset",   no_argument,       NULL, 'R' },
+       { "index",   required_argument, NULL, 'i' },
+       { "version", no_argument,       NULL, 'v' },
+       { "help",    no_argument,       NULL, 'h' },
+       { }
+};
+
+int main(int argc, char *argv[])
+{
+       const char *str;
+       sigset_t mask;
+       int exit_status;
+
+       for (;;) {
+               int opt;
+
+               opt = getopt_long(argc, argv, "A::DTRi:vh", main_options, NULL);
+               if (opt < 0)
+                       break;
+
+               switch (opt) {
+               case 'A':
+                       if (optarg)
+                               set_bdaddr_value = optarg;
+                       set_bdaddr = true;
+                       break;
+               case 'D':
+                       use_manufacturer_mode = true;
+                       get_bddata = true;
+                       break;
+               case 'T':
+                       use_manufacturer_mode = true;
+                       set_traces = true;
+                       break;
+               case 'R':
+                       reset_on_exit = true;
+                       break;
+               case 'i':
+                       if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+                               str = optarg + 3;
+                       else
+                               str = optarg;
+                       if (!isdigit(*str)) {
+                               usage();
+                               return EXIT_FAILURE;
+                       }
+                       hci_index = atoi(str);
+                       break;
+               case 'v':
+                       printf("%s\n", VERSION);
+                       return EXIT_SUCCESS;
+               case 'h':
+                       usage();
+                       return EXIT_SUCCESS;
+               default:
+                       return EXIT_FAILURE;
+               }
+       }
+
+       if (argc - optind > 0) {
+               fprintf(stderr, "Invalid command line parameters\n");
+               return EXIT_FAILURE;
+       }
+
+       mainloop_init();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       printf("Bluemoon configuration utility ver %s\n", VERSION);
+
+       hci_dev = bt_hci_new_user_channel(hci_index);
+       if (!hci_dev) {
+               fprintf(stderr, "Failed to open HCI user channel\n");
+               return EXIT_FAILURE;
+       }
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0,
+                               read_local_version_complete, NULL, NULL);
+
+       exit_status = mainloop_run();
+
+       bt_hci_unref(hci_dev);
+
+       return exit_status;
+}
index 622d391..f10d9be 100644 (file)
@@ -738,6 +738,11 @@ static void cmd_change_folder(int argc, char *argv[])
                return;
        }
 
+       if (dbus_validate_path(argv[1], NULL) == FALSE) {
+               rl_printf("Not a valid path\n");
+               return;
+       }
+
        if (check_default_player() == FALSE)
                return;
 
@@ -1080,6 +1085,7 @@ static void rl_handler(char *input)
        if (!strlen(input))
                goto done;
 
+       g_strstrip(input);
        add_history(input);
 
        argv = g_strsplit(input, " ", -1);
index ed434e6..b838c25 100644 (file)
 #include <config.h>
 #endif
 
+#include <ctype.h>
+#include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
-#include <alloca.h>
+#include <string.h>
 #include <getopt.h>
-#include <stdbool.h>
-#include <poll.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
 
+#include "monitor/mainloop.h"
 #include "monitor/bt.h"
+#include "src/shared/timeout.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+
+#define BTPROTO_HCI    1
+
+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;
+};
 
-#define le16_to_cpu(val) (val)
-#define le32_to_cpu(val) (val)
-#define cpu_to_le16(val) (val)
-#define cpu_to_le32(val) (val)
-
-struct bt_h4_pkt {
-       uint8_t type;
-       union {
-               struct {
-                       uint16_t opcode;
-                       uint8_t plen;
-                       union {
-                               uint8_t data;
-                       };
-               } cmd;
-               struct {
-                       uint8_t event;
-                       uint8_t plen;
-                       union {
-                               uint8_t data;
-                               struct bt_hci_evt_cmd_complete cmd_complete;
-                               struct bt_hci_evt_cmd_status cmd_status;
-                       };
-               } evt;
-       };
-} __attribute__ ((packed));
-
-static bool hci_request(int fd, uint16_t opcode,
-                       const void *cmd_data, uint8_t cmd_len,
-                       void *rsp_data, uint8_t rsp_size, uint8_t *rsp_len)
-{
-       struct bt_h4_pkt *cmd = alloca(4 + cmd_len);
-       struct bt_h4_pkt *rsp = alloca(2048);
-       ssize_t len;
-
-       cmd->type = BT_H4_CMD_PKT;
-       cmd->cmd.opcode = cpu_to_le16(opcode);
-       cmd->cmd.plen = cpu_to_le16(cmd_len);
-       if (cmd_len > 0)
-               memcpy(&cmd->cmd.data, cmd_data, cmd_len);
-
-       if (write(fd, cmd, 4 + cmd_len) < 0) {
-               perror("Failed to write command");
-               return false;
-       }
-
-       len = read(fd, rsp, 2048);
-       if (len < 0) {
-               perror("Failed to read event");
-               return false;
-       }
-
-       if (rsp->type != BT_H4_EVT_PKT) {
-               fprintf(stderr, "Unexpected packet type %d\n", rsp->type);
-               return false;
-       }
-
-       if (rsp->evt.event == BT_HCI_EVT_CMD_COMPLETE) {
-               if (opcode != le16_to_cpu(rsp->evt.cmd_complete.opcode))
-                       return false;
+struct hci_dev_info {
+       uint16_t dev_id;
+       char     name[8];
+       uint8_t  bdaddr[6];
+       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;
+};
 
-               if (rsp_data)
-                       memcpy(rsp_data, (&rsp->evt.data) + 3, rsp->evt.plen - 3);
+#define HCIDEVUP       _IOW('H', 201, int)
+#define HCIDEVDOWN     _IOW('H', 202, int)
+#define HCIGETDEVINFO  _IOR('H', 211, int)
 
-               if (rsp_len)
-                       *rsp_len = rsp->evt.plen - 3;
+#define HCI_UP         (1 << 0)
 
-               return true;
-       } else if (rsp->evt.event == BT_HCI_EVT_CMD_STATUS) {
-               if (opcode == le16_to_cpu(rsp->evt.cmd_status.opcode))
-                       return false;
+#define HCI_BREDR      0x00
+#define HCI_AMP                0x01
 
-               if (rsp->evt.cmd_status.status != BT_HCI_ERR_SUCCESS)
-                       return false;
+static struct hci_dev_info hci_info;
+static uint8_t hci_type;
+static struct bt_hci *hci_dev;
 
-               if (rsp_len)
-                       *rsp_len = 0;
+static bool reset_on_init = false;
+static bool reset_on_shutdown = false;
 
-               return true;
-       }
+static bool shutdown_timeout(void *user_data)
+{
+       mainloop_quit();
 
        return false;
 }
 
-static int cmd_local(int fd, int argc, char *argv[])
+static void shutdown_complete(const void *data, uint8_t size, void *user_data)
 {
-       struct bt_hci_rsp_read_local_features lf;
-       struct bt_hci_rsp_read_local_version lv;
-       struct bt_hci_rsp_read_local_commands lc;
-       struct bt_hci_cmd_read_local_ext_features lef_cmd;
-       struct bt_hci_rsp_read_local_ext_features lef;
-       uint8_t len;
-
-       if (!hci_request(fd, BT_HCI_CMD_RESET, NULL, 0, NULL, 0, &len))
-               return EXIT_FAILURE;
-
-       if (!hci_request(fd, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
-                                               &lf, sizeof(lf), &len))
-               return EXIT_FAILURE;
-
-       if (lf.status != BT_HCI_ERR_SUCCESS)
-               return EXIT_FAILURE;
-
-       printf("Features: 0x%02x 0x%02x 0x%02x 0x%02x "
-                                       "0x%02x 0x%02x 0x%02x 0x%02x\n",
-                                       lf.features[0], lf.features[1],
-                                       lf.features[2], lf.features[3],
-                                       lf.features[4], lf.features[5],
-                                       lf.features[6], lf.features[7]);
-
-       if (!hci_request(fd, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0,
-                                               &lv, sizeof(lv), &len))
-               return EXIT_FAILURE;
-
-       if (lv.status != BT_HCI_ERR_SUCCESS)
-               return EXIT_FAILURE;
+       unsigned int id = PTR_TO_UINT(user_data);
 
-       printf("Version: %d\n", lv.hci_ver);
-       printf("Manufacturer: %d\n", le16_to_cpu(lv.manufacturer));
-
-       if (!hci_request(fd, BT_HCI_CMD_READ_LOCAL_COMMANDS, NULL, 0,
-                                               &lc, sizeof(lc), &len))
-               return EXIT_FAILURE;
+       timeout_remove(id);
+       mainloop_quit();
+}
 
-       if (lc.status != BT_HCI_ERR_SUCCESS)
-               return EXIT_FAILURE;
+static void shutdown_device(void)
+{
+       unsigned int id;
 
-       if (!(lf.features[7] & 0x80))
-               return EXIT_SUCCESS;
+       bt_hci_flush(hci_dev);
 
-       lef_cmd.page = 0x01;
+       if (reset_on_shutdown) {
+               id = timeout_add(5000, shutdown_timeout, NULL, NULL);
 
-       if (!hci_request(fd, BT_HCI_CMD_READ_LOCAL_EXT_FEATURES,
-                                               &lef_cmd, sizeof(lef_cmd),
-                                               &lef, sizeof(lef), &len))
-               return EXIT_FAILURE;
+               bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+                               shutdown_complete, UINT_TO_PTR(id), NULL);
+       } else
+               mainloop_quit();
+}
 
-       if (lef.status != BT_HCI_ERR_SUCCESS)
-               return EXIT_FAILURE;
+static void local_version_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_read_local_version *rsp = data;
+
+       printf("HCI version: %u\n", rsp->hci_ver);
+       printf("HCI revision: %u\n", le16_to_cpu(rsp->hci_rev));
+
+       switch (hci_type) {
+       case HCI_BREDR:
+               printf("LMP version: %u\n", rsp->lmp_ver);
+               printf("LMP subversion: %u\n", le16_to_cpu(rsp->lmp_subver));
+               break;
+       case HCI_AMP:
+               printf("PAL version: %u\n", rsp->lmp_ver);
+               printf("PAL subversion: %u\n", le16_to_cpu(rsp->lmp_subver));
+               break;
+       }
 
-       printf("Host features: 0x%02x 0x%02x 0x%02x 0x%02x "
-                                       "0x%02x 0x%02x 0x%02x 0x%02x\n",
-                                       lef.features[0], lef.features[1],
-                                       lef.features[2], lef.features[3],
-                                       lef.features[4], lef.features[5],
-                                       lef.features[6], lef.features[7]);
+       printf("Manufacturer: %u\n", le16_to_cpu(rsp->manufacturer));
+}
 
-       if (lef.max_page < 0x02)
-               return EXIT_SUCCESS;
+static void local_commands_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       shutdown_device();
+}
 
-       lef_cmd.page = 0x02;
+static void local_features_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_COMMANDS, NULL, 0,
+                                       local_commands_callback, NULL, NULL);
+}
 
-       if (!hci_request(fd, BT_HCI_CMD_READ_LOCAL_EXT_FEATURES,
-                                               &lef_cmd, sizeof(lef_cmd),
-                                               &lef, sizeof(lef), &len))
-               return EXIT_FAILURE;
+static bool cmd_local(int argc, char *argv[])
+{
+       if (reset_on_init)
+               bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+                                               NULL, NULL, NULL);
 
-       if (lef.status != BT_HCI_ERR_SUCCESS)
-               return EXIT_FAILURE;
+       bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0,
+                                       local_version_callback, NULL, NULL);
 
-       printf("Extended features: 0x%02x 0x%02x 0x%02x 0x%02x "
-                                       "0x%02x 0x%02x 0x%02x 0x%02x\n",
-                                       lef.features[0], lef.features[1],
-                                       lef.features[2], lef.features[3],
-                                       lef.features[4], lef.features[5],
-                                       lef.features[6], lef.features[7]);
+       bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+                                       local_features_callback, NULL, NULL);
 
-       return EXIT_SUCCESS;
+       return true;
 }
 
-typedef int (*cmd_func_t)(int fd, int argc, char *argv[]);
+typedef bool (*cmd_func_t)(int argc, char *argv[]);
 
 static const struct {
        const char *name;
@@ -219,6 +180,21 @@ static const struct {
        { }
 };
 
+static void signal_callback(int signum, void *user_data)
+{
+       static bool terminated = false;
+
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               if (!terminated) {
+                       shutdown_device();
+                       terminated = true;
+               }
+               break;
+       }
+}
+
 static void usage(void)
 {
        int i;
@@ -227,15 +203,17 @@ static void usage(void)
                "Usage:\n");
        printf("\tbtinfo [options] <command>\n");
        printf("options:\n"
-               "\t-i, --device <hcidev>    Use local HCI device\n"
-               "\t-h, --help               Show help options\n");
+               "\t-i, --index <num>      Use specified controller\n"
+               "\t-h, --help             Show help options\n");
        printf("commands:\n");
        for (i = 0; cmd_table[i].name; i++)
                printf("\t%-25s%s\n", cmd_table[i].name, cmd_table[i].help);
 }
 
 static const struct option main_options[] = {
-       { "device",  required_argument, NULL, 'i' },
+       { "index",   required_argument, NULL, 'i' },
+       { "reset",   no_argument,       NULL, 'r' },
+       { "raw",     no_argument,       NULL, 'R' },
        { "version", no_argument,       NULL, 'v' },
        { "help",    no_argument,       NULL, 'h' },
        { }
@@ -243,22 +221,38 @@ static const struct option main_options[] = {
 
 int main(int argc, char *argv[])
 {
-       const char *device = NULL;
        cmd_func_t func = NULL;
-       struct sockaddr_hci addr;
-       int result, fd, i;
+       uint16_t index = 0;
+       const char *str;
+       bool use_raw = false;
+       bool power_down = false;
+       sigset_t mask;
+       int fd, i, exit_status;
 
        for (;;) {
                int opt;
 
-               opt = getopt_long(argc, argv, "i:vh",
-                                               main_options, NULL);
+               opt = getopt_long(argc, argv, "i:rRvh", main_options, NULL);
                if (opt < 0)
                        break;
 
                switch (opt) {
                case 'i':
-                       device = optarg;
+                       if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+                               str = optarg + 3;
+                       else
+                               str = optarg;
+                       if (!isdigit(*str)) {
+                               usage();
+                               return EXIT_FAILURE;
+                       }
+                       index = atoi(str);
+                       break;
+               case 'r':
+                       reset_on_init = true;
+                       break;
+               case 'R':
+                       use_raw = true;
                        break;
                case 'v':
                        printf("%s\n", VERSION);
@@ -288,30 +282,84 @@ int main(int argc, char *argv[])
                return EXIT_FAILURE;
        }
 
+       mainloop_init();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       printf("Bluetooth information utility ver %s\n", VERSION);
+
        fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
        if (fd < 0) {
-               perror("Failed to open channel");
+               perror("Failed to open HCI raw socket");
                return EXIT_FAILURE;
        }
 
-       memset(&addr, 0, sizeof(addr));
-       addr.hci_family = AF_BLUETOOTH;
-       addr.hci_channel = HCI_CHANNEL_USER;
+       memset(&hci_info, 0, sizeof(hci_info));
+       hci_info.dev_id = index;
 
-       if (device)
-               addr.hci_dev = atoi(device);
-       else
-               addr.hci_dev = 0;
-
-       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("Failed to bind channel");
+       if (ioctl(fd, HCIGETDEVINFO, (void *) &hci_info) < 0) {
+               perror("Failed to get HCI device information");
                close(fd);
                return EXIT_FAILURE;
        }
 
-       result = func(fd, argc - optind - 1, argv + optind + 1);
+       if (use_raw && !(hci_info.flags & HCI_UP)) {
+               printf("Powering on controller\n");
+
+               if (ioctl(fd, HCIDEVUP, hci_info.dev_id) < 0) {
+                       perror("Failed to power on controller");
+                       close(fd);
+                       return EXIT_FAILURE;
+               }
+
+               power_down = true;
+       }
 
        close(fd);
 
-       return result;
+       hci_type = (hci_info.type & 0x30) >> 4;
+
+       if (use_raw) {
+               hci_dev = bt_hci_new_raw_device(index);
+               if (!hci_dev) {
+                       fprintf(stderr, "Failed to open HCI raw device\n");
+                       return EXIT_FAILURE;
+               }
+       } else {
+               hci_dev = bt_hci_new_user_channel(index);
+               if (!hci_dev) {
+                       fprintf(stderr, "Failed to open HCI user channel\n");
+                       return EXIT_FAILURE;
+               }
+
+               reset_on_init = true;
+               reset_on_shutdown = true;
+       }
+
+       if (!func(argc - optind - 1, argv + optind + 1)) {
+               bt_hci_unref(hci_dev);
+               return EXIT_FAILURE;
+       }
+
+       exit_status = mainloop_run();
+
+       bt_hci_unref(hci_dev);
+
+       if (use_raw && power_down) {
+               fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+               if (fd >= 0) {
+                       printf("Powering down controller\n");
+
+                       if (ioctl(fd, HCIDEVDOWN, hci_info.dev_id) < 0)
+                               perror("Failed to power down controller");
+
+                       close(fd);
+               }
+       }
+
+       return exit_status;
 }
index a77eba1..6a87ffd 100644 (file)
@@ -35,7 +35,9 @@
 
 #include <glib.h>
 
-#include <btio/btio.h>
+#include <bluetooth/bluetooth.h>
+
+#include "btio/btio.h"
 
 #define DEFAULT_ACCEPT_TIMEOUT 2
 static int opt_update_sec = 0;
@@ -274,16 +276,23 @@ static void l2cap_connect(const char *src, const char *dst, uint8_t addr_type,
 {
        struct io_data *data;
        GError *err = NULL;
+       uint8_t src_type;
 
        printf("Connecting to %s L2CAP PSM %u\n", dst, psm);
 
        data = io_data_new(NULL, -1, disconn, -1);
 
+       if (addr_type != BDADDR_BREDR)
+               src_type = BDADDR_LE_PUBLIC;
+       else
+               src_type = BDADDR_BREDR;
+
        if (src)
                data->io = bt_io_connect(connect_cb, data,
                                        (GDestroyNotify) io_data_unref,
                                        &err,
                                        BT_IO_OPT_SOURCE, src,
+                                       BT_IO_OPT_SOURCE_TYPE, src_type,
                                        BT_IO_OPT_DEST, dst,
                                        BT_IO_OPT_DEST_TYPE, addr_type,
                                        BT_IO_OPT_PSM, psm,
@@ -295,6 +304,7 @@ static void l2cap_connect(const char *src, const char *dst, uint8_t addr_type,
                data->io = bt_io_connect(connect_cb, data,
                                        (GDestroyNotify) io_data_unref,
                                        &err,
+                                       BT_IO_OPT_SOURCE_TYPE, src_type,
                                        BT_IO_OPT_DEST, dst,
                                        BT_IO_OPT_DEST_TYPE, addr_type,
                                        BT_IO_OPT_PSM, psm,
index 1d71d74..0618de1 100644 (file)
@@ -30,6 +30,8 @@
 #include <string.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <poll.h>
 #include <getopt.h>
 #include <stdbool.h>
 #include <bluetooth/sdp.h>
 #include <bluetooth/sdp_lib.h>
 
-#include <glib.h>
-
-#include "src/shared/mgmt.h"
-#include "glib-helper.h"
+#include "src/uuid-helper.h"
 #include "lib/mgmt.h"
-#include "eir.h"
 
-static GMainLoop *event_loop = NULL;
+#include "monitor/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
 
 static bool monitor = false;
 static bool discovery = false;
@@ -96,6 +96,9 @@ static const char *settings_str[] = {
                                "hs",
                                "le",
                                "advertising",
+                               "secure-conn",
+                               "debug-keys",
+                               "privacy",
 };
 
 static void print_settings(uint32_t settings)
@@ -120,7 +123,7 @@ static void new_settings(uint16_t index, uint16_t len,
 
        if (monitor) {
                printf("hci%u new_settings: ", index);
-               print_settings(bt_get_le32(ev));
+               print_settings(get_le32(ev));
                printf("\n");
        }
 }
@@ -137,7 +140,7 @@ static void discovering(uint16_t index, uint16_t len, const void *param,
        }
 
        if (ev->discovering == 0 && discovery) {
-               g_main_loop_quit(event_loop);
+               mainloop_quit();
                return;
        }
 
@@ -188,7 +191,7 @@ static void connected(uint16_t index, uint16_t len, const void *param,
                return;
        }
 
-       eir_len = bt_get_le16(&ev->eir_len);
+       eir_len = 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);
@@ -311,14 +314,69 @@ static void confirm_name_rsp(uint8_t status, uint16_t len,
                printf("confirm_name succeeded for %s\n", addr);
 }
 
+static char *eir_get_name(const uint8_t *eir, uint16_t eir_len)
+{
+       uint8_t parsed = 0;
+
+       if (eir_len < 2)
+               return NULL;
+
+       while (parsed < eir_len - 1) {
+               uint8_t field_len = eir[0];
+
+               if (field_len == 0)
+                       break;
+
+               parsed += field_len + 1;
+
+               if (parsed > eir_len)
+                       break;
+
+               /* Check for short of complete name */
+               if (eir[1] == 0x09 || eir[1] == 0x08)
+                       return strndup((char *) &eir[2], field_len - 1);
+
+               eir += field_len + 1;
+       }
+
+       return NULL;
+}
+
+static unsigned int eir_get_flags(const uint8_t *eir, uint16_t eir_len)
+{
+       uint8_t parsed = 0;
+
+       if (eir_len < 2)
+               return 0;
+
+       while (parsed < eir_len - 1) {
+               uint8_t field_len = eir[0];
+
+               if (field_len == 0)
+                       break;
+
+               parsed += field_len + 1;
+
+               if (parsed > eir_len)
+                       break;
+
+               /* Check for flags */
+               if (eir[1] == 0x01)
+                       return eir[2];
+
+               eir += field_len + 1;
+       }
+
+       return 0;
+}
+
 static void device_found(uint16_t index, uint16_t len, const void *param,
                                                        void *user_data)
 {
        const struct mgmt_ev_device_found *ev = param;
        struct mgmt *mgmt = user_data;
-       uint32_t flags;
        uint16_t eir_len;
-       struct eir_data eir;
+       uint32_t flags;
 
        if (len < sizeof(*ev)) {
                fprintf(stderr,
@@ -328,30 +386,33 @@ static void device_found(uint16_t index, uint16_t len, const void *param,
 
        flags = btohl(ev->flags);
 
-       eir_len = bt_get_le16(&ev->eir_len);
+       eir_len = get_le16(&ev->eir_len);
        if (len != sizeof(*ev) + eir_len) {
                fprintf(stderr, "dev_found: expected %zu bytes, got %u bytes\n",
                                                sizeof(*ev) + eir_len, len);
                return;
        }
 
-       memset(&eir, 0, sizeof(eir));
-       eir_parse(&eir, ev->eir, eir_len);
-
        if (monitor || discovery) {
-               char addr[18];
+               char addr[18], *name;
+
                ba2str(&ev->addr.bdaddr, addr);
                printf("hci%u dev_found: %s type %s rssi %d "
                        "flags 0x%04x ", index, addr,
                        typestr(ev->addr.type), ev->rssi, flags);
 
-               if (eir.name)
-                       printf("name %s\n", eir.name);
+               if (ev->addr.type != BDADDR_BREDR)
+                       printf("AD flags 0x%02x ",
+                                       eir_get_flags(ev->eir, eir_len));
+
+               name = eir_get_name(ev->eir, eir_len);
+               if (name)
+                       printf("name %s\n", name);
                else
                        printf("eir_len %u\n", eir_len);
-       }
 
-       eir_data_free(&eir);
+               free(name);
+       }
 
        if (discovery && (flags & MGMT_DEV_FOUND_CONFIRM_NAME)) {
                struct mgmt_cp_confirm_name cp;
@@ -375,7 +436,7 @@ static void pin_rsp(uint8_t status, uint16_t len, const void *param,
                fprintf(stderr,
                        "PIN Code reply failed with status 0x%02x (%s)\n",
                                                status, mgmt_errstr(status));
-               g_main_loop_quit(event_loop);
+               mainloop_quit();
                return;
        }
 
@@ -404,7 +465,7 @@ static void pin_neg_rsp(uint8_t status, uint16_t len, const void *param,
                fprintf(stderr,
                        "PIN Neg reply failed with status 0x%02x (%s)\n",
                                                status, mgmt_errstr(status));
-               g_main_loop_quit(event_loop);
+               mainloop_quit();
                return;
        }
 
@@ -469,7 +530,7 @@ static void confirm_rsp(uint8_t status, uint16_t len, const void *param,
                fprintf(stderr,
                        "User Confirm reply failed. status 0x%02x (%s)\n",
                                                status, mgmt_errstr(status));
-               g_main_loop_quit(event_loop);
+               mainloop_quit();
                return;
        }
 
@@ -495,7 +556,7 @@ static void confirm_neg_rsp(uint8_t status, uint16_t len, const void *param,
                fprintf(stderr,
                        "Confirm Neg reply failed. status 0x%02x (%s)\n",
                                                status, mgmt_errstr(status));
-               g_main_loop_quit(event_loop);
+               mainloop_quit();
                return;
        }
 
@@ -532,7 +593,7 @@ static void user_confirm(uint16_t index, uint16_t len, const void *param,
        }
 
        ba2str(&ev->addr.bdaddr, addr);
-       val = bt_get_le32(&ev->value);
+       val = get_le32(&ev->value);
 
        if (monitor)
                printf("hci%u %s User Confirm %06u hint %u\n", index, addr,
@@ -553,10 +614,8 @@ static void user_confirm(uint16_t index, uint16_t len, const void *param,
        }
 
        rsp_len = strlen(rsp);
-       if (rsp[rsp_len - 1] == '\n') {
+       if (rsp[rsp_len - 1] == '\n')
                rsp[rsp_len - 1] = '\0';
-               rsp_len--;
-       }
 
        if (rsp[0] == 'y' || rsp[0] == 'Y')
                mgmt_confirm_reply(mgmt, index, &ev->addr.bdaddr);
@@ -588,10 +647,10 @@ static void version_rsp(uint8_t status, uint16_t len, const void *param,
        }
 
        printf("MGMT Version %u, revision %u\n", rp->version,
-                                               bt_get_le16(&rp->revision));
+                                               get_le16(&rp->revision));
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void cmd_version(struct mgmt *mgmt, uint16_t index, int argc,
@@ -624,8 +683,8 @@ static void commands_rsp(uint8_t status, uint16_t len, const void *param,
                goto done;
        }
 
-       num_commands = bt_get_le16(&rp->num_commands);
-       num_events = bt_get_le16(&rp->num_events);
+       num_commands = get_le16(&rp->num_commands);
+       num_events = get_le16(&rp->num_events);
 
        expected_len = sizeof(*rp) + num_commands * sizeof(uint16_t) +
                                                num_events * sizeof(uint16_t);
@@ -640,18 +699,18 @@ static void commands_rsp(uint8_t status, uint16_t len, const void *param,
 
        printf("%u commands:\n", num_commands);
        for (i = 0; i < num_commands; i++) {
-               uint16_t op = bt_get_le16(opcode++);
+               uint16_t op = 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++);
+               uint16_t ev = get_le16(opcode++);
                printf("\t%s (0x%04x)\n", mgmt_evstr(ev), ev);
        }
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void cmd_commands(struct mgmt *mgmt, uint16_t index, int argc,
@@ -668,7 +727,7 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param,
                                                        void *user_data)
 {
        const struct mgmt_rp_read_info *rp = param;
-       int id = GPOINTER_TO_INT(user_data);
+       uint16_t index = PTR_TO_UINT(user_data);
        char addr[18];
 
        pending--;
@@ -676,7 +735,7 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param,
        if (status != 0) {
                fprintf(stderr,
                        "Reading hci%u info failed with status 0x%02x (%s)\n",
-                                       id, status, mgmt_errstr(status));
+                                       index, status, mgmt_errstr(status));
                goto done;
        }
 
@@ -687,15 +746,15 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param,
 
        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),
+                       " class 0x%02x%02x%02x\n", index,
+                       addr, rp->version, 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));
+       print_settings(get_le32(&rp->supported_settings));
 
        printf("\n\tcurrent settings: ");
-       print_settings(bt_get_le32(&rp->current_settings));
+       print_settings(get_le32(&rp->current_settings));
 
        printf("\n\tname %s\n", rp->name);
        printf("\tshort name %s\n", rp->short_name);
@@ -704,7 +763,7 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param,
                return;
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void index_rsp(uint8_t status, uint16_t len, const void *param,
@@ -728,7 +787,7 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param,
                goto done;
        }
 
-       count = bt_get_le16(&rp->num_controllers);
+       count = get_le16(&rp->num_controllers);
 
        if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
                fprintf(stderr,
@@ -751,12 +810,12 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param,
                uint16_t index;
                void *data;
 
-               index = bt_get_le16(&rp->index[i]);
+               index = get_le16(&rp->index[i]);
 
                if (monitor)
                        printf("hci%u ", index);
 
-               data = GINT_TO_POINTER((int) index);
+               data = UINT_TO_PTR(index);
 
                if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
                                                info_rsp, data, NULL) == 0) {
@@ -773,7 +832,7 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param,
        return;
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
@@ -791,7 +850,7 @@ static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
                return;
        }
 
-       data = GINT_TO_POINTER((int) index);
+       data = UINT_TO_PTR(index);
 
        if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp,
                                                        data, NULL) == 0) {
@@ -825,14 +884,17 @@ static unsigned int send_cmd(struct mgmt *mgmt, uint16_t op, uint16_t id,
        struct command_data *data;
        unsigned int send_id;
 
-       data = g_new0(struct command_data, 1);
+       data = new0(struct command_data, 1);
+       if (!data)
+               return 0;
+
        data->id = id;
        data->op = op;
        data->callback = cb;
 
-       send_id = mgmt_send(mgmt, op, id, len, param, cmd_rsp, data, g_free);
+       send_id = mgmt_send(mgmt, op, id, len, param, cmd_rsp, data, free);
        if (send_id == 0)
-               g_free(data);
+               free(data);
 
        return send_id;
 }
@@ -856,11 +918,11 @@ static void setting_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
        }
 
        printf("hci%u %s complete, settings: ", id, mgmt_opstr(op));
-       print_settings(bt_get_le32(rp));
+       print_settings(get_le32(rp));
        printf("\n");
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op,
@@ -957,6 +1019,34 @@ static void cmd_ssp(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        cmd_setting(mgmt, index, MGMT_OP_SET_SSP, argc, argv);
 }
 
+static void cmd_sc(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+       uint8_t val;
+
+       if (argc < 2) {
+               printf("Specify \"on\" or \"off\" or \"only\"\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 if (strcasecmp(argv[1], "only") == 0)
+               val = 2;
+       else
+               val = atoi(argv[1]);
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       if (send_cmd(mgmt, MGMT_OP_SET_SECURE_CONN, index,
+                                       sizeof(val), &val, setting_rsp) == 0) {
+               fprintf(stderr, "Unable to send set_secure_conn cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
 static void cmd_hs(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
 {
        cmd_setting(mgmt, index, MGMT_OP_SET_HS, argc, argv);
@@ -978,6 +1068,48 @@ static void cmd_bredr(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        cmd_setting(mgmt, index, MGMT_OP_SET_BREDR, argc, argv);
 }
 
+static void cmd_privacy(struct mgmt *mgmt, uint16_t index, int argc,
+                                                               char **argv)
+{
+       struct mgmt_cp_set_privacy cp;
+       int fd;
+
+       if (argc < 2) {
+               printf("Specify \"on\" or \"off\"\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
+               cp.privacy = 0x01;
+       else if (strcasecmp(argv[1], "off") == 0)
+               cp.privacy = 0x00;
+       else
+               cp.privacy = atoi(argv[1]);
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "open(/dev/urandom): %s\n", strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       if (read(fd, cp.irk, sizeof(cp.irk)) != sizeof(cp.irk)) {
+               fprintf(stderr, "Reading from urandom failed\n");
+               close(fd);
+               exit(EXIT_FAILURE);
+       }
+
+       close(fd);
+
+       if (send_cmd(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+                                                       setting_rsp) == 0) {
+               fprintf(stderr, "Unable to send Set Privacy command\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
 static void class_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
                                                        const void *param)
 {
@@ -998,7 +1130,7 @@ static void class_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
                rp->class_of_dev[2], rp->class_of_dev[1], rp->class_of_dev[0]);
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void cmd_class(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
@@ -1051,24 +1183,56 @@ static void disconnect_rsp(uint8_t status, uint16_t len, const void *param,
                                addr, status, mgmt_errstr(status));
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
+}
+
+static void disconnect_usage(void)
+{
+       printf("Usage: btmgmt disconnect [-t type] <remote address>\n");
 }
 
+static struct option disconnect_options[] = {
+       { "help",       0, 0, 'h' },
+       { "type",       1, 0, 't' },
+       { 0, 0, 0, 0 }
+};
+
 static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc,
                                                                char **argv)
 {
        struct mgmt_cp_disconnect cp;
+       uint8_t type = BDADDR_BREDR;
+       int opt;
 
-       if (argc < 2) {
-               printf("Usage: btmgmt %s <address>\n", argv[0]);
-               exit(EXIT_FAILURE);
+       while ((opt = getopt_long(argc, argv, "+t:h", disconnect_options,
+                                                               NULL)) != -1) {
+               switch (opt) {
+               case 't':
+                       type = strtol(optarg, NULL, 0);
+                       break;
+               case 'h':
+               default:
+                       disconnect_usage();
+                       exit(EXIT_SUCCESS);
+               }
        }
 
-       str2ba(argv[1], &cp.addr.bdaddr);
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               disconnect_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(mgmt, MGMT_OP_DISCONNECT, index, sizeof(cp), &cp,
                                        disconnect_rsp, NULL, NULL) == 0) {
                fprintf(stderr, "Unable to send disconnect cmd\n");
@@ -1088,7 +1252,7 @@ static void con_rsp(uint8_t status, uint16_t len, const void *param,
                goto done;
        }
 
-       count = bt_get_le16(&rp->conn_count);
+       count = 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);
@@ -1104,7 +1268,7 @@ static void con_rsp(uint8_t status, uint16_t len, const void *param,
        }
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void cmd_con(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
@@ -1126,7 +1290,7 @@ static void find_rsp(uint8_t status, uint16_t len, const void *param,
                fprintf(stderr,
                        "Unable to start discovery. status 0x%02x (%s)\n",
                                                status, mgmt_errstr(status));
-               g_main_loop_quit(event_loop);
+               mainloop_quit();
                return;
        }
 
@@ -1202,7 +1366,7 @@ static void name_rsp(uint8_t status, uint16_t len, const void *param,
                                                "with status 0x%02x (%s)\n",
                                                status, mgmt_errstr(status));
 
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void cmd_name(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
@@ -1257,10 +1421,10 @@ static void pair_rsp(uint8_t status, uint16_t len, const void *param,
                goto done;
        }
 
-       printf("Paired with %s\n", addr);
+       printf("Paired with %s (%s)\n", addr, typestr(rp->addr.type));
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void pair_usage(void)
@@ -1280,6 +1444,7 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        struct mgmt_cp_pair_device cp;
        uint8_t cap = 0x01;
        uint8_t type = BDADDR_BREDR;
+       char addr[18];
        int opt;
 
        while ((opt = getopt_long(argc, argv, "+c:t:h", pair_options,
@@ -1315,6 +1480,9 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        cp.addr.type = type;
        cp.io_cap = cap;
 
+       ba2str(&cp.addr.bdaddr, addr);
+       printf("Pairing with %s (%s)\n", addr, typestr(cp.addr.type));
+
        if (mgmt_send(mgmt, MGMT_OP_PAIR_DEVICE, index, sizeof(cp), &cp,
                                                pair_rsp, NULL, NULL) == 0) {
                fprintf(stderr, "Unable to send pair_device cmd\n");
@@ -1352,7 +1520,7 @@ static void cancel_pair_rsp(uint8_t status, uint16_t len, const void *param,
        printf("Pairing Cancelled with %s\n", addr);
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void cancel_pair_usage(void)
@@ -1438,16 +1606,46 @@ static void unpair_rsp(uint8_t status, uint16_t len, const void *param,
        printf("%s unpaired\n", addr);
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
+static void unpair_usage(void)
+{
+       printf("Usage: btmgmt unpair [-t type] <remote address>\n");
+}
+
+static struct option unpair_options[] = {
+       { "help",       0, 0, 'h' },
+       { "type",       1, 0, 't' },
+       { 0, 0, 0, 0 }
+};
+
 static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc,
                                                                char **argv)
 {
        struct mgmt_cp_unpair_device cp;
+       uint8_t type = BDADDR_BREDR;
+       int opt;
 
-       if (argc < 2) {
-               printf("Usage: btmgmt %s <remote address>\n", argv[0]);
+       while ((opt = getopt_long(argc, argv, "+t:h", unpair_options,
+                                                               NULL)) != -1) {
+               switch (opt) {
+               case 't':
+                       type = strtol(optarg, NULL, 0);
+                       break;
+               case 'h':
+               default:
+                       unpair_usage();
+                       exit(EXIT_SUCCESS);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               unpair_usage();
                exit(EXIT_FAILURE);
        }
 
@@ -1455,7 +1653,8 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc,
                index = 0;
 
        memset(&cp, 0, sizeof(cp));
-       str2ba(argv[1], &cp.addr.bdaddr);
+       str2ba(argv[0], &cp.addr.bdaddr);
+       cp.addr.type = type;
        cp.disconnect = 1;
 
        if (mgmt_send(mgmt, MGMT_OP_UNPAIR_DEVICE, index, sizeof(cp), &cp,
@@ -1474,7 +1673,7 @@ static void keys_rsp(uint8_t status, uint16_t len, const void *param,
        else
                printf("Keys successfully loaded\n");
 
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void cmd_keys(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
@@ -1493,6 +1692,62 @@ static void cmd_keys(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
        }
 }
 
+static void ltks_rsp(uint8_t status, uint16_t len, const void *param,
+                                                       void *user_data)
+{
+       if (status != 0)
+               fprintf(stderr, "Load keys failed with status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+       else
+               printf("Long term keys successfully loaded\n");
+
+       mainloop_quit();
+}
+
+static void cmd_ltks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_load_long_term_keys cp;
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       memset(&cp, 0, sizeof(cp));
+
+       if (mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index, sizeof(cp), &cp,
+                                               ltks_rsp, NULL, NULL) == 0) {
+               fprintf(stderr, "Unable to send load_ltks cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
+static void irks_rsp(uint8_t status, uint16_t len, const void *param,
+                                                       void *user_data)
+{
+       if (status != 0)
+               fprintf(stderr, "Load IRKs failed with status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+       else
+               printf("Identity Resolving Keys successfully loaded\n");
+
+       mainloop_quit();
+}
+
+static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+       struct mgmt_cp_load_irks cp;
+
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       memset(&cp, 0, sizeof(cp));
+
+       if (mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index, sizeof(cp), &cp,
+                                               irks_rsp, NULL, NULL) == 0) {
+               fprintf(stderr, "Unable to send load_irks cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
 static void block_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
                                                        const void *param)
 {
@@ -1522,7 +1777,7 @@ static void block_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
        printf("%s %s succeeded\n", mgmt_opstr(op), addr);
 
 done:
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void block_usage(void)
@@ -1713,6 +1968,66 @@ static void cmd_clr_uuids(struct mgmt *mgmt, uint16_t index, int argc,
        cmd_remove_uuid(mgmt, index, 2, rm_argv);
 }
 
+static void local_oob_rsp(uint8_t status, uint16_t len, const void *param,
+                                                       void *user_data)
+{
+       const struct mgmt_rp_read_local_oob_data *rp = param;
+       const struct mgmt_rp_read_local_oob_ext_data *rp_ext = param;
+       int i;
+
+       if (status != 0) {
+               fprintf(stderr, "Read Local OOB Data failed "
+                                               "with status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+               goto done;
+       }
+
+       if (len < sizeof(*rp)) {
+               fprintf(stderr, "Too small (%u bytes) read_local_oob rsp\n",
+                                                                       len);
+               goto done;
+       }
+
+       printf("Hash C from P-192: ");
+       for (i = 0; i < 16; i++)
+               printf("%02x", rp->hash[i]);
+       printf("\n");
+
+       printf("Randomizer R with P-192: ");
+       for (i = 0; i < 16; i++)
+               printf("%02x", rp->randomizer[i]);
+       printf("\n");
+
+       if (len < sizeof(*rp_ext))
+               goto done;
+
+       printf("Hash C from P-256: ");
+       for (i = 0; i < 16; i++)
+               printf("%02x", rp_ext->hash256[i]);
+       printf("\n");
+
+       printf("Randomizer R with P-256: ");
+       for (i = 0; i < 16; i++)
+               printf("%02x", rp_ext->randomizer256[i]);
+       printf("\n");
+
+done:
+       mainloop_quit();
+}
+
+static void cmd_local_oob(struct mgmt *mgmt, uint16_t index,
+                                               int argc, char **argv)
+{
+       if (index == MGMT_INDEX_NONE)
+               index = 0;
+
+       if (mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL,
+                                       local_oob_rsp, NULL, NULL) == 0) {
+               fprintf(stderr, "Unable to send read_local_oob cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
 static void did_rsp(uint8_t status, uint16_t len, const void *param,
                                                        void *user_data)
 {
@@ -1723,7 +2038,7 @@ static void did_rsp(uint8_t status, uint16_t len, const void *param,
        else
                printf("Device ID successfully set\n");
 
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void did_usage(void)
@@ -1786,7 +2101,7 @@ static void static_addr_rsp(uint8_t status, uint16_t len, const void *param,
        else
                printf("Static address successfully set\n");
 
-       g_main_loop_quit(event_loop);
+       mainloop_quit();
 }
 
 static void static_addr_usage(void)
@@ -1816,6 +2131,100 @@ static void cmd_static_addr(struct mgmt *mgmt, uint16_t index,
        }
 }
 
+static void cmd_debug_keys(struct mgmt *mgmt, uint16_t index,
+                                               int argc, char **argv)
+{
+       cmd_setting(mgmt, index, MGMT_OP_SET_DEBUG_KEYS, argc, argv);
+}
+
+static void conn_info_rsp(uint8_t status, uint16_t len, const void *param,
+                                                       void *user_data)
+{
+       const struct mgmt_rp_get_conn_info *rp = param; char addr[18];
+
+       if (len == 0 && status != 0) {
+               fprintf(stderr, "Get Conn Info failed, status 0x%02x (%s)\n",
+                                               status, mgmt_errstr(status));
+               goto done;
+       }
+
+       if (len < sizeof(*rp)) {
+               fprintf(stderr, "Unexpected Get Conn Info len %u\n", len);
+               goto done;
+       }
+
+       ba2str(&rp->addr.bdaddr, addr);
+
+       if (status != 0) {
+               fprintf(stderr, "Get Conn Info for %s (%s) failed. status 0x%02x (%s)\n",
+                                               addr, typestr(rp->addr.type),
+                                               status, mgmt_errstr(status));
+               goto done;
+       }
+
+       printf("Connection Information for %s (%s)\n",
+                                               addr, typestr(rp->addr.type));
+       printf("\tRSSI %d\n\tTX power %d\n\tmaximum TX power %d\n",
+                               rp->rssi, rp->tx_power, rp->max_tx_power);
+
+done:
+       mainloop_quit();
+}
+
+static void conn_info_usage(void)
+{
+       printf("Usage: btmgmt conn-info [-t type] <remote address>\n");
+}
+
+static struct option conn_info_options[] = {
+       { "help",       0, 0, 'h' },
+       { "type",       1, 0, 't' },
+       { 0, 0, 0, 0 }
+};
+
+static void cmd_conn_info(struct mgmt *mgmt, uint16_t index,
+                                               int argc, char **argv)
+{
+       struct mgmt_cp_get_conn_info cp;
+       uint8_t type = BDADDR_BREDR;
+       int opt;
+
+       while ((opt = getopt_long(argc, argv, "+t:h", conn_info_options,
+                                                               NULL)) != -1) {
+               switch (opt) {
+               case 't':
+                       type = strtol(optarg, NULL, 0);
+                       break;
+               case 'h':
+               default:
+                       conn_info_usage();
+                       exit(EXIT_SUCCESS);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+       optind = 0;
+
+       if (argc < 1) {
+               conn_info_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(mgmt, MGMT_OP_GET_CONN_INFO, index, sizeof(cp), &cp,
+                                       conn_info_rsp, NULL, NULL) == 0) {
+               fprintf(stderr, "Unable to send get_conn_info cmd\n");
+               exit(EXIT_FAILURE);
+       }
+}
+
 static struct {
        char *cmd;
        void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
@@ -1832,10 +2241,12 @@ static struct {
        { "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"             },
+       { "sc",         cmd_sc,         "Toogle SC support"             },
+       { "hs",         cmd_hs,         "Toggle HS support"             },
+       { "le",         cmd_le,         "Toggle LE support"             },
        { "advertising",cmd_advertising,"Toggle LE advertising",        },
        { "bredr",      cmd_bredr,      "Toggle BR/EDR support",        },
+       { "privacy",    cmd_privacy,    "Toggle privacy support"        },
        { "class",      cmd_class,      "Set device major/minor class"  },
        { "disconnect", cmd_disconnect, "Disconnect device"             },
        { "con",        cmd_con,        "List connections"              },
@@ -1844,15 +2255,20 @@ static struct {
        { "pair",       cmd_pair,       "Pair with a remote device"     },
        { "cancelpair", cmd_cancel_pair,"Cancel pairing"                },
        { "unpair",     cmd_unpair,     "Unpair device"                 },
-       { "keys",       cmd_keys,       "Load Keys"                     },
+       { "keys",       cmd_keys,       "Load Link Keys"                },
+       { "ltks",       cmd_ltks,       "Load Long Term Keys"           },
+       { "irks",       cmd_irks,       "Load Identity Resolving Keys"  },
        { "block",      cmd_block,      "Block Device"                  },
        { "unblock",    cmd_unblock,    "Unblock Device"                },
        { "add-uuid",   cmd_add_uuid,   "Add UUID"                      },
-       { "rm-uuid",    cmd_remove_uuid, "Remove UUID"                  },
-       { "clr-uuids",  cmd_clr_uuids,  "Clear UUIDs",                  },
-       { "did",        cmd_did,        "Set Device ID",                },
-       { "static-addr",cmd_static_addr,"Set static address",           },
-       { NULL, NULL, 0 }
+       { "rm-uuid",    cmd_remove_uuid,"Remove UUID"                   },
+       { "clr-uuids",  cmd_clr_uuids,  "Clear UUIDs"                   },
+       { "local-oob",  cmd_local_oob,  "Local OOB data"                },
+       { "did",        cmd_did,        "Set Device ID"                 },
+       { "static-addr",cmd_static_addr,"Set static address"            },
+       { "debug-keys", cmd_debug_keys, "Toogle debug keys"             },
+       { "conn-info",  cmd_conn_info,  "Get connection information"    },
+       { }
 };
 
 static void usage(void)
@@ -1889,6 +2305,7 @@ int main(int argc, char *argv[])
        int opt, i;
        uint16_t index = MGMT_INDEX_NONE;
        struct mgmt *mgmt;
+       int exit_status;
 
        while ((opt = getopt_long(argc, argv, "+hvi:",
                                                main_options, NULL)) != -1) {
@@ -1919,13 +2336,12 @@ int main(int argc, char *argv[])
                return 0;
        }
 
-       event_loop = g_main_loop_new(NULL, FALSE);
+       mainloop_init();
 
        mgmt = mgmt_new_default();
        if (!mgmt) {
                fprintf(stderr, "Unable to open mgmt_socket\n");
-               g_main_loop_unref(event_loop);
-               return -1;
+               return EXIT_FAILURE;
        }
 
        for (i = 0; command[i].cmd; i++) {
@@ -1939,8 +2355,7 @@ int main(int argc, char *argv[])
        if (command[i].cmd == NULL) {
                fprintf(stderr, "Unknown command: %s\n", argv[0]);
                mgmt_unref(mgmt);
-               g_main_loop_unref(event_loop);
-               return -1;
+               return EXIT_FAILURE;
        }
 
        mgmt_register(mgmt, MGMT_EV_CONTROLLER_ERROR, index, controller_error,
@@ -1972,13 +2387,11 @@ int main(int argc, char *argv[])
        mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm,
                                                                mgmt, NULL);
 
-       g_main_loop_run(event_loop);
+       exit_status = mainloop_run();
 
        mgmt_cancel_all(mgmt);
        mgmt_unregister_all(mgmt);
        mgmt_unref(mgmt);
 
-       g_main_loop_unref(event_loop);
-
-       return 0;
+       return exit_status;
 }
diff --git a/tools/btproxy.c b/tools/btproxy.c
new file mode 100644 (file)
index 0000000..3503148
--- /dev/null
@@ -0,0 +1,713 @@
+/*
+ *
+ *  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 <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include "src/shared/util.h"
+#include "monitor/mainloop.h"
+#include "monitor/bt.h"
+
+#define BTPROTO_HCI    1
+struct sockaddr_hci {
+       sa_family_t     hci_family;
+       unsigned short  hci_dev;
+       unsigned short  hci_channel;
+};
+#define HCI_CHANNEL_USER       1
+
+static uint16_t hci_index = 0;
+static bool client_active = false;
+static bool debug_enabled = false;
+
+static void hexdump_print(const char *str, void *user_data)
+{
+       printf("%s%s\n", (char *) user_data, str);
+}
+
+struct proxy {
+       /* Receive commands, ACL and SCO data */
+       int host_fd;
+       uint8_t host_buf[4096];
+       uint16_t host_len;
+       bool host_shutdown;
+
+       /* Receive events, ACL and SCO data */
+       int dev_fd;
+       uint8_t dev_buf[4096];
+       uint16_t dev_len;
+       bool dev_shutdown;
+};
+
+static bool write_packet(int fd, const void *data, size_t size,
+                                                       void *user_data)
+{
+       while (size > 0) {
+               ssize_t written;
+
+               written = write(fd, data, size);
+               if (written < 0) {
+                       if (errno == EAGAIN || errno == EINTR)
+                               continue;
+                       return false;
+               }
+
+               if (debug_enabled)
+                       util_hexdump('<', data, written, hexdump_print,
+                                                               user_data);
+
+               data += written;
+               size -= written;
+       }
+
+       return true;
+}
+
+static void host_read_destroy(void *user_data)
+{
+       struct proxy *proxy = user_data;
+
+       printf("Closing host descriptor\n");
+
+       if (proxy->host_shutdown)
+               shutdown(proxy->host_fd, SHUT_RDWR);
+
+       close(proxy->host_fd);
+       proxy->host_fd = -1;
+
+       if (proxy->dev_fd < 0) {
+               client_active = false;
+               free(proxy);
+       } else
+               mainloop_remove_fd(proxy->dev_fd);
+}
+
+static void host_read_callback(int fd, uint32_t events, void *user_data)
+{
+       struct proxy *proxy = user_data;
+       struct bt_hci_cmd_hdr *cmd_hdr;
+       struct bt_hci_acl_hdr *acl_hdr;
+       struct bt_hci_sco_hdr *sco_hdr;
+       ssize_t len;
+       uint16_t pktlen;
+
+       if (events & (EPOLLERR | EPOLLHUP)) {
+               fprintf(stderr, "Error from host descriptor\n");
+               mainloop_remove_fd(proxy->host_fd);
+               return;
+       }
+
+       if (events & EPOLLRDHUP) {
+               fprintf(stderr, "Remote hangup of host descriptor\n");
+               mainloop_remove_fd(proxy->host_fd);
+               return;
+       }
+
+       len = read(proxy->host_fd, proxy->host_buf + proxy->host_len,
+                               sizeof(proxy->host_buf) - proxy->host_len);
+       if (len < 0) {
+               if (errno == EAGAIN || errno == EINTR)
+                       return;
+
+               fprintf(stderr, "Read from host descriptor failed\n");
+               mainloop_remove_fd(proxy->host_fd);
+               return;
+       }
+
+       if (debug_enabled)
+               util_hexdump('>', proxy->host_buf + proxy->host_len, len,
+                                               hexdump_print, "H: ");
+
+       proxy->host_len += len;
+
+process_packet:
+       if (proxy->host_len < 1)
+               return;
+
+       switch (proxy->host_buf[0]) {
+       case BT_H4_CMD_PKT:
+               if (proxy->host_len < 1 + sizeof(*cmd_hdr))
+                       return;
+
+               cmd_hdr = (void *) (proxy->host_buf + 1);
+               pktlen = 1 + sizeof(*cmd_hdr) + cmd_hdr->plen;
+               break;
+       case BT_H4_ACL_PKT:
+               if (proxy->host_len < 1 + sizeof(*acl_hdr))
+                       return;
+
+               acl_hdr = (void *) (proxy->host_buf + 1);
+               pktlen = 1 + sizeof(*acl_hdr) + cpu_to_le16(acl_hdr->dlen);
+               break;
+       case BT_H4_SCO_PKT:
+               if (proxy->host_len < 1 + sizeof(*sco_hdr))
+                       return;
+
+               sco_hdr = (void *) (proxy->host_buf + 1);
+               pktlen = 1 + sizeof(*sco_hdr) + sco_hdr->dlen;
+               break;
+       case 0xff:
+               /* Notification packet from /dev/vhci - ignore */
+               proxy->host_len = 0;
+               return;
+       default:
+               fprintf(stderr, "Received unknown host packet type 0x%02x\n",
+                                                       proxy->host_buf[0]);
+               mainloop_remove_fd(proxy->host_fd);
+               return;
+       }
+
+       if (proxy->host_len < pktlen)
+               return;
+
+       if (!write_packet(proxy->dev_fd, proxy->host_buf, pktlen, "D: ")) {
+               fprintf(stderr, "Write to device descriptor failed\n");
+               mainloop_remove_fd(proxy->dev_fd);
+               return;
+       }
+
+       if (proxy->host_len > pktlen) {
+               memmove(proxy->host_buf, proxy->host_buf + pktlen,
+                                               proxy->host_len - pktlen);
+               proxy->host_len -= pktlen;
+               goto process_packet;
+       }
+
+       proxy->host_len = 0;
+}
+
+static void dev_read_destroy(void *user_data)
+{
+       struct proxy *proxy = user_data;
+
+       printf("Closing device descriptor\n");
+
+       if (proxy->dev_shutdown)
+               shutdown(proxy->dev_fd, SHUT_RDWR);
+
+       close(proxy->dev_fd);
+       proxy->dev_fd = -1;
+
+       if (proxy->host_fd < 0) {
+               client_active = false;
+               free(proxy);
+       } else
+               mainloop_remove_fd(proxy->host_fd);
+}
+
+static void dev_read_callback(int fd, uint32_t events, void *user_data)
+{
+       struct proxy *proxy = user_data;
+       struct bt_hci_evt_hdr *evt_hdr;
+       struct bt_hci_acl_hdr *acl_hdr;
+       struct bt_hci_sco_hdr *sco_hdr;
+       ssize_t len;
+       uint16_t pktlen;
+
+       if (events & (EPOLLERR | EPOLLHUP)) {
+               fprintf(stderr, "Error from device descriptor\n");
+               mainloop_remove_fd(proxy->dev_fd);
+               return;
+       }
+
+       if (events & EPOLLRDHUP) {
+               fprintf(stderr, "Remote hangup of device descriptor\n");
+               mainloop_remove_fd(proxy->host_fd);
+               return;
+       }
+
+       len = read(proxy->dev_fd, proxy->dev_buf + proxy->dev_len,
+                               sizeof(proxy->dev_buf) - proxy->dev_len);
+       if (len < 0) {
+               if (errno == EAGAIN || errno == EINTR)
+                       return;
+
+               fprintf(stderr, "Read from device descriptor failed\n");
+               mainloop_remove_fd(proxy->dev_fd);
+               return;
+       }
+
+       if (debug_enabled)
+               util_hexdump('>', proxy->dev_buf + proxy->dev_len, len,
+                                               hexdump_print, "D: ");
+
+       proxy->dev_len += len;
+
+process_packet:
+       if (proxy->dev_len < 1)
+               return;
+
+       switch (proxy->dev_buf[0]) {
+       case BT_H4_EVT_PKT:
+               if (proxy->dev_len < 1 + sizeof(*evt_hdr))
+                       return;
+
+               evt_hdr = (void *) (proxy->dev_buf + 1);
+               pktlen = 1 + sizeof(*evt_hdr) + evt_hdr->plen;
+               break;
+       case BT_H4_ACL_PKT:
+               if (proxy->dev_len < 1 + sizeof(*acl_hdr))
+                       return;
+
+               acl_hdr = (void *) (proxy->dev_buf + 1);
+               pktlen = 1 + sizeof(*acl_hdr) + cpu_to_le16(acl_hdr->dlen);
+               break;
+       case BT_H4_SCO_PKT:
+               if (proxy->dev_len < 1 + sizeof(*sco_hdr))
+                       return;
+
+               sco_hdr = (void *) (proxy->dev_buf + 1);
+               pktlen = 1 + sizeof(*sco_hdr) + sco_hdr->dlen;
+               break;
+       default:
+               fprintf(stderr, "Received unknown device packet type 0x%02x\n",
+                                                       proxy->dev_buf[0]);
+               mainloop_remove_fd(proxy->dev_fd);
+               return;
+       }
+
+       if (proxy->dev_len < pktlen)
+               return;
+
+       if (!write_packet(proxy->host_fd, proxy->dev_buf, pktlen, "H: ")) {
+               fprintf(stderr, "Write to host descriptor failed\n");
+               mainloop_remove_fd(proxy->host_fd);
+               return;
+       }
+
+       if (proxy->dev_len > pktlen) {
+               memmove(proxy->dev_buf, proxy->dev_buf + pktlen,
+                                               proxy->dev_len - pktlen);
+               proxy->dev_len -= pktlen;
+               goto process_packet;
+       }
+
+       proxy->dev_len = 0;
+}
+
+static bool setup_proxy(int host_fd, bool host_shutdown,
+                                               int dev_fd, bool dev_shutdown)
+{
+       struct proxy *proxy;
+
+       proxy = new0(struct proxy, 1);
+       if (!proxy)
+               return NULL;
+
+       proxy->host_fd = host_fd;
+       proxy->host_shutdown = host_shutdown;
+
+       proxy->dev_fd = dev_fd;
+       proxy->dev_shutdown = dev_shutdown;
+
+       mainloop_add_fd(proxy->host_fd, EPOLLIN | EPOLLRDHUP,
+                               host_read_callback, proxy, host_read_destroy);
+
+       mainloop_add_fd(proxy->dev_fd, EPOLLIN | EPOLLRDHUP,
+                               dev_read_callback, proxy, dev_read_destroy);
+
+       return true;
+}
+
+static int open_channel(uint16_t index)
+{
+       struct sockaddr_hci addr;
+       int fd;
+
+       printf("Opening user channel for hci%u\n", hci_index);
+
+       fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI);
+       if (fd < 0) {
+               perror("Failed to open Bluetooth socket");
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.hci_family = AF_BLUETOOTH;
+       addr.hci_dev = index;
+       addr.hci_channel = HCI_CHANNEL_USER;
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(fd);
+               perror("Failed to bind Bluetooth socket");
+               return -1;
+       }
+
+       return fd;
+}
+
+static void server_callback(int fd, uint32_t events, void *user_data)
+{
+       union {
+               struct sockaddr common;
+               struct sockaddr_un sun;
+               struct sockaddr_in sin;
+       } addr;
+       socklen_t len;
+       int host_fd, dev_fd;
+
+       if (events & (EPOLLERR | EPOLLHUP)) {
+               mainloop_quit();
+               return;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       len = sizeof(addr);
+
+       if (getsockname(fd, &addr.common, &len) < 0) {
+               perror("Failed to get socket name");
+               return;
+       }
+
+       host_fd = accept(fd, &addr.common, &len);
+       if (host_fd < 0) {
+               perror("Failed to accept client socket");
+               return;
+       }
+
+       if (client_active) {
+               fprintf(stderr, "Active client already present\n");
+               close(host_fd);
+               return;
+       }
+
+       dev_fd = open_channel(hci_index);
+       if (dev_fd < 0) {
+               close(host_fd);
+               return;
+       }
+
+       printf("New client connected\n");
+
+       if (!setup_proxy(host_fd, true, dev_fd, false)) {
+               close(dev_fd);
+               close(host_fd);
+               return;
+       }
+
+       client_active = true;
+}
+
+static int open_unix(const char *path)
+{
+       struct sockaddr_un addr;
+       int fd;
+
+       unlink(path);
+
+       fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (fd < 0) {
+               perror("Failed to open Unix 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 Unix server socket");
+               close(fd);
+               return -1;
+       }
+
+       if (listen(fd, 1) < 0) {
+               perror("Failed to listen Unix server socket");
+               close(fd);
+               return -1;
+       }
+
+       if (chmod(path, 0666) < 0)
+               perror("Failed to change mode");
+
+       return fd;
+}
+
+static int open_tcp(const char *address, unsigned int port)
+{
+       struct sockaddr_in addr;
+       int fd, opt = 1;
+
+       fd = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (fd < 0) {
+               perror("Failed to open TCP server socket");
+               return -1;
+       }
+
+       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(address);
+       addr.sin_port = htons(port);
+
+       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Failed to bind TCP server socket");
+               close(fd);
+               return -1;
+       }
+
+       if (listen(fd, 1) < 0) {
+               perror("Failed to listen TCP server socket");
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+static int connect_tcp(const char *address, unsigned int port)
+{
+       struct sockaddr_in addr;
+       int fd;
+
+       fd = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+       if (fd < 0) {
+               perror("Failed to open TCP client socket");
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = inet_addr(address);
+       addr.sin_port = htons(port);
+
+       if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("Failed to connect TCP client socket");
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+static int open_vhci(uint8_t type)
+{
+       uint8_t create_req[2] = { 0xff, type };
+       ssize_t written;
+       int fd;
+
+       fd = open("/dev/vhci", O_RDWR | O_CLOEXEC);
+       if (fd < 0) {
+               perror("Failed to open /dev/vhci device");
+               return -1;
+       }
+
+       written = write(fd, create_req, sizeof(create_req));
+       if (written < 0) {
+               perror("Failed to set device type");
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               mainloop_quit();
+               break;
+       }
+}
+
+static void usage(void)
+{
+       printf("btproxy - Bluetooth controller proxy\n"
+               "Usage:\n");
+       printf("\tbtproxy [options]\n");
+       printf("Options:\n"
+               "\t-c, --connect <address>     Connect to server\n"
+               "\t-l, --listen [address]      Use TCP server\n"
+               "\t-u, --unix [path]           Use Unix server\n"
+               "\t-p, --port <port>           Use specified TCP port\n"
+               "\t-i, --index <num>           Use specified controller\n"
+               "\t-d, --debug                 Enable debugging output\n"
+               "\t-h, --help                  Show help options\n");
+}
+
+static const struct option main_options[] = {
+       { "connect", required_argument, NULL, 'c' },
+       { "listen",  optional_argument, NULL, 'l' },
+       { "unix",    optional_argument, NULL, 'u' },
+       { "port",    required_argument, NULL, 'p' },
+       { "index",   required_argument, NULL, 'i' },
+       { "debug",   no_argument,       NULL, 'd' },
+       { "version", no_argument,       NULL, 'v' },
+       { "help",    no_argument,       NULL, 'h' },
+       { }
+};
+
+int main(int argc, char *argv[])
+{
+       const char *connect_address = NULL;
+       const char *server_address = NULL;
+       const char *unix_path = NULL;
+       unsigned short tcp_port = 0xb1ee;       /* 45550 */
+       const char *str;
+       sigset_t mask;
+
+       for (;;) {
+               int opt;
+
+               opt = getopt_long(argc, argv, "c:l::u::p:i:dvh",
+                                               main_options, NULL);
+               if (opt < 0)
+                       break;
+
+               switch (opt) {
+               case 'c':
+                       connect_address = optarg;
+                       break;
+               case 'l':
+                       if (optarg)
+                               server_address = optarg;
+                       else
+                               server_address = "0.0.0.0";
+                       break;
+               case 'u':
+                       if (optarg)
+                               unix_path = optarg;
+                       else
+                               unix_path = "/tmp/bt-server-bredr";
+                       break;
+               case 'p':
+                       tcp_port = atoi(optarg);
+                       break;
+               case 'i':
+                       if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+                               str = optarg + 3;
+                       else
+                               str = optarg;
+                       if (!isdigit(*str)) {
+                               usage();
+                               return EXIT_FAILURE;
+                       }
+                       hci_index = atoi(str);
+                       break;
+               case 'd':
+                       debug_enabled = true;
+                       break;
+               case 'v':
+                       printf("%s\n", VERSION);
+                       return EXIT_SUCCESS;
+               case 'h':
+                       usage();
+                       return EXIT_SUCCESS;
+               default:
+                       return EXIT_FAILURE;
+               }
+       }
+
+       if (argc - optind > 0) {
+               fprintf(stderr, "Invalid command line parameters\n");
+               return EXIT_FAILURE;
+       }
+
+       if (unix_path && server_address) {
+               fprintf(stderr, "Invalid to specify TCP and Unix servers\n");
+               return EXIT_FAILURE;
+       }
+
+       if (connect_address && (unix_path || server_address)) {
+               fprintf(stderr, "Invalid to specify client and server mode\n");
+               return EXIT_FAILURE;
+       }
+
+       mainloop_init();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       if (connect_address) {
+               int host_fd, dev_fd;
+
+               printf("Connecting to %s:%u\n", connect_address, tcp_port);
+
+               dev_fd = connect_tcp(connect_address, tcp_port);
+               if (dev_fd < 0)
+                       return EXIT_FAILURE;
+
+               printf("Opening virtual device\n");
+
+               host_fd = open_vhci(0x00);
+               if (host_fd < 0) {
+                       close(dev_fd);
+                       return EXIT_FAILURE;
+               }
+
+               if (!setup_proxy(host_fd, false, dev_fd, true)) {
+                       close(dev_fd);
+                       close(host_fd);
+                       return EXIT_FAILURE;
+               }
+       } else {
+               int server_fd;
+
+               if (unix_path) {
+                       printf("Listening on %s\n", unix_path);
+
+                       server_fd = open_unix(unix_path);
+               } else if (server_address) {
+                       printf("Listening on %s:%u\n", server_address,
+                                                               tcp_port);
+
+                       server_fd = open_tcp(server_address, tcp_port);
+               } else {
+                       fprintf(stderr, "Missing emulator device\n");
+                       return EXIT_FAILURE;
+               }
+
+               if (server_fd < 0)
+                       return EXIT_FAILURE;
+
+               mainloop_add_fd(server_fd, EPOLLIN, server_callback,
+                                                       NULL, NULL);
+       }
+
+       return mainloop_run();
+}
index 306e643..6ca62d2 100644 (file)
 #include <arpa/inet.h>
 #include <sys/stat.h>
 
-#include "monitor/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)
+#include "src/shared/btsnoop.h"
 
 struct btsnoop_hdr {
        uint8_t         id[8];          /* Identification Pattern */
@@ -89,8 +76,8 @@ static int create_btsnoop(const char *path)
        }
 
        memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
-       hdr.version = htonl(btsnoop_version);
-       hdr.type = htonl(2001);
+       hdr.version = htobe32(btsnoop_version);
+       hdr.type = htobe32(2001);
 
        written = write(fd, &hdr, BTSNOOP_HDR_SIZE);
        if (written < 0) {
@@ -127,14 +114,14 @@ static int open_btsnoop(const char *path, uint32_t *type)
                return -1;
        }
 
-       if (ntohl(hdr.version) != btsnoop_version) {
+       if (be32toh(hdr.version) != btsnoop_version) {
                fprintf(stderr, "invalid btsnoop version\n");
                close(fd);
                return -1;
        }
 
        if (type)
-               *type = ntohl(hdr.type);
+               *type = be32toh(hdr.type);
 
        return fd;
 }
@@ -205,17 +192,17 @@ next_packet:
                        continue;
                }
 
-               ts = ntoh64(input_pkt[i].ts);
+               ts = be64toh(input_pkt[i].ts);
 
-               if (ts < ntoh64(input_pkt[select_input].ts))
+               if (ts < be64toh(input_pkt[select_input].ts))
                        select_input = i;
        }
 
        if (select_input < 0)
                goto close_output;
 
-       toread = ntohl(input_pkt[select_input].size);
-       flags = ntohl(input_pkt[select_input].flags);
+       toread = be32toh(input_pkt[select_input].size);
+       flags = be32toh(input_pkt[select_input].flags);
 
        len = read(input_fd[select_input], buf, toread);
        if (len < 0 || len != (ssize_t) toread) {
@@ -224,8 +211,8 @@ next_packet:
                goto next_packet;
        }
 
-       written = input_pkt[select_input].size = htonl(toread - 1);
-       written = input_pkt[select_input].len = htonl(toread - 1);
+       written = input_pkt[select_input].size = htobe32(toread - 1);
+       written = input_pkt[select_input].len = htobe32(toread - 1);
 
        switch (buf[0]) {
        case 0x01:
@@ -251,7 +238,7 @@ next_packet:
        }
 
        index = select_input;
-       input_pkt[select_input].flags = htonl((index << 16) | opcode);
+       input_pkt[select_input].flags = htobe32((index << 16) | opcode);
 
        written = write(output_fd, &input_pkt[select_input], BTSNOOP_PKT_SIZE);
        if (written != BTSNOOP_PKT_SIZE) {
@@ -307,8 +294,8 @@ next_packet:
        if (len < 0 || len != BTSNOOP_PKT_SIZE)
                goto close_input;
 
-       toread = ntohl(pkt.size);
-       flags = ntohl(pkt.flags);
+       toread = be32toh(pkt.size);
+       flags = be32toh(pkt.flags);
 
        opcode = flags & 0x00ff;
 
@@ -380,8 +367,8 @@ next_packet:
        if (len < 0 || len != BTSNOOP_PKT_SIZE)
                goto close_input;
 
-       toread = ntohl(pkt.size);
-       flags = ntohl(pkt.flags);
+       toread = be32toh(pkt.size);
+       flags = be32toh(pkt.flags);
 
        opcode = flags & 0x00ff;
 
@@ -457,7 +444,7 @@ next_packet:
        if (len < 0 || len != BTSNOOP_PKT_SIZE)
                goto close_input;
 
-       toread = ntohl(pkt.size);
+       toread = be32toh(pkt.size);
 
        len = read(fd, buf, toread);
        if (len < 0 || len != (ssize_t) toread) {
index 16b7553..4ddb98a 100644 (file)
@@ -205,8 +205,8 @@ static bool find_controllers(void)
        dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
        if (!dl) {
                perror("Failed allocate HCI device request memory");
-               result = false;
-               goto done;
+               close(fd);
+               return false;
        }
 
        dl->dev_num = HCI_MAX_DEV;
@@ -243,6 +243,7 @@ static bool find_controllers(void)
        }
 
 done:
+       free(dl);
        close(fd);
        return result;
 }
index a483bc1..5fb6bdc 100644 (file)
@@ -33,6 +33,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <dirent.h>
+#include <limits.h>
 #include <sys/ioctl.h>
 
 #include "csr.h"
diff --git a/tools/gatt-service.c b/tools/gatt-service.c
new file mode 100644 (file)
index 0000000..6bca404
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  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 <stdio.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/signalfd.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "src/error.h"
+
+#define GATT_MGR_IFACE                 "org.bluez.GattManager1"
+#define GATT_SERVICE_IFACE             "org.bluez.GattService1"
+#define GATT_CHR_IFACE                 "org.bluez.GattCharacteristic1"
+#define GATT_DESCRIPTOR_IFACE          "org.bluez.GattDescriptor1"
+
+/* Immediate Alert Service UUID */
+#define IAS_UUID                       "00001802-0000-1000-8000-00805f9b34fb"
+#define ALERT_LEVEL_CHR_UUID           "00002a06-0000-1000-8000-00805f9b34fb"
+
+/* Random UUID for testing purpose */
+#define READ_WRITE_DESCRIPTOR_UUID     "8260c653-1a54-426b-9e36-e84c238bc669"
+
+static GMainLoop *main_loop;
+static GSList *services;
+static DBusConnection *connection;
+
+struct characteristic {
+       char *uuid;
+       char *path;
+       uint8_t *value;
+       int vlen;
+       const char **props;
+};
+
+struct descriptor {
+       char *uuid;
+       char *path;
+       uint8_t *value;
+       int vlen;
+};
+
+/*
+ * Alert Level support Write Without Response only. Supported
+ * properties are defined at doc/gatt-api.txt. See "Flags"
+ * property of the GattCharacteristic1.
+ */
+static const char const *ias_alert_level_props[] = {
+                                               "write-without-response",
+                                               NULL };
+
+static gboolean desc_get_uuid(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct descriptor *desc = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid);
+
+       return TRUE;
+}
+
+static gboolean desc_get_value(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct descriptor *desc = user_data;
+       DBusMessageIter array;
+
+       printf("Descriptor(%s): Get(\"Value\")\n", desc->uuid);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_BYTE_AS_STRING, &array);
+
+       if (desc->vlen && desc->value)
+               dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+                                               &desc->value, desc->vlen);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+}
+
+static void desc_set_value(const GDBusPropertyTable *property,
+                               DBusMessageIter *iter,
+                               GDBusPendingPropertySet id, void *user_data)
+{
+       struct descriptor *desc = user_data;
+       DBusMessageIter array;
+       const uint8_t *value;
+       int vlen;
+
+       printf("Descriptor(%s): Set(\"Value\", ...)\n", desc->uuid);
+
+       dbus_message_iter_recurse(iter, &array);
+       dbus_message_iter_get_fixed_array(&array, &value, &vlen);
+
+       g_free(desc->value);
+       desc->value = g_memdup(value, vlen);
+       desc->vlen = vlen;
+
+       g_dbus_pending_property_success(id);
+
+       g_dbus_emit_property_changed(connection, desc->path,
+                                       GATT_DESCRIPTOR_IFACE, "Value");
+
+}
+
+static const GDBusPropertyTable desc_properties[] = {
+       { "UUID",               "s",    desc_get_uuid },
+       { "Value",              "ay",   desc_get_value, desc_set_value, NULL },
+       { }
+};
+
+static gboolean chr_get_uuid(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct characteristic *chr = user_data;
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chr->uuid);
+
+       return TRUE;
+}
+
+static gboolean chr_get_value(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       struct characteristic *chr = user_data;
+       DBusMessageIter array;
+
+       printf("Characteristic(%s): Get(\"Value\")\n", chr->uuid);
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_BYTE_AS_STRING, &array);
+
+       dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+                                               &chr->value, chr->vlen);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+}
+
+static gboolean chr_get_props(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *data)
+{
+       struct characteristic *chr = data;
+       DBusMessageIter array;
+       int i;
+
+       dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                       DBUS_TYPE_STRING_AS_STRING, &array);
+
+       for (i = 0; chr->props[i]; i++)
+               dbus_message_iter_append_basic(&array,
+                                       DBUS_TYPE_STRING, &chr->props[i]);
+
+       dbus_message_iter_close_container(iter, &array);
+
+       return TRUE;
+}
+
+static void chr_set_value(const GDBusPropertyTable *property,
+                               DBusMessageIter *iter,
+                               GDBusPendingPropertySet id, void *user_data)
+{
+       struct characteristic *chr = user_data;
+       DBusMessageIter array;
+       uint8_t *value;
+       int len;
+
+       printf("Characteristic(%s): Set('Value', ...)\n", chr->uuid);
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+               printf("Invalid value for Set('Value'...)\n");
+               g_dbus_pending_property_error(id,
+                                       ERROR_INTERFACE ".InvalidArguments",
+                                       "Invalid arguments in method call");
+               return;
+       }
+
+       dbus_message_iter_recurse(iter, &array);
+       dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+       g_free(chr->value);
+       chr->value = g_memdup(value, len);
+       chr->vlen = len;
+
+       g_dbus_pending_property_success(id);
+       g_dbus_emit_property_changed(connection, chr->path,
+                                               GATT_CHR_IFACE, "Value");
+}
+
+static const GDBusPropertyTable chr_properties[] = {
+       { "UUID",       "s",    chr_get_uuid },
+       { "Value", "ay", chr_get_value, chr_set_value, NULL },
+       { "Flags", "as", chr_get_props, NULL, NULL },
+       { }
+};
+
+static gboolean service_get_uuid(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       const char *uuid = user_data;
+
+       printf("Get UUID: %s\n", uuid);
+
+       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+
+       return TRUE;
+}
+
+static gboolean service_get_includes(const GDBusPropertyTable *property,
+                                       DBusMessageIter *iter, void *user_data)
+{
+       const char *uuid = user_data;
+
+       printf("Get Includes: %s\n", uuid);
+
+       return TRUE;
+}
+
+static gboolean service_exist_includes(const GDBusPropertyTable *property,
+                                                       void *user_data)
+{
+       const char *uuid = user_data;
+
+       printf("Exist Includes: %s\n", uuid);
+
+       return FALSE;
+}
+
+static const GDBusPropertyTable service_properties[] = {
+       { "UUID", "s", service_get_uuid },
+       { "Includes", "ao", service_get_includes, NULL,
+                                       service_exist_includes },
+       { }
+};
+
+static void chr_iface_destroy(gpointer user_data)
+{
+       struct characteristic *chr = user_data;
+
+       g_free(chr->uuid);
+       g_free(chr->value);
+       g_free(chr->path);
+       g_free(chr);
+}
+
+static void desc_iface_destroy(gpointer user_data)
+{
+       struct descriptor *desc = user_data;
+
+       g_free(desc->uuid);
+       g_free(desc->value);
+       g_free(desc->path);
+       g_free(desc);
+}
+
+static gboolean register_characteristic(const char *chr_uuid,
+                                               const uint8_t *value, int vlen,
+                                               const char **props,
+                                               const char *desc_uuid,
+                                               const char *service_path)
+{
+       struct characteristic *chr;
+       struct descriptor *desc;
+       static int id = 1;
+
+       chr = g_new0(struct characteristic, 1);
+       chr->uuid = g_strdup(chr_uuid);
+       chr->value = g_memdup(value, vlen);
+       chr->vlen = vlen;
+       chr->props = props;
+       chr->path = g_strdup_printf("%s/characteristic%d", service_path, id++);
+
+       if (!g_dbus_register_interface(connection, chr->path, GATT_CHR_IFACE,
+                                       NULL, NULL, chr_properties,
+                                       chr, chr_iface_destroy)) {
+               printf("Couldn't register characteristic interface\n");
+               chr_iface_destroy(chr);
+               return FALSE;
+       }
+
+       if (!desc_uuid)
+               return TRUE;
+
+       desc = g_new0(struct descriptor, 1);
+       desc->uuid = g_strdup(desc_uuid);
+       desc->path = g_strdup_printf("%s/descriptor%d", chr->path, id++);
+
+       if (!g_dbus_register_interface(connection, desc->path,
+                                       GATT_DESCRIPTOR_IFACE,
+                                       NULL, NULL, desc_properties,
+                                       desc, desc_iface_destroy)) {
+               printf("Couldn't register descriptor interface\n");
+               g_dbus_unregister_interface(connection, chr->path,
+                                                       GATT_CHR_IFACE);
+
+               desc_iface_destroy(desc);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static char *register_service(const char *uuid)
+{
+       static int id = 1;
+       char *path;
+
+       path = g_strdup_printf("/service%d", id++);
+       if (!g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
+                               NULL, NULL, service_properties,
+                               g_strdup(uuid), g_free)) {
+               printf("Couldn't register service interface\n");
+               g_free(path);
+               return NULL;
+       }
+
+       return path;
+}
+
+static void create_services()
+{
+       char *service_path;
+       uint8_t level = 0;
+
+       service_path = register_service(IAS_UUID);
+       if (!service_path)
+               return;
+
+       /* Add Alert Level Characteristic to Immediate Alert Service */
+       if (!register_characteristic(ALERT_LEVEL_CHR_UUID,
+                                               &level, sizeof(level),
+                                               ias_alert_level_props,
+                                               READ_WRITE_DESCRIPTOR_UUID,
+                                               service_path)) {
+               printf("Couldn't register Alert Level characteristic (IAS)\n");
+               g_dbus_unregister_interface(connection, service_path,
+                                                       GATT_SERVICE_IFACE);
+               g_free(service_path);
+               return;
+       }
+
+       services = g_slist_prepend(services, service_path);
+       printf("Registered service: %s\n", service_path);
+}
+
+static void register_external_service_reply(DBusPendingCall *call,
+                                                       void *user_data)
+{
+       DBusMessage *reply = dbus_pending_call_steal_reply(call);
+       DBusError derr;
+
+       dbus_error_init(&derr);
+       dbus_set_error_from_message(&derr, reply);
+
+       if (dbus_error_is_set(&derr))
+               printf("RegisterService: %s\n", derr.message);
+       else
+               printf("RegisterService: OK\n");
+
+       dbus_message_unref(reply);
+       dbus_error_free(&derr);
+}
+
+static void register_external_service(gpointer a, gpointer b)
+{
+       DBusConnection *conn = b;
+       const char *path = a;
+       DBusMessage *msg;
+       DBusPendingCall *call;
+       DBusMessageIter iter, dict;
+
+       msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
+                                       GATT_MGR_IFACE, "RegisterService");
+       if (!msg) {
+               printf("Couldn't allocate D-Bus message\n");
+               return;
+       }
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
+
+       /* TODO: Add options dictionary */
+
+       dbus_message_iter_close_container(&iter, &dict);
+
+       if (!g_dbus_send_message_with_reply(conn, msg, &call, -1)) {
+               dbus_message_unref(msg);
+               return;
+       }
+
+       dbus_pending_call_set_notify(call, register_external_service_reply,
+                                                               NULL, NULL);
+
+       dbus_pending_call_unref(call);
+}
+
+static void connect_handler(DBusConnection *conn, void *user_data)
+{
+       g_slist_foreach(services, register_external_service, conn);
+}
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       static bool __terminated = false;
+       struct signalfd_siginfo si;
+       ssize_t result;
+       int fd;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+               return FALSE;
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       result = read(fd, &si, sizeof(si));
+       if (result != sizeof(si))
+               return FALSE;
+
+       switch (si.ssi_signo) {
+       case SIGINT:
+       case SIGTERM:
+               if (!__terminated) {
+                       printf("Terminating\n");
+                       g_main_loop_quit(main_loop);
+               }
+
+               __terminated = true;
+               break;
+       }
+
+       return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+       GIOChannel *channel;
+       guint source;
+       sigset_t mask;
+       int fd;
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+               perror("Failed to set signal mask");
+               return 0;
+       }
+
+       fd = signalfd(-1, &mask, 0);
+       if (fd < 0) {
+               perror("Failed to create signal descriptor");
+               return 0;
+       }
+
+       channel = g_io_channel_unix_new(fd);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       source = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               signal_handler, NULL);
+
+       g_io_channel_unref(channel);
+
+       return source;
+}
+
+int main(int argc, char *argv[])
+{
+       GDBusClient *client;
+       guint signal;
+
+       signal = setup_signalfd();
+       if (signal == 0)
+               return -errno;
+
+       connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+
+       main_loop = g_main_loop_new(NULL, FALSE);
+
+       g_dbus_attach_object_manager(connection);
+
+       printf("gatt-service unique name: %s\n",
+                               dbus_bus_get_unique_name(connection));
+
+       create_services();
+
+       client = g_dbus_client_new(connection, "org.bluez", "/org/bluez");
+
+       g_dbus_client_set_connect_watch(client, connect_handler, NULL);
+
+       g_main_loop_run(main_loop);
+
+       g_dbus_client_unref(client);
+
+       g_source_remove(signal);
+
+       g_slist_free_full(services, g_free);
+       dbus_connection_unref(connection);
+
+       return 0;
+}
diff --git a/tools/hci-tester.c b/tools/hci-tester.c
new file mode 100644 (file)
index 0000000..a5dbde2
--- /dev/null
@@ -0,0 +1,673 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2013  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 <string.h>
+
+#include "monitor/bt.h"
+#include "src/shared/hci.h"
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+
+struct user_data {
+       const void *test_data;
+       uint16_t index_ut;
+       uint16_t index_lt;
+       struct bt_hci *hci_ut;          /* Upper Tester / IUT */
+       struct bt_hci *hci_lt;          /* Lower Tester / Reference */
+
+       uint8_t bdaddr_ut[6];
+       uint8_t bdaddr_lt[6];
+       uint16_t handle_ut;
+};
+
+static void test_pre_setup_lt_address(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       struct user_data *user = tester_get_data();
+       const struct bt_hci_rsp_read_bd_addr *rsp = data;
+
+       if (rsp->status) {
+               tester_warn("Read lower tester address failed (0x%02x)",
+                                                               rsp->status);
+               tester_pre_setup_failed();
+               return;
+       }
+
+       memcpy(user->bdaddr_lt, rsp->bdaddr, 6);
+
+       tester_pre_setup_complete();
+}
+
+static void test_pre_setup_lt_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       struct user_data *user = tester_get_data();
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               tester_warn("Reset lower tester failed (0x%02x)", status);
+               tester_pre_setup_failed();
+               return;
+       }
+
+       if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_READ_BD_ADDR, NULL, 0,
+                               test_pre_setup_lt_address, NULL, NULL)) {
+               tester_warn("Failed to read lower tester address");
+               tester_pre_setup_failed();
+               return;
+       }
+}
+
+static void test_pre_setup_ut_address(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       struct user_data *user = tester_get_data();
+       const struct bt_hci_rsp_read_bd_addr *rsp = data;
+
+       if (rsp->status) {
+               tester_warn("Read upper tester address failed (0x%02x)",
+                                                               rsp->status);
+               tester_pre_setup_failed();
+               return;
+       }
+
+       memcpy(user->bdaddr_ut, rsp->bdaddr, 6);
+
+       user->hci_lt = bt_hci_new_user_channel(user->index_lt);
+       if (!user->hci_lt) {
+               tester_warn("Failed to setup lower tester user channel");
+               tester_pre_setup_failed();
+               return;
+       }
+
+       if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_RESET, NULL, 0,
+                               test_pre_setup_lt_complete, NULL, NULL)) {
+               tester_warn("Failed to reset lower tester");
+               tester_pre_setup_failed();
+               return;
+       }
+}
+
+static void test_pre_setup_ut_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       struct user_data *user = tester_get_data();
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               tester_warn("Reset upper tester failed (0x%02x)", status);
+               tester_pre_setup_failed();
+               return;
+       }
+
+       if (user->index_lt == 0xffff) {
+               tester_pre_setup_complete();
+               return;
+       }
+
+       if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_READ_BD_ADDR, NULL, 0,
+                               test_pre_setup_ut_address, NULL, NULL)) {
+               tester_warn("Failed to read upper tester address");
+               tester_pre_setup_failed();
+               return;
+       }
+}
+
+static void test_pre_setup(const void *test_data)
+{
+       struct user_data *user = tester_get_data();
+
+       user->hci_ut = bt_hci_new_user_channel(user->index_ut);
+       if (!user->hci_ut) {
+               tester_warn("Failed to setup upper tester user channel");
+               tester_pre_setup_failed();
+               return;
+       }
+
+       if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_RESET, NULL, 0,
+                               test_pre_setup_ut_complete, NULL, NULL)) {
+               tester_warn("Failed to reset upper tester");
+               tester_pre_setup_failed();
+               return;
+       }
+}
+
+static void test_post_teardown(const void *test_data)
+{
+       struct user_data *user = tester_get_data();
+
+       bt_hci_unref(user->hci_lt);
+       user->hci_lt = NULL;
+
+       bt_hci_unref(user->hci_ut);
+       user->hci_ut = NULL;
+
+       tester_post_teardown_complete();
+}
+
+static void user_data_free(void *data)
+{
+       struct user_data *user = data;
+
+       free(user);
+}
+
+#define test_hci(name, data, setup, func, teardown) \
+       do { \
+               struct user_data *user; \
+               user = calloc(1, sizeof(struct user_data)); \
+               if (!user) \
+                       break; \
+               user->test_data = data; \
+               user->index_ut = 0; \
+               user->index_lt = 1; \
+               tester_add_full(name, data, \
+                               test_pre_setup, setup, func, teardown, \
+                               test_post_teardown, 30, user, user_data_free); \
+       } while (0)
+
+#define test_hci_local(name, data, setup, func) \
+       do { \
+               struct user_data *user; \
+               user = calloc(1, sizeof(struct user_data)); \
+               if (!user) \
+                       break; \
+               user->test_data = data; \
+               user->index_ut = 0; \
+               user->index_lt = 0xffff; \
+               tester_add_full(name, data, \
+                               test_pre_setup, setup, func, NULL, \
+                               test_post_teardown, 30, user, user_data_free); \
+       } while (0)
+
+static void setup_features_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_read_local_features *rsp = data;
+
+       if (rsp->status) {
+               tester_warn("Failed to get HCI features (0x%02x)", rsp->status);
+               tester_setup_failed();
+               return;
+       }
+
+       tester_setup_complete();
+}
+
+static void setup_features(const void *test_data)
+{
+       struct user_data *user = tester_get_data();
+
+       if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+                                       setup_features_complete, NULL, NULL)) {
+               tester_warn("Failed to send HCI features command");
+               tester_setup_failed();
+               return;
+       }
+}
+
+static void test_reset(const void *test_data)
+{
+       tester_test_passed();
+}
+
+static void test_command_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               tester_warn("HCI command failed (0x%02x)", status);
+               tester_test_failed();
+               return;
+       }
+
+       tester_test_passed();
+}
+
+static void test_command(uint16_t opcode)
+{
+       struct user_data *user = tester_get_data();
+
+       if (!bt_hci_send(user->hci_ut, opcode, NULL, 0,
+                                       test_command_complete, NULL, NULL)) {
+               tester_warn("Failed to send HCI command 0x%04x", opcode);
+               tester_test_failed();
+               return;
+       }
+}
+
+static void test_read_local_version_information(const void *test_data)
+{
+       test_command(BT_HCI_CMD_READ_LOCAL_VERSION);
+}
+
+static void test_read_local_supported_commands(const void *test_data)
+{
+       test_command(BT_HCI_CMD_READ_LOCAL_COMMANDS);
+}
+
+static void test_read_local_supported_features(const void *test_data)
+{
+       test_command(BT_HCI_CMD_READ_LOCAL_FEATURES);
+}
+
+static void test_local_extended_features_complete(const void *data,
+                                               uint8_t size, void *user_data)
+{
+       const struct bt_hci_rsp_read_local_ext_features *rsp = data;
+
+       if (rsp->status) {
+               tester_warn("Failed to get HCI extended features (0x%02x)",
+                                                               rsp->status);
+               tester_test_failed();
+               return;
+       }
+
+       tester_test_passed();
+}
+
+static void test_read_local_extended_features(const void *test_data)
+{
+       struct user_data *user = tester_get_data();
+       struct bt_hci_cmd_read_local_ext_features cmd;
+
+       cmd.page = 0x00;
+
+       if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_READ_LOCAL_EXT_FEATURES,
+                                       &cmd, sizeof(cmd),
+                                       test_local_extended_features_complete,
+                                                               NULL, NULL)) {
+               tester_warn("Failed to send HCI extended features command");
+               tester_test_failed();
+               return;
+       }
+}
+
+static void test_read_buffer_size(const void *test_data)
+{
+       test_command(BT_HCI_CMD_READ_BUFFER_SIZE);
+}
+
+static void test_read_country_code(const void *test_data)
+{
+       test_command(BT_HCI_CMD_READ_COUNTRY_CODE);
+}
+
+static void test_read_bd_addr(const void *test_data)
+{
+       test_command(BT_HCI_CMD_READ_BD_ADDR);
+}
+
+static void test_read_local_supported_codecs(const void *test_data)
+{
+       test_command(BT_HCI_CMD_READ_LOCAL_CODECS);
+}
+
+static void test_le_read_white_list_size(const void *test_data)
+{
+       test_command(BT_HCI_CMD_LE_READ_WHITE_LIST_SIZE);
+}
+
+static void test_le_clear_white_list(const void *test_data)
+{
+       test_command(BT_HCI_CMD_LE_CLEAR_WHITE_LIST);
+}
+
+static void test_inquiry_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_evt_inquiry_complete *evt = data;
+
+       if (evt->status) {
+               tester_warn("HCI inquiry complete failed (0x%02x)",
+                                                       evt->status);
+               tester_test_failed();
+               return;
+       }
+
+       tester_test_passed();
+}
+
+static void test_inquiry_status(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               tester_warn("HCI inquiry command failed (0x%02x)", status);
+               tester_test_failed();
+               return;
+       }
+}
+
+static void test_inquiry_liac(const void *test_data)
+{
+       struct user_data *user = tester_get_data();
+       struct bt_hci_cmd_inquiry cmd;
+
+       bt_hci_register(user->hci_ut, BT_HCI_EVT_INQUIRY_COMPLETE,
+                                       test_inquiry_complete, NULL, NULL);
+
+       cmd.lap[0] = 0x00;
+       cmd.lap[1] = 0x8b;
+       cmd.lap[2] = 0x9e;
+       cmd.length = 0x08;
+       cmd.num_resp = 0x00;
+
+       if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_INQUIRY, &cmd, sizeof(cmd),
+                                       test_inquiry_status, NULL, NULL)) {
+               tester_warn("Failed to send HCI inquiry command");
+               tester_test_failed();
+               return;
+       }
+}
+
+static void setup_lt_connectable_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               tester_warn("Failed to set HCI scan enable (0x%02x)", status);
+               tester_setup_failed();
+               return;
+       }
+
+       tester_setup_complete();
+}
+
+static void setup_lt_connect_request_accept(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       struct user_data *user = tester_get_data();
+       const struct bt_hci_evt_conn_request *evt = data;
+       struct bt_hci_cmd_accept_conn_request cmd;
+
+       memcpy(cmd.bdaddr, evt->bdaddr, 6);
+       cmd.role = 0x01;
+
+       if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_ACCEPT_CONN_REQUEST,
+                                       &cmd, sizeof(cmd), NULL, NULL, NULL)) {
+               tester_warn("Failed to send HCI accept connection command");
+               return;
+       }
+}
+
+static void setup_lt_connectable(const void *test_data)
+{
+       struct user_data *user = tester_get_data();
+       struct bt_hci_cmd_write_scan_enable cmd;
+
+       bt_hci_register(user->hci_lt, BT_HCI_EVT_CONN_REQUEST,
+                               setup_lt_connect_request_accept, NULL, NULL);
+
+       cmd.enable = 0x02;
+
+       if (!bt_hci_send(user->hci_lt, BT_HCI_CMD_WRITE_SCAN_ENABLE,
+                               &cmd, sizeof(cmd),
+                               setup_lt_connectable_complete, NULL, NULL)) {
+               tester_warn("Failed to send HCI scan enable command");
+               tester_setup_failed();
+               return;
+       }
+}
+
+static void test_create_connection_disconnect(void *user_data)
+{
+       tester_test_passed();
+}
+
+static void test_create_connection_complete(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       struct user_data *user = tester_get_data();
+       const struct bt_hci_evt_conn_complete *evt = data;
+
+       if (evt->status) {
+               tester_warn("HCI create connection complete failed (0x%02x)",
+                                                               evt->status);
+               tester_test_failed();
+               return;
+       }
+
+       user->handle_ut = le16_to_cpu(evt->handle);
+
+       tester_wait(2, test_create_connection_disconnect, NULL);
+}
+
+static void test_create_connection_status(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               tester_warn("HCI create connection command failed (0x%02x)",
+                                                               status);
+               tester_test_failed();
+               return;
+       }
+}
+
+static void test_create_connection(const void *test_data)
+{
+       struct user_data *user = tester_get_data();
+       struct bt_hci_cmd_create_conn cmd;
+
+       bt_hci_register(user->hci_ut, BT_HCI_EVT_CONN_COMPLETE,
+                               test_create_connection_complete, NULL, NULL);
+
+       memcpy(cmd.bdaddr, user->bdaddr_lt, 6);
+       cmd.pkt_type = cpu_to_le16(0x0008);
+       cmd.pscan_rep_mode = 0x02;
+       cmd.pscan_mode = 0x00;
+       cmd.clock_offset = cpu_to_le16(0x0000);
+       cmd.role_switch = 0x01;
+
+       if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_CREATE_CONN,
+                                               &cmd, sizeof(cmd),
+                                               test_create_connection_status,
+                                                               NULL, NULL)) {
+               tester_warn("Failed to send HCI create connection command");
+               tester_test_failed();
+               return;
+       }
+}
+
+static void teardown_timeout(void *user_data)
+{
+       tester_teardown_complete();
+}
+
+static void teardown_disconnect_status(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               tester_warn("HCI disconnect failed (0x%02x)", status);
+               tester_teardown_failed();
+               return;
+       }
+
+       tester_wait(1, teardown_timeout, NULL);
+}
+
+static void teardown_connection(const void *test_data)
+{
+       struct user_data *user = tester_get_data();
+       struct bt_hci_cmd_disconnect cmd;
+
+       cmd.handle = cpu_to_le16(user->handle_ut);
+       cmd.reason = 0x13;
+
+       if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_DISCONNECT,
+                                               &cmd, sizeof(cmd),
+                                               teardown_disconnect_status,
+                                                               NULL, NULL)) {
+               tester_warn("Failed to send HCI disconnect command");
+               tester_test_failed();
+               return;
+       }
+}
+
+static void test_adv_report(const void *data, uint8_t size, void *user_data)
+{
+       struct user_data *user = tester_get_data();
+       uint8_t subevent = *((uint8_t *) data);
+       const struct bt_hci_evt_le_adv_report *lar = data + 1;
+
+       switch (subevent) {
+       case BT_HCI_EVT_LE_ADV_REPORT:
+               if (!memcmp(lar->addr, user->bdaddr_ut, 6))
+                       tester_setup_complete();
+               break;
+       }
+}
+
+static void setup_advertising_initiated(const void *test_data)
+{
+       struct user_data *user = tester_get_data();
+       struct bt_hci_cmd_set_event_mask sem;
+       struct bt_hci_cmd_le_set_event_mask lsem;
+       struct bt_hci_cmd_le_set_scan_enable lsse;
+       struct bt_hci_cmd_le_set_adv_parameters lsap;
+       struct bt_hci_cmd_le_set_adv_enable lsae;
+
+       bt_hci_register(user->hci_lt, BT_HCI_EVT_LE_META_EVENT,
+                                       test_adv_report, NULL, NULL);
+
+       memset(sem.mask, 0, 8);
+       sem.mask[1] |= 0x20;    /* Command Complete */
+       sem.mask[1] |= 0x40;    /* Command Status */
+       sem.mask[7] |= 0x20;    /* LE Meta */
+
+       bt_hci_send(user->hci_lt, BT_HCI_CMD_SET_EVENT_MASK,
+                                       &sem, sizeof(sem), NULL, NULL, NULL);
+
+       memset(lsem.mask, 0, 8);
+       lsem.mask[0] |= 0x02;   /* LE Advertising Report */
+
+       bt_hci_send(user->hci_lt, BT_HCI_CMD_LE_SET_EVENT_MASK,
+                                       &lsem, sizeof(lsem), NULL, NULL, NULL);
+
+       lsse.enable = 0x01;
+       lsse.filter_dup = 0x00;
+
+       bt_hci_send(user->hci_lt, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+                                       &lsse, sizeof(lsse), NULL, NULL, NULL);
+
+       lsap.min_interval = cpu_to_le16(0x0800);
+       lsap.max_interval = cpu_to_le16(0x0800);
+       lsap.type = 0x03;
+       lsap.own_addr_type = 0x00;
+       lsap.direct_addr_type = 0x00;
+       memset(lsap.direct_addr, 0, 6);
+       lsap.channel_map = 0x07;
+       lsap.filter_policy = 0x00;
+
+       bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+                                       &lsap, sizeof(lsap), NULL, NULL, NULL);
+
+       lsae.enable = 0x01;
+
+       bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+                                       &lsae, sizeof(lsae), NULL, NULL, NULL);
+}
+
+static void test_reset_in_advertising_state_timeout(void *user_data)
+{
+       struct user_data *user = tester_get_data();
+       struct bt_hci_cmd_le_set_adv_enable lsae;
+       struct bt_hci_cmd_le_set_scan_enable lsse;
+
+       lsae.enable = 0x00;
+
+       bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+                                       &lsae, sizeof(lsae), NULL, NULL, NULL);
+
+       lsse.enable = 0x00;
+       lsse.filter_dup = 0x00;
+
+       bt_hci_send(user->hci_lt, BT_HCI_CMD_LE_SET_SCAN_ENABLE,
+                                       &lsse, sizeof(lsse), NULL, NULL, NULL);
+
+       tester_test_passed();
+}
+
+static void test_reset_in_advertising_state(const void *test_data)
+{
+       struct user_data *user = tester_get_data();
+
+       bt_hci_send(user->hci_ut, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
+
+       tester_wait(5, test_reset_in_advertising_state_timeout, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+       tester_init(&argc, &argv);
+
+       test_hci_local("Reset", NULL, NULL, test_reset);
+
+       test_hci_local("Read Local Version Information", NULL, NULL,
+                               test_read_local_version_information);
+       test_hci_local("Read Local Supported Commands", NULL, NULL,
+                               test_read_local_supported_commands);
+       test_hci_local("Read Local Supported Features", NULL, NULL,
+                               test_read_local_supported_features);
+       test_hci_local("Read Local Extended Features", NULL,
+                               setup_features,
+                               test_read_local_extended_features);
+       test_hci_local("Read Buffer Size", NULL, NULL,
+                               test_read_buffer_size);
+       test_hci_local("Read Country Code", NULL, NULL,
+                               test_read_country_code);
+       test_hci_local("Read BD_ADDR", NULL, NULL,
+                               test_read_bd_addr);
+       test_hci_local("Read Local Supported Codecs", NULL, NULL,
+                               test_read_local_supported_codecs);
+
+       test_hci_local("LE Read White List Size", NULL, NULL,
+                               test_le_read_white_list_size);
+       test_hci_local("LE Clear White List", NULL, NULL,
+                               test_le_clear_white_list);
+
+       test_hci_local("Inquiry (LIAC)", NULL, NULL, test_inquiry_liac);
+
+       test_hci("Create Connection", NULL,
+                               setup_lt_connectable,
+                               test_create_connection,
+                               teardown_connection);
+
+       test_hci("TP/DSU/BV-02-C Reset in Advertising State", NULL,
+                               setup_advertising_initiated,
+                               test_reset_in_advertising_state, NULL);
+
+       return tester_run();
+}
index db01b85..1904ac5 100644 (file)
@@ -84,7 +84,7 @@ static void sig_alarm(int sig)
        exit(1);
 }
 
-static int uart_speed(int s)
+int uart_speed(int s)
 {
        switch (s) {
        case 9600:
@@ -327,6 +327,11 @@ static int intel(int fd, struct uart_t *u, struct termios *ti)
        return intel_init(fd, u->init_speed, &u->speed, ti);
 }
 
+static int bcm43xx(int fd, struct uart_t *u, struct termios *ti)
+{
+       return bcm43xx_init(fd, u->speed, ti, u->bdaddr);
+}
+
 static int read_check(int fd, void *buf, int count)
 {
        int res;
@@ -1135,6 +1140,10 @@ struct uart_t uart[] = {
        { "bcm2035",    0x0A5C, 0x2035, HCI_UART_H4,   115200, 460800,
                                FLOW_CTL, DISABLE_PM, NULL, bcm2035  },
 
+       /* Broadcom BCM43XX */
+       { "bcm43xx",    0x0000, 0x0000, HCI_UART_H4,   115200, 3000000,
+                               FLOW_CTL, DISABLE_PM, NULL, bcm43xx, NULL  },
+
        { "ath3k",    0x0000, 0x0000, HCI_UART_ATH3K, 115200, 115200,
                        FLOW_CTL, DISABLE_PM, NULL, ath3k_ps, ath3k_pm  },
 
index 1b23ad7..4810a09 100644 (file)
@@ -46,6 +46,7 @@
 
 int read_hci_event(int fd, unsigned char *buf, int size);
 int set_speed(int fd, struct termios *ti, int speed);
+int uart_speed(int speed);
 
 int texas_init(int fd, int *speed, struct termios *ti);
 int texas_post(int fd, struct termios *ti);
@@ -57,3 +58,4 @@ int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
 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);
+int bcm43xx_init(int fd, int speed, struct termios *ti, const char *bdaddr);
diff --git a/tools/hciattach_bcm43xx.c b/tools/hciattach_bcm43xx.c
new file mode 100644 (file)
index 0000000..ad9b239
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <time.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#ifndef FIRMWARE_DIR
+#define FIRMWARE_DIR "/etc/firmware"
+#endif
+
+#define FW_EXT ".hcd"
+
+#define BCM43XX_CLOCK_48 1
+#define BCM43XX_CLOCK_24 2
+
+#define CMD_SUCCESS 0x00
+
+#define CC_MIN_SIZE 7
+
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+
+static int bcm43xx_read_local_name(int fd, char *name, size_t size)
+{
+       unsigned char cmd[] = { HCI_COMMAND_PKT, 0x14, 0x0C, 0x00 };
+       unsigned char *resp;
+       unsigned int name_len;
+
+       resp = malloc(size + CC_MIN_SIZE);
+       if (!resp)
+               return -1;
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+               fprintf(stderr, "Failed to write read local name command\n");
+               goto fail;
+       }
+
+       if (read_hci_event(fd, resp, size) < CC_MIN_SIZE) {
+               fprintf(stderr, "Failed to read local name, invalid HCI event\n");
+               goto fail;
+       }
+
+       if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+               fprintf(stderr, "Failed to read local name, command failure\n");
+               goto fail;
+       }
+
+       name_len = resp[2] - 1;
+
+       strncpy(name, (char *) &resp[7], MIN(name_len, size));
+       name[size - 1] = 0;
+
+       free(resp);
+       return 0;
+
+fail:
+       free(resp);
+       return -1;
+}
+
+static int bcm43xx_reset(int fd)
+{
+       unsigned char cmd[] = { HCI_COMMAND_PKT, 0x03, 0x0C, 0x00 };
+       unsigned char resp[CC_MIN_SIZE];
+
+       if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+               fprintf(stderr, "Failed to write reset command\n");
+               return -1;
+       }
+
+       if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) {
+               fprintf(stderr, "Failed to reset chip, invalid HCI event\n");
+               return -1;
+       }
+
+       if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+               fprintf(stderr, "Failed to reset chip, command failure\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int bcm43xx_set_bdaddr(int fd, const char *bdaddr)
+{
+       unsigned char cmd[] =
+               { HCI_COMMAND_PKT, 0x01, 0xfc, 0x06, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00 };
+       unsigned char resp[CC_MIN_SIZE];
+
+       printf("Set BDADDR UART: %s\n", bdaddr);
+
+       if (str2ba(bdaddr, (bdaddr_t *) (&cmd[4])) < 0) {
+               fprintf(stderr, "Incorrect bdaddr\n");
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+               fprintf(stderr, "Failed to write set bdaddr command\n");
+               return -1;
+       }
+
+       if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) {
+               fprintf(stderr, "Failed to set bdaddr, invalid HCI event\n");
+               return -1;
+       }
+
+       if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+               fprintf(stderr, "Failed to set bdaddr, command failure\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int bcm43xx_set_speed(int fd, uint32_t speed)
+{
+       unsigned char cmd[] =
+               { HCI_COMMAND_PKT, 0x18, 0xfc, 0x06, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00 };
+       unsigned char resp[CC_MIN_SIZE];
+
+       printf("Set Controller UART speed to %d bit/s\n", speed);
+
+       cmd[6] = (uint8_t) (speed);
+       cmd[7] = (uint8_t) (speed >> 8);
+       cmd[8] = (uint8_t) (speed >> 16);
+       cmd[9] = (uint8_t) (speed >> 24);
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+               fprintf(stderr, "Failed to write update baudrate command\n");
+               return -1;
+       }
+
+       if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) {
+               fprintf(stderr, "Failed to update baudrate, invalid HCI event\n");
+               return -1;
+       }
+
+       if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+               fprintf(stderr, "Failed to update baudrate, command failure\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int bcm43xx_set_clock(int fd, unsigned char clock)
+{
+       unsigned char cmd[] = { HCI_COMMAND_PKT, 0x45, 0xfc, 0x01, 0x00 };
+       unsigned char resp[CC_MIN_SIZE];
+
+       printf("Set Controller clock (%d)\n", clock);
+
+       cmd[4] = clock;
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+               fprintf(stderr, "Failed to write update clock command\n");
+               return -1;
+       }
+
+       if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) {
+               fprintf(stderr, "Failed to update clock, invalid HCI event\n");
+               return -1;
+       }
+
+       if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+               fprintf(stderr, "Failed to update clock, command failure\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int bcm43xx_load_firmware(int fd, const char *fw)
+{
+       unsigned char cmd[] = { HCI_COMMAND_PKT, 0x2e, 0xfc, 0x00 };
+       struct timespec tm_mode = { 0, 50000 };
+       struct timespec tm_ready = { 0, 2000000 };
+       unsigned char resp[CC_MIN_SIZE];
+       unsigned char tx_buf[1024];
+       int len, fd_fw, n;
+
+       printf("Flash firmware %s\n", fw);
+
+       fd_fw = open(fw, O_RDONLY);
+       if (fd_fw < 0) {
+               fprintf(stderr, "Unable to open firmware (%s)\n", fw);
+               return -1;
+       }
+
+       tcflush(fd, TCIOFLUSH);
+
+       if (write(fd, cmd, sizeof(cmd)) != sizeof(cmd)) {
+               fprintf(stderr, "Failed to write download mode command\n");
+               goto fail;
+       }
+
+       if (read_hci_event(fd, resp, sizeof(resp)) < CC_MIN_SIZE) {
+               fprintf(stderr, "Failed to load firmware, invalid HCI event\n");
+               goto fail;
+       }
+
+       if (resp[4] != cmd[1] || resp[5] != cmd[2] || resp[6] != CMD_SUCCESS) {
+               fprintf(stderr, "Failed to load firmware, command failure\n");
+               goto fail;
+       }
+
+       /* Wait 50ms to let the firmware placed in download mode */
+       nanosleep(&tm_mode, NULL);
+
+       tcflush(fd, TCIOFLUSH);
+
+       while ((n = read(fd_fw, &tx_buf[1], 3))) {
+               if (n < 0) {
+                       fprintf(stderr, "Failed to read firmware\n");
+                       goto fail;
+               }
+
+               tx_buf[0] = HCI_COMMAND_PKT;
+
+               len = tx_buf[3];
+
+               if (read(fd_fw, &tx_buf[4], len) < 0) {
+                       fprintf(stderr, "Failed to read firmware\n");
+                       goto fail;
+               }
+
+               if (write(fd, tx_buf, len + 4) != (len + 4)) {
+                       fprintf(stderr, "Failed to write firmware\n");
+                       goto fail;
+               }
+
+               read_hci_event(fd, resp, sizeof(resp));
+               tcflush(fd, TCIOFLUSH);
+       }
+
+       /* Wait for firmware ready */
+       nanosleep(&tm_ready, NULL);
+
+       close(fd_fw);
+       return 0;
+
+fail:
+       close(fd_fw);
+       return -1;
+}
+
+static int bcm43xx_locate_patch(const char *dir_name,
+               const char *chip_name, char *location)
+{
+       DIR *dir;
+       int ret = -1;
+
+       dir = opendir(dir_name);
+       if (!dir) {
+               fprintf(stderr, "Cannot open directory '%s': %s\n",
+                               dir_name, strerror(errno));
+               return -1;
+       }
+
+       /* Recursively look for a BCM43XX*.hcd */
+       while (1) {
+               struct dirent *entry = readdir(dir);
+               if (!entry)
+                       break;
+
+               if (entry->d_type & DT_DIR) {
+                       char path[PATH_MAX];
+
+                       if (!strcmp(entry->d_name, "..") || !strcmp(entry->d_name, "."))
+                               continue;
+
+                       snprintf(path, PATH_MAX, "%s/%s", dir_name, entry->d_name);
+
+                       ret = bcm43xx_locate_patch(path, chip_name, location);
+                       if (!ret)
+                               break;
+               } else if (!strncmp(chip_name, entry->d_name, strlen(chip_name))) {
+                       unsigned int name_len = strlen(entry->d_name);
+                       size_t curs_ext = name_len - sizeof(FW_EXT) + 1;
+
+                       if (curs_ext > name_len)
+                               break;
+
+                       if (strncmp(FW_EXT, &entry->d_name[curs_ext], sizeof(FW_EXT)))
+                               break;
+
+                       /* found */
+                       snprintf(location, PATH_MAX, "%s/%s", dir_name, entry->d_name);
+                       ret = 0;
+                       break;
+               }
+       }
+
+       closedir(dir);
+
+       return ret;
+}
+
+int bcm43xx_init(int fd, int speed, struct termios *ti, const char *bdaddr)
+{
+       char chip_name[20];
+       char fw_path[PATH_MAX];
+
+       printf("bcm43xx_init\n");
+
+       if (bcm43xx_reset(fd))
+               return -1;
+
+       if (bcm43xx_read_local_name(fd, chip_name, sizeof(chip_name)))
+               return -1;
+
+       if (bcm43xx_locate_patch(FIRMWARE_DIR, chip_name, fw_path)) {
+               fprintf(stderr, "Patch not found, continue anyway\n");
+       } else {
+               if (bcm43xx_load_firmware(fd, fw_path))
+                       return -1;
+
+               if (bcm43xx_reset(fd))
+                       return -1;
+       }
+
+       if (bdaddr)
+               bcm43xx_set_bdaddr(fd, bdaddr);
+
+       if (speed > 3000000 && bcm43xx_set_clock(fd, BCM43XX_CLOCK_48))
+               return -1;
+
+       if (bcm43xx_set_speed(fd, speed))
+               return -1;
+
+       return 0;
+}
index 0e02e1e..eb72a0f 100644 (file)
@@ -40,6 +40,7 @@
 #include <sys/poll.h>
 #include <sys/param.h>
 #include <sys/ioctl.h>
+#include <sys/uio.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/hci.h>
index c3caa49..5c7f3a5 100644 (file)
@@ -39,6 +39,7 @@
 #include <sys/poll.h>
 #include <sys/param.h>
 #include <sys/ioctl.h>
+#include <sys/uio.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/hci.h>
index fe45167..765d980 100644 (file)
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
 
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 
-#include "textfile.h"
-#include "csr.h"
+#include "src/textfile.h"
+#include "src/shared/util.h"
+#include "tools/csr.h"
 
 static struct hci_dev_info di;
 static int all;
@@ -824,25 +826,32 @@ static char *get_minor_device_name(int major, int minor)
                case 0:
                        break;
                case 1:
-                       strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Joystick",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                case 2:
-                       strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Gamepad",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                case 3:
-                       strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Remote control",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                case 4:
-                       strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Sensing device",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                case 5:
-                       strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Digitizer tablet",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                case 6:
-                       strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Card reader",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                default:
-                       strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "(reserved)",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                }
                if (strlen(cls_str) > 0)
@@ -1308,7 +1317,7 @@ static void cmd_inq_data(int ctl, int hdev, char *opt)
                                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)));
+                                       uint16_t val = get_le16((ptr + (i * 2)));
                                        printf(" 0x%4.4x", val);
                                }
                                printf("\n");
index 055c8fa..37a9f00 100644 (file)
@@ -59,7 +59,6 @@ enum {
 /* Default options */
 static int  snap_len = SNAP_LEN;
 static int  mode = PARSE;
-static int  permcheck = 1;
 static char *dump_file = NULL;
 static char *pppdump_file = NULL;
 static char *audio_file = NULL;
@@ -252,15 +251,15 @@ static int process_frames(int dev, int sock, int fd, unsigned long flags)
                        if (flags & DUMP_BTSNOOP) {
                                uint64_t ts;
                                uint8_t pkt_type = ((uint8_t *) frm.data)[0];
-                               dp->size = htonl(frm.data_len);
+                               dp->size = htobe32(frm.data_len);
                                dp->len  = dp->size;
-                               dp->flags = ntohl(frm.in & 0x01);
+                               dp->flags = be32toh(frm.in & 0x01);
                                dp->drops = 0;
                                ts = (frm.ts.tv_sec - 946684800ll) * 1000000ll + frm.ts.tv_usec;
-                               dp->ts = hton64(ts + 0x00E03AB44A676000ll);
+                               dp->ts = htobe64(ts + 0x00E03AB44A676000ll);
                                if (pkt_type == HCI_COMMAND_PKT ||
                                                pkt_type == HCI_EVENT_PKT)
-                                       dp->flags |= ntohl(0x02);
+                                       dp->flags |= be32toh(0x02);
                        } else {
                                dh->len = htobs(frm.data_len);
                                dh->in  = frm.in;
@@ -309,7 +308,7 @@ static void read_dump(int fd)
                if (err < 0)
                        goto failed;
                if (!err)
-                       return;
+                       goto done;
 
                if (parser.flags & DUMP_PKTLOG) {
                        switch (ph.type) {
@@ -330,11 +329,11 @@ static void read_dump(int fd)
                                frm.in = 1;
                                break;
                        default:
-                               lseek(fd, ntohl(ph.len) - 9, SEEK_CUR);
+                               lseek(fd, be32toh(ph.len) - 9, SEEK_CUR);
                                continue;
                        }
 
-                       frm.data_len = ntohl(ph.len) - 8;
+                       frm.data_len = be32toh(ph.len) - 8;
                        err = read_n(fd, frm.data + 1, frm.data_len - 1);
                } else if (parser.flags & DUMP_BTSNOOP) {
                        uint32_t opcode;
@@ -342,8 +341,8 @@ static void read_dump(int fd)
 
                        switch (btsnoop_type) {
                        case 1001:
-                               if (ntohl(dp.flags) & 0x02) {
-                                       if (ntohl(dp.flags) & 0x01)
+                               if (be32toh(dp.flags) & 0x02) {
+                                       if (be32toh(dp.flags) & 0x01)
                                                pkt_type = HCI_EVENT_PKT;
                                        else
                                                pkt_type = HCI_COMMAND_PKT;
@@ -352,17 +351,17 @@ static void read_dump(int fd)
 
                                ((uint8_t *) frm.data)[0] = pkt_type;
 
-                               frm.data_len = ntohl(dp.len) + 1;
+                               frm.data_len = be32toh(dp.len) + 1;
                                err = read_n(fd, frm.data + 1, frm.data_len - 1);
                                break;
 
                        case 1002:
-                               frm.data_len = ntohl(dp.len);
+                               frm.data_len = be32toh(dp.len);
                                err = read_n(fd, frm.data, frm.data_len);
                                break;
 
                        case 2001:
-                               opcode = ntohl(dp.flags) & 0xffff;
+                               opcode = be32toh(dp.flags) & 0xffff;
 
                                switch (opcode) {
                                case 2:
@@ -396,7 +395,7 @@ static void read_dump(int fd)
 
                                ((uint8_t *) frm.data)[0] = pkt_type;
 
-                               frm.data_len = ntohl(dp.len) + 1;
+                               frm.data_len = be32toh(dp.len) + 1;
                                err = read_n(fd, frm.data + 1, frm.data_len - 1);
                        }
                } else {
@@ -407,20 +406,20 @@ static void read_dump(int fd)
                if (err < 0)
                        goto failed;
                if (!err)
-                       return;
+                       goto done;
 
                frm.ptr = frm.data;
                frm.len = frm.data_len;
 
                if (parser.flags & DUMP_PKTLOG) {
                        uint64_t ts;
-                       ts = ntoh64(ph.ts);
+                       ts = be64toh(ph.ts);
                        frm.ts.tv_sec = ts >> 32;
                        frm.ts.tv_usec = ts & 0xffffffff;
                } else if (parser.flags & DUMP_BTSNOOP) {
                        uint64_t ts;
-                       frm.in = ntohl(dp.flags) & 0x01;
-                       ts = ntoh64(dp.ts) - 0x00E03AB44A676000ll;
+                       frm.in = be32toh(dp.flags) & 0x01;
+                       ts = be64toh(dp.ts) - 0x00E03AB44A676000ll;
                        frm.ts.tv_sec = (ts / 1000000ll) + 946684800ll;
                        frm.ts.tv_usec = ts % 1000000ll;
                } else {
@@ -432,8 +431,13 @@ static void read_dump(int fd)
                parse(&frm);
        }
 
+done:
+       free(frm.data);
+       return;
+
 failed:
        perror("Read failed");
+       free(frm.data);
        exit(1);
 }
 
@@ -464,8 +468,8 @@ static int open_file(char *file, int mode, unsigned long flags)
                if (!memcmp(hdr->id, btsnoop_id, sizeof(btsnoop_id))) {
                        parser.flags |= DUMP_BTSNOOP;
 
-                       btsnoop_version = ntohl(hdr->version);
-                       btsnoop_type = ntohl(hdr->type);
+                       btsnoop_version = be32toh(hdr->version);
+                       btsnoop_type = be32toh(hdr->type);
 
                        printf("btsnoop version: %d datalink type: %d\n",
                                                btsnoop_version, btsnoop_type);
@@ -496,8 +500,8 @@ static int open_file(char *file, int mode, unsigned long flags)
                        btsnoop_type = 1002;
 
                        memcpy(hdr->id, btsnoop_id, sizeof(btsnoop_id));
-                       hdr->version = htonl(btsnoop_version);
-                       hdr->type = htonl(btsnoop_type);
+                       hdr->version = htobe32(btsnoop_version);
+                       hdr->type = htobe32(btsnoop_type);
 
                        printf("btsnoop version: %d datalink type: %d\n",
                                                btsnoop_version, btsnoop_type);
@@ -522,31 +526,7 @@ static int open_socket(int dev, unsigned long flags)
 {
        struct sockaddr_hci addr;
        struct hci_filter flt;
-       struct hci_dev_info di;
-       int sk, dd, opt;
-
-       if (permcheck && dev != HCI_DEV_NONE) {
-               dd = hci_open_dev(dev);
-               if (dd < 0) {
-                       perror("Can't open device");
-                       return -1;
-               }
-
-               if (hci_devinfo(dev, &di) < 0) {
-                       perror("Can't get device info");
-                       return -1;
-               }
-
-               opt = hci_test_bit(HCI_RAW, &di.flags);
-               if (ioctl(dd, HCISETRAW, opt) < 0) {
-                       if (errno == EACCES) {
-                               perror("Can't access device");
-                               return -1;
-                       }
-               }
-
-               hci_close_dev(dd);
-       }
+       int sk, opt;
 
        /* Create HCI socket */
        sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
@@ -683,7 +663,6 @@ static struct option main_options[] = {
        { "pppdump",            1, 0, 'D' },
        { "audio",              1, 0, 'A' },
        { "novendor",           0, 0, 'Y' },
-       { "nopermcheck",        0, 0, 'Z' },
        { "help",               0, 0, 'h' },
        { "version",            0, 0, 'v' },
        { 0 }
@@ -700,7 +679,7 @@ int main(int argc, char *argv[])
        uint16_t obex_port;
 
        while ((opt = getopt_long(argc, argv,
-                               "i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:YZhv",
+                               "i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Yhv",
                                main_options, NULL)) != -1) {
                switch(opt) {
                case 'i':
@@ -788,10 +767,6 @@ int main(int argc, char *argv[])
                        flags |= DUMP_NOVENDOR;
                        break;
 
-               case 'Z':
-                       permcheck = 0;
-                       break;
-
                case 'v':
                        printf("%s\n", VERSION);
                        exit(0);
index f2e4fa4..ffaf953 100644 (file)
 #include <sys/socket.h>
 #include <signal.h>
 
-#include <glib.h>
-
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/hci.h>
 #include <bluetooth/hci_lib.h>
 
-#include "textfile.h"
-#include "oui.h"
+#include "src/oui.h"
 
 /* Unofficial value, might still change */
 #define LE_LINK                0x03
@@ -336,25 +333,32 @@ static char *get_minor_device_name(int major, int minor)
                case 0:
                        break;
                case 1:
-                       strncat(cls_str, "Joystick", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Joystick",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                case 2:
-                       strncat(cls_str, "Gamepad", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Gamepad",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                case 3:
-                       strncat(cls_str, "Remote control", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Remote control",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                case 4:
-                       strncat(cls_str, "Sensing device", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Sensing device",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                case 5:
-                       strncat(cls_str, "Digitizer tablet", sizeof(cls_str) - strlen(cls_str));
-               break;
+                       strncat(cls_str, "Digitizer tablet",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
+                       break;
                case 6:
-                       strncat(cls_str, "Card reader", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "Card reader",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                default:
-                       strncat(cls_str, "(reserved)", sizeof(cls_str) - strlen(cls_str));
+                       strncat(cls_str, "(reserved)",
+                                       sizeof(cls_str) - strlen(cls_str) - 1);
                        break;
                }
                if (strlen(cls_str) > 0)
@@ -409,36 +413,6 @@ static char *major_classes[] = {
        "Audio/Video", "Peripheral", "Imaging", "Uncategorized"
 };
 
-static char *get_device_name(const bdaddr_t *local, const bdaddr_t *peer)
-{
-       char filename[PATH_MAX + 1];
-       char local_addr[18], peer_addr[18];
-       GKeyFile *key_file;
-       char *str = NULL;
-       int len;
-
-       ba2str(local, local_addr);
-       ba2str(peer, peer_addr);
-
-       snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local_addr,
-                       peer_addr);
-       filename[PATH_MAX] = '\0';
-       key_file = g_key_file_new();
-
-       if (g_key_file_load_from_file(key_file, filename, 0, NULL)) {
-               str = g_key_file_get_string(key_file, "General", "Name", NULL);
-               if (str) {
-                       len = strlen(str);
-                       if (len > HCI_MAX_NAME_LENGTH)
-                               str[HCI_MAX_NAME_LENGTH] = '\0';
-               }
-       }
-
-       g_key_file_free(key_file);
-
-       return str;
-}
-
 /* Display local devices */
 
 static struct option dev_options[] = {
@@ -562,7 +536,6 @@ static struct option scan_options[] = {
        { "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' },
@@ -581,12 +554,12 @@ static void cmd_scan(int dev_id, int argc, char **argv)
        uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
        int num_rsp, length, flags;
        uint8_t cls[3], features[8];
-       char addr[18], name[249], *comp, *tmp;
+       char addr[18], name[249], *comp;
        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;
+       int extcls = 0, extinf = 0, extoui = 0;
+       int i, n, l, opt, dd, cc;
 
        length  = 8;    /* ~10 seconds */
        num_rsp = 0;
@@ -621,10 +594,6 @@ static void cmd_scan(int dev_id, int argc, char **argv)
                        flags |= IREQ_CACHE_FLUSH;
                        break;
 
-               case 'r':
-                       refresh = 1;
-                       break;
-
                case 'C':
                        extcls = 1;
                        break;
@@ -683,26 +652,9 @@ static void cmd_scan(int dev_id, int argc, char **argv)
        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,
@@ -763,27 +715,22 @@ static void cmd_scan(int dev_id, int argc, char **argv)
                        }
                }
 
-               if (handle > 0 || !nc) {
-                       if (hci_read_remote_name_with_clock_offset(dd,
+               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;
+               } else {
+                       for (n = 0; n < 248 && name[n]; n++) {
+                               if ((unsigned char) name[i] < 32 || name[i] == 127)
+                                       name[i] = '.';
                        }
+
+                       name[248] = '\0';
                }
 
                if (strlen(name) > 0)
-                       printf("Device name:\t%s%s\n", name, nc ? " [cached]" : "");
+                       printf("Device name:\t%s\n", name);
 
                if (extcls) {
                        memcpy(cls, (info+i)->dev_class, 3);
@@ -954,6 +901,7 @@ static void cmd_info(int dev_id, int argc, char **argv)
                                        htobs(di.pkt_type & ACL_PTYPE_MASK),
                                        0, 0x01, &handle, 25000) < 0) {
                        perror("Can't create connection");
+                       free(cr);
                        close(dd);
                        exit(1);
                }
@@ -962,6 +910,8 @@ static void cmd_info(int dev_id, int argc, char **argv)
        } else
                handle = htobs(cr->conn_info->handle);
 
+       free(cr);
+
        printf("\tBD Address:  %s\n", argv[0]);
 
        comp = batocomp(&bdaddr);
diff --git a/tools/hex2hcd.c b/tools/hex2hcd.c
new file mode 100644 (file)
index 0000000..d9b5d3b
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012 Canonical
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public 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>
+
+#define RBUF_SIZE      640
+
+static unsigned int asc_to_int(char a)
+{
+       if (a >= 'A')
+               return (a - 'A') + 10;
+       else
+               return a - '0';
+}
+
+static unsigned int hex_to_int(const char *h)
+{
+       return asc_to_int(*h) * 0x10 + asc_to_int(*(h + 1));
+}
+
+static unsigned int lhex_to_int(const char *h)
+{
+       return hex_to_int(h) * 0x100 + hex_to_int(h + 2);
+}
+
+static int check_sum(const char *str, int len)
+{
+       unsigned int sum, cal;
+       int i;
+       sum = hex_to_int(str + len - 2);
+       for (cal = 0, i = 1; i < len - 2; i += 2)
+               cal += hex_to_int(str + i);
+       cal = 0x100 - (cal & 0xFF);
+       return sum == cal;
+}
+
+static int check_hex_line(const char *str, unsigned int len)
+{
+       if ((str[0] != ':') || (len < 11) || !check_sum(str, len) ||
+                       (hex_to_int(str + 1) * 2 + 11 != len))
+               return 0;
+       return 1;
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned int i, addr = 0;
+       FILE *ifp, *ofp;
+       char *rbuf;
+       ssize_t len;
+       size_t buflen;
+
+       if (argc != 3) {
+               printf("Usage: %s <input hex file> <output file>\n", argv[0]);
+               return 0;
+       }
+
+       ifp = fopen(argv[1], "r");
+       ofp = fopen(argv[2], "w");
+       if ((ifp == NULL) || (ofp == NULL)) {
+               puts("failed to open file.");
+               return -EIO;
+       }
+
+       rbuf = NULL;
+       while ((len = getline(&rbuf, &buflen, ifp)) > 0) {
+               int type;
+               char obuf[7];
+               unsigned int dest_addr;
+               while ((rbuf[len - 1] == '\r') || (rbuf[len - 1] == '\n'))
+                       len--;
+               printf("%d, %s\n", (int)len, rbuf);
+               if (!check_hex_line(rbuf, len))
+                       break;
+               type = hex_to_int(rbuf + 7);
+               switch (type) {
+                       case 4:
+                               addr = lhex_to_int(rbuf + 9) * 0x10000;
+                               printf("bump addr to 0x%08X\n", addr);
+                               break;
+                       case 0:
+                               dest_addr = addr + lhex_to_int(rbuf + 3);
+                               obuf[0] = 0x4c;
+                               obuf[1] = 0xfc;
+                               obuf[2] = hex_to_int(rbuf + 1) + 4;
+                               obuf[3] = dest_addr;
+                               obuf[4] = dest_addr >> 8;
+                               obuf[5] = dest_addr >> 16;
+                               obuf[6] = dest_addr >> 24;
+                               if (fwrite(obuf, 7, 1, ofp) != 1)
+                                       goto output_err;
+                               for (i = 0; i < hex_to_int(rbuf + 1); i++) {
+                                       obuf[0] = hex_to_int(rbuf + 9 + i * 2);
+                                       if (fwrite(obuf, 1, 1, ofp) != 1)
+                                               goto output_err;
+                               }
+                               break;
+                       case 1:
+                               if (fwrite("\x4e\xfc\x04\xff\xff\xff\xff", 7, 1, ofp) != 1)
+                                       goto output_err;
+                               goto end;
+                       default:
+                               return -EINVAL;
+               }
+       }
+
+       puts("hex file formatting error");
+       return -EINVAL;
+
+output_err:
+       puts("error on writing output file");
+       return -EIO;
+
+end:
+       return 0;
+}
+
index 95b4abf..2dbfca7 100644 (file)
@@ -35,6 +35,7 @@
 #include <string.h>
 #include <dirent.h>
 #include <getopt.h>
+#include <limits.h>
 #include <sys/ioctl.h>
 #include <linux/types.h>
 #include <linux/hiddev.h>
diff --git a/tools/ibeacon.c b/tools/ibeacon.c
new file mode 100644 (file)
index 0000000..28967de
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ *
+ *  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 <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "monitor/mainloop.h"
+#include "monitor/bt.h"
+#include "src/shared/timeout.h"
+#include "src/shared/util.h"
+#include "src/shared/hci.h"
+
+static int urandom_fd;
+static struct bt_hci *hci_dev;
+
+static bool shutdown_timeout(void *user_data)
+{
+       mainloop_quit();
+
+       return false;
+}
+
+static void shutdown_complete(const void *data, uint8_t size, void *user_data)
+{
+       unsigned int id = PTR_TO_UINT(user_data);
+
+       timeout_remove(id);
+       mainloop_quit();
+}
+
+static void shutdown_device(void)
+{
+       uint8_t enable = 0x00;
+       unsigned int id;
+
+       bt_hci_flush(hci_dev);
+
+       id = timeout_add(5000, shutdown_timeout, NULL, NULL);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+                                       &enable, 1, NULL, NULL, NULL);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+                               shutdown_complete, UINT_TO_PTR(id), NULL);
+}
+
+static void set_random_address(void)
+{
+       struct bt_hci_cmd_le_set_random_address cmd;
+       ssize_t len;
+
+       len = read(urandom_fd, cmd.addr, sizeof(cmd.addr));
+       if (len < 0 || len != sizeof(cmd.addr)) {
+               fprintf(stderr, "Failed to read random data\n");
+               return;
+       }
+
+       /* Clear top most significant bits */
+       cmd.addr[5] &= 0x3f;
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS,
+                                       &cmd, sizeof(cmd), NULL, NULL, NULL);
+}
+
+static void set_adv_parameters(void)
+{
+       struct bt_hci_cmd_le_set_adv_parameters cmd;
+
+       cmd.min_interval = cpu_to_le16(0x0800);
+       cmd.max_interval = cpu_to_le16(0x0800);
+       cmd.type = 0x03;                /* Non-connectable advertising */
+       cmd.own_addr_type = 0x01;       /* Use random address */
+       cmd.direct_addr_type = 0x00;
+       memset(cmd.direct_addr, 0, 6);
+       cmd.channel_map = 0x07;
+       cmd.filter_policy = 0x00;
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+                                       &cmd, sizeof(cmd), NULL, NULL, NULL);
+}
+
+static void set_adv_enable(void)
+{
+       uint8_t enable = 0x01;
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_ENABLE,
+                                       &enable, 1, NULL, NULL, NULL);
+}
+
+static void adv_data_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       uint8_t status = *((uint8_t *) data);
+
+       if (status) {
+               fprintf(stderr, "Failed to set advertising data\n");
+               shutdown_device();
+               return;
+       }
+
+       set_random_address();
+       set_adv_parameters();
+       set_adv_enable();
+}
+
+static void adv_tx_power_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_le_read_adv_tx_power *rsp = data;
+       struct bt_hci_cmd_le_set_adv_data cmd;
+
+       if (rsp->status) {
+               fprintf(stderr, "Failed to read advertising TX power\n");
+               shutdown_device();
+               return;
+       }
+
+       cmd.data[0] = 0x02;             /* Field length */
+       cmd.data[1] = 0x01;             /* Flags */
+       cmd.data[2] = 0x02;             /* LE General Discoverable Mode */
+       cmd.data[2] |= 0x04;            /* BR/EDR Not Supported */
+
+       cmd.data[3] = 0x1a;             /* Field length */
+       cmd.data[4] = 0xff;             /* Vendor field */
+       cmd.data[5] = 0x4c;             /* Apple (76) - LSB */
+       cmd.data[6] = 0x00;             /* Apple (76) - MSB */
+       cmd.data[7] = 0x02;             /* iBeacon type */
+       cmd.data[8] = 0x15;             /* Length */
+       memset(cmd.data + 9, 0, 16);    /* UUID */
+       cmd.data[25] = 0x00;            /* Major - LSB */
+       cmd.data[26] = 0x00;            /* Major - MSB */
+       cmd.data[27] = 0x00;            /* Minor - LSB */
+       cmd.data[28] = 0x00;            /* Minor - MSB */
+       cmd.data[29] = 0xc5;            /* TX power level */
+
+       cmd.data[30] = 0x00;            /* Field terminator */
+
+       cmd.len = 1 + cmd.data[0] + 1 + cmd.data[3];
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_LE_SET_ADV_DATA, &cmd, sizeof(cmd),
+                                       adv_data_callback, NULL, NULL);
+}
+
+static void local_features_callback(const void *data, uint8_t size,
+                                                       void *user_data)
+{
+       const struct bt_hci_rsp_read_local_features *rsp = data;
+
+       if (rsp->status) {
+               fprintf(stderr, "Failed to read local features\n");
+               shutdown_device();
+               return;
+       }
+
+       if (!(rsp->features[4] & 0x40)) {
+               fprintf(stderr, "Controller without Low Energy support\n");
+               shutdown_device();
+               return;
+       }
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_LE_READ_ADV_TX_POWER, NULL, 0,
+                                       adv_tx_power_callback, NULL, NULL);
+}
+
+static void start_ibeacon(void)
+{
+       bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0, NULL, NULL, NULL);
+
+       bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_FEATURES, NULL, 0,
+                                       local_features_callback, NULL, NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+       static bool terminated = false;
+
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               if (!terminated) {
+                       shutdown_device();
+                       terminated = true;
+               }
+               break;
+       }
+}
+
+static void usage(void)
+{
+       printf("ibeacon - Low Energy iBeacon testing tool\n"
+               "Usage:\n");
+       printf("\tibeacon [options]\n");
+       printf("Options:\n"
+               "\t-i, --index <num>      Use specified controller\n"
+               "\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+       { "index",   required_argument, NULL, 'i' },
+       { "version", no_argument,       NULL, 'v' },
+       { "help",    no_argument,       NULL, 'h' },
+       { }
+};
+
+int main(int argc, char *argv[])
+{
+       uint16_t index = 0;
+       const char *str;
+       sigset_t mask;
+       int exit_status;
+
+       for (;;) {
+               int opt;
+
+               opt = getopt_long(argc, argv, "i:vh", main_options, NULL);
+               if (opt < 0)
+                       break;
+
+               switch (opt) {
+               case 'i':
+                       if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
+                               str = optarg + 3;
+                       else
+                               str = optarg;
+                       if (!isdigit(*str)) {
+                               usage();
+                               return EXIT_FAILURE;
+                       }
+                       index = atoi(str);
+                       break;
+               case 'v':
+                       printf("%s\n", VERSION);
+                       return EXIT_SUCCESS;
+               case 'h':
+                       usage();
+                       return EXIT_SUCCESS;
+               default:
+                       return EXIT_FAILURE;
+               }
+       }
+
+       if (argc - optind > 0) {
+               fprintf(stderr, "Invalid command line parameters\n");
+               return EXIT_FAILURE;
+       }
+
+       urandom_fd = open("/dev/urandom", O_RDONLY);
+       if (urandom_fd < 0) {
+               fprintf(stderr, "Failed to open /dev/urandom device\n");
+               return EXIT_FAILURE;
+       }
+
+       mainloop_init();
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGINT);
+       sigaddset(&mask, SIGTERM);
+
+       mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+       printf("Low Energy iBeacon utility ver %s\n", VERSION);
+
+       hci_dev = bt_hci_new_user_channel(index);
+       if (!hci_dev) {
+               fprintf(stderr, "Failed to open HCI user channel\n");
+               exit_status = EXIT_FAILURE;
+               goto done;
+       }
+
+       start_ibeacon();
+
+       exit_status = mainloop_run();
+
+       bt_hci_unref(hci_dev);
+
+done:
+       close(urandom_fd);
+
+       return exit_status;
+}
index 505ac79..79362b2 100644 (file)
@@ -50,22 +50,37 @@ struct test_data {
        struct hciemu *hciemu;
        enum hciemu_type hciemu_type;
        unsigned int io_id;
+       uint16_t handle;
+       uint16_t scid;
+       uint16_t dcid;
 };
 
-struct l2cap_client_data {
+struct l2cap_data {
        uint16_t client_psm;
        uint16_t server_psm;
+       uint16_t cid;
        int expect_err;
-};
 
-struct l2cap_server_data {
-       uint16_t server_psm;
-       uint8_t send_req_code;
-       const void *send_req;
-       uint16_t send_req_len;
-       uint8_t expect_rsp_code;
-       const void *expect_rsp;
-       uint16_t expect_rsp_len;
+       uint8_t send_cmd_code;
+       const void *send_cmd;
+       uint16_t send_cmd_len;
+       uint8_t expect_cmd_code;
+       const void *expect_cmd;
+       uint16_t expect_cmd_len;
+
+       uint16_t data_len;
+       const void *read_data;
+       const void *write_data;
+
+       bool enable_ssp;
+       int sec_level;
+       bool reject_ssp;
+
+       bool expect_pin;
+       uint8_t pin_len;
+       const void *pin;
+       uint8_t client_pin_len;
+       const void *client_pin;
 };
 
 static void mgmt_debug(const char *str, void *user_data)
@@ -242,24 +257,113 @@ static void test_data_free(void *test_data)
                                test_post_teardown, 2, user, test_data_free); \
        } while (0)
 
-static const struct l2cap_client_data client_connect_success_test = {
+static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 }; /* "0000" */
+
+static const struct l2cap_data client_connect_success_test = {
+       .client_psm = 0x1001,
+       .server_psm = 0x1001,
+};
+
+static const struct l2cap_data client_connect_ssp_success_test_1 = {
+       .client_psm = 0x1001,
+       .server_psm = 0x1001,
+       .enable_ssp = true,
+};
+
+static const struct l2cap_data client_connect_ssp_success_test_2 = {
        .client_psm = 0x1001,
        .server_psm = 0x1001,
+       .enable_ssp = true,
+       .sec_level  = BT_SECURITY_HIGH,
 };
 
-static const struct l2cap_client_data client_connect_nval_psm_test = {
+static const struct l2cap_data client_connect_pin_success_test = {
        .client_psm = 0x1001,
+       .server_psm = 0x1001,
+       .sec_level  = BT_SECURITY_MEDIUM,
+       .pin = pair_device_pin,
+       .pin_len = sizeof(pair_device_pin),
+       .client_pin = pair_device_pin,
+       .client_pin_len = sizeof(pair_device_pin),
+};
+
+static uint8_t l2_data[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+static const struct l2cap_data client_connect_read_success_test = {
+       .client_psm = 0x1001,
+       .server_psm = 0x1001,
+       .read_data = l2_data,
+       .data_len = sizeof(l2_data),
+};
+
+static const struct l2cap_data client_connect_write_success_test = {
+       .client_psm = 0x1001,
+       .server_psm = 0x1001,
+       .write_data = l2_data,
+       .data_len = sizeof(l2_data),
+};
+
+static const struct l2cap_data client_connect_nval_psm_test_1 = {
+       .client_psm = 0x1001,
+       .expect_err = ECONNREFUSED,
+};
+
+static const struct l2cap_data client_connect_nval_psm_test_2 = {
+       .client_psm = 0x0001,
        .expect_err = ECONNREFUSED,
 };
 
+static const struct l2cap_data client_connect_nval_psm_test_3 = {
+       .client_psm = 0x0001,
+       .expect_err = ECONNREFUSED,
+       .enable_ssp = true,
+};
+
 static const uint8_t l2cap_connect_req[] = { 0x01, 0x10, 0x41, 0x00 };
 
-static const struct l2cap_server_data l2cap_server_success_test = {
+static const struct l2cap_data l2cap_server_success_test = {
+       .server_psm = 0x1001,
+       .send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+       .send_cmd = l2cap_connect_req,
+       .send_cmd_len = sizeof(l2cap_connect_req),
+       .expect_cmd_code = BT_L2CAP_PDU_CONN_RSP,
+};
+
+static const struct l2cap_data l2cap_server_read_success_test = {
+       .server_psm = 0x1001,
+       .send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+       .send_cmd = l2cap_connect_req,
+       .send_cmd_len = sizeof(l2cap_connect_req),
+       .expect_cmd_code = BT_L2CAP_PDU_CONN_RSP,
+       .read_data = l2_data,
+       .data_len = sizeof(l2_data),
+};
+
+static const struct l2cap_data l2cap_server_write_success_test = {
        .server_psm = 0x1001,
-       .send_req_code = BT_L2CAP_PDU_CONN_REQ,
-       .send_req = l2cap_connect_req,
-       .send_req_len = sizeof(l2cap_connect_req),
-       .expect_rsp_code = BT_L2CAP_PDU_CONN_RSP,
+       .send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+       .send_cmd = l2cap_connect_req,
+       .send_cmd_len = sizeof(l2cap_connect_req),
+       .expect_cmd_code = BT_L2CAP_PDU_CONN_RSP,
+       .write_data = l2_data,
+       .data_len = sizeof(l2_data),
+};
+
+static const uint8_t l2cap_sec_block_rsp[] = { 0x00, 0x00,     /* dcid */
+                                               0x41, 0x00,     /* scid */
+                                               0x03, 0x00,     /* Sec Block */
+                                               0x00, 0x00      /* status */
+                                       };
+
+static const struct l2cap_data l2cap_server_sec_block_test = {
+       .server_psm = 0x1001,
+       .send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+       .send_cmd = l2cap_connect_req,
+       .send_cmd_len = sizeof(l2cap_connect_req),
+       .expect_cmd_code = BT_L2CAP_PDU_CONN_RSP,
+       .expect_cmd = l2cap_sec_block_rsp,
+       .expect_cmd_len = sizeof(l2cap_sec_block_rsp),
+       .enable_ssp = true,
 };
 
 static const uint8_t l2cap_nval_psm_rsp[] = {  0x00, 0x00,     /* dcid */
@@ -268,59 +372,74 @@ static const uint8_t l2cap_nval_psm_rsp[] = {     0x00, 0x00,     /* dcid */
                                                0x00, 0x00      /* status */
                                        };
 
-static const struct l2cap_server_data l2cap_server_nval_psm_test = {
-       .send_req_code = BT_L2CAP_PDU_CONN_REQ,
-       .send_req = l2cap_connect_req,
-       .send_req_len = sizeof(l2cap_connect_req),
-       .expect_rsp_code = BT_L2CAP_PDU_CONN_RSP,
-       .expect_rsp = l2cap_nval_psm_rsp,
-       .expect_rsp_len = sizeof(l2cap_nval_psm_rsp),
+static const struct l2cap_data l2cap_server_nval_psm_test = {
+       .send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+       .send_cmd = l2cap_connect_req,
+       .send_cmd_len = sizeof(l2cap_connect_req),
+       .expect_cmd_code = BT_L2CAP_PDU_CONN_RSP,
+       .expect_cmd = l2cap_nval_psm_rsp,
+       .expect_cmd_len = sizeof(l2cap_nval_psm_rsp),
 };
 
 static const uint8_t l2cap_nval_conn_req[] = { 0x00 };
 static const uint8_t l2cap_nval_pdu_rsp[] = { 0x00, 0x00 };
 
-static const struct l2cap_server_data l2cap_server_nval_pdu_test1 = {
-       .send_req_code = BT_L2CAP_PDU_CONN_REQ,
-       .send_req = l2cap_nval_conn_req,
-       .send_req_len = sizeof(l2cap_nval_conn_req),
-       .expect_rsp_code = BT_L2CAP_PDU_CMD_REJECT,
-       .expect_rsp = l2cap_nval_pdu_rsp,
-       .expect_rsp_len = sizeof(l2cap_nval_pdu_rsp),
+static const struct l2cap_data l2cap_server_nval_pdu_test1 = {
+       .send_cmd_code = BT_L2CAP_PDU_CONN_REQ,
+       .send_cmd = l2cap_nval_conn_req,
+       .send_cmd_len = sizeof(l2cap_nval_conn_req),
+       .expect_cmd_code = BT_L2CAP_PDU_CMD_REJECT,
+       .expect_cmd = l2cap_nval_pdu_rsp,
+       .expect_cmd_len = sizeof(l2cap_nval_pdu_rsp),
 };
 
 static const uint8_t l2cap_nval_dc_req[] = { 0x12, 0x34, 0x56, 0x78 };
 static const uint8_t l2cap_nval_cid_rsp[] = { 0x02, 0x00,
                                                0x12, 0x34, 0x56, 0x78 };
 
-static const struct l2cap_server_data l2cap_server_nval_cid_test1 = {
-       .send_req_code = BT_L2CAP_PDU_DISCONN_REQ,
-       .send_req = l2cap_nval_dc_req,
-       .send_req_len = sizeof(l2cap_nval_dc_req),
-       .expect_rsp_code = BT_L2CAP_PDU_CMD_REJECT,
-       .expect_rsp = l2cap_nval_cid_rsp,
-       .expect_rsp_len = sizeof(l2cap_nval_cid_rsp),
+static const struct l2cap_data l2cap_server_nval_cid_test1 = {
+       .send_cmd_code = BT_L2CAP_PDU_DISCONN_REQ,
+       .send_cmd = l2cap_nval_dc_req,
+       .send_cmd_len = sizeof(l2cap_nval_dc_req),
+       .expect_cmd_code = BT_L2CAP_PDU_CMD_REJECT,
+       .expect_cmd = l2cap_nval_cid_rsp,
+       .expect_cmd_len = sizeof(l2cap_nval_cid_rsp),
 };
 
 static const uint8_t l2cap_nval_cfg_req[] = { 0x12, 0x34, 0x00, 0x00 };
 static const uint8_t l2cap_nval_cfg_rsp[] = { 0x02, 0x00,
                                                0x12, 0x34, 0x00, 0x00 };
 
-static const struct l2cap_server_data l2cap_server_nval_cid_test2 = {
-       .send_req_code = BT_L2CAP_PDU_CONFIG_REQ,
-       .send_req = l2cap_nval_cfg_req,
-       .send_req_len = sizeof(l2cap_nval_cfg_req),
-       .expect_rsp_code = BT_L2CAP_PDU_CMD_REJECT,
-       .expect_rsp = l2cap_nval_cfg_rsp,
-       .expect_rsp_len = sizeof(l2cap_nval_cfg_rsp),
+static const struct l2cap_data l2cap_server_nval_cid_test2 = {
+       .send_cmd_code = BT_L2CAP_PDU_CONFIG_REQ,
+       .send_cmd = l2cap_nval_cfg_req,
+       .send_cmd_len = sizeof(l2cap_nval_cfg_req),
+       .expect_cmd_code = BT_L2CAP_PDU_CMD_REJECT,
+       .expect_cmd = l2cap_nval_cfg_rsp,
+       .expect_cmd_len = sizeof(l2cap_nval_cfg_rsp),
 };
 
-static const struct l2cap_client_data le_client_connect_success_test = {
+static const struct l2cap_data le_client_connect_success_test_1 = {
        .client_psm = 0x0080,
        .server_psm = 0x0080,
 };
 
-static const struct l2cap_client_data le_client_connect_nval_psm_test = {
+static const struct l2cap_data le_client_connect_success_test_2 = {
+       .client_psm = 0x0080,
+       .server_psm = 0x0080,
+       .sec_level  = BT_SECURITY_MEDIUM,
+};
+
+static const uint8_t cmd_reject_rsp[] = { 0x01, 0x01, 0x02, 0x00, 0x00, 0x00 };
+
+static const struct l2cap_data le_client_connect_reject_test_1 = {
+       .client_psm = 0x0080,
+       .send_cmd = cmd_reject_rsp,
+       .send_cmd_len = sizeof(cmd_reject_rsp),
+       .expect_err = ECONNREFUSED,
+};
+
+static const struct l2cap_data le_client_connect_nval_psm_test = {
        .client_psm = 0x0080,
        .expect_err = ECONNREFUSED,
 };
@@ -332,27 +451,67 @@ static const uint8_t le_connect_req[] = { 0x80, 0x00, /* PSM */
                                                0x05, 0x00, /* Credits */
 };
 
-static const struct l2cap_server_data le_server_success_test = {
+static const struct l2cap_data le_server_success_test = {
        .server_psm = 0x0080,
-       .send_req_code = BT_L2CAP_PDU_LE_CONN_REQ,
-       .send_req = le_connect_req,
-       .send_req_len = sizeof(le_connect_req),
-       .expect_rsp_code = BT_L2CAP_PDU_LE_CONN_RSP,
+       .send_cmd_code = BT_L2CAP_PDU_LE_CONN_REQ,
+       .send_cmd = le_connect_req,
+       .send_cmd_len = sizeof(le_connect_req),
+       .expect_cmd_code = BT_L2CAP_PDU_LE_CONN_RSP,
+};
+
+static const struct l2cap_data le_att_client_connect_success_test_1 = {
+       .cid = 0x0004,
+       .sec_level = BT_SECURITY_LOW,
+};
+
+static const struct l2cap_data le_att_server_success_test_1 = {
+       .cid = 0x0004,
 };
 
-static void client_connectable_complete(uint16_t opcode, uint8_t status,
+static void client_cmd_complete(uint16_t opcode, uint8_t status,
                                        const void *param, uint8_t len,
                                        void *user_data)
 {
+       struct test_data *data = tester_get_data();
+       const struct l2cap_data *test = data->test_data;
+       struct bthost *bthost;
+
+       bthost = hciemu_client_get_host(data->hciemu);
+
        switch (opcode) {
        case BT_HCI_CMD_WRITE_SCAN_ENABLE:
        case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+               tester_print("Client set connectable status 0x%02x", status);
+               if (!status && test && test->enable_ssp) {
+                       bthost_write_ssp_mode(bthost, 0x01);
+                       return;
+               }
+               break;
+       case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+               tester_print("Client enable SSP status 0x%02x", status);
                break;
        default:
                return;
        }
 
-       tester_print("Client set connectable status 0x%02x", status);
+
+       if (status)
+               tester_setup_failed();
+       else
+               tester_setup_complete();
+}
+
+static void server_cmd_complete(uint16_t opcode, uint8_t status,
+                                       const void *param, uint8_t len,
+                                       void *user_data)
+{
+       switch (opcode) {
+       case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+               tester_print("Server enable SSP status 0x%02x", status);
+               break;
+       default:
+               return;
+       }
 
        if (status)
                tester_setup_failed();
@@ -374,7 +533,7 @@ static void setup_powered_client_callback(uint8_t status, uint16_t length,
        tester_print("Controller powered on");
 
        bthost = hciemu_client_get_host(data->hciemu);
-       bthost_set_cmd_complete_cb(bthost, client_connectable_complete, data);
+       bthost_set_cmd_complete_cb(bthost, client_cmd_complete, user_data);
        if (data->hciemu_type == HCIEMU_TYPE_LE)
                bthost_set_adv_enable(bthost, 0x01);
        else
@@ -384,6 +543,10 @@ static void setup_powered_client_callback(uint8_t status, uint16_t length,
 static void setup_powered_server_callback(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
+       struct test_data *data = tester_get_data();
+       const struct l2cap_data *test = data->test_data;
+       struct bthost *bthost;
+
        if (status != MGMT_STATUS_SUCCESS) {
                tester_setup_failed();
                return;
@@ -391,22 +554,155 @@ static void setup_powered_server_callback(uint8_t status, uint16_t length,
 
        tester_print("Controller powered on");
 
-       tester_setup_complete();
+       if (!test->enable_ssp) {
+               tester_setup_complete();
+               return;
+       }
+
+       bthost = hciemu_client_get_host(data->hciemu);
+       bthost_set_cmd_complete_cb(bthost, server_cmd_complete, user_data);
+       bthost_write_ssp_mode(bthost, 0x01);
 }
 
-static void setup_powered_client(const void *test_data)
+static void user_confirm_request_callback(uint16_t index, uint16_t length,
+                                                       const void *param,
+                                                       void *user_data)
 {
+       const struct mgmt_ev_user_confirm_request *ev = param;
        struct test_data *data = tester_get_data();
+       const struct l2cap_data *test = data->test_data;
+       struct mgmt_cp_user_confirm_reply cp;
+       uint16_t opcode;
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+
+       if (test->reject_ssp)
+               opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+       else
+               opcode = MGMT_OP_USER_CONFIRM_REPLY;
+
+       mgmt_reply(data->mgmt, opcode, data->mgmt_index, sizeof(cp), &cp,
+                                                       NULL, NULL, NULL);
+}
+
+static void pin_code_request_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_pin_code_request *ev = param;
+       struct test_data *data = user_data;
+       const struct l2cap_data *test = data->test_data;
+       struct mgmt_cp_pin_code_reply cp;
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+
+       if (!test->pin) {
+               mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY,
+                               data->mgmt_index, sizeof(cp.addr), &cp.addr,
+                               NULL, NULL, NULL);
+               return;
+       }
+
+       cp.pin_len = test->pin_len;
+       memcpy(cp.pin_code, test->pin, test->pin_len);
+
+       mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_REPLY, data->mgmt_index,
+                       sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void bthost_send_rsp(const void *buf, uint16_t len, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct l2cap_data *l2data = data->test_data;
+       struct bthost *bthost;
+
+       if (l2data->expect_cmd_len && len != l2data->expect_cmd_len) {
+               tester_test_failed();
+               return;
+       }
+
+       if (l2data->expect_cmd && memcmp(buf, l2data->expect_cmd,
+                                               l2data->expect_cmd_len)) {
+               tester_test_failed();
+               return;
+       }
+
+       if (!l2data->send_cmd)
+               return;
+
+       bthost = hciemu_client_get_host(data->hciemu);
+       bthost_send_cid(bthost, data->handle, data->dcid,
+                               l2data->send_cmd, l2data->send_cmd_len);
+}
+
+static void send_rsp_new_conn(uint16_t handle, void *user_data)
+{
+       struct test_data *data = user_data;
+       struct bthost *bthost;
+
+       tester_print("New connection with handle 0x%04x", handle);
+
+       data->handle = handle;
+
+       if (data->hciemu_type == HCIEMU_TYPE_LE)
+               data->dcid = 0x0005;
+       else
+               data->dcid = 0x0001;
+
+       bthost = hciemu_client_get_host(data->hciemu);
+       bthost_add_cid_hook(bthost, data->handle, data->dcid,
+                                               bthost_send_rsp, NULL);
+}
+
+static void setup_powered_common(void)
+{
+       struct test_data *data = tester_get_data();
+       const struct l2cap_data *test = data->test_data;
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
        unsigned char param[] = { 0x01 };
 
-       tester_print("Powering on controller");
+       mgmt_register(data->mgmt, MGMT_EV_USER_CONFIRM_REQUEST,
+                       data->mgmt_index, user_confirm_request_callback,
+                       NULL, NULL);
 
-       if (data->hciemu_type == HCIEMU_TYPE_BREDR)
+       if (test && (test->pin || test->expect_pin))
+               mgmt_register(data->mgmt, MGMT_EV_PIN_CODE_REQUEST,
+                               data->mgmt_index, pin_code_request_callback,
+                               data, NULL);
+
+       if (test && test->client_pin)
+               bthost_set_pin_code(bthost, test->client_pin,
+                                                       test->client_pin_len);
+       if (test && test->reject_ssp)
+               bthost_set_reject_user_confirm(bthost, true);
+
+       if (data->hciemu_type == HCIEMU_TYPE_LE)
+               mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+                               sizeof(param), param, NULL, NULL, NULL);
+
+       if (test && test->enable_ssp)
                mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
                                sizeof(param), param, NULL, NULL, NULL);
-       else
-               mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+
+       mgmt_send(data->mgmt, MGMT_OP_SET_PAIRABLE, data->mgmt_index,
                                sizeof(param), param, NULL, NULL, NULL);
+}
+
+static void setup_powered_client(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct l2cap_data *test = data->test_data;
+       unsigned char param[] = { 0x01 };
+
+       setup_powered_common();
+
+       tester_print("Powering on controller");
+
+       if (test && (test->expect_cmd || test->send_cmd)) {
+               struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+               bthost_set_connect_cb(bthost, send_rsp_new_conn, data);
+       }
 
        mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
                        sizeof(param), param, setup_powered_client_callback,
@@ -418,20 +714,17 @@ static void setup_powered_server(const void *test_data)
        struct test_data *data = tester_get_data();
        unsigned char param[] = { 0x01 };
 
+       setup_powered_common();
+
        tester_print("Powering on controller");
 
-       if (data->hciemu_type == HCIEMU_TYPE_BREDR) {
-               mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
-                               sizeof(param), param,
-                               NULL, NULL, NULL);
-               mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index,
-                               sizeof(param), param, NULL, NULL, NULL);
-       } else {
-               mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
-                               sizeof(param), param, NULL, NULL, NULL);
-               mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index,
+       mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
                                sizeof(param), param, NULL, NULL, NULL);
-       }
+
+       if (data->hciemu_type != HCIEMU_TYPE_BREDR)
+               mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING,
+                               data->mgmt_index, sizeof(param), param, NULL,
+                               NULL, NULL);
 
        mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
                        sizeof(param), param, setup_powered_server_callback,
@@ -455,11 +748,133 @@ static void test_basic(const void *test_data)
        tester_test_passed();
 }
 
+static gboolean client_received_data(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct l2cap_data *l2data = data->test_data;
+       char buf[1024];
+       int sk;
+
+       sk = g_io_channel_unix_get_fd(io);
+       if (read(sk, buf, l2data->data_len) != l2data->data_len) {
+               tester_warn("Unable to read %u bytes", l2data->data_len);
+               tester_test_failed();
+               return FALSE;
+       }
+
+       if (memcmp(buf, l2data->read_data, l2data->data_len))
+               tester_test_failed();
+       else
+               tester_test_passed();
+
+       return FALSE;
+}
+
+static gboolean server_received_data(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct l2cap_data *l2data = data->test_data;
+       char buf[1024];
+       int sk;
+
+       sk = g_io_channel_unix_get_fd(io);
+       if (read(sk, buf, l2data->data_len) != l2data->data_len) {
+               tester_warn("Unable to read %u bytes", l2data->data_len);
+               tester_test_failed();
+               return FALSE;
+       }
+
+       if (memcmp(buf, l2data->read_data, l2data->data_len))
+               tester_test_failed();
+       else
+               tester_test_passed();
+
+       return FALSE;
+}
+
+static void bthost_received_data(const void *buf, uint16_t len,
+                                                       void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct l2cap_data *l2data = data->test_data;
+
+       if (len != l2data->data_len) {
+               tester_test_failed();
+               return;
+       }
+
+       if (memcmp(buf, l2data->write_data, l2data->data_len))
+               tester_test_failed();
+       else
+               tester_test_passed();
+}
+
+static void server_bthost_received_data(const void *buf, uint16_t len,
+                                                       void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct l2cap_data *l2data = data->test_data;
+
+       if (len != l2data->data_len) {
+               tester_test_failed();
+               return;
+       }
+
+       if (memcmp(buf, l2data->write_data, l2data->data_len))
+               tester_test_failed();
+       else
+               tester_test_passed();
+}
+
+static bool check_mtu(struct test_data *data, int sk)
+{
+       const struct l2cap_data *l2data = data->test_data;
+       struct l2cap_options l2o;
+       socklen_t len;
+
+       memset(&l2o, 0, sizeof(l2o));
+
+       if (data->hciemu_type == HCIEMU_TYPE_LE &&
+                               (l2data->client_psm || l2data->server_psm)) {
+               /* LE CoC enabled kernels should support BT_RCVMTU and
+                * BT_SNDMTU.
+                */
+               len = sizeof(l2o.imtu);
+               if (getsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU,
+                                                       &l2o.imtu, &len) < 0) {
+                       tester_warn("getsockopt(BT_RCVMTU): %s (%d)",
+                                       strerror(errno), errno);
+                       return false;
+               }
+
+               len = sizeof(l2o.omtu);
+               if (getsockopt(sk, SOL_BLUETOOTH, BT_SNDMTU,
+                                                       &l2o.omtu, &len) < 0) {
+                       tester_warn("getsockopt(BT_SNDMTU): %s (%d)",
+                                       strerror(errno), errno);
+                       return false;
+               }
+       } else {
+               /* For non-LE CoC enabled kernels we need to fall back to
+                * L2CAP_OPTIONS, so test support for it as well */
+               len = sizeof(l2o);
+               if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) {
+                        tester_warn("getsockopt(L2CAP_OPTIONS): %s (%d)",
+                                               strerror(errno), errno);
+                        return false;
+                }
+       }
+
+       return true;
+}
+
 static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond,
                                                        gpointer user_data)
 {
        struct test_data *data = tester_get_data();
-       const struct l2cap_client_data *l2data = data->test_data;
+       const struct l2cap_data *l2data = data->test_data;
        int err, sk_err, sk;
        socklen_t len = sizeof(sk_err);
 
@@ -472,11 +887,46 @@ static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond,
        else
                err = -sk_err;
 
-       if (err < 0)
+       if (err < 0) {
                tester_warn("Connect failed: %s (%d)", strerror(-err), -err);
-       else
-               tester_print("Successfully connected");
+               goto failed;
+       }
 
+       tester_print("Successfully connected");
+
+       if (!check_mtu(data, sk)) {
+               tester_test_failed();
+               return FALSE;
+       }
+
+       if (l2data->read_data) {
+               struct bthost *bthost;
+
+               bthost = hciemu_client_get_host(data->hciemu);
+               g_io_add_watch(io, G_IO_IN, client_received_data, NULL);
+
+               bthost_send_cid(bthost, data->handle, data->dcid,
+                                       l2data->read_data, l2data->data_len);
+
+               return FALSE;
+       } else if (l2data->write_data) {
+               struct bthost *bthost;
+               ssize_t ret;
+
+               bthost = hciemu_client_get_host(data->hciemu);
+               bthost_add_cid_hook(bthost, data->handle, data->dcid,
+                                       bthost_received_data, NULL);
+
+               ret = write(sk, l2data->write_data, l2data->data_len);
+               if (ret != l2data->data_len) {
+                       tester_warn("Unable to write all data");
+                       tester_test_failed();
+               }
+
+               return FALSE;
+       }
+
+failed:
        if (-err != l2data->expect_err)
                tester_test_failed();
        else
@@ -485,7 +935,8 @@ static gboolean l2cap_connect_cb(GIOChannel *io, GIOCondition cond,
        return FALSE;
 }
 
-static int create_l2cap_sock(struct test_data *data, uint16_t psm)
+static int create_l2cap_sock(struct test_data *data, uint16_t psm,
+                                               uint16_t cid, int sec_level)
 {
        const uint8_t *master_bdaddr;
        struct sockaddr_l2 addr;
@@ -503,12 +954,14 @@ static int create_l2cap_sock(struct test_data *data, uint16_t psm)
        master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
        if (!master_bdaddr) {
                tester_warn("No master bdaddr");
+               close(sk);
                return -ENODEV;
        }
 
        memset(&addr, 0, sizeof(addr));
        addr.l2_family = AF_BLUETOOTH;
        addr.l2_psm = htobs(psm);
+       addr.l2_cid = htobs(cid);
        bacpy(&addr.l2_bdaddr, (void *) master_bdaddr);
        if (data->hciemu_type == HCIEMU_TYPE_LE)
                addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
@@ -523,10 +976,27 @@ static int create_l2cap_sock(struct test_data *data, uint16_t psm)
                return err;
        }
 
+       if (sec_level) {
+               struct bt_security sec;
+
+               memset(&sec, 0, sizeof(sec));
+               sec.level = sec_level;
+
+               if (setsockopt(sk, SOL_BLUETOOTH, BT_SECURITY, &sec,
+                                                       sizeof(sec)) < 0) {
+                       err = -errno;
+                       tester_warn("Can't set security level: %s (%d)",
+                                               strerror(errno), errno);
+                       close(sk);
+                       return err;
+               }
+       }
+
        return sk;
 }
 
-static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm)
+static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm,
+                                                               uint16_t cid)
 {
        const uint8_t *client_bdaddr;
        struct sockaddr_l2 addr;
@@ -542,6 +1012,7 @@ static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm)
        addr.l2_family = AF_BLUETOOTH;
        bacpy(&addr.l2_bdaddr, (void *) client_bdaddr);
        addr.l2_psm = htobs(psm);
+       addr.l2_cid = htobs(cid);
        if (data->hciemu_type == HCIEMU_TYPE_LE)
                addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
        else
@@ -558,25 +1029,41 @@ static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm)
        return 0;
 }
 
+static void client_l2cap_connect_cb(uint16_t handle, uint16_t cid,
+                                                       void *user_data)
+{
+       struct test_data *data = user_data;
+
+       data->dcid = cid;
+       data->handle = handle;
+}
+
 static void test_connect(const void *test_data)
 {
        struct test_data *data = tester_get_data();
-       const struct l2cap_client_data *l2data = data->test_data;
+       const struct l2cap_data *l2data = data->test_data;
        GIOChannel *io;
        int sk;
 
        if (l2data->server_psm) {
                struct bthost *bthost = hciemu_client_get_host(data->hciemu);
-               bthost_set_server_psm(bthost, l2data->server_psm);
+
+               if (!l2data->data_len)
+                       bthost_add_l2cap_server(bthost, l2data->server_psm,
+                                               NULL, NULL);
+               else
+                       bthost_add_l2cap_server(bthost, l2data->server_psm,
+                                               client_l2cap_connect_cb, data);
        }
 
-       sk = create_l2cap_sock(data, 0);
+       sk = create_l2cap_sock(data, 0, l2data->cid, l2data->sec_level);
        if (sk < 0) {
                tester_test_failed();
                return;
        }
 
-       if (connect_l2cap_sock(data, sk, l2data->client_psm) < 0) {
+       if (connect_l2cap_sock(data, sk, l2data->client_psm,
+                                                       l2data->cid) < 0) {
                close(sk);
                tester_test_failed();
                return;
@@ -596,6 +1083,7 @@ static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond,
                                                        gpointer user_data)
 {
        struct test_data *data = tester_get_data();
+       const struct l2cap_data *l2data = data->test_data;
        int sk, new_sk;
 
        data->io_id = 0;
@@ -609,6 +1097,45 @@ static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond,
                return FALSE;
        }
 
+       if (!check_mtu(data, new_sk)) {
+               tester_test_failed();
+               return FALSE;
+       }
+
+       if (l2data->read_data) {
+               struct bthost *bthost;
+               GIOChannel *new_io;
+
+               new_io = g_io_channel_unix_new(new_sk);
+               g_io_channel_set_close_on_unref(new_io, TRUE);
+
+               bthost = hciemu_client_get_host(data->hciemu);
+               g_io_add_watch(new_io, G_IO_IN, server_received_data, NULL);
+               bthost_send_cid(bthost, data->handle, data->dcid,
+                                       l2data->read_data, l2data->data_len);
+
+               g_io_channel_unref(new_io);
+
+               return FALSE;
+       } else if (l2data->write_data) {
+               struct bthost *bthost;
+               ssize_t ret;
+
+               bthost = hciemu_client_get_host(data->hciemu);
+               bthost_add_cid_hook(bthost, data->handle, data->scid,
+                                       server_bthost_received_data, NULL);
+
+               ret = write(new_sk, l2data->write_data, l2data->data_len);
+               close(new_sk);
+
+               if (ret != l2data->data_len) {
+                       tester_warn("Unable to write all data");
+                       tester_test_failed();
+               }
+
+               return FALSE;
+       }
+
        tester_print("Successfully connected");
 
        close(new_sk);
@@ -622,28 +1149,41 @@ static void client_l2cap_rsp(uint8_t code, const void *data, uint16_t len,
                                                        void *user_data)
 {
        struct test_data *test_data = user_data;
-       const struct l2cap_server_data *l2data = test_data->test_data;
+       const struct l2cap_data *l2data = test_data->test_data;
 
        tester_print("Client received response code 0x%02x", code);
 
-       if (code != l2data->expect_rsp_code) {
+       if (code != l2data->expect_cmd_code) {
                tester_warn("Unexpected L2CAP response code (expected 0x%02x)",
-                                               l2data->expect_rsp_code);
+                                               l2data->expect_cmd_code);
                goto failed;
        }
 
-       if (!l2data->expect_rsp) {
+       if (code == BT_L2CAP_PDU_CONN_RSP) {
+
+               const struct bt_l2cap_pdu_conn_rsp *rsp = data;
+               if (len == sizeof(rsp) && !rsp->result && !rsp->status)
+                       return;
+
+               test_data->dcid = rsp->dcid;
+               test_data->scid = rsp->scid;
+
+               if (l2data->data_len)
+                       return;
+       }
+
+       if (!l2data->expect_cmd) {
                tester_test_passed();
                return;
        }
 
-       if (l2data->expect_rsp_len != len) {
+       if (l2data->expect_cmd_len != len) {
                tester_warn("Unexpected L2CAP response length (%u != %u)",
-                                               len, l2data->expect_rsp_len);
+                                               len, l2data->expect_cmd_len);
                goto failed;
        }
 
-       if (memcmp(l2data->expect_rsp, data, len) != 0) {
+       if (memcmp(l2data->expect_cmd, data, len) != 0) {
                tester_warn("Unexpected L2CAP response");
                goto failed;
        }
@@ -655,18 +1195,20 @@ failed:
        tester_test_failed();
 }
 
-static void client_new_conn(uint16_t handle, void *user_data)
+static void send_req_new_conn(uint16_t handle, void *user_data)
 {
        struct test_data *data = user_data;
-       const struct l2cap_server_data *l2data = data->test_data;
+       const struct l2cap_data *l2data = data->test_data;
        struct bthost *bthost;
 
        tester_print("New client connection with handle 0x%04x", handle);
 
-       if (l2data->send_req) {
+       data->handle = handle;
+
+       if (l2data->send_cmd) {
                bthost_l2cap_rsp_cb cb;
 
-               if (l2data->expect_rsp_code)
+               if (l2data->expect_cmd_code)
                        cb = client_l2cap_rsp;
                else
                        cb = NULL;
@@ -674,8 +1216,8 @@ static void client_new_conn(uint16_t handle, void *user_data)
                tester_print("Sending L2CAP Request from client");
 
                bthost = hciemu_client_get_host(data->hciemu);
-               bthost_l2cap_req(bthost, handle, l2data->send_req_code,
-                                       l2data->send_req, l2data->send_req_len,
+               bthost_l2cap_req(bthost, handle, l2data->send_cmd_code,
+                                       l2data->send_cmd, l2data->send_cmd_len,
                                        cb, data);
        }
 }
@@ -683,15 +1225,16 @@ static void client_new_conn(uint16_t handle, void *user_data)
 static void test_server(const void *test_data)
 {
        struct test_data *data = tester_get_data();
-       const struct l2cap_server_data *l2data = data->test_data;
+       const struct l2cap_data *l2data = data->test_data;
        const uint8_t *master_bdaddr;
        uint8_t addr_type;
        struct bthost *bthost;
        GIOChannel *io;
        int sk;
 
-       if (l2data->server_psm) {
-               sk = create_l2cap_sock(data, l2data->server_psm);
+       if (l2data->server_psm || l2data->cid) {
+               sk = create_l2cap_sock(data, l2data->server_psm,
+                                       l2data->cid, l2data->sec_level);
                if (sk < 0) {
                        tester_test_failed();
                        return;
@@ -701,6 +1244,7 @@ static void test_server(const void *test_data)
                        tester_warn("listening on socket failed: %s (%u)",
                                        strerror(errno), errno);
                        tester_test_failed();
+                       close(sk);
                        return;
                }
 
@@ -722,7 +1266,7 @@ static void test_server(const void *test_data)
        }
 
        bthost = hciemu_client_get_host(data->hciemu);
-       bthost_set_connect_cb(bthost, client_new_conn, data);
+       bthost_set_connect_cb(bthost, send_req_new_conn, data);
 
        if (data->hciemu_type == HCIEMU_TYPE_BREDR)
                addr_type = BDADDR_BREDR;
@@ -732,23 +1276,99 @@ static void test_server(const void *test_data)
        bthost_hci_connect(bthost, master_bdaddr, addr_type);
 }
 
+static void test_getpeername_not_connected(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct sockaddr_l2 addr;
+       socklen_t len;
+       int sk;
+
+       sk = create_l2cap_sock(data, 0, 0, 0);
+       if (sk < 0) {
+               tester_test_failed();
+               return;
+       }
+
+       len = sizeof(addr);
+       if (getpeername(sk, (struct sockaddr *) &addr, &len) == 0) {
+               tester_warn("getpeername succeeded on non-connected socket");
+               tester_test_failed();
+               goto done;
+       }
+
+       if (errno != ENOTCONN) {
+               tester_warn("Unexpexted getpeername error: %s (%d)",
+                                               strerror(errno), errno);
+               tester_test_failed();
+               goto done;
+       }
+
+       tester_test_passed();
+
+done:
+       close(sk);
+}
+
 int main(int argc, char *argv[])
 {
        tester_init(&argc, &argv);
 
        test_l2cap_bredr("Basic L2CAP Socket - Success", NULL,
                                        setup_powered_client, test_basic);
+       test_l2cap_bredr("Non-connected getpeername - Failure", NULL,
+                                       setup_powered_client,
+                                       test_getpeername_not_connected);
 
        test_l2cap_bredr("L2CAP BR/EDR Client - Success",
                                        &client_connect_success_test,
                                        setup_powered_client, test_connect);
-       test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM",
-                                       &client_connect_nval_psm_test,
+
+       test_l2cap_bredr("L2CAP BR/EDR Client SSP - Success 1",
+                                       &client_connect_ssp_success_test_1,
+                                       setup_powered_client, test_connect);
+       test_l2cap_bredr("L2CAP BR/EDR Client SSP - Success 2",
+                                       &client_connect_ssp_success_test_2,
+                                       setup_powered_client, test_connect);
+       test_l2cap_bredr("L2CAP BR/EDR Client PIN Code - Success",
+                                       &client_connect_pin_success_test,
+                                       setup_powered_client, test_connect);
+
+       test_l2cap_bredr("L2CAP BR/EDR Client - Read Success",
+                                       &client_connect_read_success_test,
+                                       setup_powered_client, test_connect);
+
+       test_l2cap_bredr("L2CAP BR/EDR Client - Write Success",
+                                       &client_connect_write_success_test,
+                                       setup_powered_client, test_connect);
+
+       test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM 1",
+                                       &client_connect_nval_psm_test_1,
+                                       setup_powered_client, test_connect);
+
+       test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM 2",
+                                       &client_connect_nval_psm_test_2,
+                                       setup_powered_client, test_connect);
+
+       test_l2cap_bredr("L2CAP BR/EDR Client - Invalid PSM 3",
+                                       &client_connect_nval_psm_test_3,
                                        setup_powered_client, test_connect);
 
        test_l2cap_bredr("L2CAP BR/EDR Server - Success",
                                        &l2cap_server_success_test,
                                        setup_powered_server, test_server);
+
+       test_l2cap_bredr("L2CAP BR/EDR Server - Read Success",
+                                       &l2cap_server_read_success_test,
+                                       setup_powered_server, test_server);
+
+       test_l2cap_bredr("L2CAP BR/EDR Server - Write Success",
+                                       &l2cap_server_write_success_test,
+                                       setup_powered_server, test_server);
+
+       test_l2cap_bredr("L2CAP BR/EDR Server - Security Block",
+                                       &l2cap_server_sec_block_test,
+                                       setup_powered_server, test_server);
+
        test_l2cap_bredr("L2CAP BR/EDR Server - Invalid PSM",
                                        &l2cap_server_nval_psm_test,
                                        setup_powered_server, test_server);
@@ -763,13 +1383,27 @@ int main(int argc, char *argv[])
                                setup_powered_server, test_server);
 
        test_l2cap_le("L2CAP LE Client - Success",
-                               &le_client_connect_success_test,
+                               &le_client_connect_success_test_1,
+                               setup_powered_client, test_connect);
+       test_l2cap_le("L2CAP LE Client SMP - Success",
+                               &le_client_connect_success_test_2,
                                setup_powered_client, test_connect);
+       test_l2cap_le("L2CAP LE Client - Command Reject",
+                                       &le_client_connect_reject_test_1,
+                                       setup_powered_client, test_connect);
        test_l2cap_le("L2CAP LE Client - Invalid PSM",
                                        &le_client_connect_nval_psm_test,
                                        setup_powered_client, test_connect);
        test_l2cap_le("L2CAP LE Server - Success", &le_server_success_test,
                                        setup_powered_server, test_server);
 
+
+       test_l2cap_le("L2CAP LE ATT Client - Success",
+                               &le_att_client_connect_success_test_1,
+                               setup_powered_client, test_connect);
+       test_l2cap_le("L2CAP LE ATT Server - Success",
+                               &le_att_server_success_test_1,
+                               setup_powered_server, test_server);
+
        return tester_run();
 }
index 9a7c809..c70bac0 100644 (file)
@@ -47,6 +47,8 @@
 #include <bluetooth/hci_lib.h>
 #include <bluetooth/l2cap.h>
 
+#include "src/shared/util.h"
+
 #define NIBBLE_TO_ASCII(c)  ((c) < 0x0a ? (c) + 0x30 : (c) + 0x57)
 
 #define BREDR_DEFAULT_PSM      0x1011
@@ -287,7 +289,7 @@ static int setopts(int sk, struct l2cap_options *opts)
 {
        if (bdaddr_type == BDADDR_BREDR || cid)
                return setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts,
-                                                               sizeof(opts));
+                                                               sizeof(*opts));
 
        return setsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, &opts->imtu,
                                                        sizeof(opts->imtu));
@@ -782,7 +784,7 @@ static void dump_mode(int sk)
                data_size = imtu;
 
        if (defer_setup) {
-               len = read(sk, buf, sizeof(buf));
+               len = read(sk, buf, data_size);
                if (len < 0)
                        syslog(LOG_ERR, "Initial read error: %s (%d)",
                                                strerror(errno), errno);
@@ -842,7 +844,7 @@ static void recv_mode(int sk)
                data_size = imtu;
 
        if (defer_setup) {
-               len = read(sk, buf, sizeof(buf));
+               len = read(sk, buf, data_size);
                if (len < 0)
                        syslog(LOG_ERR, "Initial read error: %s (%d)",
                                                strerror(errno), errno);
@@ -909,7 +911,7 @@ static void recv_mode(int sk)
                        }
 
                        /* Check sequence */
-                       sq = bt_get_le32(buf);
+                       sq = get_le32(buf);
                        if (seq != sq) {
                                syslog(LOG_INFO, "seq missmatch: %d -> %d", seq, sq);
                                seq = sq;
@@ -917,7 +919,7 @@ static void recv_mode(int sk)
                        seq++;
 
                        /* Check length */
-                       l = bt_get_le16(buf + 4);
+                       l = get_le16(buf + 4);
                        if (len != l) {
                                syslog(LOG_INFO, "size missmatch: %d -> %d", len, l);
                                continue;
@@ -976,8 +978,8 @@ static void do_send(int sk)
 
        seq = 0;
        while ((num_frames == -1) || (num_frames-- > 0)) {
-               bt_put_le32(seq, buf);
-               bt_put_le16(data_size, buf + 4);
+               put_le32(seq, buf);
+               put_le16(data_size, buf + 4);
 
                seq++;
 
index e2fe73d..4cc7ba4 100644 (file)
@@ -26,6 +26,7 @@
 #endif
 
 #include <stdlib.h>
+#include <stdbool.h>
 
 #include <glib.h>
 
@@ -33,7 +34,9 @@
 #include "lib/mgmt.h"
 
 #include "monitor/bt.h"
+#include "emulator/bthost.h"
 
+#include "src/shared/util.h"
 #include "src/shared/tester.h"
 #include "src/shared/mgmt.h"
 #include "src/shared/hciemu.h"
@@ -302,7 +305,7 @@ static void test_condition_complete(struct test_data *data)
                user->test_data = data; \
                user->expected_version = 0x06; \
                user->expected_manufacturer = 0x003f; \
-               user->expected_supported_settings = 0x000007ff; \
+               user->expected_supported_settings = 0x00003fff; \
                user->initial_settings = 0x00000080; \
                user->unmet_conditions = 0; \
                tester_add_full(name, data, \
@@ -321,7 +324,7 @@ static void test_condition_complete(struct test_data *data)
                user->test_data = data; \
                user->expected_version = 0x05; \
                user->expected_manufacturer = 0x003f; \
-               user->expected_supported_settings = 0x000001ff; \
+               user->expected_supported_settings = 0x000011ff; \
                user->initial_settings = 0x00000080; \
                user->unmet_conditions = 0; \
                tester_add_full(name, data, \
@@ -340,7 +343,7 @@ static void test_condition_complete(struct test_data *data)
                user->test_data = data; \
                user->expected_version = 0x06; \
                user->expected_manufacturer = 0x003f; \
-               user->expected_supported_settings = 0x00000611; \
+               user->expected_supported_settings = 0x00003611; \
                user->initial_settings = 0x00000200; \
                user->unmet_conditions = 0; \
                tester_add_full(name, data, \
@@ -360,13 +363,18 @@ struct generic_data {
        uint16_t setup_expect_hci_command;
        const void *setup_expect_hci_param;
        uint8_t setup_expect_hci_len;
+       uint16_t setup_send_opcode;
+       const void *setup_send_param;
+       uint16_t setup_send_len;
        bool send_index_none;
        uint16_t send_opcode;
        const void *send_param;
        uint16_t send_len;
+       const void * (*send_func)(uint16_t *len);
        uint8_t expect_status;
        const void *expect_param;
        uint16_t expect_len;
+       const void * (*expect_func)(uint16_t *len);
        uint32_t expect_settings_set;
        uint32_t expect_settings_unset;
        uint16_t expect_alt_ev;
@@ -375,6 +383,18 @@ struct generic_data {
        uint16_t expect_hci_command;
        const void *expect_hci_param;
        uint8_t expect_hci_len;
+       const void * (*expect_hci_func)(uint8_t *len);
+       bool expect_pin;
+       uint8_t pin_len;
+       const void *pin;
+       uint8_t client_pin_len;
+       const void *client_pin;
+       bool client_enable_ssp;
+       uint8_t io_cap;
+       uint8_t client_io_cap;
+       uint8_t client_auth_req;
+       bool reject_ssp;
+       bool client_reject_ssp;
 };
 
 static const char dummy_data[] = { 0x00 };
@@ -728,7 +748,7 @@ static uint8_t set_connectable_off_adv_param[] = {
                0x00, 0x08,                             /* min_interval */
                0x00, 0x08,                             /* max_interval */
                0x03,                                   /* type */
-               0x00,                                   /* own_addr_type */
+               0x01,                                   /* own_addr_type */
                0x00,                                   /* direct_addr_type */
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00,     /* direct_addr */
                0x07,                                   /* channel_map */
@@ -1235,6 +1255,114 @@ static const struct generic_data set_ssp_on_invalid_index_test = {
        .expect_status = MGMT_STATUS_INVALID_INDEX,
 };
 
+static uint16_t settings_powered_ssp[] = { MGMT_OP_SET_SSP,
+                                               MGMT_OP_SET_POWERED, 0 };
+
+static const char set_sc_on_param[] = { 0x01 };
+static const char set_sc_only_on_param[] = { 0x02 };
+static const char set_sc_invalid_param[] = { 0x03 };
+static const char set_sc_garbage_param[] = { 0x01, 0x00 };
+static const char set_sc_settings_param_1[] = { 0xc0, 0x08, 0x00, 0x00 };
+static const char set_sc_settings_param_2[] = { 0xc1, 0x08, 0x00, 0x00 };
+static const char set_sc_on_write_sc_support_param[] = { 0x01 };
+
+static const struct generic_data set_sc_on_success_test_1 = {
+       .setup_settings = settings_ssp,
+       .send_opcode = MGMT_OP_SET_SECURE_CONN,
+       .send_param = set_sc_on_param,
+       .send_len = sizeof(set_sc_on_param),
+       .expect_status = MGMT_STATUS_SUCCESS,
+       .expect_param = set_sc_settings_param_1,
+       .expect_len = sizeof(set_sc_settings_param_1),
+       .expect_settings_set = MGMT_SETTING_SECURE_CONN,
+};
+
+static const struct generic_data set_sc_on_success_test_2 = {
+       .setup_settings = settings_powered_ssp,
+       .send_opcode = MGMT_OP_SET_SECURE_CONN,
+       .send_param = set_sc_on_param,
+       .send_len = sizeof(set_sc_on_param),
+       .expect_status = MGMT_STATUS_SUCCESS,
+       .expect_param = set_sc_settings_param_2,
+       .expect_len = sizeof(set_sc_settings_param_2),
+       .expect_settings_set = MGMT_SETTING_SECURE_CONN,
+       .expect_hci_command = BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT,
+       .expect_hci_param = set_sc_on_write_sc_support_param,
+       .expect_hci_len = sizeof(set_sc_on_write_sc_support_param),
+};
+
+static const struct generic_data set_sc_on_invalid_param_test_1 = {
+       .setup_settings = settings_ssp,
+       .send_opcode = MGMT_OP_SET_SECURE_CONN,
+       .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_sc_on_invalid_param_test_2 = {
+       .setup_settings = settings_ssp,
+       .send_opcode = MGMT_OP_SET_SECURE_CONN,
+       .send_param = set_sc_invalid_param,
+       .send_len = sizeof(set_sc_invalid_param),
+       .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_sc_on_invalid_param_test_3 = {
+       .setup_settings = settings_ssp,
+       .send_opcode = MGMT_OP_SET_SECURE_CONN,
+       .send_param = set_sc_garbage_param,
+       .send_len = sizeof(set_sc_garbage_param),
+       .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data set_sc_on_invalid_index_test = {
+       .setup_settings = settings_ssp,
+       .send_index_none = true,
+       .send_opcode = MGMT_OP_SET_SECURE_CONN,
+       .send_param = set_sc_on_param,
+       .send_len = sizeof(set_sc_on_param),
+       .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data set_sc_on_not_supported_test_1 = {
+       .setup_settings = settings_ssp,
+       .send_opcode = MGMT_OP_SET_SECURE_CONN,
+       .send_param = set_sc_on_param,
+       .send_len = sizeof(set_sc_on_param),
+       .expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const struct generic_data set_sc_on_not_supported_test_2 = {
+       .setup_settings = settings_powered_ssp,
+       .send_opcode = MGMT_OP_SET_SECURE_CONN,
+       .send_param = set_sc_on_param,
+       .send_len = sizeof(set_sc_on_param),
+       .expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const struct generic_data set_sc_only_on_success_test_1 = {
+       .setup_settings = settings_ssp,
+       .send_opcode = MGMT_OP_SET_SECURE_CONN,
+       .send_param = set_sc_only_on_param,
+       .send_len = sizeof(set_sc_only_on_param),
+       .expect_status = MGMT_STATUS_SUCCESS,
+       .expect_param = set_sc_settings_param_1,
+       .expect_len = sizeof(set_sc_settings_param_1),
+       .expect_settings_set = MGMT_SETTING_SECURE_CONN,
+};
+
+static const struct generic_data set_sc_only_on_success_test_2 = {
+       .setup_settings = settings_powered_ssp,
+       .send_opcode = MGMT_OP_SET_SECURE_CONN,
+       .send_param = set_sc_only_on_param,
+       .send_len = sizeof(set_sc_only_on_param),
+       .expect_status = MGMT_STATUS_SUCCESS,
+       .expect_param = set_sc_settings_param_2,
+       .expect_len = sizeof(set_sc_settings_param_2),
+       .expect_settings_set = MGMT_SETTING_SECURE_CONN,
+       .expect_hci_command = BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT,
+       .expect_hci_param = set_sc_on_write_sc_support_param,
+       .expect_hci_len = sizeof(set_sc_on_write_sc_support_param),
+};
+
 static const char set_hs_on_param[] = { 0x01 };
 static const char set_hs_invalid_param[] = { 0x02 };
 static const char set_hs_garbage_param[] = { 0x01, 0x00 };
@@ -1470,9 +1598,6 @@ static const struct generic_data set_bredr_off_failure_test_3 = {
        .expect_status = MGMT_STATUS_INVALID_PARAMS,
 };
 
-static uint16_t settings_powered_ssp[] = { MGMT_OP_SET_SSP,
-                                               MGMT_OP_SET_POWERED, 0 };
-
 static const char set_local_name_param[260] = { 'T', 'e', 's', 't', ' ',
                                                'n', 'a', 'm', 'e' };
 static const char write_local_name_hci[248] = { 'T', 'e', 's', 't', ' ',
@@ -1598,6 +1723,9 @@ static const char stop_discovery_inq_param[] = { 0x33, 0x8b, 0x9e, 0x08, 0x00 };
 
 static const struct generic_data stop_discovery_success_test_1 = {
        .setup_settings = settings_powered_le,
+       .setup_send_opcode = MGMT_OP_START_DISCOVERY,
+       .setup_send_param = start_discovery_bredrle_param,
+       .setup_send_len = sizeof(start_discovery_bredrle_param),
        .send_opcode = MGMT_OP_STOP_DISCOVERY,
        .send_param = stop_discovery_bredrle_param,
        .send_len = sizeof(stop_discovery_bredrle_param),
@@ -1613,10 +1741,10 @@ static const struct generic_data stop_discovery_success_test_1 = {
 };
 
 static const struct generic_data stop_discovery_bredr_success_test_1 = {
-       .setup_settings = settings_powered_le,
-       .setup_expect_hci_command = BT_HCI_CMD_INQUIRY,
-       .setup_expect_hci_param = stop_discovery_inq_param,
-       .setup_expect_hci_len = sizeof(stop_discovery_inq_param),
+       .setup_settings = settings_powered,
+       .setup_send_opcode = MGMT_OP_START_DISCOVERY,
+       .setup_send_param = start_discovery_bredr_param,
+       .setup_send_len = sizeof(start_discovery_bredr_param),
        .send_opcode = MGMT_OP_STOP_DISCOVERY,
        .send_param = stop_discovery_bredr_param,
        .send_len = sizeof(stop_discovery_bredr_param),
@@ -1641,6 +1769,9 @@ static const struct generic_data stop_discovery_rejected_test_1 = {
 
 static const struct generic_data stop_discovery_invalid_param_test_1 = {
        .setup_settings = settings_powered_le,
+       .setup_send_opcode = MGMT_OP_START_DISCOVERY,
+       .setup_send_param = start_discovery_bredrle_param,
+       .setup_send_len = sizeof(start_discovery_bredrle_param),
        .send_opcode = MGMT_OP_STOP_DISCOVERY,
        .send_param = stop_discovery_bredrle_invalid_param,
        .send_len = sizeof(stop_discovery_bredrle_invalid_param),
@@ -2037,33 +2168,20 @@ static const char load_ltks_invalid_param_2[] = {
        0x00, 0x00,                                     /* diversifier */
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* rand */
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (1/2) */
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2 */
-};
-/* Invalid authenticated value */
-static const char load_ltks_invalid_param_3[] = {
-       0x01, 0x00,                                     /* count */
-       0x00, 0x01, 0x02, 0x03, 0x04, 0x05,             /* bdaddr */
-       0x01,                                           /* addr type */
-       0x02,                                           /* authenticated */
-       0x00,                                           /* master */
-       0x00,                                           /* encryption size */
-       0x00, 0x00,                                     /* diversifier */
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* rand */
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (1/2) */
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2) */
 };
 /* Invalid master value */
-static const char load_ltks_invalid_param_4[] = {
+static const char load_ltks_invalid_param_3[] = {
        0x01, 0x00,                                     /* count */
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05,             /* bdaddr */
        0x01,                                           /* addr type */
-       0x00,                                           /* authunticated */
+       0x00,                                           /* authenticated */
        0x02,                                           /* master */
        0x00,                                           /* encryption size */
        0x00, 0x00,                                     /* diversifier */
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* rand */
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (1/2) */
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* value (2/2) */
 };
 
 static const struct generic_data load_ltks_success_test_1 = {
@@ -2094,13 +2212,6 @@ static const struct generic_data load_ltks_invalid_params_test_3 = {
        .expect_status = MGMT_STATUS_INVALID_PARAMS,
 };
 
-static const struct generic_data load_ltks_invalid_params_test_4 = {
-       .send_opcode = MGMT_OP_LOAD_LONG_TERM_KEYS,
-       .send_param = load_ltks_invalid_param_4,
-       .send_len = sizeof(load_ltks_invalid_param_4),
-       .expect_status = MGMT_STATUS_INVALID_PARAMS,
-};
-
 static const char pair_device_param[] = {
                        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 };
 static const char pair_device_rsp[] = {
@@ -2128,6 +2239,301 @@ static const struct generic_data pair_device_invalid_param_test_1 = {
        .expect_len = sizeof(pair_device_invalid_param_rsp_1),
 };
 
+static const void *pair_device_send_param_func(uint16_t *len)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       static uint8_t param[8];
+
+       memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+       param[6] = 0x00; /* Address type */
+       param[7] = test->io_cap;
+
+       *len = sizeof(param);
+
+       return param;
+}
+
+static const void *pair_device_expect_param_func(uint16_t *len)
+{
+       struct test_data *data = tester_get_data();
+       static uint8_t param[7];
+
+       memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
+       param[6] = 0x00; /* Address type */
+
+       *len = sizeof(param);
+
+       return param;
+}
+
+static uint16_t settings_powered_pairable[] = { MGMT_OP_SET_PAIRABLE,
+                                               MGMT_OP_SET_POWERED, 0 };
+static uint8_t auth_req_param[] = { 0x2a, 0x00 };
+static uint8_t pair_device_pin[] = { 0x30, 0x30, 0x30, 0x30 }; /* "0000" */
+
+static const struct generic_data pair_device_success_test_1 = {
+       .setup_settings = settings_powered_pairable,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_SUCCESS,
+       .expect_func = pair_device_expect_param_func,
+       .expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+       .expect_alt_ev_len = 26,
+       .expect_hci_command = BT_HCI_CMD_AUTH_REQUESTED,
+       .expect_hci_param = auth_req_param,
+       .expect_hci_len = sizeof(auth_req_param),
+       .pin = pair_device_pin,
+       .pin_len = sizeof(pair_device_pin),
+       .client_pin = pair_device_pin,
+       .client_pin_len = sizeof(pair_device_pin),
+};
+
+static uint16_t settings_powered_pairable_linksec[] = { MGMT_OP_SET_PAIRABLE,
+                                                       MGMT_OP_SET_POWERED,
+                                                       MGMT_OP_SET_LINK_SECURITY,
+                                                       0 };
+
+static const struct generic_data pair_device_success_test_2 = {
+       .setup_settings = settings_powered_pairable_linksec,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_SUCCESS,
+       .expect_func = pair_device_expect_param_func,
+       .expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+       .expect_alt_ev_len = 26,
+       .expect_hci_command = BT_HCI_CMD_AUTH_REQUESTED,
+       .expect_hci_param = auth_req_param,
+       .expect_hci_len = sizeof(auth_req_param),
+       .pin = pair_device_pin,
+       .pin_len = sizeof(pair_device_pin),
+       .client_pin = pair_device_pin,
+       .client_pin_len = sizeof(pair_device_pin),
+};
+
+static const void *client_bdaddr_param_func(uint8_t *len)
+{
+       struct test_data *data = tester_get_data();
+       static uint8_t bdaddr[6];
+
+       memcpy(bdaddr, hciemu_get_client_bdaddr(data->hciemu), 6);
+
+       *len = sizeof(bdaddr);
+
+       return bdaddr;
+}
+
+static const struct generic_data pair_device_reject_test_1 = {
+       .setup_settings = settings_powered_pairable,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_AUTH_FAILED,
+       .expect_func = pair_device_expect_param_func,
+       .expect_alt_ev = MGMT_EV_AUTH_FAILED,
+       .expect_alt_ev_len = 8,
+       .expect_hci_command = BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY,
+       .expect_hci_func = client_bdaddr_param_func,
+       .expect_pin = true,
+       .client_pin = pair_device_pin,
+       .client_pin_len = sizeof(pair_device_pin),
+};
+
+static const struct generic_data pair_device_reject_test_2 = {
+       .setup_settings = settings_powered_pairable,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_AUTH_FAILED,
+       .expect_func = pair_device_expect_param_func,
+       .expect_alt_ev = MGMT_EV_AUTH_FAILED,
+       .expect_alt_ev_len = 8,
+       .expect_hci_command = BT_HCI_CMD_AUTH_REQUESTED,
+       .expect_hci_param = auth_req_param,
+       .expect_hci_len = sizeof(auth_req_param),
+       .pin = pair_device_pin,
+       .pin_len = sizeof(pair_device_pin),
+};
+
+static const struct generic_data pair_device_reject_test_3 = {
+       .setup_settings = settings_powered_pairable_linksec,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_AUTH_FAILED,
+       .expect_func = pair_device_expect_param_func,
+       .expect_hci_command = BT_HCI_CMD_PIN_CODE_REQUEST_NEG_REPLY,
+       .expect_hci_func = client_bdaddr_param_func,
+       .expect_pin = true,
+       .client_pin = pair_device_pin,
+       .client_pin_len = sizeof(pair_device_pin),
+};
+
+static const struct generic_data pair_device_reject_test_4 = {
+       .setup_settings = settings_powered_pairable_linksec,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_AUTH_FAILED,
+       .expect_func = pair_device_expect_param_func,
+       .pin = pair_device_pin,
+       .pin_len = sizeof(pair_device_pin),
+};
+
+static uint16_t settings_powered_pairable_ssp[] = {    MGMT_OP_SET_PAIRABLE,
+                                                       MGMT_OP_SET_SSP,
+                                                       MGMT_OP_SET_POWERED,
+                                                       0 };
+
+static const struct generic_data pair_device_ssp_test_1 = {
+       .setup_settings = settings_powered_pairable_ssp,
+       .client_enable_ssp = true,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_SUCCESS,
+       .expect_func = pair_device_expect_param_func,
+       .expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+       .expect_alt_ev_len = 26,
+       .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+       .expect_hci_func = client_bdaddr_param_func,
+       .io_cap = 0x03, /* NoInputNoOutput */
+       .client_io_cap = 0x03, /* NoInputNoOutput */
+};
+
+static const struct generic_data pair_device_ssp_test_2 = {
+       .setup_settings = settings_powered_pairable_ssp,
+       .client_enable_ssp = true,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_SUCCESS,
+       .expect_func = pair_device_expect_param_func,
+       .expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+       .expect_alt_ev_len = 26,
+       .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+       .expect_hci_func = client_bdaddr_param_func,
+       .io_cap = 0x01, /* DisplayYesNo */
+       .client_io_cap = 0x01, /* DisplayYesNo */
+};
+
+static const struct generic_data pair_device_ssp_reject_1 = {
+       .setup_settings = settings_powered_pairable_ssp,
+       .client_enable_ssp = true,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_AUTH_FAILED,
+       .expect_func = pair_device_expect_param_func,
+       .expect_alt_ev = MGMT_EV_AUTH_FAILED,
+       .expect_alt_ev_len = 8,
+       .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_NEG_REPLY,
+       .expect_hci_func = client_bdaddr_param_func,
+       .io_cap = 0x01, /* DisplayYesNo */
+       .client_io_cap = 0x01, /* DisplayYesNo */
+       .client_auth_req = 0x01, /* No Bonding - MITM */
+       .reject_ssp = true,
+};
+
+static const struct generic_data pair_device_ssp_reject_2 = {
+       .setup_settings = settings_powered_pairable_ssp,
+       .client_enable_ssp = true,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_AUTH_FAILED,
+       .expect_func = pair_device_expect_param_func,
+       .expect_alt_ev = MGMT_EV_AUTH_FAILED,
+       .expect_alt_ev_len = 8,
+       .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+       .expect_hci_func = client_bdaddr_param_func,
+       .io_cap = 0x01, /* DisplayYesNo */
+       .client_io_cap = 0x01, /* DisplayYesNo */
+       .client_reject_ssp = true,
+};
+
+static const struct generic_data pair_device_ssp_nonpairable_1 = {
+       .setup_settings = settings_powered_ssp,
+       .client_enable_ssp = true,
+       .send_opcode = MGMT_OP_PAIR_DEVICE,
+       .send_func = pair_device_send_param_func,
+       .expect_status = MGMT_STATUS_AUTH_FAILED,
+       .expect_func = pair_device_expect_param_func,
+       .expect_alt_ev = MGMT_EV_AUTH_FAILED,
+       .expect_alt_ev_len = 8,
+       .io_cap = 0x01, /* DisplayYesNo */
+       .client_io_cap = 0x01, /* DisplayYesNo */
+};
+
+static uint16_t settings_powered_connectable_pairable[] = {
+                                               MGMT_OP_SET_PAIRABLE,
+                                               MGMT_OP_SET_CONNECTABLE,
+                                               MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pairing_acceptor_legacy_1 = {
+       .setup_settings = settings_powered_connectable_pairable,
+       .pin = pair_device_pin,
+       .pin_len = sizeof(pair_device_pin),
+       .client_pin = pair_device_pin,
+       .client_pin_len = sizeof(pair_device_pin),
+       .expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+       .expect_alt_ev_len = 26,
+};
+
+static const struct generic_data pairing_acceptor_legacy_2 = {
+       .setup_settings = settings_powered_connectable_pairable,
+       .expect_pin = true,
+       .client_pin = pair_device_pin,
+       .client_pin_len = sizeof(pair_device_pin),
+       .expect_alt_ev = MGMT_EV_AUTH_FAILED,
+       .expect_alt_ev_len = 8,
+};
+
+static uint16_t settings_powered_connectable_pairable_linksec[] = {
+                                               MGMT_OP_SET_PAIRABLE,
+                                               MGMT_OP_SET_CONNECTABLE,
+                                               MGMT_OP_SET_LINK_SECURITY,
+                                               MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pairing_acceptor_linksec_1 = {
+       .setup_settings = settings_powered_connectable_pairable_linksec,
+       .pin = pair_device_pin,
+       .pin_len = sizeof(pair_device_pin),
+       .client_pin = pair_device_pin,
+       .client_pin_len = sizeof(pair_device_pin),
+       .expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+       .expect_alt_ev_len = 26,
+};
+
+static const struct generic_data pairing_acceptor_linksec_2 = {
+       .setup_settings = settings_powered_connectable_pairable_linksec,
+       .expect_pin = true,
+       .client_pin = pair_device_pin,
+       .client_pin_len = sizeof(pair_device_pin),
+       .expect_alt_ev = MGMT_EV_CONNECT_FAILED,
+       .expect_alt_ev_len = 8,
+};
+
+static uint16_t settings_powered_connectable_pairable_ssp[] = {
+                                               MGMT_OP_SET_PAIRABLE,
+                                               MGMT_OP_SET_CONNECTABLE,
+                                               MGMT_OP_SET_SSP,
+                                               MGMT_OP_SET_POWERED, 0 };
+
+static const struct generic_data pairing_acceptor_ssp_1 = {
+       .setup_settings = settings_powered_connectable_pairable_ssp,
+       .client_enable_ssp = true,
+       .expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+       .expect_alt_ev_len = 26,
+       .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+       .expect_hci_func = client_bdaddr_param_func,
+       .io_cap = 0x03, /* NoInputNoOutput */
+       .client_io_cap = 0x03, /* NoInputNoOutput */
+};
+
+static const struct generic_data pairing_acceptor_ssp_2 = {
+       .setup_settings = settings_powered_connectable_pairable_ssp,
+       .client_enable_ssp = true,
+       .expect_alt_ev = MGMT_EV_NEW_LINK_KEY,
+       .expect_alt_ev_len = 26,
+       .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+       .expect_hci_func = client_bdaddr_param_func,
+       .io_cap = 0x01, /* DisplayYesNo */
+       .client_io_cap = 0x01, /* DisplayYesNo */
+};
+
 static const char unpair_device_param[] = {
                        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00, 0x00 };
 static const char unpair_device_rsp[] = {
@@ -2237,6 +2643,160 @@ static const struct generic_data set_scan_params_success_test = {
        .expect_status = MGMT_STATUS_SUCCESS,
 };
 
+static const char load_irks_empty_list[] = { 0x00, 0x00 };
+
+static const struct generic_data load_irks_success1_test = {
+       .send_opcode = MGMT_OP_LOAD_IRKS,
+       .send_param = load_irks_empty_list,
+       .send_len = sizeof(load_irks_empty_list),
+       .expect_status = MGMT_STATUS_SUCCESS,
+};
+
+static const char load_irks_one_irk[] = { 0x01, 0x00,
+                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x01,
+                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+static const struct generic_data load_irks_success2_test = {
+       .send_opcode = MGMT_OP_LOAD_IRKS,
+       .send_param = load_irks_one_irk,
+       .send_len = sizeof(load_irks_one_irk),
+       .expect_status = MGMT_STATUS_SUCCESS,
+};
+
+static const char load_irks_nval_addr_type[] = { 0x01, 0x00,
+                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00,
+                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+static const struct generic_data load_irks_nval_param1_test = {
+       .send_opcode = MGMT_OP_LOAD_IRKS,
+       .send_param = load_irks_nval_addr_type,
+       .send_len = sizeof(load_irks_nval_addr_type),
+       .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char load_irks_nval_rand_addr[] = { 0x01, 0x00,
+                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x02,
+                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+static const struct generic_data load_irks_nval_param2_test = {
+       .send_opcode = MGMT_OP_LOAD_IRKS,
+       .send_param = load_irks_nval_rand_addr,
+       .send_len = sizeof(load_irks_nval_rand_addr),
+       .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const char load_irks_nval_len[] = { 0x02, 0x00, 0xff, 0xff };
+
+static const struct generic_data load_irks_nval_param3_test = {
+       .send_opcode = MGMT_OP_LOAD_IRKS,
+       .send_param = load_irks_nval_len,
+       .send_len = sizeof(load_irks_nval_len),
+       .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data load_irks_not_supported_test = {
+       .send_opcode = MGMT_OP_LOAD_IRKS,
+       .send_param = load_irks_empty_list,
+       .send_len = sizeof(load_irks_empty_list),
+       .expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const char set_privacy_valid_param[] = { 0x01,
+                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+static const char set_privacy_settings_param[] = { 0x80, 0x20, 0x00, 0x00 };
+
+static const struct generic_data set_privacy_success_test = {
+       .send_opcode = MGMT_OP_SET_PRIVACY,
+       .send_param = set_privacy_valid_param,
+       .send_len = sizeof(set_privacy_valid_param),
+       .expect_status = MGMT_STATUS_SUCCESS,
+       .expect_param = set_privacy_settings_param,
+       .expect_len = sizeof(set_privacy_settings_param),
+       .expect_settings_set = MGMT_SETTING_PRIVACY,
+};
+
+static const struct generic_data set_privacy_powered_test = {
+       .setup_settings = settings_powered,
+       .send_opcode = MGMT_OP_SET_PRIVACY,
+       .send_param = set_privacy_valid_param,
+       .send_len = sizeof(set_privacy_valid_param),
+       .expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const char set_privacy_nval_param[] = { 0xff,
+                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+static const struct generic_data set_privacy_nval_param_test = {
+       .send_opcode = MGMT_OP_SET_PRIVACY,
+       .send_param = set_privacy_nval_param,
+       .send_len = sizeof(set_privacy_nval_param),
+       .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static void client_cmd_complete(uint16_t opcode, uint8_t status,
+                                       const void *param, uint8_t len,
+                                       void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       struct bthost *bthost;
+
+       bthost = hciemu_client_get_host(data->hciemu);
+
+       switch (opcode) {
+       case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+       case BT_HCI_CMD_LE_SET_ADV_ENABLE:
+               tester_print("Client set connectable status 0x%02x", status);
+               if (!status && test->client_enable_ssp) {
+                       bthost_write_ssp_mode(bthost, 0x01);
+                       return;
+               }
+               break;
+       case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+               tester_print("Client enable SSP status 0x%02x", status);
+               break;
+       default:
+               return;
+       }
+
+       if (status)
+               tester_setup_failed();
+       else
+               tester_setup_complete();
+}
+
+static void setup_bthost(void)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost;
+
+       bthost = hciemu_client_get_host(data->hciemu);
+       bthost_set_cmd_complete_cb(bthost, client_cmd_complete, data);
+       if (data->hciemu_type == HCIEMU_TYPE_LE)
+               bthost_set_adv_enable(bthost, 0x01);
+       else
+               bthost_write_scan_enable(bthost, 0x03);
+}
+
+static void setup_ssp_acceptor(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+
+       if (!test->io_cap)
+               return;
+
+       mgmt_send(data->mgmt, MGMT_OP_SET_IO_CAPABILITY, data->mgmt_index,
+                                       sizeof(test->io_cap), &test->io_cap,
+                                       NULL, NULL, NULL);
+
+       setup_bthost();
+}
+
 static void setup_powered_callback(uint8_t status, uint16_t length,
                                        const void *param, void *user_data)
 {
@@ -2247,7 +2807,7 @@ static void setup_powered_callback(uint8_t status, uint16_t length,
 
        tester_print("Controller powered on");
 
-       tester_setup_complete();
+       setup_bthost();
 }
 
 static void setup_class(const void *test_data)
@@ -2279,57 +2839,16 @@ static void setup_discovery_callback(uint8_t status, uint16_t length,
        tester_setup_complete();
 }
 
-static bool setup_command_hci_callback(const void *data, uint16_t len,
-                                                               void *user_data)
-{
-       struct test_data *tdata = tester_get_data();
-       const struct generic_data *test = tdata->test_data;
-
-       tester_print("HCI Command 0x%04x length %u (setup)",
-                                       test->setup_expect_hci_command, len);
-
-       if (len != test->setup_expect_hci_len) {
-               tester_warn("Invalid parameter size for HCI command (setup)");
-               tester_setup_failed();
-               goto done;
-       }
-
-       if (memcmp(data, test->setup_expect_hci_param, len) != 0) {
-               tester_warn("Unexpected HCI command parameter value (setup)");
-               tester_setup_failed();
-               goto done;
-       }
-
-       tester_setup_complete();
-
-done:
-       hciemu_del_hook(tdata->hciemu, HCIEMU_HOOK_PRE_EVT,
-                       test->setup_expect_hci_command);
-
-       return false;
-}
-
 static void setup_start_discovery(const void *test_data)
 {
        struct test_data *data = tester_get_data();
        const struct generic_data *test = data->test_data;
+       const void *send_param = test->setup_send_param;
+       uint16_t send_len = test->setup_send_len;
 
-       if (test->setup_expect_hci_command) {
-               tester_print("Registering HCI command callback (setup)");
-               hciemu_add_hook(data->hciemu, HCIEMU_HOOK_PRE_EVT,
-                               test->setup_expect_hci_command,
-                               setup_command_hci_callback,
-                               NULL);
-               mgmt_send(data->mgmt, MGMT_OP_START_DISCOVERY, data->mgmt_index,
-                               test->send_len, test->send_param,
-                               NULL, NULL, NULL);
-       } else {
-               unsigned char disc_param[] = { 0x07 };
-
-               mgmt_send(data->mgmt, MGMT_OP_START_DISCOVERY, data->mgmt_index,
-                                       sizeof(disc_param), disc_param,
-                                       setup_discovery_callback, NULL, NULL);
-       }
+       mgmt_send(data->mgmt, test->setup_send_opcode, data->mgmt_index,
+                               send_len, send_param, setup_discovery_callback,
+                               NULL, NULL);
 }
 
 static void setup_multi_uuid32(const void *test_data)
@@ -2531,15 +3050,93 @@ static void setup_complete(uint8_t status, uint16_t length,
        if (data->test_setup)
                data->test_setup(data);
        else
-               tester_setup_complete();
+               setup_bthost();
+}
+
+static void pin_code_request_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       const struct mgmt_ev_pin_code_request *ev = param;
+       struct test_data *data = user_data;
+       const struct generic_data *test = data->test_data;
+       struct mgmt_cp_pin_code_reply cp;
+
+       test_condition_complete(data);
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+
+       if (!test->pin) {
+               mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY,
+                               data->mgmt_index, sizeof(cp.addr), &cp.addr,
+                               NULL, NULL, NULL);
+               return;
+       }
+
+       cp.pin_len = test->pin_len;
+       memcpy(cp.pin_code, test->pin, test->pin_len);
+
+       mgmt_reply(data->mgmt, MGMT_OP_PIN_CODE_REPLY, data->mgmt_index,
+                       sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void user_confirm_request_callback(uint16_t index, uint16_t length,
+                                                       const void *param,
+                                                       void *user_data)
+{
+       const struct mgmt_ev_user_confirm_request *ev = param;
+       struct test_data *data = user_data;
+       const struct generic_data *test = data->test_data;
+       struct mgmt_cp_user_confirm_reply cp;
+       uint16_t opcode;
+
+       memset(&cp, 0, sizeof(cp));
+       memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+
+       if (test->reject_ssp)
+               opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+       else
+               opcode = MGMT_OP_USER_CONFIRM_REPLY;
+
+       mgmt_reply(data->mgmt, opcode, data->mgmt_index, sizeof(cp), &cp,
+                                                       NULL, NULL, NULL);
 }
 
 static void test_setup(const void *test_data)
 {
        struct test_data *data = tester_get_data();
        const struct generic_data *test = data->test_data;
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
        const uint16_t *cmd;
 
+       if (!test)
+               goto proceed;
+
+       if (test->pin || test->expect_pin) {
+               mgmt_register(data->mgmt, MGMT_EV_PIN_CODE_REQUEST,
+                               data->mgmt_index, pin_code_request_callback,
+                               data, NULL);
+               test_add_condition(data);
+       }
+
+       mgmt_register(data->mgmt, MGMT_EV_USER_CONFIRM_REQUEST,
+                       data->mgmt_index, user_confirm_request_callback,
+                       data, NULL);
+
+       if (test->client_pin)
+               bthost_set_pin_code(bthost, test->client_pin,
+                                                       test->client_pin_len);
+
+       if (test->client_io_cap)
+               bthost_set_io_capability(bthost, test->client_io_cap);
+
+       if (test->client_auth_req)
+               bthost_set_auth_req(bthost, test->client_auth_req);
+
+       if (test->client_reject_ssp)
+               bthost_set_reject_user_confirm(bthost, true);
+
+proceed:
        if (!test || !test->setup_settings) {
                if (data->test_setup)
                        data->test_setup(data);
@@ -2569,14 +3166,16 @@ static void test_setup(const void *test_data)
                        param = discov_param;
                }
 
-               mgmt_send(data->mgmt, *cmd, data->mgmt_index,
-                               param_size, param, func, data, NULL);
-
                if (*cmd == MGMT_OP_SET_LE && test->setup_nobredr) {
                        unsigned char off[] = { 0x00 };
+                       mgmt_send(data->mgmt, *cmd, data->mgmt_index,
+                                       param_size, param, NULL, NULL, NULL);
                        mgmt_send(data->mgmt, MGMT_OP_SET_BREDR,
                                        data->mgmt_index, sizeof(off), off,
-                                       NULL, data, NULL);
+                                       func, data, NULL);
+               } else {
+                       mgmt_send(data->mgmt, *cmd, data->mgmt_index,
+                                       param_size, param, func, data, NULL);
                }
        }
 }
@@ -2606,7 +3205,7 @@ static void command_generic_new_settings_alt(uint16_t index, uint16_t length,
                return;
        }
 
-       settings = bt_get_le32(param);
+       settings = get_le32(param);
 
        tester_print("New settings 0x%08x received", settings);
 
@@ -2646,7 +3245,8 @@ static void command_generic_event_alt(uint16_t index, uint16_t length,
 
        tester_print("New %s event received", mgmt_evstr(test->expect_alt_ev));
 
-       if (memcmp(param, test->expect_alt_ev_param,
+       if (test->expect_alt_ev_param &&
+                       memcmp(param, test->expect_alt_ev_param,
                                                test->expect_alt_ev_len) != 0)
                return;
 
@@ -2663,6 +3263,8 @@ static void command_generic_callback(uint8_t status, uint16_t length,
 {
        struct test_data *data = tester_get_data();
        const struct generic_data *test = data->test_data;
+       const void *expect_param = test->expect_param;
+       uint16_t expect_len = test->expect_len;
 
        tester_print("Command 0x%04x finished with status 0x%02x",
                                                test->send_opcode, status);
@@ -2672,13 +3274,16 @@ static void command_generic_callback(uint8_t status, uint16_t length,
                return;
        }
 
-       if (length != test->expect_len) {
+       if (test->expect_func)
+               expect_param = test->expect_func(&expect_len);
+
+       if (length != expect_len) {
                tester_test_failed();
                return;
        }
 
-       if (test->expect_param && test->expect_len > 0 &&
-                               memcmp(param, test->expect_param, length)) {
+       if (expect_param && expect_len > 0 &&
+                               memcmp(param, expect_param, length)) {
                tester_test_failed();
                return;
        }
@@ -2691,19 +3296,24 @@ static void command_hci_callback(uint16_t opcode, const void *param,
 {
        struct test_data *data = user_data;
        const struct generic_data *test = data->test_data;
+       const void *expect_hci_param = test->expect_hci_param;
+       uint8_t expect_hci_len = test->expect_hci_len;
 
        tester_print("HCI Command 0x%04x length %u", opcode, length);
 
        if (opcode != test->expect_hci_command)
                return;
 
-       if (length != test->expect_hci_len) {
+       if (test->expect_hci_func)
+               expect_hci_param = test->expect_hci_func(&expect_hci_len);
+
+       if (length != expect_hci_len) {
                tester_warn("Invalid parameter size for HCI command");
                tester_test_failed();
                return;
        }
 
-       if (memcmp(param, test->expect_hci_param, length) != 0) {
+       if (memcmp(param, expect_hci_param, length) != 0) {
                tester_warn("Unexpected HCI command parameter value");
                tester_test_failed();
                return;
@@ -2716,6 +3326,8 @@ static void test_command_generic(const void *test_data)
 {
        struct test_data *data = tester_get_data();
        const struct generic_data *test = data->test_data;
+       const void *send_param = test->send_param;
+       uint16_t send_len = test->send_len;
        unsigned int id;
        uint16_t index;
 
@@ -2752,12 +3364,64 @@ static void test_command_generic(const void *test_data)
 
        tester_print("Sending command 0x%04x", test->send_opcode);
 
-       mgmt_send(data->mgmt, test->send_opcode, index,
-                                       test->send_len, test->send_param,
+       if (test->send_func)
+               send_param = test->send_func(&send_len);
+
+       mgmt_send(data->mgmt, test->send_opcode, index, send_len, send_param,
                                        command_generic_callback, NULL, NULL);
        test_add_condition(data);
 }
 
+static void pairing_new_conn(uint16_t handle, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost;
+
+       tester_print("New connection with handle 0x%04x", handle);
+
+       bthost = hciemu_client_get_host(data->hciemu);
+
+       bthost_request_auth(bthost, handle);
+}
+
+static void test_pairing_acceptor(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct generic_data *test = data->test_data;
+       const uint8_t *master_bdaddr;
+       struct bthost *bthost;
+       uint8_t addr_type;
+
+       if (test->expect_alt_ev) {
+               unsigned int id;
+
+               tester_print("Registering %s notification",
+                                       mgmt_evstr(test->expect_alt_ev));
+               id = mgmt_register(data->mgmt_alt, test->expect_alt_ev,
+                                       data->mgmt_index,
+                                       command_generic_event_alt, NULL, NULL);
+               data->mgmt_alt_ev_id = id;
+               test_add_condition(data);
+       }
+
+       master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
+       if (!master_bdaddr) {
+               tester_warn("No master bdaddr");
+               tester_test_failed();
+               return;
+       }
+
+       bthost = hciemu_client_get_host(data->hciemu);
+       bthost_set_connect_cb(bthost, pairing_new_conn, data);
+
+       if (data->hciemu_type == HCIEMU_TYPE_BREDRLE)
+               addr_type = BDADDR_BREDR;
+       else
+               addr_type = BDADDR_LE_PUBLIC;
+
+       bthost_hci_connect(bthost, master_bdaddr, addr_type);
+}
+
 int main(int argc, char *argv[])
 {
        tester_init(&argc, &argv);
@@ -3014,6 +3678,38 @@ int main(int argc, char *argv[])
                                &set_ssp_on_invalid_index_test,
                                NULL, test_command_generic);
 
+       test_bredrle("Set Secure Connections on - Success 1",
+                               &set_sc_on_success_test_1,
+                               NULL, test_command_generic);
+       test_bredrle("Set Secure Connections on - Success 2",
+                               &set_sc_on_success_test_2,
+                               NULL, test_command_generic);
+       test_bredrle("Set Secure Connections on - Invalid params 1",
+                               &set_sc_on_invalid_param_test_1,
+                               NULL, test_command_generic);
+       test_bredrle("Set Secure Connections on - Invalid params 2",
+                               &set_sc_on_invalid_param_test_2,
+                               NULL, test_command_generic);
+       test_bredrle("Set Secure Connections on - Invalid params 3",
+                               &set_sc_on_invalid_param_test_3,
+                               NULL, test_command_generic);
+       test_bredrle("Set Secure Connections on - Invalid index",
+                               &set_sc_on_invalid_index_test,
+                               NULL, test_command_generic);
+       test_bredr("Set Secure Connections on - Not supported 1",
+                               &set_sc_on_not_supported_test_1,
+                               NULL, test_command_generic);
+       test_bredr("Set Secure Connections on - Not supported 2",
+                               &set_sc_on_not_supported_test_2,
+                               NULL, test_command_generic);
+
+       test_bredrle("Set Secure Connections Only on - Success 1",
+                               &set_sc_only_on_success_test_1,
+                               NULL, test_command_generic);
+       test_bredrle("Set Secure Connections Only on - Success 2",
+                               &set_sc_only_on_success_test_2,
+                               NULL, test_command_generic);
+
        test_bredrle("Set High Speed on - Success",
                                &set_hs_on_success_test,
                                NULL, test_command_generic);
@@ -3195,9 +3891,6 @@ int main(int argc, char *argv[])
        test_bredrle("Load Long Term Keys - Invalid Parameters 3",
                                &load_ltks_invalid_params_test_3,
                                NULL, test_command_generic);
-       test_bredrle("Load Long Term Keys - Invalid Parameters 4",
-                               &load_ltks_invalid_params_test_4,
-                               NULL, test_command_generic);
 
        test_bredrle("Pair Device - Not Powered 1",
                                &pair_device_not_powered_test_1,
@@ -3205,6 +3898,58 @@ int main(int argc, char *argv[])
        test_bredrle("Pair Device - Invalid Parameters 1",
                                &pair_device_invalid_param_test_1,
                                NULL, test_command_generic);
+       test_bredrle("Pair Device - Legacy Success 1",
+                               &pair_device_success_test_1,
+                               NULL, test_command_generic);
+       test_bredrle("Pair Device - Sec Mode 3 Success 1",
+                               &pair_device_success_test_2,
+                               NULL, test_command_generic);
+       test_bredrle("Pair Device - Legacy Reject 1",
+                               &pair_device_reject_test_1,
+                               NULL, test_command_generic);
+       test_bredrle("Pair Device - Legacy Reject 2",
+                               &pair_device_reject_test_2,
+                               NULL, test_command_generic);
+       test_bredrle("Pair Device - Sec Mode 3 Reject 1",
+                               &pair_device_reject_test_3,
+                               NULL, test_command_generic);
+       test_bredrle("Pair Device - Sec Mode 3 Reject 2",
+                               &pair_device_reject_test_4,
+                               NULL, test_command_generic);
+       test_bredrle("Pair Device - SSP Just-Works Success 1",
+                               &pair_device_ssp_test_1,
+                               NULL, test_command_generic);
+       test_bredrle("Pair Device - SSP Confirm Success 1",
+                               &pair_device_ssp_test_2,
+                               NULL, test_command_generic);
+       test_bredrle("Pair Device - SSP Confirm Reject 1",
+                               &pair_device_ssp_reject_1,
+                               NULL, test_command_generic);
+       test_bredrle("Pair Device - SSP Confirm Reject 2",
+                               &pair_device_ssp_reject_2,
+                               NULL, test_command_generic);
+       test_bredrle("Pair Device - SSP Non-pairable 1",
+                               &pair_device_ssp_nonpairable_1,
+                               NULL, test_command_generic);
+
+       test_bredrle("Pairing Acceptor - Legacy 1",
+                               &pairing_acceptor_legacy_1, NULL,
+                               test_pairing_acceptor);
+       test_bredrle("Pairing Acceptor - Legacy 2",
+                               &pairing_acceptor_legacy_2, NULL,
+                               test_pairing_acceptor);
+       test_bredrle("Pairing Acceptor - Link Sec 1",
+                               &pairing_acceptor_linksec_1, NULL,
+                               test_pairing_acceptor);
+       test_bredrle("Pairing Acceptor - Link Sec 2",
+                               &pairing_acceptor_linksec_2, NULL,
+                               test_pairing_acceptor);
+       test_bredrle("Pairing Acceptor - SSP 1",
+                               &pairing_acceptor_ssp_1, setup_ssp_acceptor,
+                               test_pairing_acceptor);
+       test_bredrle("Pairing Acceptor - SSP 2",
+                               &pairing_acceptor_ssp_2, setup_ssp_acceptor,
+                               test_pairing_acceptor);
 
        test_bredrle("Unpair Device - Not Powered 1",
                                &unpair_device_not_powered_test_1,
@@ -3239,5 +3984,34 @@ int main(int argc, char *argv[])
                                &set_scan_params_success_test,
                                NULL, test_command_generic);
 
+       test_bredrle("Load IRKs - Success 1",
+                               &load_irks_success1_test,
+                               NULL, test_command_generic);
+       test_bredrle("Load IRKs - Success 2",
+                               &load_irks_success2_test,
+                               NULL, test_command_generic);
+       test_bredrle("Load IRKs - Invalid Parameters 1",
+                               &load_irks_nval_param1_test,
+                               NULL, test_command_generic);
+       test_bredrle("Load IRKs - Invalid Parameters 2",
+                               &load_irks_nval_param2_test,
+                               NULL, test_command_generic);
+       test_bredrle("Load IRKs - Invalid Parameters 3",
+                               &load_irks_nval_param3_test,
+                               NULL, test_command_generic);
+       test_bredr("Load IRKs - Not Supported",
+                               &load_irks_not_supported_test,
+                               NULL, test_command_generic);
+
+       test_bredrle("Set Privacy - Success",
+                               &set_privacy_success_test,
+                               NULL, test_command_generic);
+       test_bredrle("Set Privacy - Rejected",
+                               &set_privacy_powered_test,
+                               NULL, test_command_generic);
+       test_bredrle("Set Privacy - Invalid Parameters",
+                               &set_privacy_nval_param_test,
+                               NULL, test_command_generic);
+
        return tester_run();
 }
index 54dbcbb..4716a8c 100644 (file)
@@ -37,7 +37,7 @@
 #include <readline/history.h>
 
 #include <gobex/gobex.h>
-#include <btio/btio.h>
+#include "btio/btio.h"
 
 static GMainLoop *main_loop = NULL;
 static GObex *obex = NULL;
index 86c2271..3fed6dc 100644 (file)
@@ -34,7 +34,7 @@
 #include <errno.h>
 
 #include <gobex/gobex.h>
-#include <btio/btio.h>
+#include "btio/btio.h"
 
 static GMainLoop *main_loop = NULL;
 
index 0491b51..512a145 100644 (file)
@@ -431,14 +431,12 @@ static void set_default_session(GDBusProxy *proxy)
 
        default_session = proxy;
 
-       if (proxy == NULL) {
+       if (!g_dbus_proxy_get_property(proxy, "Destination", &iter)) {
                desc = g_strdup(PROMPT_ON);
                goto done;
        }
 
-       if (g_dbus_proxy_get_property(proxy, "Destination", &iter))
-               dbus_message_iter_get_basic(&iter, &desc);
-
+       dbus_message_iter_get_basic(&iter, &desc);
        desc = g_strdup_printf(COLOR_BLUE "[%s]" COLOR_OFF "# ", desc);
 
 done:
@@ -598,6 +596,86 @@ static void cmd_cancel(int argc, char *argv[])
                                                g_dbus_proxy_get_path(proxy));
 }
 
+static void suspend_reply(DBusMessage *message, void *user_data)
+{
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               rl_printf("Failed to suspend: %s\n", error.name);
+               dbus_error_free(&error);
+               return;
+       }
+
+       rl_printf("Suspend successful\n");
+}
+
+static void cmd_suspend(int argc, char *argv[])
+{
+       GDBusProxy *proxy;
+
+       if (argc < 2) {
+               rl_printf("Missing transfer address argument\n");
+               return;
+       }
+
+       proxy = find_transfer(argv[1]);
+       if (!proxy) {
+               rl_printf("Transfer %s not available\n", argv[1]);
+               return;
+       }
+
+       if (g_dbus_proxy_method_call(proxy, "Suspend", NULL, suspend_reply,
+                                               NULL, NULL) == FALSE) {
+               rl_printf("Failed to suspend transfer\n");
+               return;
+       }
+
+       rl_printf("Attempting to suspend transfer %s\n",
+                                               g_dbus_proxy_get_path(proxy));
+}
+
+static void resume_reply(DBusMessage *message, void *user_data)
+{
+       DBusError error;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               rl_printf("Failed to resume: %s\n", error.name);
+               dbus_error_free(&error);
+               return;
+       }
+
+       rl_printf("Resume successful\n");
+}
+
+static void cmd_resume(int argc, char *argv[])
+{
+       GDBusProxy *proxy;
+
+       if (argc < 2) {
+               rl_printf("Missing transfer address argument\n");
+               return;
+       }
+
+       proxy = find_transfer(argv[1]);
+       if (!proxy) {
+               rl_printf("Transfer %s not available\n", argv[1]);
+               return;
+       }
+
+       if (g_dbus_proxy_method_call(proxy, "Resume", NULL, resume_reply,
+                                               NULL, NULL) == FALSE) {
+               rl_printf("Failed to resume transfer\n");
+               return;
+       }
+
+       rl_printf("Attempting to resume transfer %s\n",
+                                               g_dbus_proxy_get_path(proxy));
+}
+
 static GDBusProxy *find_opp(const char *path)
 {
        GSList *l;
@@ -1203,13 +1281,8 @@ static void list_messages_setup(DBusMessageIter *iter, void *user_data)
        dbus_message_iter_close_container(iter, &dict);
 }
 
-static void pbap_ls(GDBusProxy *proxy, int argc, char *argv[])
+static void pbap_list(GDBusProxy *proxy, int argc, char *argv[])
 {
-       if (argc > 1) {
-               pbap_search(proxy, argc, argv);
-               return;
-       }
-
        if (g_dbus_proxy_method_call(proxy, "List", list_setup, list_reply,
                                                NULL, NULL) == FALSE) {
                rl_printf("Failed to List\n");
@@ -1219,6 +1292,51 @@ static void pbap_ls(GDBusProxy *proxy, int argc, char *argv[])
        rl_printf("Attempting to List\n");
 }
 
+static void get_size_reply(DBusMessage *message, void *user_data)
+{
+       GDBusProxy *proxy = user_data;
+       DBusError error;
+       DBusMessageIter iter;
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, message) == TRUE) {
+               rl_printf("Failed to GetSize: %s\n", error.name);
+               dbus_error_free(&error);
+               return;
+       }
+
+       dbus_message_iter_init(message, &iter);
+
+       print_iter("\t", "Size", &iter);
+
+       pbap_list(proxy, 0, NULL);
+}
+
+static void pbap_get_size(GDBusProxy *proxy, int argc, char *argv[])
+{
+       if (g_dbus_proxy_method_call(proxy, "GetSize", NULL, get_size_reply,
+                                               proxy, NULL) == FALSE) {
+               rl_printf("Failed to GetSize\n");
+               return;
+       }
+
+       rl_printf("Attempting to GetSize\n");
+}
+
+static void pbap_ls(GDBusProxy *proxy, int argc, char *argv[])
+{
+       if (argc > 1) {
+               if (strcmp("-l", argv[1]))
+                       pbap_search(proxy, argc, argv);
+               else
+                       pbap_get_size(proxy, argc, argv);
+               return;
+       }
+
+       pbap_list(proxy, argc, argv);
+}
+
 static void map_ls_messages(GDBusProxy *proxy, int argc, char *argv[])
 {
        if (g_dbus_proxy_method_call(proxy, "ListMessages", list_messages_setup,
@@ -1858,9 +1976,11 @@ static const struct {
        { "select",       "<session>", cmd_select, "Select default session" },
        { "info",         "<object>", cmd_info, "Object information" },
        { "cancel",       "<transfer>", cmd_cancel, "Cancel transfer" },
+       { "suspend",      "<transfer>", cmd_suspend, "Suspend transfer" },
+       { "resume",       "<transfer>", cmd_resume, "Resume transfer" },
        { "send",         "<file>",   cmd_send, "Send file" },
        { "cd",           "<path>",   cmd_cd, "Change current folder" },
-       { "ls",           NULL,       cmd_ls, "List current folder" },
+       { "ls",           "<options>", cmd_ls, "List current folder" },
        { "cp",          "<source file> <destination file>",   cmd_cp,
                                "Copy source file to destination file" },
        { "mv",          "<source file> <destination file>",   cmd_mv,
@@ -1925,6 +2045,7 @@ static void rl_handler(char *input)
        if (!strlen(input))
                goto done;
 
+       g_strstrip(input);
        add_history(input);
 
        argv = g_strsplit(input, " ", -1);
index b523533..7c320ea 100644 (file)
@@ -1180,12 +1180,16 @@ response:
                switch (status) {
                case 0x00:
                        printf("(POWER_ON)\n");
+                       break;
                case 0x01:
                        printf("(POWER_OFF)\n");
+                       break;
                case 0x02:
                        printf("(UNPLUGGED)\n");
+                       break;
                default:
                        printf("(UNKOWN)\n");
+                       break;
                }
                break;
        case AVRCP_EVENT_PLAYER_APPLICATION_SETTING_CHANGED:
index 17b776d..351f843 100644 (file)
@@ -792,7 +792,7 @@ static inline void ext_inquiry_data_dump(int level, struct frame *frm,
                                type == 0x02 ? "Shortened" : "Complete");
 
                for (i = 0; i < len / 2; i++)
-                       printf(" 0x%4.4x", bt_get_le16(data + i * 2));
+                       printf(" 0x%4.4x", get_le16(data + i * 2));
 
                printf("\n");
                break;
index f0fb78b..a057964 100644 (file)
@@ -250,9 +250,9 @@ static uint32_t get_val(uint8_t *ptr, uint8_t len)
        case 1:
                return *ptr;
        case 2:
-               return bt_get_le16(ptr);
+               return get_le16(ptr);
        case 4:
-               return bt_get_le32(ptr);
+               return get_le32(ptr);
        }
        return 0;
 }
@@ -595,9 +595,9 @@ static void conf_rfc(void *ptr, int len, int in, uint16_t handle,
                uint16_t rto, mto, mps;
                txwin = *((uint8_t *) (ptr + 1));
                maxtrans = *((uint8_t *) (ptr + 2));
-               rto = bt_get_le16(ptr + 3);
-               mto = bt_get_le16(ptr + 5);
-               mps = bt_get_le16(ptr + 7);
+               rto = get_le16(ptr + 3);
+               mto = get_le16(ptr + 5);
+               mps = get_le16(ptr + 7);
                printf(", TxWin %d, MaxTx %d, RTo %d, MTo %d, MPS %d",
                                        txwin, maxtrans, rto, mto, mps);
        }
@@ -853,7 +853,7 @@ static void info_opt(int level, int type, void *ptr, int len)
                                }
                break;
        case 0x0003:
-               fc_mask = bt_get_le64(ptr);
+               fc_mask = get_le64(ptr);
                printf("Fixed channel list 0x%8.8" PRIx64 "\n", fc_mask);
                if (parser.flags & DUMP_VERBOSE)
                        for (i=0; l2cap_fix_chan[i].name; i++)
@@ -916,7 +916,7 @@ static void l2cap_ctrl_ext_parse(int level, struct frame *frm, uint32_t ctrl)
                printf(" %s", sar2str(sar));
                if (sar == L2CAP_SAR_START) {
                        uint16_t len;
-                       len = bt_get_le16(frm->ptr);
+                       len = get_le16(frm->ptr);
                        frm->ptr += L2CAP_SDULEN_SIZE;
                        frm->len -= L2CAP_SDULEN_SIZE;
                        printf(" (len %d)", len);
@@ -949,7 +949,7 @@ static void l2cap_ctrl_parse(int level, struct frame *frm, uint32_t ctrl)
                printf(" %s", sar2str(sar));
                if (sar == L2CAP_SAR_START) {
                        uint16_t len;
-                       len = bt_get_le16(frm->ptr);
+                       len = get_le16(frm->ptr);
                        frm->ptr += L2CAP_SDULEN_SIZE;
                        frm->len -= L2CAP_SDULEN_SIZE;
                        printf(" (len %d)", len);
@@ -1062,7 +1062,7 @@ static inline void a2mp_discover_req(int level, struct frame *frm, uint16_t len)
 
        do {
                len -= 2;
-               mask = bt_get_le16(octet);
+               mask = get_le16(octet);
                printf(" 0x%4.4x", mask);
 
                extension = octet[1] & 0x80;
@@ -1102,7 +1102,7 @@ static inline void a2mp_discover_rsp(int level, struct frame *frm, uint16_t len)
 
        do {
                len -= 2;
-               mask = bt_get_le16(octet);
+               mask = get_le16(octet);
                printf(" 0x%4.4x", mask);
 
                extension = octet[1] & 0x80;
@@ -1324,7 +1324,7 @@ static void l2cap_parse(int level, struct frame *frm)
                if (p_filter(FILT_L2CAP))
                        return;
 
-               psm = bt_get_le16(frm->ptr);
+               psm = get_le16(frm->ptr);
                frm->ptr += 2;
                frm->len -= 2;
 
@@ -1433,7 +1433,7 @@ static void l2cap_parse(int level, struct frame *frm)
                                frm->ptr += 2;
                                frm->len -= 4;
                        }
-                       fcs = bt_get_le16(frm->ptr + frm->len);
+                       fcs = get_le16(frm->ptr + frm->len);
                }
 
                if (!p_filter(FILT_L2CAP)) {
index c65b4b5..62b8ac5 100644 (file)
@@ -30,6 +30,7 @@
 #include <netinet/in.h>
 
 #include "lib/bluetooth.h"
+#include "src/shared/util.h"
 
 struct frame {
        void            *data;
@@ -173,7 +174,7 @@ static inline uint16_t get_u16(struct frame *frm)
        uint16_t *u16_ptr = frm->ptr;
        frm->ptr += 2;
        frm->len -= 2;
-       return ntohs(bt_get_unaligned(u16_ptr));
+       return get_be16(u16_ptr);
 }
 
 static inline uint32_t get_u32(struct frame *frm)
@@ -181,13 +182,13 @@ static inline uint32_t get_u32(struct frame *frm)
        uint32_t *u32_ptr = frm->ptr;
        frm->ptr += 4;
        frm->len -= 4;
-       return ntohl(bt_get_unaligned(u32_ptr));
+       return get_be32(u32_ptr);
 }
 
 static inline uint64_t get_u64(struct frame *frm)
 {
        uint64_t *u64_ptr = frm->ptr;
-       uint64_t u64 = bt_get_unaligned(u64_ptr), tmp;
+       uint64_t u64 = get_unaligned(u64_ptr), tmp;
        frm->ptr += 8;
        frm->len -= 8;
        tmp = ntohl(u64 & 0xffffffff);
index 947ca56..256e172 100644 (file)
@@ -103,7 +103,7 @@ static void hdlc_dump(int level, struct frame *frm)
        uint8_t ctrl = get_u8(frm);
        uint16_t fcs, proto;
 
-       fcs = bt_get_unaligned((uint16_t *) (frm->ptr + frm->len - 2));
+       fcs = get_unaligned((uint16_t *) (frm->ptr + frm->len - 2));
        frm->len -= 2;
 
        p_indent(level, frm);
index 77fa03c..2c7e45b 100644 (file)
@@ -47,6 +47,8 @@
 #include <bluetooth/sdp.h>
 #include <bluetooth/sdp_lib.h>
 
+#include "src/shared/util.h"
+
 /* Test modes */
 enum {
        SEND,
@@ -466,8 +468,11 @@ static void save_mode(int sk)
        while ((len = read(sk, b, data_size)) > 0) {
                ret = write(save_fd, b, len);
                if (ret < 0)
-                       return;
+                       goto done;
        }
+
+done:
+       free(b);
 }
 
 static void recv_mode(int sk)
@@ -564,8 +569,8 @@ static void do_send(int sk)
 
        seq = 0;
        while ((num_frames == -1) || (num_frames-- > 0)) {
-               bt_put_le32(seq, buf);
-               bt_put_le16(data_size, buf + 4);
+               put_le32(seq, buf);
+               put_le16(data_size, buf + 4);
 
                seq++;
 
diff --git a/tools/rfcomm-tester.c b/tools/rfcomm-tester.c
new file mode 100644 (file)
index 0000000..8be6e72
--- /dev/null
@@ -0,0 +1,762 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+#include "bluetooth/rfcomm.h"
+
+#include "monitor/bt.h"
+#include "emulator/bthost.h"
+
+#include "src/shared/tester.h"
+#include "src/shared/mgmt.h"
+#include "src/shared/hciemu.h"
+
+struct test_data {
+       struct mgmt *mgmt;
+       uint16_t mgmt_index;
+       struct hciemu *hciemu;
+       enum hciemu_type hciemu_type;
+       const void *test_data;
+       unsigned int io_id;
+       uint16_t conn_handle;
+};
+
+struct rfcomm_client_data {
+       uint8_t server_channel;
+       uint8_t client_channel;
+       int expected_connect_err;
+       const uint8_t *send_data;
+       const uint8_t *read_data;
+       uint16_t data_len;
+};
+
+struct rfcomm_server_data {
+       uint8_t server_channel;
+       uint8_t client_channel;
+       bool expected_status;
+       const uint8_t *send_data;
+       const uint8_t *read_data;
+       uint16_t data_len;
+};
+
+static void mgmt_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       tester_print("%s%s", prefix, str);
+}
+
+static void read_info_callback(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct mgmt_rp_read_info *rp = param;
+       char addr[18];
+       uint16_t manufacturer;
+       uint32_t supported_settings, current_settings;
+
+       tester_print("Read Info callback");
+       tester_print("  Status: 0x%02x", status);
+
+       if (status || !param) {
+               tester_pre_setup_failed();
+               return;
+       }
+
+       ba2str(&rp->bdaddr, addr);
+       manufacturer = btohs(rp->manufacturer);
+       supported_settings = btohl(rp->supported_settings);
+       current_settings = btohl(rp->current_settings);
+
+       tester_print("  Address: %s", addr);
+       tester_print("  Version: 0x%02x", rp->version);
+       tester_print("  Manufacturer: 0x%04x", manufacturer);
+       tester_print("  Supported settings: 0x%08x", supported_settings);
+       tester_print("  Current settings: 0x%08x", current_settings);
+       tester_print("  Class: 0x%02x%02x%02x",
+                       rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+       tester_print("  Name: %s", rp->name);
+       tester_print("  Short name: %s", rp->short_name);
+
+       if (strcmp(hciemu_get_address(data->hciemu), addr)) {
+               tester_pre_setup_failed();
+               return;
+       }
+
+       tester_pre_setup_complete();
+}
+
+static void index_added_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+
+       tester_print("Index Added callback");
+       tester_print("  Index: 0x%04x", index);
+
+       data->mgmt_index = index;
+
+       mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL,
+                                       read_info_callback, NULL, NULL);
+}
+
+static void index_removed_callback(uint16_t index, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+
+       tester_print("Index Removed callback");
+       tester_print("  Index: 0x%04x", index);
+
+       if (index != data->mgmt_index)
+               return;
+
+       mgmt_unregister_index(data->mgmt, data->mgmt_index);
+
+       mgmt_unref(data->mgmt);
+       data->mgmt = NULL;
+
+       tester_post_teardown_complete();
+}
+
+static void read_index_list_callback(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+
+       tester_print("Read Index List callback");
+       tester_print("  Status: 0x%02x", status);
+
+       if (status || !param) {
+               tester_pre_setup_failed();
+               return;
+       }
+
+       mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE,
+                                       index_added_callback, NULL, NULL);
+
+       mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE,
+                                       index_removed_callback, NULL, NULL);
+
+       data->hciemu = hciemu_new(data->hciemu_type);
+       if (!data->hciemu) {
+               tester_warn("Failed to setup HCI emulation");
+               tester_pre_setup_failed();
+       }
+
+       tester_print("New hciemu instance created");
+}
+
+static void test_pre_setup(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       data->mgmt = mgmt_new_default();
+       if (!data->mgmt) {
+               tester_warn("Failed to setup management interface");
+               tester_pre_setup_failed();
+               return;
+       }
+
+       if (tester_use_debug())
+               mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL);
+
+       mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL,
+                                       read_index_list_callback, NULL, NULL);
+}
+
+static void test_post_teardown(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+
+       if (data->io_id > 0) {
+               g_source_remove(data->io_id);
+               data->io_id = 0;
+       }
+
+       hciemu_unref(data->hciemu);
+       data->hciemu = NULL;
+}
+
+static void test_data_free(void *test_data)
+{
+       struct test_data *data = test_data;
+
+       free(data);
+}
+
+static void client_connectable_complete(uint16_t opcode, uint8_t status,
+                                       const void *param, uint8_t len,
+                                       void *user_data)
+{
+       switch (opcode) {
+       case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+               break;
+       default:
+               return;
+       }
+
+       tester_print("Client set connectable status 0x%02x", status);
+
+       if (status)
+               tester_setup_failed();
+       else
+               tester_setup_complete();
+}
+
+static void setup_powered_client_callback(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost;
+
+       if (status != MGMT_STATUS_SUCCESS) {
+               tester_setup_failed();
+               return;
+       }
+
+       tester_print("Controller powered on");
+
+       bthost = hciemu_client_get_host(data->hciemu);
+       bthost_set_cmd_complete_cb(bthost, client_connectable_complete, data);
+       bthost_write_scan_enable(bthost, 0x03);
+}
+
+static void setup_powered_client(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       unsigned char param[] = { 0x01 };
+
+       tester_print("Powering on controller");
+
+       mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+                       sizeof(param), param, setup_powered_client_callback,
+                       NULL, NULL);
+}
+
+static void setup_powered_server_callback(uint8_t status, uint16_t length,
+                                       const void *param, void *user_data)
+{
+       if (status != MGMT_STATUS_SUCCESS) {
+               tester_setup_failed();
+               return;
+       }
+
+       tester_print("Controller powered on");
+
+       tester_setup_complete();
+}
+
+static void setup_powered_server(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       unsigned char param[] = { 0x01 };
+
+       tester_print("Powering on controller");
+
+       mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
+                               sizeof(param), param,
+                               NULL, NULL, NULL);
+
+       mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+                       sizeof(param), param, setup_powered_server_callback,
+                       NULL, NULL);
+}
+
+const struct rfcomm_client_data connect_success = {
+       .server_channel = 0x0c,
+       .client_channel = 0x0c
+};
+
+const uint8_t data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+
+const struct rfcomm_client_data connect_send_success = {
+       .server_channel = 0x0c,
+       .client_channel = 0x0c,
+       .data_len = sizeof(data),
+       .send_data = data
+};
+
+const struct rfcomm_client_data connect_read_success = {
+       .server_channel = 0x0c,
+       .client_channel = 0x0c,
+       .data_len = sizeof(data),
+       .read_data = data
+};
+
+const struct rfcomm_client_data connect_nval = {
+       .server_channel = 0x0c,
+       .client_channel = 0x0e,
+       .expected_connect_err = -ECONNREFUSED
+};
+
+const struct rfcomm_server_data listen_success = {
+       .server_channel = 0x0c,
+       .client_channel = 0x0c,
+       .expected_status = true
+};
+
+const struct rfcomm_server_data listen_send_success = {
+       .server_channel = 0x0c,
+       .client_channel = 0x0c,
+       .expected_status = true,
+       .data_len = sizeof(data),
+       .send_data = data
+};
+
+const struct rfcomm_server_data listen_read_success = {
+       .server_channel = 0x0c,
+       .client_channel = 0x0c,
+       .expected_status = true,
+       .data_len = sizeof(data),
+       .read_data = data
+};
+
+const struct rfcomm_server_data listen_nval = {
+       .server_channel = 0x0c,
+       .client_channel = 0x0e,
+       .expected_status = false
+};
+
+static void test_basic(const void *test_data)
+{
+       int sk;
+
+       sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+       if (sk < 0) {
+               tester_warn("Can't create socket: %s (%d)", strerror(errno),
+                                                                       errno);
+               tester_test_failed();
+               return;
+       }
+
+       close(sk);
+
+       tester_test_passed();
+}
+
+static int create_rfcomm_sock(bdaddr_t *address, uint8_t channel)
+{
+       int sk;
+       struct sockaddr_rc addr;
+
+       sk = socket(PF_BLUETOOTH, SOCK_STREAM | SOCK_NONBLOCK, BTPROTO_RFCOMM);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       addr.rc_channel = channel;
+       bacpy(&addr.rc_bdaddr, address);
+
+       if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(sk);
+               return -1;
+       }
+
+       return sk;
+}
+
+static int connect_rfcomm_sock(int sk, const bdaddr_t *bdaddr, uint8_t channel)
+{
+       struct sockaddr_rc addr;
+       int err;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.rc_family = AF_BLUETOOTH;
+       bacpy(&addr.rc_bdaddr, bdaddr);
+       addr.rc_channel = htobs(channel);
+
+       err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+       if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
+               return err;
+
+       return 0;
+}
+
+static gboolean client_received_data(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct rfcomm_client_data *client_data = data->test_data;
+       int sk;
+       ssize_t ret;
+       char buf[248];
+
+       sk = g_io_channel_unix_get_fd(io);
+
+       ret = read(sk, buf, client_data->data_len);
+       if (client_data->data_len != ret) {
+               tester_test_failed();
+               return false;
+       }
+
+       if (memcmp(client_data->read_data, buf, client_data->data_len))
+               tester_test_failed();
+       else
+               tester_test_passed();
+
+       return false;
+}
+
+static gboolean rc_connect_cb(GIOChannel *io, GIOCondition cond,
+               gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct rfcomm_client_data *client_data = data->test_data;
+       socklen_t len = sizeof(int);
+       int sk, err, sk_err;
+
+       data->io_id = 0;
+
+       sk = g_io_channel_unix_get_fd(io);
+
+       if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+               err = -errno;
+       else
+               err = -sk_err;
+
+       if (client_data->expected_connect_err &&
+                               err == client_data->expected_connect_err) {
+               tester_test_passed();
+               return false;
+       }
+
+       if (client_data->send_data) {
+               ssize_t ret;
+
+               ret = write(sk, client_data->send_data, client_data->data_len);
+               if (client_data->data_len != ret)
+                       tester_test_failed();
+
+               return false;
+       } else if (client_data->read_data) {
+               g_io_add_watch(io, G_IO_IN, client_received_data, NULL);
+               bthost_send_rfcomm_data(hciemu_client_get_host(data->hciemu),
+                                               data->conn_handle,
+                                               client_data->client_channel,
+                                               client_data->read_data,
+                                               client_data->data_len);
+               return false;
+       }
+
+       if (err < 0)
+               tester_test_failed();
+       else
+               tester_test_passed();
+
+       return false;
+}
+
+static void client_hook_func(const void *data, uint16_t len,
+                                                       void *user_data)
+{
+       struct test_data *test_data = tester_get_data();
+       const struct rfcomm_client_data *client_data = test_data->test_data;
+       ssize_t ret;
+
+       if (client_data->data_len != len) {
+               tester_test_failed();
+               return;
+       }
+
+       ret = memcmp(client_data->send_data, data, len);
+       if (ret)
+               tester_test_failed();
+       else
+               tester_test_passed();
+}
+
+static void server_hook_func(const void *data, uint16_t len,
+                                                       void *user_data)
+{
+       struct test_data *test_data = tester_get_data();
+       const struct rfcomm_server_data *server_data = test_data->test_data;
+       ssize_t ret;
+
+       if (server_data->data_len != len) {
+               tester_test_failed();
+               return;
+       }
+
+       ret = memcmp(server_data->send_data, data, len);
+       if (ret)
+               tester_test_failed();
+       else
+               tester_test_passed();
+}
+
+static void rfcomm_connect_cb(uint16_t handle, uint16_t cid,
+                                               void *user_data, bool status)
+{
+       struct test_data *data = tester_get_data();
+       const struct rfcomm_client_data *client_data = data->test_data;
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       if (client_data->send_data)
+               bthost_add_rfcomm_chan_hook(bthost, handle,
+                                               client_data->client_channel,
+                                               client_hook_func, NULL);
+       else if (client_data->read_data)
+               data->conn_handle = handle;
+}
+
+static void test_connect(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+       const struct rfcomm_client_data *client_data = data->test_data;
+       const uint8_t *client_addr, *master_addr;
+       GIOChannel *io;
+       int sk;
+
+       bthost_add_l2cap_server(bthost, 0x0003, NULL, NULL);
+       bthost_add_rfcomm_server(bthost, client_data->server_channel,
+                                               rfcomm_connect_cb, NULL);
+
+       master_addr = hciemu_get_master_bdaddr(data->hciemu);
+       client_addr = hciemu_get_client_bdaddr(data->hciemu);
+
+       sk = create_rfcomm_sock((bdaddr_t *) master_addr, 0);
+
+       if (connect_rfcomm_sock(sk, (const bdaddr_t *) client_addr,
+                                       client_data->client_channel) < 0) {
+               close(sk);
+               tester_test_failed();
+               return;
+       }
+
+       io = g_io_channel_unix_new(sk);
+       g_io_channel_set_close_on_unref(io, TRUE);
+
+       data->io_id = g_io_add_watch(io, G_IO_OUT, rc_connect_cb, NULL);
+
+       g_io_channel_unref(io);
+
+       tester_print("Connect in progress %d", sk);
+}
+
+static gboolean server_received_data(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct rfcomm_server_data *server_data = data->test_data;
+       char buf[1024];
+       ssize_t ret;
+       int sk;
+
+       sk = g_io_channel_unix_get_fd(io);
+
+       ret = read(sk, buf, server_data->data_len);
+       if (ret != server_data->data_len) {
+               tester_test_failed();
+               return false;
+       }
+
+       if (memcmp(buf, server_data->read_data, server_data->data_len))
+               tester_test_failed();
+       else
+               tester_test_passed();
+
+       return false;
+}
+
+static gboolean rfcomm_listen_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct rfcomm_server_data *server_data = data->test_data;
+       int sk, new_sk;
+
+       data->io_id = 0;
+
+       sk = g_io_channel_unix_get_fd(io);
+
+       new_sk = accept(sk, NULL, NULL);
+       if (new_sk < 0) {
+               tester_test_failed();
+               return false;
+       }
+
+       if (server_data->send_data) {
+               ssize_t ret;
+
+               ret = write(new_sk, server_data->send_data,
+                                                       server_data->data_len);
+               if (ret != server_data->data_len)
+                       tester_test_failed();
+
+               close(new_sk);
+               return false;
+       } else if (server_data->read_data) {
+               GIOChannel *new_io;
+
+               new_io = g_io_channel_unix_new(new_sk);
+               g_io_channel_set_close_on_unref(new_io, TRUE);
+
+               data->io_id = g_io_add_watch(new_io, G_IO_IN,
+                                               server_received_data, NULL);
+
+               g_io_channel_unref(new_io);
+               return false;
+       }
+
+       close(new_sk);
+
+       tester_test_passed();
+
+       return false;
+}
+
+static void connection_cb(uint16_t handle, uint16_t cid, void *user_data,
+                                                               bool status)
+{
+       struct test_data *data = tester_get_data();
+       const struct rfcomm_server_data *server_data = data->test_data;
+       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+
+       if (server_data->read_data) {
+               data->conn_handle = handle;
+               bthost_send_rfcomm_data(bthost, data->conn_handle,
+                                               server_data->client_channel,
+                                               server_data->read_data,
+                                               server_data->data_len);
+               return;
+       } else if (server_data->data_len) {
+               return;
+       }
+
+       if (server_data->expected_status == status)
+               tester_test_passed();
+       else
+               tester_test_failed();
+}
+
+static void client_new_conn(uint16_t handle, void *user_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct rfcomm_server_data *server_data = data->test_data;
+       struct bthost *bthost;
+
+       bthost = hciemu_client_get_host(data->hciemu);
+       bthost_add_rfcomm_chan_hook(bthost, handle,
+                                               server_data->client_channel,
+                                               server_hook_func, NULL);
+       bthost_connect_rfcomm(bthost, handle, server_data->client_channel,
+                                               connection_cb, NULL);
+}
+
+static void test_server(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       const struct rfcomm_server_data *server_data = data->test_data;
+       const uint8_t *master_addr;
+       struct bthost *bthost;
+       GIOChannel *io;
+       int sk;
+
+       master_addr = hciemu_get_master_bdaddr(data->hciemu);
+
+       sk = create_rfcomm_sock((bdaddr_t *) master_addr,
+                                               server_data->server_channel);
+       if (sk < 0) {
+               tester_test_failed();
+               return;
+       }
+
+       if (listen(sk, 5) < 0) {
+               tester_warn("listening on socket failed: %s (%u)",
+                               strerror(errno), errno);
+               tester_test_failed();
+               close(sk);
+               return;
+       }
+
+       io = g_io_channel_unix_new(sk);
+       g_io_channel_set_close_on_unref(io, TRUE);
+
+       data->io_id = g_io_add_watch(io, G_IO_IN, rfcomm_listen_cb, NULL);
+       g_io_channel_unref(io);
+
+       tester_print("Listening for connections");
+
+       bthost = hciemu_client_get_host(data->hciemu);
+       bthost_set_connect_cb(bthost, client_new_conn, data);
+
+       bthost_hci_connect(bthost, master_addr, BDADDR_BREDR);
+}
+
+#define test_rfcomm(name, data, setup, func) \
+       do { \
+               struct test_data *user; \
+               user = malloc(sizeof(struct test_data)); \
+               if (!user) \
+                       break; \
+               user->hciemu_type = HCIEMU_TYPE_BREDR; \
+               user->test_data = data; \
+               user->io_id = 0; \
+               tester_add_full(name, data, \
+                               test_pre_setup, setup, func, NULL, \
+                               test_post_teardown, 2, user, test_data_free); \
+       } while (0)
+
+int main(int argc, char *argv[])
+{
+       tester_init(&argc, &argv);
+
+       test_rfcomm("Basic RFCOMM Socket - Success", NULL,
+                                       setup_powered_client, test_basic);
+       test_rfcomm("Basic RFCOMM Socket Client - Success", &connect_success,
+                                       setup_powered_client, test_connect);
+       test_rfcomm("Basic RFCOMM Socket Client - Write Success",
+                               &connect_send_success, setup_powered_client,
+                               test_connect);
+       test_rfcomm("Basic RFCOMM Socket Client - Read Success",
+                               &connect_read_success, setup_powered_client,
+                               test_connect);
+       test_rfcomm("Basic RFCOMM Socket Client - Conn Refused",
+                       &connect_nval, setup_powered_client, test_connect);
+       test_rfcomm("Basic RFCOMM Socket Server - Success", &listen_success,
+                                       setup_powered_server, test_server);
+       test_rfcomm("Basic RFCOMM Socket Server - Write Success",
+                               &listen_send_success, setup_powered_server,
+                               test_server);
+       test_rfcomm("Basic RFCOMM Socket Server - Read Success",
+                               &listen_read_success, setup_powered_server,
+                               test_server);
+       test_rfcomm("Basic RFCOMM Socket Server - Conn Refused", &listen_nval,
+                                       setup_powered_server, test_server);
+
+       return tester_run();
+}
index 1e8351f..db45ef0 100644 (file)
@@ -415,7 +415,7 @@ end:
        close(sk);
 }
 
-static int create_sco_sock(struct test_data *data, uint16_t psm)
+static int create_sco_sock(struct test_data *data)
 {
        const uint8_t *master_bdaddr;
        struct sockaddr_sco addr;
@@ -514,7 +514,7 @@ static void test_connect(const void *test_data)
        GIOChannel *io;
        int sk;
 
-       sk = create_sco_sock(data, 0);
+       sk = create_sco_sock(data);
        if (sk < 0) {
                tester_test_failed();
                return;
@@ -543,7 +543,7 @@ static void test_connect_transp(const void *test_data)
        int sk, err;
        struct bt_voice voice;
 
-       sk = create_sco_sock(data, 0);
+       sk = create_sco_sock(data);
        if (sk < 0) {
                tester_test_failed();
                return;
@@ -592,13 +592,13 @@ int main(int argc, char *argv[])
        test_sco("eSCO CVSD - Success", &connect_success, setup_powered,
                                                        test_connect);
 
-       test_sco("eSCO MSBC - Success", &connect_success, setup_powered,
+       test_sco("eSCO mSBC - Success", &connect_success, setup_powered,
                                                        test_connect_transp);
 
        test_sco_11("SCO CVSD 1.1 - Success", &connect_success, setup_powered,
                                                        test_connect);
 
-       test_sco_11("SCO MSBC 1.1 - Failure", &connect_failure, setup_powered,
+       test_sco_11("SCO mSBC 1.1 - Failure", &connect_failure, setup_powered,
                                                        test_connect_transp);
 
        return tester_run();
index 69150b1..e5530d9 100644 (file)
@@ -40,6 +40,8 @@
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/sco.h>
 
+#include "src/shared/util.h"
+
 /* Test modes */
 enum {
        SEND,
@@ -255,7 +257,7 @@ static void dump_mode(int sk)
                                                        strerror(errno), errno);
 
        if (defer_setup) {
-               len = read(sk, buf, sizeof(buf));
+               len = read(sk, buf, data_size);
                if (len < 0)
                        syslog(LOG_ERR, "Initial read error: %s (%d)",
                                                strerror(errno), errno);
@@ -283,7 +285,7 @@ static void recv_mode(int sk)
                                                        strerror(errno), errno);
 
        if (defer_setup) {
-               len = read(sk, buf, sizeof(buf));
+               len = read(sk, buf, data_size);
                if (len < 0)
                        syslog(LOG_ERR, "Initial read error: %s (%d)",
                                                strerror(errno), errno);
@@ -345,8 +347,8 @@ static void send_mode(char *svr)
 
        seq = 0;
        while (1) {
-               bt_put_le32(seq, buf);
-               bt_put_le16(data_size, buf + 4);
+               put_le32(seq, buf);
+               put_le16(data_size, buf + 4);
 
                seq++;
 
index b4b65ec..1600c3e 100644 (file)
@@ -45,7 +45,7 @@
 
 #include <netinet/in.h>
 
-#include "sdp-xml.h"
+#include "src/sdp-xml.h"
 
 #ifndef APPLE_AGENT_SVCLASS_ID
 #define APPLE_AGENT_SVCLASS_ID 0x2112
@@ -1048,7 +1048,7 @@ static void print_service_desc(void *value, void *user)
                        if (proto == RFCOMM_UUID)
                                printf("    Channel: %d\n", p->val.uint8);
                        else
-                               printf("    uint8: 0x%x\n", p->val.uint8);
+                               printf("    uint8: 0x%02x\n", p->val.uint8);
                        break;
                case SDP_UINT16:
                        if (proto == L2CAP_UUID) {
@@ -1060,9 +1060,9 @@ static void print_service_desc(void *value, void *user)
                                if (i == 1)
                                        printf("    Version: 0x%04x\n", p->val.uint16);
                                else
-                                       printf("    uint16: 0x%x\n", p->val.uint16);
+                                       printf("    uint16: 0x%04x\n", p->val.uint16);
                        else
-                               printf("    uint16: 0x%x\n", p->val.uint16);
+                               printf("    uint16: 0x%04x\n", p->val.uint16);
                        break;
                case SDP_SEQ16:
                        printf("    SEQ16:");
diff --git a/tools/seq2bseq.c b/tools/seq2bseq.c
new file mode 100644 (file)
index 0000000..7657a57
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2012-2013  Intel Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+static int convert_line(int fd, const char *line)
+{
+       const char *ptr = line;
+       char str[3];
+       unsigned char val;
+
+       if (line[0] == '*' || line[0] == '\r' || line[0] == '\r')
+               return 0;
+
+       while (1) {
+               str[0] = *ptr++;
+               str[1] = *ptr++;
+               str[2] = '\0';
+
+               val = strtol(str, NULL, 16);
+
+               if (write(fd, &val, 1) < 0)
+                       return -errno;
+
+               if (*ptr == '\r' || *ptr == '\n')
+                       break;
+
+               while (*ptr == ' ')
+                       ptr++;
+       }
+
+       return 0;
+}
+
+static void convert_file(const char *input_path, const char *output_path)
+{
+       size_t line_size = 1024;
+       char line_buffer[line_size];
+       char *path;
+       const char *ptr;
+       FILE *fp;
+       struct stat st;
+       off_t cur = 0;
+       int fd;
+
+       if (output_path) {
+               path = strdup(output_path);
+               if (!path) {
+                       perror("Failed to allocate string");
+                       return;
+               }
+       } else {
+               ptr = strrchr(input_path, '.');
+               if (ptr) {
+                       path = malloc(ptr - input_path + 6);
+                       if (!path) {
+                               perror("Failed to allocate string");
+                               return;
+                       }
+                       strncpy(path, input_path, ptr - input_path);
+                       strcpy(path + (ptr - input_path), ".bseq");
+               } else {
+                       if (asprintf(&path, "%s.bseq", input_path) < 0) {
+                               perror("Failed to allocate string");
+                               return;
+                       }
+               }
+       }
+
+       printf("Converting %s to %s\n", input_path, path);
+
+       fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+       free(path);
+
+       if (fd < 0) {
+               perror("Failed to create output file");
+               return;
+       }
+
+       if (stat(input_path, &st) < 0) {
+               fprintf(stderr, "Failed get file size\n");
+               close(fd);
+               return;
+       }
+
+       if (st.st_size == 0) {
+               fprintf(stderr, "Empty file\n");
+               close(fd);
+               return;
+       }
+
+       fp = fopen(input_path, "r");
+       if (!fp) {
+               fprintf(stderr, "Failed to open input file\n");
+               close(fd);
+               return;
+       }
+
+       while (1) {
+               char *str;
+               int err;
+
+               str = fgets(line_buffer, line_size - 1, fp);
+               if (!str)
+                       break;
+
+               cur += strlen(str);
+
+               err = convert_line(fd, str);
+               if (err < 0) {
+                       fprintf(stderr, "Failed to convert file (%s)\n",
+                                                               strerror(-err));
+                       break;
+               }
+       }
+
+       fclose(fp);
+
+       close(fd);
+}
+
+static void usage(void)
+{
+       printf("Intel Bluetooth firmware converter\n"
+               "Usage:\n");
+       printf("\tseq2bseq [options] <file>\n");
+       printf("Options:\n"
+               "\t-o, --output <file>    Provide firmware output file\n"
+               "\t-h, --help             Show help options\n");
+}
+
+static const struct option main_options[] = {
+       { "output",  required_argument, NULL, 'o' },
+       { "version", no_argument,       NULL, 'v' },
+       { "help",    no_argument,       NULL, 'h' },
+       { }
+};
+
+int main(int argc, char *argv[])
+{
+       const char *output_path = NULL;
+       int i;
+
+       for (;;) {
+               int opt;
+
+               opt = getopt_long(argc, argv, "o:vh", main_options, NULL);
+               if (opt < 0)
+                       break;
+
+               switch (opt) {
+               case 'o':
+                       output_path = optarg;
+                       break;
+               case 'v':
+                       printf("%s\n", VERSION);
+                       return EXIT_SUCCESS;
+               case 'h':
+                       usage();
+                       return EXIT_SUCCESS;
+               default:
+                       return EXIT_FAILURE;
+               }
+       }
+
+       if (argc - optind < 1) {
+               fprintf(stderr, "No input firmware files provided\n");
+               return EXIT_FAILURE;
+       }
+
+       if (output_path && argc - optind > 1) {
+               fprintf(stderr, "Only single input firmware supported\n");
+               return EXIT_FAILURE;
+       }
+
+       for (i = optind; i < argc; i++)
+               convert_file(argv[i], output_path);
+
+       return EXIT_SUCCESS;
+}
index 05d620a..e09c802 100644 (file)
 #include "monitor/bt.h"
 #include "emulator/bthost.h"
 
+#include "src/shared/crypto.h"
 #include "src/shared/tester.h"
 #include "src/shared/mgmt.h"
 #include "src/shared/hciemu.h"
 
-#ifndef SOL_ALG
-#define SOL_ALG 279
-#endif
-
-#ifndef AF_ALG
-#define AF_ALG  38
-#define PF_ALG  AF_ALG
-
-#include <linux/types.h>
-
-struct sockaddr_alg {
-       __u16   salg_family;
-       __u8    salg_type[14];
-       __u32   salg_feat;
-       __u32   salg_mask;
-       __u8    salg_name[64];
-};
-
-struct af_alg_iv {
-       __u32   ivlen;
-       __u8    iv[0];
-};
-
-#define ALG_SET_KEY                     1
-#define ALG_SET_IV                      2
-#define ALG_SET_OP                      3
-
-#define ALG_OP_DECRYPT                  0
-#define ALG_OP_ENCRYPT                  1
-
-#else
-#include <linux/if_alg.h>
-#endif
-
 #define SMP_CID 0x0006
 
 struct test_data {
@@ -94,14 +61,14 @@ struct test_data {
        bool out;
        uint16_t handle;
        size_t counter;
-       int alg_sk;
-       uint8_t smp_tk[16];
-       uint8_t smp_prnd[16];
-       uint8_t smp_rrnd[16];
-       uint8_t smp_pcnf[16];
-       uint8_t smp_preq[7];
-       uint8_t smp_prsp[7];
-       uint8_t smp_ltk[16];
+       struct bt_crypto *crypto;
+       uint8_t tk[16];
+       uint8_t prnd[16];
+       uint8_t rrnd[16];
+       uint8_t pcnf[16];
+       uint8_t preq[7];
+       uint8_t prsp[7];
+       uint8_t ltk[16];
 };
 
 struct smp_req_rsp {
@@ -116,174 +83,6 @@ struct smp_data {
        size_t req_count;
 };
 
-static int alg_setup(void)
-{
-       struct sockaddr_alg salg;
-       int sk;
-
-       sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
-       if (sk < 0) {
-               fprintf(stderr, "socket(AF_ALG): %s\n", strerror(errno));
-               return -1;
-       }
-
-       memset(&salg, 0, sizeof(salg));
-       salg.salg_family = AF_ALG;
-       strcpy((char *) salg.salg_type, "skcipher");
-       strcpy((char *) salg.salg_name, "ecb(aes)");
-
-       if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
-               fprintf(stderr, "bind(AF_ALG): %s\n", strerror(errno));
-               close(sk);
-               return -1;
-       }
-
-       return sk;
-}
-
-static int alg_new(int alg_sk, const uint8_t *key)
-{
-       int sk;
-
-       if (setsockopt(alg_sk, SOL_ALG, ALG_SET_KEY, key, 16) < 0) {
-               tester_warn("setsockopt(ALG_SET_KEY): %s", strerror(errno));
-               return -1;
-       }
-
-       sk = accept4(alg_sk, NULL, 0, SOCK_CLOEXEC);
-       if (sk < 0) {
-               tester_warn("accept4(AF_ALG): %s", strerror(errno));
-               return -1;
-       }
-
-       return sk;
-}
-
-static int alg_encrypt(int sk, uint8_t in[16], uint8_t out[16])
-{
-       __u32 alg_op = ALG_OP_ENCRYPT;
-       char cbuf[CMSG_SPACE(sizeof(alg_op))];
-       struct cmsghdr *cmsg;
-       struct msghdr msg;
-       struct iovec iov;
-       int ret;
-
-       memset(cbuf, 0, sizeof(cbuf));
-       memset(&msg, 0, sizeof(msg));
-
-       msg.msg_control = cbuf;
-       msg.msg_controllen = sizeof(cbuf);
-
-       cmsg = CMSG_FIRSTHDR(&msg);
-       cmsg->cmsg_level = SOL_ALG;
-       cmsg->cmsg_type = ALG_SET_OP;
-       cmsg->cmsg_len = CMSG_LEN(sizeof(alg_op));
-       memcpy(CMSG_DATA(cmsg), &alg_op, sizeof(alg_op));
-
-       iov.iov_base = in;
-       iov.iov_len = 16;
-
-       msg.msg_iov = &iov;
-       msg.msg_iovlen = 1;
-
-       ret = sendmsg(sk, &msg, 0);
-       if (ret < 0) {
-               tester_warn("sendmsg(AF_ALG): %s", strerror(errno));
-               return ret;
-       }
-
-       ret = read(sk, out, 16);
-       if (ret < 0)
-               tester_warn("read(AF_ALG): %s", strerror(errno));
-
-       return 0;
-}
-
-static int smp_e(uint8_t key[16], uint8_t in[16], uint8_t out[16])
-{
-       struct test_data *data = tester_get_data();
-       int sk, err;
-
-       sk = alg_new(data->alg_sk, key);
-       if (sk < 0)
-               return sk;
-
-       err = alg_encrypt(sk, in, out);
-
-       close(sk);
-
-       return err;
-}
-
-static inline void swap128(const uint8_t src[16], uint8_t dst[16])
-{
-       int i;
-       for (i = 0; i < 16; i++)
-               dst[15 - i] = src[i];
-}
-
-static inline void swap56(const uint8_t src[7], uint8_t dst[7])
-{
-       int i;
-       for (i = 0; i < 7; i++)
-               dst[6 - i] = src[i];
-}
-
-typedef struct {
-       uint64_t a, b;
-} u128;
-
-static inline void u128_xor(u128 *r, const u128 *p, const u128 *q)
-{
-       r->a = p->a ^ q->a;
-       r->b = p->b ^ q->b;
-}
-
-static int smp_c1(uint8_t r[16], uint8_t res[16])
-{
-       struct test_data *data = tester_get_data();
-       uint8_t p1[16], p2[16];
-       int err;
-
-       memset(p1, 0, 16);
-
-       /* p1 = pres || preq || _rat || _iat */
-       swap56(data->smp_prsp, p1);
-       swap56(data->smp_preq, p1 + 7);
-       p1[14] = data->ra_type;
-       p1[15] = data->ia_type;
-
-       memset(p2, 0, 16);
-
-       /* p2 = padding || ia || ra */
-       baswap((bdaddr_t *) (p2 + 4), (bdaddr_t *) data->ia);
-       baswap((bdaddr_t *) (p2 + 10), (bdaddr_t *) data->ra);
-
-       /* res = r XOR p1 */
-       u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
-
-       /* res = e(k, res) */
-       err = smp_e(data->smp_tk, res, res);
-       if (err)
-               return err;
-
-       /* res = res XOR p2 */
-       u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
-
-       /* res = e(k, res) */
-       return smp_e(data->smp_tk, res, res);
-}
-
-static int smp_s1(uint8_t r1[16], uint8_t r2[16], uint8_t res[16])
-{
-       struct test_data *data = tester_get_data();
-
-       memcpy(res, r1 + 8, 8);
-       memcpy(res + 8, r2 + 8, 8);
-
-       return smp_e(data->smp_tk, res, res);
-}
-
 static void mgmt_debug(const char *str, void *user_data)
 {
        const char *prefix = user_data;
@@ -396,9 +195,9 @@ static void test_pre_setup(const void *test_data)
 {
        struct test_data *data = tester_get_data();
 
-       data->alg_sk = alg_setup();
-       if (data->alg_sk < 0) {
-               tester_warn("Failed to setup AF_ALG socket");
+       data->crypto = bt_crypto_new();
+       if (!data->crypto) {
+               tester_warn("Failed to setup crypto");
                tester_pre_setup_failed();
                return;
        }
@@ -406,6 +205,7 @@ static void test_pre_setup(const void *test_data)
        data->mgmt = mgmt_new_default();
        if (!data->mgmt) {
                tester_warn("Failed to setup management interface");
+               bt_crypto_unref(data->crypto);
                tester_pre_setup_failed();
                return;
        }
@@ -426,9 +226,9 @@ static void test_post_teardown(const void *test_data)
                data->io_id = 0;
        }
 
-       if (data->alg_sk >= 0) {
-               close(data->alg_sk);
-               data->alg_sk = -1;
+       if (data->crypto) {
+               bt_crypto_unref(data->crypto);
+               data->crypto = NULL;
        }
 
        hciemu_unref(data->hciemu);
@@ -449,7 +249,6 @@ static void test_data_free(void *test_data)
                if (!user) \
                        break; \
                user->hciemu_type = HCIEMU_TYPE_LE; \
-               user->alg_sk = -1; \
                user->test_data = data; \
                tester_add_full(name, data, \
                                test_pre_setup, setup, func, NULL, \
@@ -482,21 +281,34 @@ static const struct smp_data smp_server_nval_req_2_test = {
        .req_count = G_N_ELEMENTS(srv_nval_req_1),
 };
 
+static const uint8_t smp_nval_req_3[] = { 0x01, 0xff };
+static const uint8_t smp_nval_req_3_rsp[] = { 0x05, 0x08 };
+
+static const struct smp_req_rsp srv_nval_req_2[] = {
+       { smp_nval_req_2, sizeof(smp_nval_req_3),
+                       smp_nval_req_3_rsp, sizeof(smp_nval_req_3_rsp) },
+};
+
+static const struct smp_data smp_server_nval_req_3_test = {
+       .req = srv_nval_req_2,
+       .req_count = G_N_ELEMENTS(srv_nval_req_2),
+};
+
 static const uint8_t smp_basic_req_1[] = {     0x01,   /* Pairing Request */
                                                0x03,   /* NoInputNoOutput */
                                                0x00,   /* OOB Flag */
                                                0x01,   /* Bonding - no MITM */
                                                0x10,   /* Max key size */
-                                               0x00,   /* Init. key dist. */
-                                               0x01,   /* Rsp. key dist. */
+                                               0x05,   /* Init. key dist. */
+                                               0x05,   /* Rsp. key dist. */
 };
 static const uint8_t smp_basic_req_1_rsp[] = { 0x02,   /* Pairing Response */
                                                0x03,   /* NoInputNoOutput */
                                                0x00,   /* OOB Flag */
                                                0x01,   /* Bonding - no MITM */
                                                0x10,   /* Max key size */
-                                               0x00,   /* Init. key dist. */
-                                               0x01,   /* Rsp. key dist. */
+                                               0x05,   /* Init. key dist. */
+                                               0x05,   /* Rsp. key dist. */
 };
 
 static const uint8_t smp_confirm_req_1[17] = { 0x03 };
@@ -592,61 +404,59 @@ static void pair_device_complete(uint8_t status, uint16_t length,
        tester_test_passed();
 }
 
-static const void *get_pdu(const uint8_t *data)
+static const void *get_pdu(const uint8_t *pdu)
 {
-       struct test_data *test_data = tester_get_data();
-       uint8_t opcode = data[0];
+       struct test_data *data = tester_get_data();
+       uint8_t opcode = pdu[0];
        static uint8_t buf[17];
-       uint8_t res[16];
 
        switch (opcode) {
        case 0x01: /* Pairing Request */
-               memcpy(test_data->smp_preq, data, sizeof(test_data->smp_preq));
+               memcpy(data->preq, pdu, sizeof(data->preq));
                break;
        case 0x02: /* Pairing Response */
-               memcpy(test_data->smp_prsp, data, sizeof(test_data->smp_prsp));
+               memcpy(data->prsp, pdu, sizeof(data->prsp));
                break;
        case 0x03: /* Pairing Confirm */
-               buf[0] = data[0];
-               smp_c1(test_data->smp_prnd, res);
-               swap128(res, &buf[1]);
+               buf[0] = pdu[0];
+               bt_crypto_c1(data->crypto, data->tk, data->prnd, data->prsp,
+                                       data->preq, data->ia_type, data->ia,
+                                       data->ra_type, data->ra, &buf[1]);
                return buf;
        case 0x04: /* Pairing Random */
-               buf[0] = data[0];
-               swap128(test_data->smp_prnd, &buf[1]);
+               buf[0] = pdu[0];
+               memcpy(&buf[1], data->prnd, 16);
                return buf;
        default:
                break;
        }
 
-       return data;
+       return pdu;
 }
 
 static bool verify_random(const uint8_t rnd[16])
 {
        struct test_data *data = tester_get_data();
-       uint8_t confirm[16], res[16], key[16];
-       int err;
+       uint8_t confirm[16];
 
-       err = smp_c1(data->smp_rrnd, res);
-       if (err < 0)
+       if (!bt_crypto_c1(data->crypto, data->tk, data->rrnd, data->prsp,
+                                       data->preq, data->ia_type, data->ia,
+                                       data->ra_type, data->ra, confirm))
                return false;
 
-       swap128(res, confirm);
-
-       if (memcmp(data->smp_pcnf, confirm, sizeof(data->smp_pcnf) != 0)) {
+       if (memcmp(data->pcnf, confirm, sizeof(data->pcnf) != 0)) {
                tester_warn("Confirmation values don't match");
                return false;
        }
 
        if (data->out) {
                struct bthost *bthost = hciemu_client_get_host(data->hciemu);
-               smp_s1(data->smp_rrnd, data->smp_prnd, key);
-               swap128(key, data->smp_ltk);
-               bthost_le_start_encrypt(bthost, data->handle, data->smp_ltk);
+               bt_crypto_s1(data->crypto, data->tk, data->rrnd, data->prnd,
+                                                               data->ltk);
+               bthost_le_start_encrypt(bthost, data->handle, data->ltk);
        } else {
-               smp_s1(data->smp_prnd, data->smp_rrnd, key);
-               swap128(key, data->smp_ltk);
+               bt_crypto_s1(data->crypto, data->tk, data->prnd, data->rrnd,
+                                                               data->ltk);
        }
 
        return true;
@@ -687,16 +497,16 @@ static void smp_server(const void *data, uint16_t len, void *user_data)
 
        switch (opcode) {
        case 0x01: /* Pairing Request */
-               memcpy(test_data->smp_preq, data, sizeof(test_data->smp_preq));
+               memcpy(test_data->preq, data, sizeof(test_data->preq));
                break;
        case 0x02: /* Pairing Response */
-               memcpy(test_data->smp_prsp, data, sizeof(test_data->smp_prsp));
+               memcpy(test_data->prsp, data, sizeof(test_data->prsp));
                break;
        case 0x03: /* Pairing Confirm */
-               memcpy(test_data->smp_pcnf, data + 1, 16);
+               memcpy(test_data->pcnf, data + 1, 16);
                goto next;
        case 0x04: /* Pairing Random */
-               swap128(data + 1, test_data->smp_rrnd);
+               memcpy(test_data->rrnd, data + 1, 16);
                if (!verify_random(data + 1))
                        goto failed;
                goto next;
@@ -833,6 +643,8 @@ static void setup_powered_server(const void *test_data)
                                sizeof(param), param, NULL, NULL, NULL);
        mgmt_send(data->mgmt, MGMT_OP_SET_PAIRABLE, data->mgmt_index,
                                sizeof(param), param, NULL, NULL, NULL);
+       mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
+                               sizeof(param), param, NULL, NULL, NULL);
        mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index,
                                sizeof(param), param, NULL, NULL, NULL);
        mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
@@ -868,6 +680,9 @@ int main(int argc, char *argv[])
        test_smp("SMP Server - Invalid Request 2",
                                        &smp_server_nval_req_2_test,
                                        setup_powered_server, test_server);
+       test_smp("SMP Server - Invalid Request 3",
+                                       &smp_server_nval_req_3_test,
+                                       setup_powered_server, test_server);
 
        test_smp("SMP Client - Basic Request 1",
                                        &smp_client_basic_req_1_test,
diff --git a/unit/test-avctp.c b/unit/test-avctp.c
new file mode 100644 (file)
index 0000000..8f7d5ad
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/log.h"
+
+#include "android/avctp.h"
+
+struct test_pdu {
+       bool valid;
+       const uint8_t *data;
+       size_t size;
+};
+
+struct test_data {
+       char *test_name;
+       struct test_pdu *pdu_list;
+};
+
+struct context {
+       GMainLoop *main_loop;
+       struct avctp *session;
+       guint source;
+       guint process;
+       int fd;
+       unsigned int pdu_offset;
+       const struct test_data *data;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...)                                       \
+       {                                                       \
+               .valid = true,                                  \
+               .data = data(args),                             \
+               .size = sizeof(data(args)),                     \
+       }
+
+#define define_test(name, function, args...)                           \
+       do {                                                            \
+               const struct test_pdu pdus[] = {                        \
+                       args, { }                                       \
+               };                                                      \
+               static struct test_data data;                           \
+               data.test_name = g_strdup(name);                        \
+               data.pdu_list = g_malloc(sizeof(pdus));                 \
+               memcpy(data.pdu_list, pdus, sizeof(pdus));              \
+               g_test_add_data_func(name, &data, function);            \
+       } while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       g_print("%s%s\n", prefix, str);
+}
+
+static void test_free(gconstpointer user_data)
+{
+       const struct test_data *data = user_data;
+
+       g_free(data->test_name);
+       g_free(data->pdu_list);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+       struct context *context = user_data;
+
+       if (context->process > 0)
+               g_source_remove(context->process);
+
+       g_main_loop_quit(context->main_loop);
+
+       return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       ssize_t len;
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       len = write(context->fd, pdu->data, pdu->size);
+
+       if (g_test_verbose())
+               util_hexdump('<', pdu->data, len, test_debug, "AVCTP: ");
+
+       g_assert_cmpint(len, ==, pdu->size);
+
+       context->process = 0;
+       return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+       if (!context->data->pdu_list[context->pdu_offset].valid) {
+               context_quit(context);
+               return;
+       }
+
+       context->process = g_idle_add(send_pdu, context);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       unsigned char buf[512];
+       ssize_t len;
+       int fd;
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               context->source = 0;
+               g_print("%s: cond %x\n", __func__, cond);
+               return FALSE;
+       }
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       len = read(fd, buf, sizeof(buf));
+
+       g_assert(len > 0);
+
+       if (g_test_verbose())
+               util_hexdump('>', buf, len, test_debug, "AVCTP: ");
+
+       g_assert_cmpint(len, ==, pdu->size);
+
+       g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+       context_process(context);
+
+       return TRUE;
+}
+
+static struct context *create_context(uint16_t version, gconstpointer data)
+{
+       struct context *context = g_new0(struct context, 1);
+       GIOChannel *channel;
+       int err, sv[2];
+
+       context->main_loop = g_main_loop_new(NULL, FALSE);
+       g_assert(context->main_loop);
+
+       err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+       g_assert(err == 0);
+
+       context->session = avctp_new(sv[0], 672, 672, version);
+       g_assert(context->session != NULL);
+
+       channel = g_io_channel_unix_new(sv[1]);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       context->source = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               test_handler, context);
+       g_assert(context->source > 0);
+
+       g_io_channel_unref(channel);
+
+       context->fd = sv[1];
+       context->data = data;
+
+       return context;
+}
+
+static void destroy_context(struct context *context)
+{
+       if (context->source > 0)
+               g_source_remove(context->source);
+
+       avctp_shutdown(context->session);
+
+       g_main_loop_unref(context->main_loop);
+
+       test_free(context->data);
+       g_free(context);
+}
+
+static void execute_context(struct context *context)
+{
+       g_main_loop_run(context->main_loop);
+
+       destroy_context(context);
+}
+
+static ssize_t handler(struct avctp *session,
+                                       uint8_t transaction, uint8_t *code,
+                                       uint8_t *subunit, uint8_t *operands,
+                                       size_t operand_count, void *user_data)
+{
+       DBG("transaction %d code %d subunit %d operand_count %zu",
+               transaction, *code, *subunit, operand_count);
+
+       g_assert_cmpint(transaction, ==, 0);
+       g_assert_cmpint(*code, ==, 0);
+       g_assert_cmpint(*subunit, ==, 0);
+       g_assert_cmpint(operand_count, ==, 0);
+
+       return operand_count;
+}
+
+static gboolean handler_response(struct avctp *session,
+                                       uint8_t code, uint8_t subunit,
+                                       uint8_t *operands, size_t operand_count,
+                                       void *user_data)
+{
+       struct context *context = user_data;
+
+       DBG("code 0x%02x subunit %d operand_count %zu", code, subunit,
+                                                               operand_count);
+
+       g_assert_cmpint(code, ==, 0x0a);
+       g_assert_cmpint(subunit, ==, 0);
+       g_assert_cmpint(operand_count, ==, 0);
+
+       return context_quit(context);
+}
+
+static void test_client(gconstpointer data)
+{
+       struct context *context = create_context(0x0100, data);
+
+       avctp_send_vendor_req(context->session, AVC_CTYPE_CONTROL, 0, NULL,
+                                               0, handler_response, context);
+
+       execute_context(context);
+}
+
+static void test_server(gconstpointer data)
+{
+       struct context *context = create_context(0x0100, data);
+
+       if (g_str_equal(context->data->test_name, "/TP/NFR/BV-03-C")) {
+               int ret;
+
+               ret = avctp_register_pdu_handler(context->session,
+                                       AVC_OP_VENDORDEP, handler, NULL);
+               DBG("ret %d", ret);
+               g_assert_cmpint(ret, !=, 0);
+       }
+
+       g_idle_add(send_pdu, context);
+
+       execute_context(context);
+}
+
+static void test_dummy(gconstpointer data)
+{
+       struct context *context = create_context(0x0100, data);
+
+       destroy_context(context);
+}
+
+int main(int argc, char *argv[])
+{
+       g_test_init(&argc, &argv, NULL);
+
+       if (g_test_verbose())
+               __btd_log_init("*", 0);
+
+       /* Connection Channel Management tests */
+
+       /*
+        * Tests are checking that IUT is able to request establishing
+        * channels, since we already have connection through socketpair
+        * the tests are dummy.
+        */
+       define_test("/TP/CCM/BV-01-C", test_dummy, raw_pdu(0x00));
+       define_test("/TP/CCM/BV-02-C", test_dummy, raw_pdu(0x00));
+       define_test("/TP/CCM/BV-03-C", test_dummy, raw_pdu(0x00));
+       define_test("/TP/CCM/BV-04-C", test_dummy, raw_pdu(0x00));
+
+       /* Non-Fragmented Messages tests */
+
+       define_test("/TP/NFR/BV-01-C", test_client,
+                               raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00));
+
+       define_test("/TP/NFR/BV-02-C", test_server,
+                               raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00),
+                               raw_pdu(0x02, 0x11, 0x0e, 0x0a, 0x00, 0x00));
+
+       define_test("/TP/NFR/BV-03-C", test_server,
+                               raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00),
+                               raw_pdu(0x02, 0x11, 0x0e, 0x00, 0x00, 0x00));
+
+       define_test("/TP/NFR/BV-04-C", test_client,
+                               raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x00, 0x00),
+                               raw_pdu(0x02, 0x11, 0x0e, 0x0a, 0x00, 0x00));
+
+       define_test("/TP/NFR/BI-01-C", test_server,
+                               raw_pdu(0x00, 0xff, 0xff, 0x00, 0x00, 0x00),
+                               raw_pdu(0x03, 0xff, 0xff));
+
+       return g_test_run();
+}
index fb555b8..8fe5ce3 100644 (file)
@@ -41,6 +41,7 @@
 
 struct test_pdu {
        bool valid;
+       bool fragmented;
        const uint8_t *data;
        size_t size;
 };
@@ -59,10 +60,18 @@ struct test_data {
                .size = sizeof(data(args)),                     \
        }
 
+#define frg_pdu(args...) \
+       {                                                       \
+               .valid = true,                                  \
+               .fragmented = true,                             \
+               .data = data(args),                             \
+               .size = sizeof(data(args)),                     \
+       }
+
 #define define_test(name, function, args...) \
        do {                                                            \
                const struct test_pdu pdus[] = {                        \
-                       args, { }, { }                                  \
+                       args, { }                                       \
                };                                                      \
                static struct test_data data;                           \
                data.test_name = g_strdup(name);                        \
@@ -77,6 +86,7 @@ struct context {
        struct avdtp_local_sep *sep;
        struct avdtp_stream *stream;
        guint source;
+       guint process;
        int fd;
        int mtu;
        gboolean pending_open;
@@ -104,6 +114,9 @@ static gboolean context_quit(gpointer user_data)
 {
        struct context *context = user_data;
 
+       if (context->process > 0)
+               g_source_remove(context->process);
+
        g_main_loop_quit(context->main_loop);
 
        return FALSE;
@@ -122,11 +135,15 @@ static gboolean send_pdu(gpointer user_data)
        if (g_test_verbose())
                util_hexdump('<', pdu->data, len, test_debug, "AVDTP: ");
 
-       g_assert(len == (ssize_t) pdu->size);
+       g_assert_cmpint(len, ==, pdu->size);
 
        if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-02-C"))
                g_timeout_add_seconds(1, context_quit, context);
 
+       if (pdu->fragmented)
+               return send_pdu(user_data);
+
+       context->process = 0;
        return FALSE;
 }
 
@@ -137,7 +154,7 @@ static void context_process(struct context *context)
                return;
        }
 
-       g_idle_add(send_pdu, context);
+       context->process = g_idle_add(send_pdu, context);
 }
 
 static gboolean transport_open(struct avdtp_stream *stream)
@@ -162,8 +179,10 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
 
        pdu = &context->data->pdu_list[context->pdu_offset++];
 
-       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               context->source = 0;
                return FALSE;
+       }
 
        fd = g_io_channel_unix_get_fd(channel);
 
@@ -174,7 +193,7 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
        if (g_test_verbose())
                util_hexdump('>', buf, len, test_debug, "AVDTP: ");
 
-       g_assert((size_t) len == pdu->size);
+       g_assert_cmpint(len, ==, pdu->size);
 
        g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
 
@@ -191,12 +210,14 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
                g_assert_cmpint(ret, ==, 0);
        }
 
-       context_process(context);
+       if (!pdu->fragmented)
+               context_process(context);
 
        return TRUE;
 }
 
-static struct context *create_context(uint16_t version, gconstpointer data)
+static struct context *context_new(uint16_t version, uint16_t imtu,
+                                       uint16_t omtu, gconstpointer data)
 {
        struct context *context = g_new0(struct context, 1);
        GIOChannel *channel;
@@ -208,7 +229,7 @@ static struct context *create_context(uint16_t version, gconstpointer data)
        err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
        g_assert(err == 0);
 
-       context->session = avdtp_new(sv[0], 672, 672, version);
+       context->session = avdtp_new(sv[0], imtu, omtu, version);
        g_assert(context->session != NULL);
 
        channel = g_io_channel_unix_new(sv[1]);
@@ -230,11 +251,17 @@ static struct context *create_context(uint16_t version, gconstpointer data)
        return context;
 }
 
+static struct context *create_context(uint16_t version, gconstpointer data)
+{
+       return context_new(version, 672, 672, data);
+}
+
 static void execute_context(struct context *context)
 {
        g_main_loop_run(context->main_loop);
 
-       g_source_remove(context->source);
+       if (context->source > 0)
+               g_source_remove(context->source);
        avdtp_unref(context->session);
 
        g_main_loop_unref(context->main_loop);
@@ -245,8 +272,8 @@ static void execute_context(struct context *context)
 
 static gboolean sep_getcap_ind(struct avdtp *session,
                                        struct avdtp_local_sep *sep,
-                                       gboolean get_all, GSList **caps,
-                                       uint8_t *err, void *user_data)
+                                       GSList **caps, uint8_t *err,
+                                       void *user_data)
 {
        struct avdtp_service_capability *media_transport, *media_codec;
        struct avdtp_media_codec_capability *codec_caps;
@@ -500,6 +527,21 @@ static void test_server_1_3(gconstpointer data)
        avdtp_unregister_sep(sep);
 }
 
+static void test_server_1_3_sink(gconstpointer data)
+{
+       struct context *context = create_context(0x0103, data);
+       struct avdtp_local_sep *sep;
+
+       sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+                                       0x00, TRUE, &sep_ind, NULL, context);
+
+       g_idle_add(send_pdu, context);
+
+       execute_context(context);
+
+       avdtp_unregister_sep(sep);
+}
+
 static void test_server_0_sep(gconstpointer data)
 {
        struct context *context = create_context(0x0100, data);
@@ -509,6 +551,62 @@ static void test_server_0_sep(gconstpointer data)
        execute_context(context);
 }
 
+static gboolean sep_getcap_ind_frg(struct avdtp *session,
+                                       struct avdtp_local_sep *sep,
+                                       GSList **caps, uint8_t *err,
+                                       void *user_data)
+{
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct avdtp_service_capability *content_protection;
+       struct avdtp_media_codec_capability *codec_caps;
+       uint8_t cap[4] = { 0xff, 0xff, 2, 64 };
+       uint8_t frg_cap[96] = {};
+
+       *caps = NULL;
+
+       media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+                                               NULL, 0);
+
+       *caps = g_slist_append(*caps, media_transport);
+
+       codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap));
+       codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+       codec_caps->media_codec_type = 0x00;
+       memcpy(codec_caps->data, cap, sizeof(cap));
+
+       media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps,
+                                       sizeof(*codec_caps) + sizeof(cap));
+
+       *caps = g_slist_append(*caps, media_codec);
+       g_free(codec_caps);
+
+       content_protection = avdtp_service_cap_new(AVDTP_CONTENT_PROTECTION,
+                                               frg_cap, sizeof(frg_cap));
+       *caps = g_slist_append(*caps, content_protection);
+
+       return TRUE;
+}
+
+static struct avdtp_sep_ind sep_ind_frg = {
+       .get_capability         = sep_getcap_ind_frg,
+};
+
+static void test_server_frg(gconstpointer data)
+{
+       struct context *context = context_new(0x0100, 48, 48, data);
+       struct avdtp_local_sep *sep;
+
+       sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
+                                               0x00, TRUE, &sep_ind_frg,
+                                               NULL, context);
+
+       g_idle_add(send_pdu, context);
+
+       execute_context(context);
+
+       avdtp_unregister_sep(sep);
+}
+
 static void discover_cb(struct avdtp *session, GSList *seps,
                                struct avdtp_error *err, void *user_data)
 {
@@ -544,6 +642,12 @@ static void discover_cb(struct avdtp *session, GSList *seps,
        g_assert(err == NULL);
        g_assert_cmpint(g_slist_length(seps), !=, 0);
 
+       if (g_str_equal(context->data->test_name, "/TP/SIG/FRA/BV-02-C")) {
+               g_assert(err == NULL);
+               context_quit(context);
+               return;
+       }
+
        rsep = avdtp_find_remote_sep(session, context->sep);
        g_assert(rsep != NULL);
 
@@ -604,6 +708,23 @@ static void test_client_1_3(gconstpointer data)
        avdtp_unregister_sep(sep);
 }
 
+static void test_client_frg(gconstpointer data)
+{
+       struct context *context = context_new(0x0100, 48, 48, data);
+       struct avdtp_local_sep *sep;
+
+       sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+                                       0x00, TRUE, NULL, &sep_cfm,
+                                       context);
+       context->sep = sep;
+
+       avdtp_discover(context->session, discover_cb, context);
+
+       execute_context(context);
+
+       avdtp_unregister_sep(sep);
+}
+
 int main(int argc, char *argv[])
 {
        g_test_init(&argc, &argv, NULL);
@@ -805,7 +926,7 @@ int main(int argc, char *argv[])
                        raw_pdu(0x02, 0x01, 0x04, 0x00),
                        raw_pdu(0x10, 0x0c, 0x04),
                        raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
-                               0xff, 0xff, 0x02, 0x40));
+                               0xff, 0xff, 0x02, 0x40, 0x08, 0x00));
        define_test("/TP/SIG/SMG/BV-27-C", test_server_1_3,
                        raw_pdu(0x00, 0x01),
                        raw_pdu(0x02, 0x01, 0x04, 0x00),
@@ -1086,5 +1207,128 @@ int main(int argc, char *argv[])
                        raw_pdu(0xa0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
                                0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
 
+       /*
+        * Signaling Message Fragmentation Service
+        *
+        * verify that the IUT (INT and ACP) fragments the signaling messages
+        * that cannot fit in a single L2CAP packet.
+        */
+       define_test("/TP/SIG/FRA/BV-01-C", test_server_frg,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       frg_pdu(0x16, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00,
+                               0x00, 0xff, 0xff, 0x02, 0x40, 0x04, 0x60, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00),
+                       frg_pdu(0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00),
+                       raw_pdu(0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00));
+       define_test("/TP/SIG/FRA/BV-02-C", test_client_frg,
+                       raw_pdu(0xb0, 0x01),
+                       raw_pdu(0xb2, 0x01, 0x04, 0x00),
+                       raw_pdu(0xc0, 0x02, 0x04),
+                       frg_pdu(0xc6, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00,
+                               0x00, 0xff, 0xff, 0x02, 0x40, 0x04, 0x60, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00),
+                       frg_pdu(0xca, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00),
+                       raw_pdu(0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00));
+
+       /*
+        * Delay Reporting
+        *
+        * Verify that the stream management signaling procedure of delay
+        * reporting is implemented according to its specification in AVDTP.
+        */
+       define_test("/TP/SIG/SYN/BV-01-C", test_server_1_3_sink,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x08),
+                       raw_pdu(0x10, 0x0c, 0x04),
+                       raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40, 0x08, 0x00));
+       define_test("/TP/SIG/SYN/BV-02-C", test_client_1_3,
+                       raw_pdu(0xd0, 0x01),
+                       raw_pdu(0xd2, 0x01, 0x04, 0x00),
+                       raw_pdu(0xe0, 0x0c, 0x04),
+                       raw_pdu(0xe2, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40, 0x0f, 0x00, 0x08, 0x00),
+                       raw_pdu(0xf0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+                               0x00));
+       define_test("/TP/SIG/SYN/BV-03-C", test_server_1_3_sink,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x08),
+                       raw_pdu(0x10, 0x0c, 0x04),
+                       raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40, 0x08, 0x00),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+                               0x00),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x00, 0x0d, 0x04, 0x00, 0x00));
+       define_test("/TP/SIG/SYN/BV-04-C", test_client_1_3,
+                       raw_pdu(0x10, 0x01),
+                       raw_pdu(0x12, 0x01, 0x04, 0x00),
+                       raw_pdu(0x20, 0x0c, 0x04),
+                       raw_pdu(0x22, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40, 0x0f, 0x00, 0x08, 0x00),
+                       raw_pdu(0x30, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+                               0x00),
+                       raw_pdu(0x32, 0x03),
+                       raw_pdu(0x40, 0x0d, 0x04, 0x00, 0x00));
+       define_test("/TP/SIG/SYN/BV-05-C", test_server_1_3,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x0c, 0x04),
+                       raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40, 0x08, 0x00),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+                               0x00),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x32, 0x06),
+                       raw_pdu(0x40, 0x0d, 0x04, 0x00, 0x00),
+                       raw_pdu(0x42, 0x0d));
+       define_test("/TP/SIG/SYN/BV-06-C", test_server_1_3,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x0c, 0x04),
+                       raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40, 0x08, 0x00),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+                               0x00),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x32, 0x06),
+                       raw_pdu(0x40, 0x07, 0x04),
+                       raw_pdu(0x42, 0x07),
+                       raw_pdu(0x50, 0x0d, 0x04, 0x00, 0x00),
+                       raw_pdu(0x52, 0x0d));
+
        return g_test_run();
 }
diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
new file mode 100644 (file)
index 0000000..c41f2e6
--- /dev/null
@@ -0,0 +1,1627 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/log.h"
+#include "lib/bluetooth.h"
+
+#include "android/avctp.h"
+#include "android/avrcp-lib.h"
+
+struct test_pdu {
+       bool valid;
+       bool fragmented;
+       bool browse;
+       const uint8_t *data;
+       size_t size;
+};
+
+struct test_data {
+       char *test_name;
+       struct test_pdu *pdu_list;
+};
+
+struct context {
+       GMainLoop *main_loop;
+       struct avrcp *session;
+       guint source;
+       guint browse_source;
+       guint process;
+       int fd;
+       int browse_fd;
+       unsigned int pdu_offset;
+       const struct test_data *data;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...)                                       \
+       {                                                       \
+               .valid = true,                                  \
+               .data = data(args),                             \
+               .size = sizeof(data(args)),                     \
+       }
+
+#define brs_pdu(args...)                                       \
+       {                                                       \
+               .valid = true,                                  \
+               .browse = true,                                 \
+               .data = data(args),                             \
+               .size = sizeof(data(args)),                     \
+       }
+
+#define frg_pdu(args...)                                       \
+       {                                                       \
+               .valid = true,                                  \
+               .fragmented = true,                             \
+               .data = data(args),                             \
+               .size = sizeof(data(args)),                     \
+       }
+
+#define define_test(name, function, args...)                           \
+       do {                                                            \
+               const struct test_pdu pdus[] = {                        \
+                       args, { }                                       \
+               };                                                      \
+               static struct test_data data;                           \
+               data.test_name = g_strdup(name);                        \
+               data.pdu_list = g_malloc(sizeof(pdus));                 \
+               memcpy(data.pdu_list, pdus, sizeof(pdus));              \
+               g_test_add_data_func(name, &data, function);            \
+       } while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       g_print("%s%s\n", prefix, str);
+}
+
+static void test_free(gconstpointer user_data)
+{
+       const struct test_data *data = user_data;
+
+       g_free(data->test_name);
+       g_free(data->pdu_list);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+       struct context *context = user_data;
+
+       if (context->process > 0)
+               g_source_remove(context->process);
+
+       g_main_loop_quit(context->main_loop);
+
+       return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       ssize_t len;
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       if (pdu->browse)
+               len = write(context->browse_fd, pdu->data, pdu->size);
+       else
+               len = write(context->fd, pdu->data, pdu->size);
+
+       if (g_test_verbose())
+               util_hexdump('<', pdu->data, len, test_debug, "AVRCP: ");
+
+       g_assert_cmpint(len, ==, pdu->size);
+
+       if (pdu->fragmented)
+               return send_pdu(user_data);
+
+       context->process = 0;
+       return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+       if (!context->data->pdu_list[context->pdu_offset].valid) {
+               context_quit(context);
+               return;
+       }
+
+       context->process = g_idle_add(send_pdu, context);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       unsigned char buf[512];
+       ssize_t len;
+       int fd;
+
+       DBG("");
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               context->source = 0;
+               g_print("%s: cond %x\n", __func__, cond);
+               return FALSE;
+       }
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       len = read(fd, buf, sizeof(buf));
+
+       g_assert(len > 0);
+
+       if (g_test_verbose())
+               util_hexdump('>', buf, len, test_debug, "AVRCP: ");
+
+       g_assert_cmpint(len, ==, pdu->size);
+
+       g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+       if (!pdu->fragmented)
+               context_process(context);
+
+       return TRUE;
+}
+
+static gboolean browse_test_handler(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       unsigned char buf[512];
+       ssize_t len;
+       int fd;
+
+       DBG("");
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               context->browse_source = 0;
+               g_print("%s: cond %x\n", __func__, cond);
+               return FALSE;
+       }
+
+       fd = g_io_channel_unix_get_fd(channel);
+
+       len = read(fd, buf, sizeof(buf));
+
+       g_assert(len > 0);
+
+       if (g_test_verbose())
+               util_hexdump('>', buf, len, test_debug, "AVRCP: ");
+
+       g_assert_cmpint(len, ==, pdu->size);
+
+       g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+       if (!pdu->fragmented)
+               context_process(context);
+
+       return TRUE;
+}
+
+static struct context *create_context(uint16_t version, gconstpointer data)
+{
+       struct context *context = g_new0(struct context, 1);
+       GIOChannel *channel;
+       int err, sv[2];
+
+       DBG("");
+
+       context->main_loop = g_main_loop_new(NULL, FALSE);
+       g_assert(context->main_loop);
+
+       /* Control channel setup */
+
+       err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+       g_assert(!err);
+
+       context->session = avrcp_new(sv[0], 672, 672, version);
+       g_assert(context->session != NULL);
+
+       channel = g_io_channel_unix_new(sv[1]);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       context->source = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               test_handler, context);
+       g_assert(context->source > 0);
+
+       g_io_channel_unref(channel);
+
+       context->fd = sv[1];
+
+       /* Browsing channel setup */
+
+       err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+       g_assert(!err);
+
+       err = avrcp_connect_browsing(context->session, sv[0], 672, 672);
+       g_assert(!err);
+
+       channel = g_io_channel_unix_new(sv[1]);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       context->browse_source = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               browse_test_handler, context);
+       g_assert(context->browse_source > 0);
+
+       g_io_channel_unref(channel);
+
+       context->browse_fd = sv[1];
+
+       context->data = data;
+
+       return context;
+}
+
+static void destroy_context(struct context *context)
+{
+       if (context->source > 0)
+               g_source_remove(context->source);
+
+       avrcp_shutdown(context->session);
+
+       if (context->browse_source > 0)
+               g_source_remove(context->browse_source);
+
+       g_main_loop_unref(context->main_loop);
+
+       test_free(context->data);
+       g_free(context);
+}
+
+static void test_dummy(gconstpointer data)
+{
+       struct context *context =  create_context(0x0100, data);
+
+       destroy_context(context);
+}
+
+static void execute_context(struct context *context)
+{
+       g_main_loop_run(context->main_loop);
+
+       destroy_context(context);
+}
+
+static bool handle_play(struct avrcp *session, bool pressed, void *user_data)
+{
+       DBG("");
+
+       return true;
+}
+
+static bool handle_volume_up(struct avrcp *session, bool pressed,
+                                                       void *user_data)
+{
+       DBG("");
+
+       return true;
+}
+
+static bool handle_channel_up(struct avrcp *session, bool pressed,
+                                                       void *user_data)
+{
+       DBG("");
+
+       return true;
+}
+
+static bool handle_select(struct avrcp *session, bool pressed, void *user_data)
+{
+       DBG("");
+
+       return true;
+}
+
+static bool handle_vendor_uniq(struct avrcp *session, bool pressed,
+                                                               void *user_data)
+{
+       DBG("");
+
+       return true;
+}
+
+static const struct avrcp_passthrough_handler passthrough_handlers[] = {
+               { AVC_PLAY, handle_play },
+               { AVC_VOLUME_UP, handle_volume_up },
+               { AVC_CHANNEL_UP, handle_channel_up },
+               { AVC_SELECT, handle_select },
+               { AVC_VENDOR_UNIQUE, handle_vendor_uniq },
+               { },
+};
+
+static int get_capabilities(struct avrcp *session, uint8_t transaction,
+                                                       void *user_data)
+{
+       return -EINVAL;
+}
+
+static int list_attributes(struct avrcp *session, uint8_t transaction,
+                                                       void *user_data)
+{
+       DBG("");
+
+       avrcp_list_player_attributes_rsp(session, transaction, 0, NULL);
+
+       return -EAGAIN;
+}
+
+static int get_attribute_text(struct avrcp *session, uint8_t transaction,
+                                       uint8_t number, uint8_t *attrs,
+                                       void *user_data)
+{
+       const char *text[number];
+
+       DBG("");
+
+       if (number) {
+               memset(text, 0, number);
+               text[0] = "equalizer";
+       }
+
+       avrcp_get_player_attribute_text_rsp(session, transaction, number, attrs,
+                                                                       text);
+
+       return -EAGAIN;
+}
+
+static int list_values(struct avrcp *session, uint8_t transaction,
+                                               uint8_t attr, void *user_data)
+{
+       DBG("");
+
+       avrcp_list_player_values_rsp(session, transaction, 0, NULL);
+
+       return -EINVAL;
+}
+
+static int get_value_text(struct avrcp *session, uint8_t transaction,
+                               uint8_t attr, uint8_t number, uint8_t *values,
+                               void *user_data)
+{
+       const char *text[number];
+
+       DBG("");
+
+       if (number) {
+               memset(text, 0, number);
+               text[0] = "on";
+       }
+
+       avrcp_get_player_values_text_rsp(session, transaction, number,
+                                                               values, text);
+
+       return -EINVAL;
+}
+
+static int get_value(struct avrcp *session, uint8_t transaction,
+                       uint8_t number, uint8_t *attrs, void *user_data)
+{
+       uint8_t values[number];
+
+       DBG("");
+
+       memset(values, 0, number);
+
+       avrcp_get_current_player_value_rsp(session, transaction, number, attrs,
+                                                                       values);
+
+       return -EAGAIN;
+}
+
+static int set_value(struct avrcp *session, uint8_t transaction,
+                       uint8_t number, uint8_t *attrs, void *user_data)
+{
+       DBG("");
+
+       avrcp_set_player_value_rsp(session, transaction);
+
+       return -EAGAIN;
+}
+
+static int get_play_status(struct avrcp *session, uint8_t transaction,
+                                                       void *user_data)
+{
+       DBG("");
+
+       avrcp_get_play_status_rsp(session, transaction, 0xaaaaaaaa, 0xbbbbbbbb,
+                                                                       0x00);
+
+       return -EAGAIN;
+}
+
+static int get_element_attributes(struct avrcp *session, uint8_t transaction,
+                                       uint64_t uid, uint8_t number,
+                                       uint32_t *attrs, void *user_data)
+{
+       DBG("");
+
+       avrcp_get_element_attrs_rsp(session, transaction, NULL, 0);
+
+       return -EAGAIN;
+}
+
+static int register_notification(struct avrcp *session, uint8_t transaction,
+                                       uint8_t event, uint32_t interval,
+                                       void *user_data)
+{
+       struct context *context = user_data;
+       uint8_t pdu[9];
+       size_t pdu_len;
+
+       DBG("");
+
+       pdu[0] = event;
+       pdu_len = 1;
+
+       switch (event) {
+       case AVRCP_EVENT_TRACK_CHANGED:
+               if (g_str_equal(context->data->test_name, "/TP/NFY/BV-05-C") ||
+                       g_str_equal(context->data->test_name,
+                                                       "/TP/NFY/BV-08-C"))
+                       memset(&pdu[1], 0, 8);
+               else
+                       memset(&pdu[1], 0xff, 8);
+
+               pdu_len += 8;
+               break;
+       case AVRCP_EVENT_SETTINGS_CHANGED:
+               pdu[1] = 0x01;
+               pdu[2] = 0x01;
+               pdu[3] = 0x02;
+               pdu_len = 4;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_INTERIM,
+                                               pdu, pdu_len);
+
+       avrcp_register_notification_rsp(session, transaction, AVC_CTYPE_CHANGED,
+                                               pdu, pdu_len);
+
+       return -EAGAIN;
+}
+
+static int set_volume(struct avrcp *session, uint8_t transaction,
+                                       uint8_t volume, void *user_data)
+{
+       DBG("");
+
+       avrcp_set_volume_rsp(session, transaction, volume);
+
+       return -EAGAIN;
+}
+
+static int set_addressed(struct avrcp *session, uint8_t transaction,
+                                               uint16_t id, void *user_data)
+{
+       DBG("");
+
+
+       avrcp_set_addressed_player_rsp(session, transaction,
+                                                       AVRCP_STATUS_SUCCESS);
+
+       return -EAGAIN;
+}
+
+static int get_folder_items(struct avrcp *session, uint8_t transaction,
+                               uint8_t scope, uint32_t start, uint32_t end,
+                               uint16_t number, uint32_t *attrs,
+                               void *user_data)
+{
+       struct context *context = user_data;
+
+       DBG("");
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BI-02-C"))
+               return -ERANGE;
+
+       if (start > 1)
+               return -ERANGE;
+
+       avrcp_get_folder_items_rsp(session, transaction, 0xabcd, 0, NULL, NULL,
+                                                                       NULL);
+
+       return -EAGAIN;
+}
+
+static int change_path(struct avrcp *session, uint8_t transaction,
+                                       uint16_t counter, uint8_t direction,
+                                       uint64_t uid, void *user_data)
+{
+       DBG("");
+
+       if (!uid)
+               return -ENOTDIR;
+
+       avrcp_change_path_rsp(session, transaction, 0);
+
+       return -EAGAIN;
+}
+
+static int get_item_attributes(struct avrcp *session, uint8_t transaction,
+                                       uint8_t scope, uint64_t uid,
+                                       uint16_t counter, uint8_t number,
+                                       uint32_t *attrs, void *user_data)
+{
+       DBG("");
+
+       avrcp_get_item_attributes_rsp(session, transaction, 0, NULL, NULL);
+
+       return -EAGAIN;
+}
+
+static int play_item(struct avrcp *session, uint8_t transaction, uint8_t scope,
+                       uint64_t uid, uint16_t counter, void *user_data)
+{
+       DBG("");
+
+       if (!uid)
+               return -ENOENT;
+
+       avrcp_play_item_rsp(session, transaction);
+
+       return -EAGAIN;
+}
+
+static int search(struct avrcp *session, uint8_t transaction,
+                                       const char *string, void *user_data)
+{
+       DBG("");
+
+       avrcp_search_rsp(session, transaction, 0xaabb, 0);
+
+       return -EAGAIN;
+}
+
+static int add_to_now_playing(struct avrcp *session, uint8_t transaction,
+                               uint8_t scope, uint64_t uid, uint16_t counter,
+                               void *user_data)
+{
+       DBG("");
+
+       if (!uid)
+               return -ENOENT;
+
+       avrcp_add_to_now_playing_rsp(session, transaction);
+
+       return -EAGAIN;
+}
+
+static const struct avrcp_control_ind control_ind = {
+       .get_capabilities = get_capabilities,
+       .list_attributes = list_attributes,
+       .get_attribute_text = get_attribute_text,
+       .list_values = list_values,
+       .get_value_text = get_value_text,
+       .get_value = get_value,
+       .set_value = set_value,
+       .get_play_status = get_play_status,
+       .get_element_attributes = get_element_attributes,
+       .register_notification = register_notification,
+       .set_volume = set_volume,
+       .set_addressed = set_addressed,
+       .get_folder_items = get_folder_items,
+       .change_path = change_path,
+       .get_item_attributes = get_item_attributes,
+       .play_item = play_item,
+       .search = search,
+       .add_to_now_playing = add_to_now_playing,
+};
+
+static void test_server(gconstpointer data)
+{
+       struct context *context = create_context(0x0100, data);
+
+       avrcp_set_passthrough_handlers(context->session, passthrough_handlers,
+                                                               context);
+       avrcp_register_player(context->session, &control_ind, NULL, context);
+
+       g_idle_add(send_pdu, context);
+
+       execute_context(context);
+}
+
+static void test_client(gconstpointer data)
+{
+       struct context *context = create_context(0x0100, data);
+
+       if (g_str_equal(context->data->test_name, "/TP/MPS/BV-01-C"))
+               avrcp_set_addressed_player(context->session, 0xabcd);
+
+       if (g_str_equal(context->data->test_name, "/TP/MPS/BV-03-C"))
+               avrcp_set_browsed_player(context->session, 0xabcd);
+
+       if (g_str_equal(context->data->test_name, "/TP/MPS/BV-08-C"))
+               avrcp_get_folder_items(context->session,
+                                       AVRCP_MEDIA_PLAYER_LIST, 0, 2, 0, NULL);
+
+       if (g_str_equal(context->data->test_name, "/TP/MPS/BV-01-I"))
+               avrcp_get_folder_items(context->session,
+                                       AVRCP_MEDIA_PLAYER_LIST, 0, 2, 0, NULL);
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-01-C"))
+               avrcp_get_folder_items(context->session,
+                                       AVRCP_MEDIA_PLAYER_VFS, 0, 2, 0, NULL);
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-04-C"))
+               avrcp_change_path(context->session, 0x01, 0x01, 0xaabb);
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/CB/BV-07-C"))
+               avrcp_get_item_attributes(context->session,
+                                       AVRCP_MEDIA_PLAYER_VFS, 0x01, 0xaabb,
+                                       0, NULL);
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/SRC/BV-01-C"))
+               avrcp_search(context->session, "Country");
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/SRC/BV-03-C"))
+               avrcp_get_folder_items(context->session, AVRCP_MEDIA_SEARCH,
+                                               0, 2, 0, NULL);
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/SRC/BV-05-C"))
+               avrcp_get_item_attributes(context->session,
+                                       AVRCP_MEDIA_SEARCH, 0x01, 0xaabb,
+                                       0, NULL);
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-01-C"))
+               avrcp_play_item(context->session, AVRCP_MEDIA_NOW_PLAYING, 1,
+                                                                       1);
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-03-C"))
+               avrcp_add_to_now_playing(context->session,
+                                       AVRCP_MEDIA_NOW_PLAYING, 0x01, 0xaabb);
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-05-C"))
+               avrcp_get_folder_items(context->session,
+                                       AVRCP_MEDIA_NOW_PLAYING, 0, 2, 0, NULL);
+
+       if (g_str_equal(context->data->test_name, "/TP/MCN/NP/BV-08-C"))
+               avrcp_get_item_attributes(context->session,
+                                       AVRCP_MEDIA_NOW_PLAYING, 0x01, 0xaabb,
+                                       0, NULL);
+
+       if (g_str_equal(context->data->test_name, "/TP/CFG/BV-01-C"))
+               avrcp_get_capabilities(context->session, CAP_EVENTS_SUPPORTED);
+
+       if (g_str_equal(context->data->test_name, "/TP/PAS/BV-01-C"))
+               avrcp_list_player_attributes(context->session);
+
+       if (g_str_equal(context->data->test_name, "/TP/PAS/BV-03-C")) {
+               uint8_t attrs[2] = { AVRCP_ATTRIBUTE_EQUALIZER,
+                                               AVRCP_ATTRIBUTE_REPEAT_MODE };
+
+               avrcp_get_player_attribute_text(context->session, sizeof(attrs),
+                                                                       attrs);
+       }
+
+       if (g_str_equal(context->data->test_name, "/TP/PAS/BV-05-C"))
+               avrcp_list_player_values(context->session,
+                                               AVRCP_ATTRIBUTE_EQUALIZER);
+
+       if (g_str_equal(context->data->test_name, "/TP/PAS/BV-07-C")) {
+               uint8_t values[2] = { AVRCP_EQUALIZER_OFF, AVRCP_EQUALIZER_ON };
+
+               avrcp_get_player_value_text(context->session,
+                                               AVRCP_ATTRIBUTE_EQUALIZER,
+                                               sizeof(values), values);
+       }
+
+       if (g_str_equal(context->data->test_name, "/TP/PAS/BV-09-C")) {
+               uint8_t attrs[2] = { AVRCP_ATTRIBUTE_EQUALIZER,
+                                               AVRCP_ATTRIBUTE_REPEAT_MODE };
+
+               avrcp_get_current_player_value(context->session, sizeof(attrs),
+                                                                       attrs);
+       }
+
+       if (g_str_equal(context->data->test_name, "/TP/PAS/BV-11-C")) {
+               uint8_t attrs[2] = { AVRCP_ATTRIBUTE_EQUALIZER,
+                                               AVRCP_ATTRIBUTE_REPEAT_MODE };
+               uint8_t values[2] = { 0xaa, 0xff };
+
+               avrcp_set_player_value(context->session, sizeof(attrs), attrs,
+                                                               values);
+       }
+
+       if (g_str_equal(context->data->test_name, "/TP/MDI/BV-01-C"))
+               avrcp_get_play_status(context->session);
+
+       if (g_str_equal(context->data->test_name, "/TP/MDI/BV-03-C"))
+               avrcp_get_element_attributes(context->session);
+
+       if (g_str_equal(context->data->test_name, "/TP/NFY/BV-01-C"))
+               avrcp_register_notification(context->session,
+                                               AVRCP_EVENT_STATUS_CHANGED, 0);
+
+       if (g_str_equal(context->data->test_name, "/TP/BGN/BV-01-I"))
+               avrcp_send_passthrough(context->session, IEEEID_BTSIG,
+                                               AVC_VENDOR_NEXT_GROUP);
+
+       if (g_str_equal(context->data->test_name, "/TP/BGN/BV-02-I"))
+               avrcp_send_passthrough(context->session, IEEEID_BTSIG,
+                                               AVC_VENDOR_PREV_GROUP);
+
+       if (g_str_equal(context->data->test_name, "/TP/VLH/BV-01-C"))
+               avrcp_set_volume(context->session, 0x00);
+
+       execute_context(context);
+}
+
+int main(int argc, char *argv[])
+{
+       g_test_init(&argc, &argv, NULL);
+
+       if (g_test_verbose())
+               __btd_log_init("*", 0);
+
+       /* Media Player Selection Commands and Notifications */
+
+       /* SetAddressedPlayer - CT */
+       define_test("/TP/MPS/BV-01-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+                               0x00, 0x19, 0x58, 0x60, 0x00, 0x00,
+                               0x02, 0xab, 0xcd));
+
+       /* SetAddressedPlayer - TG */
+       define_test("/TP/MPS/BV-02-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_SET_ADDRESSED_PLAYER,
+                               0x00, 0x00, 0x02, 0xab, 0xcd),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_STABLE,
+                               0x48, 0x00, 0x00, 0x19, 0x58,
+                               AVRCP_SET_ADDRESSED_PLAYER,
+                               0x00, 0x00, 0x01, 0x04));
+
+       /* SetBrowsedPlayer - CT */
+       define_test("/TP/MPS/BV-03-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x70, 0x00, 0x02,
+                               0xab, 0xcd));
+
+       /* GetFolderItems - CT */
+       define_test("/TP/MPS/BV-08-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_PLAYER_LIST,
+                               0x00, 0x00, 0x00, 0x00, /* start */
+                               0x00, 0x00, 0x00, 0x02, /* end */
+                               0x00));
+
+       /* GetFolderItems - TG */
+       define_test("/TP/MPS/BV-09-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_PLAYER_LIST,
+                               0x00, 0x00, 0x00, 0x00, /* start */
+                               0x00, 0x00, 0x00, 0x02, /* end */
+                               0x00),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00));
+
+       /*
+        * Media Content Navigation Commands and Notifications for Content
+        * Browsing.
+        */
+
+       /* GetFolderItems - Virtual FS - CT */
+       define_test("/TP/MCN/CB/BV-01-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+                               0x00, 0x00, 0x00, 0x00, /* start */
+                               0x00, 0x00, 0x00, 0x02, /* end */
+                               0x00));
+
+       /* GetFolderItems - Virtual FS - TG */
+       define_test("/TP/MCN/CB/BV-02-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+                               0x00, 0x00, 0x00, 0x00, /* start */
+                               0x00, 0x00, 0x00, 0x02, /* end */
+                               0x00),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00));
+
+       /* ChangePath - CT */
+       define_test("/TP/MCN/CB/BV-04-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+                               0x00, 0x0b,
+                               0xaa, 0xbb,             /* counter */
+                               0x01,                   /* direction */
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01  /* Folder UID */));
+
+       /* ChangePath - TG */
+       define_test("/TP/MCN/CB/BV-05-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+                               0x00, 0x0b,
+                               0xaa, 0xbb,             /* counter */
+                               0x01,                   /* direction */
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01  /* Folder UID */),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+                               0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00));
+
+       /* ChangePath - TG */
+       define_test("/TP/MCN/CB/BV-06-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+                               0x00, 0x0b,
+                               0xaa, 0xbb,             /* counter */
+                               0x00,                   /* direction */
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01  /* Folder UID */),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+                               0x00, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00));
+
+       /* GetItemAttributes - CT */
+       define_test("/TP/MCN/CB/BV-07-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+                               0x00, 0x0c, AVRCP_MEDIA_PLAYER_VFS,
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01, /* uuid */
+                               0xaa, 0xbb,             /* counter */
+                               0x00));                 /* num attr */
+
+       /* GetItemAttributes - TG */
+       define_test("/TP/MCN/CB/BV-08-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+                               0x00, 0x0c, AVRCP_MEDIA_PLAYER_VFS,
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01, /* uuid */
+                               0xaa, 0xbb,             /* counter */
+                               0x00),                  /* num attr */
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+                               0x00, 0x02, 0x04, 0x00));
+
+       /* GetFolderItems - Virtual FS - TG */
+       define_test("/TP/MCN/CB/BI-01-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+                               0x00, 0x00, 0x00, 0x01, /* start */
+                               0x00, 0x00, 0x00, 0x00, /* end */
+                               0x00),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x01, 0x0b));
+
+       /* GetFolderItems - Virtual FS - TG */
+       define_test("/TP/MCN/CB/BI-02-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+                               0x00, 0x00, 0x00, 0x00, /* start */
+                               0x00, 0x00, 0x00, 0x01, /* end */
+                               0x00),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x01, 0x0b));
+
+       /* GetFolderItems - Virtual FS - TG */
+       define_test("/TP/MCN/CB/BI-03-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_PLAYER_VFS,
+                               0x00, 0x00, 0x00, 0x02, /* start */
+                               0x00, 0x00, 0x00, 0x03, /* end */
+                               0x00),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x01, 0x0b));
+
+       /* ChangePath - TG */
+       define_test("/TP/MCN/CB/BI-04-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+                               0x00, 0x0b,
+                               0xaa, 0xbb,             /* counter */
+                               0x01,                   /* direction */
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00  /* Folder UID */),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_CHANGE_PATH,
+                               0x00, 0x01, 0x08));
+
+       /* Media Content Navigation Commands and Notifications for Search */
+
+       /* Search - CT */
+       define_test("/TP/MCN/SRC/BV-01-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, AVRCP_SEARCH,
+                               0x00, 0x0b, 0x00, 0x6a,
+                               0x00, 0x07,
+                               0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79));
+
+       define_test("/TP/MCN/SRC/BV-02-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_SEARCH,
+                               0x00, 0x0b, 0x00, 0x6a,
+                               0x00, 0x07,
+                               0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_SEARCH,
+                               0x00, 0x07, 0x04,
+                               0xaa, 0xbb,             /* counter */
+                               0x00, 0x00, 0x00, 0x00));
+
+       /* GetFolderItems - CT */
+       define_test("/TP/MCN/SRC/BV-03-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_SEARCH,
+                               0x00, 0x00, 0x00, 0x00, /* start */
+                               0x00, 0x00, 0x00, 0x02, /* end */
+                               0x00));
+
+       /* GetFolderItems - NowPlaying - TG */
+       define_test("/TP/MCN/SCR/BV-04-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_SEARCH,
+                               0x00, 0x00, 0x00, 0x00, /* start */
+                               0x00, 0x00, 0x00, 0x02, /* end */
+                               0x00),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00));
+
+       /* GetItemAttributes - CT */
+       define_test("/TP/MCN/SRC/BV-05-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+                               0x00, 0x0c, AVRCP_MEDIA_SEARCH,
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01, /* uuid */
+                               0xaa, 0xbb,             /* counter */
+                               0x00));                 /* num attr */
+
+       /* GetItemAttributes - TG */
+       define_test("/TP/MCN/SRC/BV-06-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+                               0x00, 0x0c, AVRCP_MEDIA_SEARCH,
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01, /* uid */
+                               0xaa, 0xbb,             /* counter */
+                               0x00),                  /* num attr */
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+                               0x00, 0x02, 0x04, 0x00));
+
+       /* Media Content Navigation Commands and Notifications for NowPlaying */
+
+       /* PlayItem - NowPlaying - CT */
+       define_test("/TP/MCN/NP/BV-01-C", test_client,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_PLAY_ITEM,
+                               0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+                               0x00, 0x01));
+
+       /* PlayItem - NowPlaying - TG */
+       define_test("/TP/MCN/NP/BV-02-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_PLAY_ITEM,
+                               0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+                               0x00, 0x01),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_PLAY_ITEM,
+                               0x00, 0x01, 0x04));
+
+       /* AddToNowPlaying - NowPlaying - CT */
+       define_test("/TP/MCN/NP/BV-03-C", test_client,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING,
+                               0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01, /* uid */
+                               0xaa, 0xbb));
+
+       /* AddToNowPlaying - NowPlaying - TG */
+       define_test("/TP/MCN/NP/BV-04-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING,
+                               0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01, /* uid */
+                               0xaa, 0xbb),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING,
+                               0x00, 0x01, 0x04));
+
+       /* GetFolderItems - NowPlaying - CT */
+       define_test("/TP/MCN/NP/BV-05-C", test_client,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_NOW_PLAYING,
+                               0x00, 0x00, 0x00, 0x00, /* start */
+                               0x00, 0x00, 0x00, 0x02, /* end */
+                               0x00));
+
+       /* GetFolderItems - NowPlaying - TG */
+       define_test("/TP/MCN/NP/BV-06-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_NOW_PLAYING,
+                               0x00, 0x00, 0x00, 0x00, /* start */
+                               0x00, 0x00, 0x00, 0x02, /* end */
+                               0x00),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x05, 0x04, 0xab, 0xcd, 0x00, 0x00));
+
+       /* GetItemAttributes - CT */
+       define_test("/TP/MCN/NP/BV-08-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+                               0x00, 0x0c, AVRCP_MEDIA_NOW_PLAYING,
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01, /* uid */
+                               0xaa, 0xbb,             /* counter */
+                               0x00));                 /* num attr */
+
+       /* GetItemAttributes - TG */
+       define_test("/TP/MCN/CB/BV-09-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+                               0x00, 0x0c, AVRCP_MEDIA_NOW_PLAYING,
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x01, /* uid */
+                               0xaa, 0xbb,             /* counter */
+                               0x00),                  /* num attr */
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GET_ITEM_ATTRIBUTES,
+                               0x00, 0x02, 0x04, 0x00));
+
+       /* PlayItem - NowPlaying - TG */
+       define_test("/TP/MCN/NP/BI-01-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_PLAY_ITEM,
+                               0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, /* uid */
+                               0xaa, 0xbb),            /* counter */
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_PLAY_ITEM,
+                               0x00, 0x01, 0x09));
+
+       /* AddToNowPlaying - NowPlaying - TG */
+       define_test("/TP/MCN/NP/BI-02-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING,
+                               0x00, 0x0b, AVRCP_MEDIA_NOW_PLAYING,
+                               0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, /* uid */
+                               0xaa, 0xbb),            /* counter */
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_ADD_TO_NOW_PLAYING,
+                               0x00, 0x01, 0x09));
+
+       /* Media Player Selection IOP tests */
+
+       /* Listing of available media players */
+       define_test("/TP/MPS/BV-01-I", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, AVRCP_GET_FOLDER_ITEMS,
+                               0x00, 0x0a, AVRCP_MEDIA_PLAYER_LIST,
+                               0x00, 0x00, 0x00, 0x00, /* start */
+                               0x00, 0x00, 0x00, 0x02, /* end */
+                               0x00));
+
+       /* Connection Establishment for Browsing tests */
+
+       /*
+        * Tests are checking connection establishment and release
+        * for browsing channel. Since we are connected through socketpair
+        * the tests are dummy
+        */
+       define_test("/TP/CON/BV-01-C", test_dummy, raw_pdu(0x00));
+       define_test("/TP/CON/BV-02-C", test_dummy, raw_pdu(0x00));
+       define_test("/TP/CON/BV-03-C", test_dummy, raw_pdu(0x00));
+       define_test("/TP/CON/BV-04-C", test_dummy, raw_pdu(0x00));
+       define_test("/TP/CON/BV-05-C", test_dummy, raw_pdu(0x00));
+
+       /* Connection Establishment for Control tests */
+
+       /*
+        * Tests are checking connection establishement and release
+        * for control channel. Since we are connected through socketpair
+        * the tests are dummy
+        */
+       define_test("/TP/CEC/BV-01-I", test_dummy, raw_pdu(0x00));
+       define_test("/TP/CEC/BV-02-I", test_dummy, raw_pdu(0x00));
+       define_test("/TP/CRC/BV-01-I", test_dummy, raw_pdu(0x00));
+       define_test("/TP/CRC/BV-02-I", test_dummy, raw_pdu(0x00));
+
+       /* Information collection for control tests */
+
+       define_test("/TP/ICC/BV-01-I", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0xf8, 0x30,
+                               0xff, 0xff, 0xff, 0xff, 0xff),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0xf8, 0x30,
+                               0x07, 0x48, 0xff, 0xff, 0xff));
+
+       define_test("/TP/ICC/BV-02-I", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0xf8, 0x31,
+                               0x07, 0xff, 0xff, 0xff, 0xff),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0xf8, 0x31,
+                               0x07, 0x48, 0xff, 0xff, 0xff));
+
+       define_test("/TP/PTT/BV-01-I", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+                               0x44, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+                               0x44, 0x00));
+
+       define_test("/TP/PTT/BV-02-I", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+                               AVC_VOLUME_UP, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+                               AVC_VOLUME_UP, 0x00));
+
+       define_test("/TP/PTT/BV-03-I", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+                               AVC_CHANNEL_UP, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+                               AVC_CHANNEL_UP, 0x00));
+
+       define_test("/TP/PTT/BV-04-I", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+                               AVC_SELECT, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+                               AVC_SELECT, 0x00));
+
+       define_test("/TP/PTT/BV-05-I", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+                               AVC_PLAY, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+                               AVC_PLAY, 0x00),
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x7c,
+                               AVC_PLAY | 0x80, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x09, 0x48, 0x7c,
+                               AVC_PLAY | 0x80, 0x00));
+
+       /* Metadata transfer tests */
+
+       define_test("/TP/CFG/BV-01-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58, 0x10, 0x00, 0x00,
+                               0x01, 0x03));
+
+       define_test("/TP/CFG/BV-02-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58, 0x10, 0x00, 0x00,
+                               0x01, 0x02),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58, 0x10, 0x00, 0x00,
+                               0x05, 0x02, 0x01, 0x00, 0x19, 0x58));
+
+       define_test("/TP/CFG/BI-01-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58, 0x10, 0x00, 0x00,
+                               0x01, 0x7f),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+                               0x48, 0x00, 0x00, 0x19, 0x58, 0x10,
+                               0x00, 0x00, 0x01,
+                               AVRCP_STATUS_INVALID_PARAM));
+
+       /* Player Application Settings tests */
+
+       define_test("/TP/PAS/BV-01-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58, 0x11, 0x00, 0x00,
+                               0x00));
+
+       define_test("/TP/PAS/BV-02-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58, 0x11, 0x00, 0x00,
+                               0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58, 0x11, 0x00, 0x00,
+                               0x01, 0x00));
+
+       define_test("/TP/PAS/BV-03-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+                               0x00, 0x00, 0x03, 0x02,
+                               AVRCP_ATTRIBUTE_EQUALIZER,
+                               AVRCP_ATTRIBUTE_REPEAT_MODE));
+
+       define_test("/TP/PAS/BV-04-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+                               0x00, 0x00, 0x02, 0x01, 0x01),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+                               0x00, 0x00, 0x0e, 0x01, 0x01, 0x00,
+                               0x6a, 0x09, 0x65, 0x71, 0x75, 0x61,
+                               0x6c, 0x69, 0x7a, 0x65, 0x72));
+
+       define_test("/TP/PAS/BV-05-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_LIST_PLAYER_VALUES,
+                               0x00, 0x00, 0x01,
+                               AVRCP_ATTRIBUTE_EQUALIZER));
+
+       define_test("/TP/PAS/BV-06-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_LIST_PLAYER_VALUES,
+                               0x00, 0x00, 0x01, AVRCP_ATTRIBUTE_EQUALIZER),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_LIST_PLAYER_VALUES,
+                               0x00, 0x00, 0x01, 0x00));
+
+       define_test("/TP/PAS/BV-07-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_PLAYER_VALUE_TEXT,
+                               0x00, 0x00, 0x04,
+                               AVRCP_ATTRIBUTE_EQUALIZER, 0x02,
+                               AVRCP_EQUALIZER_OFF,
+                               AVRCP_EQUALIZER_ON));
+
+       define_test("/TP/PAS/BV-08-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_PLAYER_VALUE_TEXT,
+                               0x00, 0x00, 0x03, AVRCP_ATTRIBUTE_EQUALIZER,
+                               0x01, 0x01),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_PLAYER_VALUE_TEXT,
+                               0x00, 0x00, 0x07, 0x01, 0x01, 0x00,
+                               0x6a, 0x02, 0x6f, 0x6e));
+
+       define_test("/TP/PAS/BV-09-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_CURRENT_PLAYER_VALUE,
+                               0x00, 0x00, 0x03, 0x02,
+                               AVRCP_ATTRIBUTE_EQUALIZER,
+                               AVRCP_ATTRIBUTE_REPEAT_MODE));
+
+       define_test("/TP/PAS/BV-10-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_CURRENT_PLAYER_VALUE,
+                               0x00, 0x00, 0x03, 0x02,
+                               AVRCP_ATTRIBUTE_EQUALIZER,
+                               AVRCP_ATTRIBUTE_REPEAT_MODE),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_CURRENT_PLAYER_VALUE,
+                               0x00, 0x00, 0x05, 0x02,
+                               AVRCP_ATTRIBUTE_EQUALIZER, 0x00,
+                               AVRCP_ATTRIBUTE_REPEAT_MODE, 0x00));
+
+       define_test("/TP/PAS/BV-11-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_SET_PLAYER_VALUE,
+                               0x00, 0x00, 0x05, 0x02,
+                               AVRCP_ATTRIBUTE_EQUALIZER, 0xaa,
+                               AVRCP_ATTRIBUTE_REPEAT_MODE, 0xff));
+
+       /* Get player app setting attribute text invalid behavior - TG */
+       define_test("/TP/PAS/BI-01-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+                               0x00, 0x00, 0x02, 0x01,
+                               /* Invalid attribute id */
+                               0x7f),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+                               0x48, 0x00, 0x00, 0x19, 0x58,
+                               AVRCP_GET_PLAYER_ATTRIBUTE_TEXT,
+                               0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+       /* List player application setting values invalid behavior - TG */
+       define_test("/TP/PAS/BI-02-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_LIST_PLAYER_VALUES,
+                               0x00, 0x00, 0x01,
+                               /* Invalid attribute id */
+                               0x7f),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+                               0x48, 0x00, 0x00, 0x19, 0x58,
+                               AVRCP_LIST_PLAYER_VALUES,
+                               0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+       /* Get player application setting value text invalid behavior - TG */
+       define_test("/TP/PAS/BI-03-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_PLAYER_VALUE_TEXT,
+                               0x00, 0x00, 0x03, AVRCP_ATTRIBUTE_EQUALIZER,
+                               0x01,
+                               /* Invalid setting value */
+                               0x7f),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+                               0x48, 0x00, 0x00, 0x19, 0x58,
+                               AVRCP_GET_PLAYER_VALUE_TEXT,
+                               0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+       /* Get current player application setting value invalid behavior - TG */
+       define_test("/TP/PAS/BI-04-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_GET_CURRENT_PLAYER_VALUE,
+                               0x00, 0x00, 0x02, 0x01,
+                               /* Invalid attribute */
+                               0x7f),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+                               0x48, 0x00, 0x00, 0x19, 0x58,
+                               AVRCP_GET_CURRENT_PLAYER_VALUE,
+                               0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+       /* Set player application setting value invalid behavior - TG */
+       define_test("/TP/PAS/BI-05-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               AVRCP_SET_PLAYER_VALUE,
+                               0x00, 0x00, 0x03, 0x01,
+                               AVRCP_ATTRIBUTE_REPEAT_MODE, 0x7f),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+                               0x48, 0x00, 0x00, 0x19, 0x58,
+                               AVRCP_SET_PLAYER_VALUE,
+                               0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+       /* Media Information Commands */
+
+       /* Get play status - CT */
+       define_test("/TP/MDI/BV-01-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_GET_PLAY_STATUS,
+                               0x00, 0x00, 0x00));
+
+       /* Get play status - TG */
+       define_test("/TP/MDI/BV-02-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_GET_PLAY_STATUS,
+                               0x00, 0x00, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_GET_PLAY_STATUS,
+                               0x00, 0x00, 0x09,
+                               0xbb, 0xbb, 0xbb, 0xbb, /* duration */
+                               0xaa, 0xaa, 0xaa, 0xaa, /* position */
+                               0x00));
+
+       /* Get element attributes - CT */
+       define_test("/TP/MDI/BV-03-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+                               0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
+
+       /* Get element attributes - TG */
+       define_test("/TP/MDI/BV-04-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+                               0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+                               0x00, 0x00, 0x00));
+
+       /* Get element attributes - TG */
+       define_test("/TP/MDI/BV-05-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x01, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+                               0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+                               0x00, 0x00, 0x00, 0x01),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_GET_ELEMENT_ATTRIBUTES,
+                               0x00, 0x00, 0x00));
+
+       /* Notification Commands */
+
+       /* Register notification - CT */
+       define_test("/TP/NFY/BV-01-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x05, AVRCP_EVENT_STATUS_CHANGED,
+                               0x00, 0x00, 0x00, 0x00));
+
+       /* Register notification - TG */
+       define_test("/TP/NFY/BV-02-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED,
+                               0x00, 0x00, 0x00, 0x00),
+                       frg_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED,
+                               0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                               0xff, 0xff),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED,
+                               0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                               0xff, 0xff));
+
+       /* Register notification - TG */
+       define_test("/TP/NFY/BV-03-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x05,
+                               AVRCP_EVENT_SETTINGS_CHANGED,
+                               0x00, 0x00, 0x00, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x04,
+                               AVRCP_EVENT_SETTINGS_CHANGED,
+                               0x01, 0x01, 0x02),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_CHANGED, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x04,
+                               AVRCP_EVENT_SETTINGS_CHANGED,
+                               0x01, 0x01, 0x02));
+
+       /* Register notification - Track Changed - No Selected Track - TG */
+       define_test("/TP/NFY/BV-04-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED,
+                               0x00, 0x00, 0x00, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED,
+                               0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                               0xff, 0xff));
+
+       /* Register notification - Track Changed - Track Playing - TG */
+       define_test("/TP/NFY/BV-05-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED,
+                               0x00, 0x00, 0x00, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00));
+
+       /* Register notification - Track Changed - Selected Track - TG */
+       define_test("/TP/NFY/BV-08-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x05, AVRCP_EVENT_TRACK_CHANGED,
+                               0x00, 0x00, 0x00, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_INTERIM, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x09, AVRCP_EVENT_TRACK_CHANGED,
+                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                               0x00, 0x00));
+
+       /* Register notification - Register for events invalid behavior - TG */
+       define_test("/TP/NFY/BI-01-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x05,
+                               /* Invalid event id */
+                               0xff,
+                               0x00, 0x00, 0x00, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+                               0x48, 0x00, 0x00, 0x19, 0x58,
+                               AVRCP_REGISTER_NOTIFICATION,
+                               0x00, 0x00, 0x01, AVRCP_STATUS_INVALID_PARAM));
+
+       /* Invalid commands */
+
+       /* Invalid PDU ID - TG */
+       define_test("/TP/INV/BI-01-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x03, 0x48, 0x00,
+                               0x00, 0x19, 0x58,
+                               /* Invalid PDU ID */
+                               0xff,
+                               0x00, 0x00, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_REJECTED,
+                               0x48, 0x00, 0x00, 0x19, 0x58,
+                               0xff, 0x00, 0x00, 0x01,
+                               AVRCP_STATUS_INVALID_COMMAND));
+
+       /* Invalid PDU ID - Browsing TG */
+       define_test("/TP/INV/BI-02-C", test_server,
+                       brs_pdu(0x00, 0x11, 0x0e, 0xff, 0x00, 0x00),
+                       brs_pdu(0x02, 0x11, 0x0e, AVRCP_GENERAL_REJECT,
+                               0x00, 0x01, AVRCP_STATUS_INVALID_COMMAND));
+
+       /* Next Group command transfer - CT */
+       define_test("/TP/BGN/BV-01-I", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48,
+                               AVC_OP_PASSTHROUGH,
+                               AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+                               0x58, 0x00, AVC_VENDOR_NEXT_GROUP));
+
+       /* Next Group command transfer - TG */
+       define_test("/TP/BGN/BV-01-I", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48,
+                               AVC_OP_PASSTHROUGH,
+                               AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+                               0x58, 0x00, AVC_VENDOR_NEXT_GROUP),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_ACCEPTED,
+                               0x48, AVC_OP_PASSTHROUGH,
+                               AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+                               0x58, 0x00, AVC_VENDOR_NEXT_GROUP));
+
+       /* Previous Group command transfer - CT */
+       define_test("/TP/BGN/BV-02-I", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48,
+                               AVC_OP_PASSTHROUGH,
+                               AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+                               0x58, 0x00, AVC_VENDOR_PREV_GROUP));
+
+       /* Previous Group command transfer - TG */
+       define_test("/TP/BGN/BV-02-I", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48,
+                               AVC_OP_PASSTHROUGH,
+                               AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+                               0x58, 0x00, AVC_VENDOR_PREV_GROUP),
+                       raw_pdu(0x02, 0x11, 0x0e, AVC_CTYPE_ACCEPTED,
+                               0x48, AVC_OP_PASSTHROUGH,
+                               AVC_VENDOR_UNIQUE, 0x05, 0x00, 0x19,
+                               0x58, 0x00, AVC_VENDOR_PREV_GROUP));
+
+       /* Volume Level Handling */
+
+       /* Set absolute volume – CT */
+       define_test("/TP/VLH/BV-01-C", test_client,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+                               0x00, 0x00, 0x01, 0x00));
+
+       /* Set absolute volume – TG */
+       define_test("/TP/VLH/BV-02-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+                               0x00, 0x00, 0x01, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+                               0x00, 0x00, 0x01, 0x00));
+
+       /* Set absolute volume – TG */
+       define_test("/TP/VLH/BI-01-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+                               0x00, 0x00, 0x00),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0a, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+                               0x00, 0x00, 0x01, 0x01));
+
+       /* Set absolute volume – TG */
+       define_test("/TP/VLH/BI-02-C", test_server,
+                       raw_pdu(0x00, 0x11, 0x0e, 0x00, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+                               0x00, 0x00, 0x01, 0x80),
+                       raw_pdu(0x02, 0x11, 0x0e, 0x0c, 0x48, 0x00,
+                               0x00, 0x19, 0x58, AVRCP_SET_ABSOLUTE_VOLUME,
+                               0x00, 0x00, 0x01, 0x00));
+
+       return g_test_run();
+}
index 6d9d554..919a83b 100644 (file)
@@ -25,6 +25,8 @@
 #include <config.h>
 #endif
 
+#include <stdbool.h>
+
 #include <glib.h>
 
 #include <bluetooth/bluetooth.h>
@@ -36,9 +38,9 @@
 struct test_data {
        const void *eir_data;
        size_t eir_size;
-       int flags;
+       unsigned int flags;
        const char *name;
-       gboolean name_complete;
+       bool name_complete;
        int8_t tx_power;
        const char **uuid;
 };
@@ -91,9 +93,8 @@ static const char *macbookair_uuid[] = {
 static const struct test_data macbookair_test = {
        .eir_data = macbookair_data,
        .eir_size = sizeof(macbookair_data),
-       .flags = -1,
        .name = "Marcel’s MacBook Air",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 127,
        .uuid = macbookair_uuid,
 };
@@ -146,9 +147,8 @@ static const char *iphone5_uuid[] = {
 static const struct test_data iphone5_test = {
        .eir_data = iphone5_data,
        .eir_size = sizeof(iphone5_data),
-       .flags = -1,
        .name = "Marcel’s iPhone 5",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 127,
        .uuid = iphone5_uuid,
 };
@@ -199,9 +199,8 @@ static const char *ipadmini_uuid[] = {
 static const struct test_data ipadmini_test = {
        .eir_data = ipadmini_data,
        .eir_size = sizeof(ipadmini_data),
-       .flags = -1,
        .name = "Marcel's iPad mini",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 127,
        .uuid = ipadmini_uuid,
 };
@@ -251,9 +250,8 @@ static const char *gigaset_sl400h_uuid[] = {
 static const struct test_data gigaset_sl400h_test = {
        .eir_data = gigaset_sl400h_data,
        .eir_size = sizeof(gigaset_sl400h_data),
-       .flags = -1,
        .name = "Marcel's SL400H",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 127,
        .uuid = gigaset_sl400h_uuid,
 };
@@ -303,9 +301,8 @@ static const char *gigaset_sl910_uuid[] = {
 static const struct test_data gigaset_sl910_test = {
        .eir_data = gigaset_sl910_data,
        .eir_size = sizeof(gigaset_sl910_data),
-       .flags = -1,
        .name = "Marcel's SL910",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 127,
        .uuid = gigaset_sl910_uuid,
 };
@@ -357,9 +354,8 @@ static const char *nokia_bh907_uuid[] = {
 static const struct test_data nokia_bh907_test = {
        .eir_data = nokia_bh907_data,
        .eir_size = sizeof(nokia_bh907_data),
-       .flags = -1,
        .name = "Nokia Reaction BH-907",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 4,
        .uuid = nokia_bh907_uuid,
 };
@@ -405,9 +401,8 @@ static const char *fuelband_uuid[] = {
 static const struct test_data fuelband_test = {
        .eir_data = fuelband_data,
        .eir_size = sizeof(fuelband_data),
-       .flags = -1,
        .name = "Nike+ FuelBand",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 0,
        .uuid = fuelband_uuid,
 };
@@ -429,7 +424,7 @@ static const struct test_data bluesc_test = {
        .eir_size = sizeof(bluesc_data),
        .flags = 0x06,
        .name = "Wahoo BlueSC v1.4",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 127,
        .uuid = bluesc_uuid,
 };
@@ -451,7 +446,7 @@ static const struct test_data wahoo_scale_test = {
        .eir_size = sizeof(wahoo_scale_data),
        .flags = 0x06,
        .name = "Wahoo Scale v1.3",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 127,
        .uuid = wahoo_scale_uuid,
 };
@@ -471,7 +466,7 @@ static const struct test_data mio_alpha_test = {
        .eir_size = sizeof(mio_alpha_data),
        .flags = 0x06,
        .name = "ALPHA",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 127,
        .uuid = mio_alpha_uuid,
 };
@@ -493,7 +488,7 @@ static const struct test_data cookoo_test = {
        .eir_size = sizeof(cookoo_data),
        .flags = 0x05,
        .name = "COOKOO watch",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 127,
        .uuid = cookoo_uuid,
 };
@@ -510,7 +505,7 @@ static const struct test_data citizen_adv_test = {
        .eir_size = sizeof(citizen_adv_data),
        .flags = 0x05,
        .name = "Eco-Drive Proximity",
-       .name_complete = TRUE,
+       .name_complete = true,
        .tx_power = 127,
 };
 
@@ -528,7 +523,6 @@ static const char *citizen_scan_uuid[] = {
 static const struct test_data citizen_scan_test = {
        .eir_data = citizen_scan_data,
        .eir_size = sizeof(citizen_scan_data),
-       .flags = -1,
        .tx_power = 0,
        .uuid = citizen_scan_uuid,
 };
index 685729a..d0b6ce7 100644 (file)
@@ -38,6 +38,7 @@ struct context {
        GDBusClient *dbus_client;
        GDBusProxy *proxy;
        void *data;
+       gboolean client_ready;
        guint timeout_source;
 };
 
@@ -85,6 +86,7 @@ static struct context *create_context(void)
        dbus_connection_set_exit_on_disconnect(context->dbus_conn, FALSE);
 
        g_dbus_attach_object_manager(context->dbus_conn);
+       context->client_ready = FALSE;
 
        return context;
 }
@@ -101,6 +103,7 @@ static void destroy_context(struct context *context)
 
        dbus_connection_flush(context->dbus_conn);
        dbus_connection_close(context->dbus_conn);
+       dbus_connection_unref(context->dbus_conn);
 
        g_main_loop_unref(context->main_loop);
 
@@ -956,6 +959,60 @@ static void client_force_disconnect(void)
 
        g_main_loop_run(context->main_loop);
 
+       g_dbus_unregister_interface(conn, SERVICE_PATH, SERVICE_NAME1);
+       g_dbus_detach_object_manager(conn);
+       dbus_connection_unref(conn);
+
+       destroy_context(context);
+}
+
+static void client_ready_watch(GDBusClient *client, void *user_data)
+{
+       struct context *context = user_data;
+
+       context->client_ready = TRUE;
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+       struct context *context = user_data;
+
+       /*
+        * Proxy added callback should not be called after Client ready
+        * watch. Ready means that all objects has been reported to the
+        * upper-layer.
+        */
+       g_assert(context->client_ready == FALSE);
+
+       g_main_loop_quit(context->main_loop);
+}
+
+static void client_ready(void)
+{
+       struct context *context = create_context();
+       static const GDBusPropertyTable string_properties[] = {
+               { "String", "s", get_string, set_string, string_exists },
+               { },
+       };
+
+       if (context == NULL)
+               return;
+
+       g_dbus_register_interface(context->dbus_conn,
+                               SERVICE_PATH, SERVICE_NAME,
+                               methods, signals, string_properties,
+                               context, NULL);
+
+       context->dbus_client = g_dbus_client_new(context->dbus_conn,
+                                               SERVICE_NAME, SERVICE_PATH);
+
+       g_dbus_client_set_ready_watch(context->dbus_client, client_ready_watch,
+                                                               context);
+       g_dbus_client_set_proxy_handlers(context->dbus_client,
+                                               proxy_added, NULL, NULL, context);
+
+       g_main_loop_run(context->main_loop);
+
        destroy_context(context);
 }
 
@@ -996,5 +1053,7 @@ int main(int argc, char *argv[])
        g_test_add_func("/gdbus/client_force_disconnect",
                                                client_force_disconnect);
 
+       g_test_add_func("/gdbus/client_ready", client_ready);
+
        return g_test_run();
 }
index e6a42cc..976c541 100644 (file)
@@ -250,6 +250,8 @@ static void test_apparam_get_multi(void)
        g_assert(string != NULL);
        g_assert_cmpstr(string, ==, "ABC");
 
+       g_free(string);
+
        g_obex_apparam_free(apparam);
 }
 
index ef05047..ffb5bdc 100644 (file)
@@ -106,6 +106,9 @@ static guint8 get_req_first_srm_wait[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x27
        0, 'f', 0, 'i', 0, 'l', 0, 'e', 0, '.', 0, 't', 0, 'x', 0, 't', 0, 0,
        G_OBEX_HDR_SRMP, 0x01 };
 
+static guint8 get_req_srm_wait[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x05,
+       G_OBEX_HDR_SRMP, 0x01 };
+
 static guint8 get_req_last[] = { G_OBEX_OP_GET | FINAL_BIT, 0x00, 0x03, };
 
 static guint8 get_rsp_first_app[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, 0x00, 0x0A,
@@ -124,6 +127,8 @@ static guint8 get_rsp_first_srm_wait_next[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT,
                                        G_OBEX_HDR_SRMP, 0x02,
                                        G_OBEX_HDR_BODY, 0x00, 0x0d,
                                        0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+static guint8 get_rsp_srm_wait[] = { G_OBEX_RSP_CONTINUE | FINAL_BIT,
+                                       0x00, 0x03 };
 static guint8 get_rsp_zero[255] = { G_OBEX_RSP_CONTINUE | FINAL_BIT, 0x00, 0xff,
                                        G_OBEX_HDR_BODY, 0x00, 0xfc };
 static guint8 get_rsp_zero_wait_next[255] = { G_OBEX_RSP_CONTINUE | FINAL_BIT,
@@ -191,7 +196,13 @@ static void transfer_complete(GObex *obex, GError *err, gpointer user_data)
 
 static gboolean resume_obex(gpointer user_data)
 {
-       g_obex_resume(user_data);
+       struct test_data *d = user_data;
+
+       if (!g_main_loop_is_running(d->mainloop))
+               return FALSE;
+
+       g_obex_resume(d->obex);
+
        return FALSE;
 }
 
@@ -232,7 +243,7 @@ static gssize provide_eagain(void *buf, gsize len, gpointer user_data)
        }
 
        if (d->provide_delay > 0) {
-               g_timeout_add(d->provide_delay, resume_obex, d->obex);
+               g_timeout_add(d->provide_delay, resume_obex, d);
                d->provide_delay = 0;
                return -EAGAIN;
        }
@@ -260,7 +271,7 @@ static gssize provide_data(void *buf, gsize len, gpointer user_data)
 
        if (d->provide_delay > 0) {
                g_obex_suspend(d->obex);
-               g_timeout_add(d->provide_delay, resume_obex, d->obex);
+               g_timeout_add(d->provide_delay, resume_obex, d);
        }
 
        d->total += sizeof(body_data);
@@ -511,6 +522,7 @@ static void test_stream_put_req_abort(void)
        g_obex_unref(obex);
 
        g_assert_error(d.err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED);
+       g_error_free(d.err);
 }
 
 static void test_stream_put_rsp_abort(void)
@@ -556,6 +568,7 @@ static void test_stream_put_rsp_abort(void)
        g_obex_unref(obex);
 
        g_assert_error(d.err, G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED);
+       g_error_free(d.err);
 }
 
 static void handle_put_seq_wait(GObex *obex, GObexPacket *req,
@@ -850,6 +863,66 @@ static void test_packet_get_req_wait(void)
        g_assert_no_error(d.err);
 }
 
+static gboolean rcv_seq_delay(const void *buf, gsize len, gpointer user_data)
+{
+       struct test_data *d = user_data;
+
+       if (d->provide_delay > 0) {
+               g_obex_suspend(d->obex);
+               g_timeout_add_seconds(d->provide_delay, resume_obex, d);
+               d->provide_delay = 0;
+       }
+
+       return TRUE;
+}
+
+static void test_packet_get_req_suspend_resume(void)
+{
+       GIOChannel *io;
+       GIOCondition cond;
+       guint io_id, timer_id;
+       GObex *obex;
+       struct test_data d = { 0, NULL, {
+               { get_req_first_srm, sizeof(get_req_first_srm) },
+               { get_req_srm_wait, sizeof(get_req_srm_wait) },
+               { get_req_srm_wait, sizeof(get_req_srm_wait) },
+               { get_req_last, sizeof(get_req_last) } }, {
+               { get_rsp_first_srm, sizeof(get_rsp_first_srm) },
+               { get_rsp_srm_wait, sizeof(get_rsp_srm_wait) },
+               { get_rsp_srm_wait, sizeof(get_rsp_srm_wait) },
+               { get_rsp_last, sizeof(get_rsp_last) } } };
+
+       create_endpoints(&obex, &io, SOCK_SEQPACKET);
+       d.obex = obex;
+       d.provide_delay = 1;
+
+       cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+       d.mainloop = g_main_loop_new(NULL, FALSE);
+
+       timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+       g_obex_get_req(obex, rcv_seq_delay, transfer_complete, &d, &d.err,
+                               G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+                               G_OBEX_HDR_NAME, "file.txt",
+                               G_OBEX_HDR_INVALID);
+       g_assert_no_error(d.err);
+
+       g_main_loop_run(d.mainloop);
+
+       g_assert_cmpuint(d.count, ==, 4);
+
+       g_main_loop_unref(d.mainloop);
+
+       g_source_remove(timer_id);
+       g_io_channel_unref(io);
+       g_source_remove(io_id);
+       g_obex_unref(obex);
+
+       g_assert_no_error(d.err);
+}
+
 static void test_packet_get_req_wait_next(void)
 {
        GIOChannel *io;
@@ -1025,6 +1098,78 @@ static void test_stream_put_req(void)
        g_assert_no_error(d.err);
 }
 
+static gssize provide_seq_delay(void *buf, gsize len, gpointer user_data)
+{
+       struct test_data *d = user_data;
+       int fd;
+       gssize ret;
+
+       if (d->provide_delay > 0) {
+               g_obex_suspend(d->obex);
+               g_timeout_add_seconds(d->provide_delay, resume_obex, d);
+               d->provide_delay = 0;
+       }
+
+       if (d->count == RANDOM_PACKETS - 1)
+               return 0;
+
+       fd = open("/dev/urandom", O_RDONLY | O_NOCTTY, 0);
+       if (fd < 0) {
+               g_set_error(&d->err, TEST_ERROR, TEST_ERROR_UNEXPECTED,
+                               "open(/dev/urandom): %s", strerror(errno));
+               g_main_loop_quit(d->mainloop);
+               return -1;
+       }
+
+       ret = read(fd, buf, len);
+       close(fd);
+       return ret;
+}
+
+static void test_packet_put_req_suspend_resume(void)
+{
+       GIOChannel *io;
+       GIOCondition cond;
+       guint io_id;
+       GObex *obex;
+       struct test_data d = { 0, NULL, {
+                       { NULL, 0 },
+                       { NULL, 0 },
+                       { NULL, 0 },
+                       { put_req_last, sizeof(put_req_last) } }, {
+                       { put_rsp_first_srm, sizeof(put_rsp_first_srm) },
+                       { NULL, 0 },
+                       { NULL, 0 },
+                       { put_rsp_last, sizeof(put_rsp_last) } } };
+
+       create_endpoints(&obex, &io, SOCK_SEQPACKET);
+       d.obex = obex;
+       d.provide_delay = 1;
+
+       cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+       d.mainloop = g_main_loop_new(NULL, FALSE);
+
+       g_obex_put_req(obex, provide_seq_delay, transfer_complete, &d, &d.err,
+                               G_OBEX_HDR_TYPE, hdr_type, sizeof(hdr_type),
+                               G_OBEX_HDR_NAME, "random.bin",
+                               G_OBEX_HDR_INVALID);
+       g_assert_no_error(d.err);
+
+       g_main_loop_run(d.mainloop);
+
+       g_assert_cmpuint(d.count, ==, RANDOM_PACKETS);
+
+       g_main_loop_unref(d.mainloop);
+
+       g_io_channel_unref(io);
+       g_source_remove(io_id);
+       g_obex_unref(obex);
+
+       g_assert_no_error(d.err);
+}
+
 static void test_packet_put_req_wait(void)
 {
        GIOChannel *io;
@@ -1547,7 +1692,7 @@ static gboolean rcv_data_delay(const void *buf, gsize len, gpointer user_data)
 
        if (d->provide_delay > 0) {
                g_obex_suspend(d->obex);
-               g_timeout_add(d->provide_delay, resume_obex, d->obex);
+               g_timeout_add(d->provide_delay, resume_obex, d);
        }
 
        return TRUE;
@@ -1805,7 +1950,8 @@ static void test_conn_rsp(void)
 
        g_source_remove(timer_id);
        g_io_channel_unref(io);
-       g_source_remove(io_id);
+       if (!d.io_completed)
+               g_source_remove(io_id);
        g_obex_unref(obex);
 
        g_assert_no_error(d.err);
@@ -2060,7 +2206,8 @@ static void test_conn_get_wrg_rsp(void)
 
        g_source_remove(timer_id);
        g_io_channel_unref(io);
-       g_source_remove(io_id);
+       if (!d.io_completed)
+               g_source_remove(io_id);
        g_obex_unref(obex);
 
        g_assert_no_error(d.err);
@@ -2237,6 +2384,8 @@ int main(int argc, char *argv[])
        g_test_add_func("/gobex/test_packet_put_req", test_packet_put_req);
        g_test_add_func("/gobex/test_packet_put_req_wait",
                                                test_packet_put_req_wait);
+       g_test_add_func("/gobex/test_packet_put_req_suspend_resume",
+                                       test_packet_put_req_suspend_resume);
 
        g_test_add_func("/gobex/test_packet_put_rsp", test_packet_put_rsp);
        g_test_add_func("/gobex/test_packet_put_rsp_wait",
@@ -2249,6 +2398,8 @@ int main(int argc, char *argv[])
        g_test_add_func("/gobex/test_packet_get_req", test_packet_get_req);
        g_test_add_func("/gobex/test_packet_get_req_wait",
                                                test_packet_get_req_wait);
+       g_test_add_func("/gobex/test_packet_get_req_suspend_resume",
+                                       test_packet_get_req_suspend_resume);
 
        g_test_add_func("/gobex/test_packet_get_req_wait_next",
                                                test_packet_get_req_wait_next);
index 66307c2..180f817 100644 (file)
@@ -45,6 +45,25 @@ static uint8_t pkt_connect_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT,
 static uint8_t pkt_connect_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x07,
                                        0x10, 0x00, 0x10, 0x00 };
 
+static uint8_t pkt_disconnect_req[] = { G_OBEX_OP_DISCONNECT | FINAL_BIT,
+                                       0x00, 0x03 };
+static uint8_t pkt_disconnect_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x03 };
+
+static uint8_t pkt_unauth_rsp[] = { 0x41 | FINAL_BIT, 0x00, 0x1c,
+                                       0x10, 0x00, 0x10, 0x00, 0x4d, 0x00,
+                                       0x15, 0x00, 0x10, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                       0x00 };
+static uint8_t pkt_auth_req[] = { G_OBEX_OP_CONNECT | FINAL_BIT, 0x00, 0x1c,
+                                       0x10, 0x00, 0x10, 0x00, 0x4e, 0x00,
+                                       0x15, 0x00, 0x10, 0x5a, 0xd4, 0x93,
+                                       0x93, 0xba, 0x4a, 0xf8, 0xac, 0xce,
+                                       0x7f, 0x5b, 0x1a, 0x05, 0x38, 0x74,
+                                       0x24 };
+static uint8_t pkt_auth_rsp[] = { 0x20 | FINAL_BIT, 0x00, 0x07,
+                                       0x10, 0x00, 0x10, 0x00 };
+
 static uint8_t pkt_setpath_req[] = { G_OBEX_OP_SETPATH | FINAL_BIT, 0x00, 0x10,
                                        0x02, 0x00,
                                        G_OBEX_HDR_NAME, 0x00, 0x0b,
@@ -235,7 +254,7 @@ static void send_req(GObexPacket *req, GObexResponseFunc rsp_func,
        GError *gerr = NULL;
        GIOChannel *io;
        GIOCondition cond;
-       guint io_id, timer_id, test_time;
+       guint timer_id, test_time;
        GObex *obex;
 
        create_endpoints(&obex, &io, transport_type);
@@ -244,7 +263,7 @@ static void send_req(GObexPacket *req, GObexResponseFunc rsp_func,
        g_assert_no_error(gerr);
 
        cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
-       io_id = g_io_add_watch(io, cond, send_rsp_func, &gerr);
+       g_io_add_watch(io, cond, send_rsp_func, &gerr);
 
        mainloop = g_main_loop_new(NULL, FALSE);
 
@@ -262,7 +281,6 @@ static void send_req(GObexPacket *req, GObexResponseFunc rsp_func,
 
        g_source_remove(timer_id);
        g_io_channel_unref(io);
-       g_source_remove(io_id);
        g_obex_unref(obex);
 
        g_assert_no_error(gerr);
@@ -466,6 +484,7 @@ struct rcv_buf_info {
        GError *err;
        const guint8 *buf;
        gsize len;
+       gboolean completed;
 };
 
 static gboolean rcv_data(GIOChannel *io, GIOCondition cond, gpointer user_data)
@@ -505,6 +524,7 @@ static gboolean rcv_data(GIOChannel *io, GIOCondition cond, gpointer user_data)
 
 done:
        g_main_loop_quit(mainloop);
+       r->completed = TRUE;
        return FALSE;
 }
 
@@ -546,7 +566,8 @@ static void test_send_connect(int transport_type)
 
        g_source_remove(timer_id);
        g_io_channel_unref(io);
-       g_source_remove(io_id);
+       if (!r.completed)
+               g_source_remove(io_id);
        g_obex_unref(obex);
 
        g_assert_no_error(r.err);
@@ -661,7 +682,8 @@ static void test_send_on_demand(int transport_type, GObexDataProducer func)
 
        g_source_remove(timer_id);
        g_io_channel_unref(io);
-       g_source_remove(io_id);
+       if (!r.completed)
+               g_source_remove(io_id);
        g_obex_unref(obex);
 
        g_assert_no_error(r.err);
@@ -880,6 +902,120 @@ static void test_connect(void)
        g_assert_no_error(d.err);
 }
 
+static void test_obex_disconnect(void)
+{
+       GIOChannel *io;
+       GIOCondition cond;
+       guint io_id, timer_id;
+       GObex *obex;
+       struct test_data d = { 0, NULL, {
+                       { pkt_disconnect_req, sizeof(pkt_disconnect_req) } }, {
+                       { pkt_disconnect_rsp, sizeof(pkt_disconnect_rsp) } } };
+
+       create_endpoints(&obex, &io, SOCK_STREAM);
+
+       cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+       d.mainloop = g_main_loop_new(NULL, FALSE);
+
+       timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+       g_obex_disconnect(obex, req_complete, &d, &d.err);
+       g_assert_no_error(d.err);
+
+       g_main_loop_run(d.mainloop);
+
+       g_assert_cmpuint(d.count, ==, 1);
+
+       g_main_loop_unref(d.mainloop);
+
+       g_source_remove(timer_id);
+       g_io_channel_unref(io);
+       g_source_remove(io_id);
+       g_obex_unref(obex);
+
+       g_assert_no_error(d.err);
+}
+
+static void test_auth(void)
+{
+       GIOChannel *io;
+       GIOCondition cond;
+       guint io_id, timer_id;
+       GObex *obex;
+       struct test_data d = { 0, NULL, {
+                               { pkt_connect_req, sizeof(pkt_connect_req) },
+                               { pkt_auth_req, sizeof(pkt_auth_req) } }, {
+                               { pkt_unauth_rsp, sizeof(pkt_unauth_rsp) },
+                               { pkt_auth_rsp, sizeof(pkt_auth_rsp) } },
+                               };
+
+       create_endpoints(&obex, &io, SOCK_STREAM);
+
+       cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+       d.mainloop = g_main_loop_new(NULL, FALSE);
+
+       timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+       g_obex_connect(obex, req_complete, &d, &d.err, G_OBEX_HDR_INVALID);
+       g_assert_no_error(d.err);
+
+       g_main_loop_run(d.mainloop);
+
+       g_assert_cmpuint(d.count, ==, 2);
+
+       g_main_loop_unref(d.mainloop);
+
+       g_source_remove(timer_id);
+       g_io_channel_unref(io);
+       g_source_remove(io_id);
+       g_obex_unref(obex);
+
+       g_assert_no_error(d.err);
+}
+
+static void test_auth_fail(void)
+{
+       GIOChannel *io;
+       GIOCondition cond;
+       guint io_id, timer_id;
+       GObex *obex;
+       struct test_data d = { 0, NULL, {
+                               { pkt_connect_req, sizeof(pkt_connect_req) },
+                               { pkt_auth_req, sizeof(pkt_auth_req) } }, {
+                               { pkt_unauth_rsp, sizeof(pkt_unauth_rsp) },
+                               { pkt_unauth_rsp, sizeof(pkt_unauth_rsp) } },
+                               };
+
+       create_endpoints(&obex, &io, SOCK_STREAM);
+
+       cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+       io_id = g_io_add_watch(io, cond, test_io_cb, &d);
+
+       d.mainloop = g_main_loop_new(NULL, FALSE);
+
+       timer_id = g_timeout_add_seconds(1, test_timeout, &d);
+
+       g_obex_connect(obex, req_complete, &d, &d.err, G_OBEX_HDR_INVALID);
+       g_assert_no_error(d.err);
+
+       g_main_loop_run(d.mainloop);
+
+       g_assert_cmpuint(d.count, ==, 2);
+
+       g_main_loop_unref(d.mainloop);
+
+       g_source_remove(timer_id);
+       g_io_channel_unref(io);
+       g_source_remove(io_id);
+       g_obex_unref(obex);
+
+       g_assert_no_error(d.err);
+}
+
 static void test_setpath(void)
 {
        GIOChannel *io;
@@ -1185,6 +1321,9 @@ int main(int argc, char *argv[])
                                        test_cancel_req_delay_pkt);
 
        g_test_add_func("/gobex/test_connect", test_connect);
+       g_test_add_func("/gobex/test_obex_disconnect", test_obex_disconnect);
+       g_test_add_func("/gobex/test_auth", test_auth);
+       g_test_add_func("/gobex/test_auth_fail", test_auth_fail);
 
        g_test_add_func("/gobex/test_setpath", test_setpath);
        g_test_add_func("/gobex/test_setpath_up", test_setpath_up);
diff --git a/unit/test-hfp.c b/unit/test-hfp.c
new file mode 100644 (file)
index 0000000..20aa0b5
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Intel Corporation. All rights reserved.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include "src/shared/hfp.h"
+
+struct context {
+       GMainLoop *main_loop;
+       guint watch_id;
+       int fd_server;
+       int fd_client;
+       struct hfp_gw *hfp;
+       const struct test_data *data;
+       unsigned int pdu_offset;
+};
+
+struct test_pdu {
+       bool valid;
+       const uint8_t *data;
+       size_t size;
+       enum hfp_gw_cmd_type type;
+       bool fragmented;
+};
+
+struct test_data {
+       char *test_name;
+       struct test_pdu *pdu_list;
+       hfp_result_func_t result_func;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...)                                       \
+       {                                                       \
+               .valid = true,                                  \
+               .data = data(args),                             \
+               .size = sizeof(data(args)),                     \
+       }
+
+#define data_end()                                             \
+       {                                                       \
+               .valid = false,                                 \
+       }
+
+#define type_pdu(cmd_type, args...)                            \
+       {                                                       \
+               .valid = true,                                  \
+               .data = data(args),                             \
+               .size = sizeof(data(args)),                     \
+               .type = cmd_type,                               \
+       }
+
+#define frg_pdu(args...)                                       \
+       {                                                       \
+               .valid = true,                                  \
+               .data = data(args),                             \
+               .size = sizeof(data(args)),                     \
+               .fragmented = true,                             \
+       }
+
+#define define_test(name, function, result_function, args...)          \
+       do {                                                            \
+               const struct test_pdu pdus[] = {                        \
+                       args, { }                                       \
+               };                                                      \
+               static struct test_data data;                           \
+               data.test_name = g_strdup(name);                        \
+               data.pdu_list = g_malloc(sizeof(pdus));                 \
+               data.result_func = result_function;                     \
+               memcpy(data.pdu_list, pdus, sizeof(pdus));              \
+               g_test_add_data_func(name, &data, function);            \
+       } while (0)
+
+static void context_quit(struct context *context)
+{
+       g_main_loop_quit(context->main_loop);
+}
+
+static void test_free(gconstpointer user_data)
+{
+       const struct test_data *data = user_data;
+
+       g_free(data->test_name);
+       g_free(data->pdu_list);
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       g_assert(!pdu->valid);
+       context_quit(context);
+
+       context->watch_id = 0;
+
+       return FALSE;
+}
+
+static void cmd_handler(const char *command, void *user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       unsigned int cmd_len = strlen(command);
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       g_assert(cmd_len == pdu->size);
+       g_assert(!memcmp(command, pdu->data, cmd_len));
+
+       hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static void prefix_handler(struct hfp_gw_result *result,
+                               enum hfp_gw_cmd_type type, void *user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       g_assert(type == pdu->type);
+
+       hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static struct context *create_context(gconstpointer data)
+{
+       struct context *context = g_new0(struct context, 1);
+       GIOChannel *channel;
+       int err, sv[2];
+
+       context->main_loop = g_main_loop_new(NULL, FALSE);
+       g_assert(context->main_loop);
+
+       err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+       g_assert(err == 0);
+
+       channel = g_io_channel_unix_new(sv[1]);
+
+       g_io_channel_set_close_on_unref(channel, TRUE);
+       g_io_channel_set_encoding(channel, NULL, NULL);
+       g_io_channel_set_buffered(channel, FALSE);
+
+       context->watch_id = g_io_add_watch(channel,
+                               G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               test_handler, context);
+       g_assert(context->watch_id > 0);
+
+       g_io_channel_unref(channel);
+
+       context->fd_server = sv[1];
+       context->fd_client = sv[0];
+       context->data = data;
+
+       return context;
+}
+
+static void execute_context(struct context *context)
+{
+       g_main_loop_run(context->main_loop);
+
+       if (context->watch_id)
+               g_source_remove(context->watch_id);
+
+       g_main_loop_unref(context->main_loop);
+
+       test_free(context->data);
+
+       if (context->hfp)
+               hfp_gw_unref(context->hfp);
+
+       g_free(context);
+}
+
+static void test_init(gconstpointer data)
+{
+       struct context *context = create_context(data);
+
+       context->hfp = hfp_gw_new(context->fd_client);
+
+       g_assert(context->hfp);
+       g_assert(hfp_gw_set_close_on_unref(context->hfp, true));
+
+       hfp_gw_unref(context->hfp);
+       context->hfp = NULL;
+
+       execute_context(context);
+}
+
+static void test_command_handler(gconstpointer data)
+{
+       struct context *context = create_context(data);
+       const struct test_pdu *pdu;
+       ssize_t len;
+       bool ret;
+
+       context->hfp = hfp_gw_new(context->fd_client);
+       g_assert(context->hfp);
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       ret = hfp_gw_set_close_on_unref(context->hfp, true);
+       g_assert(ret);
+
+       ret = hfp_gw_set_command_handler(context->hfp, cmd_handler,
+                                                               context, NULL);
+       g_assert(ret);
+
+       len = write(context->fd_server, pdu->data, pdu->size);
+       g_assert_cmpint(len, ==, pdu->size);
+
+       execute_context(context);
+}
+
+static void test_register(gconstpointer data)
+{
+       struct context *context = create_context(data);
+       const struct test_pdu *pdu;
+       ssize_t len;
+       bool ret;
+
+       context->hfp = hfp_gw_new(context->fd_client);
+       g_assert(context->hfp);
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       ret = hfp_gw_set_close_on_unref(context->hfp, true);
+       g_assert(ret);
+
+       if (context->data->result_func) {
+               ret = hfp_gw_register(context->hfp, context->data->result_func,
+                                       (char *)pdu->data, context, NULL);
+               g_assert(ret);
+       }
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       len = write(context->fd_server, pdu->data, pdu->size);
+       g_assert_cmpint(len, ==, pdu->size);
+
+       execute_context(context);
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       ssize_t len;
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       len = write(context->fd_server, pdu->data, pdu->size);
+       g_assert_cmpint(len, ==, pdu->size);
+
+       pdu = &context->data->pdu_list[context->pdu_offset];
+       if (pdu->fragmented)
+               g_idle_add(send_pdu, context);
+
+       return FALSE;
+}
+
+static void test_fragmented(gconstpointer data)
+{
+       struct context *context = create_context(data);
+       bool ret;
+
+       context->hfp = hfp_gw_new(context->fd_client);
+       g_assert(context->hfp);
+
+       ret = hfp_gw_set_close_on_unref(context->hfp, true);
+       g_assert(ret);
+
+       g_idle_add(send_pdu, context);
+
+       execute_context(context);
+}
+
+static void check_ustring_1(struct hfp_gw_result *result,
+                               enum hfp_gw_cmd_type type, void *user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       unsigned int i = 3, j = 0;
+       char str[10];
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       g_assert(type == pdu->type);
+
+       g_assert(hfp_gw_result_get_unquoted_string(result, str, sizeof(str)));
+
+       while (context->data->pdu_list[1].data[i] != '\r') {
+               g_assert(j < sizeof(str));
+               g_assert(str[j] == context->data->pdu_list[1].data[i]);
+
+               i++;
+               j++;
+       }
+
+       g_assert(str[j] == '\0');
+
+       hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static void check_ustring_2(struct hfp_gw_result *result,
+                               enum hfp_gw_cmd_type type, void *user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       char str[10];
+
+       memset(str, 'X', sizeof(str));
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       g_assert(type == pdu->type);
+
+       g_assert(!hfp_gw_result_get_unquoted_string(result, str, 3));
+
+       g_assert(str[3] == 'X');
+
+       hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static void check_string_1(struct hfp_gw_result *result,
+                               enum hfp_gw_cmd_type type, void *user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       unsigned int i = 4, j = 0;
+       char str[10];
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       g_assert(type == pdu->type);
+
+       g_assert(hfp_gw_result_get_string(result, str, sizeof(str)));
+
+       while (context->data->pdu_list[1].data[i] != '\"') {
+               g_assert(j < sizeof(str));
+               g_assert(str[j] == context->data->pdu_list[1].data[i]);
+
+               i++;
+               j++;
+       }
+
+       g_assert(context->data->pdu_list[1].data[i] == '\"');
+       g_assert(str[j] == '\0');
+
+       hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+static void check_string_2(struct hfp_gw_result *result,
+                               enum hfp_gw_cmd_type type, void *user_data)
+{
+       struct context *context = user_data;
+       const struct test_pdu *pdu;
+       char str[10];
+
+       memset(str, 'X', sizeof(str));
+
+       pdu = &context->data->pdu_list[context->pdu_offset++];
+
+       g_assert(type == pdu->type);
+
+       g_assert(!hfp_gw_result_get_string(result, str, 3));
+
+       g_assert(str[3] == 'X');
+
+       hfp_gw_send_result(context->hfp, HFP_RESULT_ERROR);
+}
+
+int main(int argc, char *argv[])
+{
+       g_test_init(&argc, &argv, NULL);
+
+       define_test("/hfp/test_init", test_init, NULL, data_end());
+       define_test("/hfp/test_cmd_handler_1", test_command_handler, NULL,
+                       raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\r'),
+                       raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F'),
+                       data_end());
+       define_test("/hfp/test_cmd_handler_2", test_command_handler, NULL,
+                       raw_pdu('A', 'T', 'D', '1', '2', '3', '4', '\r'),
+                       raw_pdu('A', 'T', 'D', '1', '2', '3', '4'),
+                       data_end());
+       define_test("/hfp/test_register_1", test_register, prefix_handler,
+                       raw_pdu('+', 'B', 'R', 'S', 'F', '\0'),
+                       raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '\r'),
+                       type_pdu(HFP_GW_CMD_TYPE_COMMAND, 0),
+                       data_end());
+       define_test("/hfp/test_register_2", test_register, prefix_handler,
+                       raw_pdu('+', 'B', 'R', 'S', 'F', '\0'),
+                       raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '=', '\r'),
+                       type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+                       data_end());
+       define_test("/hfp/test_register_3", test_register, prefix_handler,
+                       raw_pdu('+', 'B', 'R', 'S', 'F', '\0'),
+                       raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '?', '\r'),
+                       type_pdu(HFP_GW_CMD_TYPE_READ, 0),
+                       data_end());
+       define_test("/hfp/test_register_4", test_register, prefix_handler,
+                       raw_pdu('+', 'B', 'R', 'S', 'F', '\0'),
+                       raw_pdu('A', 'T', '+', 'B', 'R', 'S', 'F', '=', '?',
+                                                                       '\r'),
+                       type_pdu(HFP_GW_CMD_TYPE_TEST, 0),
+                       data_end());
+       define_test("/hfp/test_register_5", test_register, prefix_handler,
+                       raw_pdu('D', '\0'),
+                       raw_pdu('A', 'T', 'D', '1', '2', '3', '4', '5', '\r'),
+                       type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+                       data_end());
+       define_test("/hfp/test_fragmented_1", test_fragmented, NULL,
+                       frg_pdu('A'), frg_pdu('T'), frg_pdu('+'), frg_pdu('B'),
+                       frg_pdu('R'), frg_pdu('S'), frg_pdu('F'), frg_pdu('\r'),
+                       data_end());
+       define_test("/hfp/test_ustring_1", test_register, check_ustring_1,
+                       raw_pdu('D', '\0'),
+                       raw_pdu('A', 'T', 'D', '0', '1', '2', '3', '\r'),
+                       type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+                       data_end());
+       define_test("/hfp/test_ustring_2", test_register, check_ustring_2,
+                       raw_pdu('D', '\0'),
+                       raw_pdu('A', 'T', 'D', '0', '1', '2', '3', '\r'),
+                       type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+                       data_end());
+       define_test("/hfp/test_string_1", test_register, check_string_1,
+                       raw_pdu('D', '\0'),
+                       raw_pdu('A', 'T', 'D', '\"', '0', '1', '2', '3', '\"',
+                                                                       '\r'),
+                       type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+                       data_end());
+       define_test("/hfp/test_string_2", test_register, check_string_2,
+                       raw_pdu('D', '\0'),
+                       raw_pdu('A', 'T', 'D', '\"', '0', '1', '2', '3', '\"',
+                                                                       '\r'),
+                       type_pdu(HFP_GW_CMD_TYPE_SET, 0),
+                       data_end());
+
+       return g_test_run();
+}
index def133a..ef0cffc 100644 (file)
 #include <stdlib.h>
 #include <errno.h>
 
+#include "src/shared/util.h"
+
 #include "lib/sdp.h"
 #include "lib/sdp_lib.h"
 
+static void test_ntoh64(void)
+{
+       uint64_t test = 0x123456789abcdef;
+
+       g_assert(ntoh64(test) == be64toh(test));
+       g_assert(ntoh64(test) == be64_to_cpu(test));
+}
+
+static void test_hton64(void)
+{
+       uint64_t test = 0x123456789abcdef;
+
+       g_assert(hton64(test) == htobe64(test));
+       g_assert(hton64(test) == cpu_to_be64(test));
+}
+
 static void test_sdp_get_access_protos_valid(void)
 {
        sdp_record_t *rec;
@@ -439,6 +457,9 @@ int main(int argc, char *argv[])
 {
        g_test_init(&argc, &argv, NULL);
 
+       g_test_add_func("/lib/ntoh64", test_ntoh64);
+       g_test_add_func("/lib/hton64", test_hton64);
+
        g_test_add_func("/lib/sdp_get_access_protos/valid",
                                        test_sdp_get_access_protos_valid);
        g_test_add_func("/lib/sdp_get_access_protos/nodata",
diff --git a/unit/test-queue.c b/unit/test-queue.c
new file mode 100644 (file)
index 0000000..7c6d2ad
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *
+ *  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 <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+
+static void test_basic(void)
+{
+       struct queue *queue;
+       unsigned int n, i;
+
+       queue = queue_new();
+       g_assert(queue != NULL);
+
+       for (n = 0; n < 1024; n++) {
+               for (i = 1; i < n + 2; i++)
+                       queue_push_tail(queue, UINT_TO_PTR(i));
+
+               g_assert(queue_length(queue) == n + 1);
+
+               for (i = 1; i < n + 2; i++) {
+                       void *ptr;
+
+                       ptr = queue_pop_head(queue);
+                       g_assert(ptr != NULL);
+                       g_assert(i == PTR_TO_UINT(ptr));
+               }
+
+               g_assert(queue_isempty(queue) == true);
+       }
+
+       queue_destroy(queue, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/queue/basic", test_basic);
+
+       return g_test_run();
+}
diff --git a/unit/test-ringbuf.c b/unit/test-ringbuf.c
new file mode 100644 (file)
index 0000000..e28e04b
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ *
+ *  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 <string.h>
+#include <stdint.h>
+
+#include <glib.h>
+
+#include "src/shared/ringbuf.h"
+
+static unsigned int nlpo2(unsigned int x)
+{
+       x--;
+       x |= (x >> 1);
+       x |= (x >> 2);
+       x |= (x >> 4);
+       x |= (x >> 8);
+       x |= (x >> 16);
+       return x + 1;
+}
+
+static unsigned int fls(unsigned int x)
+{
+       return x ? sizeof(x) * 8 - __builtin_clz(x) : 0;
+}
+
+static unsigned int align_power2(unsigned int u)
+{
+       return 1 << fls(u - 1);
+}
+
+static void test_power2(void)
+{
+       size_t i;
+
+       for (i = 1; i < 1000000; i++) {
+               size_t size1, size2, size3 = 1;
+
+               size1 = nlpo2(i);
+               size2 = align_power2(i);
+
+               /* Find the next power of two */
+               while (size3 < i && size3 < SIZE_MAX)
+                       size3 <<= 1;
+
+               if (g_test_verbose())
+                       g_print("%zu -> size1=%zu size2=%zu size3=%zu\n",
+                                               i, size1, size2, size3);
+
+               g_assert(size1 == size2);
+               g_assert(size2 == size3);
+               g_assert(size3 == size1);
+       }
+}
+
+static void test_alloc(void)
+{
+       int i;
+
+       for (i = 2; i < 10000; i++) {
+               struct ringbuf *rb;
+
+               if (g_test_verbose())
+                       g_print("Iteration %i\n", i);
+
+               rb = ringbuf_new(i);
+               g_assert(rb != NULL);
+
+               g_assert(ringbuf_capacity(rb) == ringbuf_avail(rb));
+
+               ringbuf_free(rb);
+       }
+}
+
+static void test_printf(void)
+{
+       static size_t rb_size = 500;
+       static size_t rb_capa = 512;
+       struct ringbuf *rb;
+       int i;
+
+       rb = ringbuf_new(rb_size);
+       g_assert(rb != NULL);
+       g_assert(ringbuf_capacity(rb) == rb_capa);
+
+       for (i = 0; i < 10000; i++) {
+               size_t len, count = i % rb_capa;
+               char *str, *ptr;
+
+               if (!count)
+                       continue;
+
+               if (g_test_verbose())
+                       g_print("Iteration %i\n", i);
+
+               len = asprintf(&str, "%*c", (int) count, 'x');
+               g_assert(len == count);
+
+               len = ringbuf_printf(rb, "%s", str);
+               g_assert(len == count);
+               g_assert(ringbuf_len(rb) == count);
+               g_assert(ringbuf_avail(rb) == rb_capa - len);
+
+               ptr = ringbuf_peek(rb, 0, &len);
+               g_assert(ptr != NULL);
+               g_assert(len == count);
+               g_assert(strncmp(str, ptr, len) == 0);
+
+               len = ringbuf_drain(rb, count);
+               g_assert(len == count);
+               g_assert(ringbuf_len(rb) == 0);
+               g_assert(ringbuf_avail(rb) == rb_capa);
+
+               free(str);
+       }
+
+       ringbuf_free(rb);
+}
+
+int main(int argc, char *argv[])
+{
+       g_test_init(&argc, &argv, NULL);
+
+       g_test_add_func("/ringbuf/power2", test_power2);
+       g_test_add_func("/ringbuf/alloc", test_alloc);
+       g_test_add_func("/ringbuf/printf", test_printf);
+
+       return g_test_run();
+}
index 6d699e2..a89dbfc 100644 (file)
@@ -37,6 +37,7 @@
 #include "lib/sdp_lib.h"
 
 #include "src/shared/util.h"
+#include "src/log.h"
 #include "src/sdpd.h"
 
 struct sdp_pdu {
@@ -72,7 +73,7 @@ struct test_data {
 #define define_test(name, _mtu, args...) \
        do {                                                            \
                const struct sdp_pdu pdus[] = {                         \
-                       args, { }, { }                                  \
+                       args, { }                                       \
                };                                                      \
                static struct test_data data;                           \
                data.mtu = _mtu;                                        \
@@ -128,12 +129,6 @@ static void sdp_debug(const char *str, void *user_data)
        g_print("%s%s\n", prefix, str);
 }
 
-void btd_debug(const char *format, ...);
-
-void btd_debug(const char *format, ...)
-{
-}
-
 static void context_quit(struct context *context)
 {
        g_main_loop_quit(context->main_loop);
@@ -797,6 +792,9 @@ int main(int argc, char *argv[])
 {
        g_test_init(&argc, &argv, NULL);
 
+       if (g_test_verbose())
+               __btd_log_init("*", 0);
+
        /*
         * Service Search Request
         *
index 79ab3e6..6c7e9d0 100644 (file)
@@ -46,10 +46,10 @@ static unsigned char uuid_base_binary[] = {
                        0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb };
 
 static struct uuid_test_data uuid_base = {
-       .str = "00000000-0000-1000-8000-00805F9B34FB",
+       .str = "00000000-0000-1000-8000-00805f9b34fb",
        .binary = uuid_base_binary,
        .type = BT_UUID128,
-       .str128 = "00000000-0000-1000-8000-00805F9B34FB",
+       .str128 = "00000000-0000-1000-8000-00805f9b34fb",
        .binary128 = uuid_base_binary,
 };
 
@@ -97,7 +97,6 @@ static void test_uuid(gconstpointer data)
 {
        const struct uuid_test_data *test_data = data;
        bt_uuid_t uuid;
-       uint128_t n128, u128;
 
        g_assert(bt_string_to_uuid(&uuid, test_data->str) == 0);
        g_assert(uuid.type == test_data->type);
@@ -110,9 +109,11 @@ static void test_uuid(gconstpointer data)
                g_assert(uuid.value.u32 == test_data->val32);
                break;
        case BT_UUID128:
-               memcpy(&n128, test_data->binary, 16);
-               ntoh128(&n128, &u128);
-               g_assert(memcmp(&uuid.value.u128, &u128, 16) == 0);
+               /*
+                * No matter the system type: 128-bit UUID should use
+                * big-endian (human readable format).
+                */
+               g_assert(memcmp(&uuid.value.u128, test_data->binary, 16) == 0);
                break;
        default:
                return;
index c76acdf..71fe7ca 100644 (file)
@@ -193,5 +193,6 @@ send:
 
 failed:
        g_main_loop_quit(d->mainloop);
+       d->io_completed = TRUE;
        return FALSE;
 }
index 752ce61..96528a6 100644 (file)
@@ -41,6 +41,7 @@ struct test_data {
        guint id;
        gsize total;
        GMainLoop *mainloop;
+       gboolean io_completed;
 };
 
 #define TEST_ERROR test_error_quark()