Imported Upstream version 5.12 upstream/5.12
authorWu Zheng <wu.zheng@intel.com>
Thu, 12 Dec 2013 09:50:37 +0000 (04:50 -0500)
committerWu Zheng <wu.zheng@intel.com>
Thu, 12 Dec 2013 09:50:37 +0000 (04:50 -0500)
94 files changed:
ChangeLog
Makefile.am
Makefile.in
Makefile.plugins
acinclude.m4
android/Android.mk
android/Makefile.am
android/README
android/a2dp.c
android/a2dp.h
android/avdtp.c [new file with mode: 0644]
android/avdtp.h [new file with mode: 0644]
android/bluetooth.c
android/bluetooth.h
android/client/if-sock.c
android/hal-a2dp.c
android/hal-bluetooth.c
android/hal-hidhost.c
android/hal-ipc.c
android/hal-ipc.h
android/hal-msg.h
android/hal-pan.c
android/hal-sock.c
android/hal-utils.c
android/hal.h
android/hidhost.c
android/hidhost.h
android/ipc.c
android/ipc.h
android/main.c
android/pan.c
android/pan.h
android/pics-did.txt [new file with mode: 0644]
android/pics-gap.txt [new file with mode: 0644]
android/pics-hid.txt [new file with mode: 0644]
android/pics-opp.txt [new file with mode: 0644]
android/pics-pan.txt [new file with mode: 0644]
android/pics-pbap.txt [new file with mode: 0644]
android/socket.c
android/socket.h
client/main.c
configure
configure.ac
doc/device-api.txt
emulator/btdev.c
emulator/bthost.c
emulator/bthost.h
lib/bluetooth.c
lib/bluetooth.h
lib/hci.c
monitor/bt.h
monitor/l2cap.c
monitor/ll.c
monitor/packet.c
plugins/autopair.c
plugins/neard.c
plugins/sixaxis.c [new file with mode: 0644]
plugins/wiimote.c
profiles/audio/a2dp.c
profiles/audio/avctp.c
profiles/audio/avdtp.c
profiles/audio/avdtp.h
profiles/audio/sink.c
profiles/audio/sink.h
profiles/audio/source.c
profiles/audio/source.h
profiles/health/hdp.c
profiles/health/hdp_util.c
profiles/input/device.c
profiles/input/manager.c
profiles/input/server.c
profiles/network/bnep.c [moved from profiles/network/common.c with 58% similarity]
profiles/network/bnep.h [moved from profiles/network/common.h with 87% similarity]
profiles/network/connection.c
profiles/network/manager.c
profiles/network/server.c
profiles/sap/server.c
src/adapter.c
src/adapter.h
src/agent.c
src/attrib-server.c
src/bluetooth.ver
src/device.c
src/device.h
src/eir.c
src/eir.h
src/profile.c
tools/l2cap-tester.c
tools/l2test.c
tools/obex-server-tool.c
tools/sdptool.c
tools/smp-tester.c
unit/test-avdtp.c [new file with mode: 0644]
unit/test-eir.c

index ca48336..1db42c9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+ver 5.12:
+       Fix issue with missing reply to DisconnectProfile.
+       Fix issue with icon property and class of device changes.
+       Fix issue with HID devices when SDP record is not available.
+       Fix issue with handling auto-pairing of printers.
+       Fix issue with agent authorization handling.
+       Add support for PS3 controller setup and pairing.
+       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 328ccdb..81a458b 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:2:17
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:3:17
 lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 endif
 
@@ -249,6 +249,14 @@ unit_test_sdp_SOURCES = unit/test-sdp.c \
                                src/sdpd-service.c src/sdpd-request.c
 unit_test_sdp_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
 
+unit_tests += unit/test-avdtp
+
+unit_test_avdtp_SOURCES = unit/test-avdtp.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/log.h src/log.c \
+                               android/avdtp.c android/avdtp.h
+unit_test_avdtp_LDADD = @GLIB_LIBS@
+
 unit_tests += unit/test-gdbus-client
 
 unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c
@@ -320,7 +328,7 @@ $(lib_libbluetooth_la_OBJECTS): $(local_headers)
 
 lib/bluetooth/%.h: lib/%.h
        $(AM_V_at)$(MKDIR_P) lib/bluetooth
-       $(AM_V_GEN)$(LN_S) -f $(abs_top_builddir)/$< $@
+       $(AM_V_GEN)$(LN_S) -f "$(abs_top_builddir)"/$< $@
 
 clean-local:
        $(RM) -r lib/bluetooth
index 9556969..9dfabfc 100644 (file)
@@ -108,10 +108,11 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \
 @EXPERIMENTAL_TRUE@    profiles/thermometer/thermometer.c \
 @EXPERIMENTAL_TRUE@    profiles/heartrate/heartrate.c \
 @EXPERIMENTAL_TRUE@    profiles/cyclingspeed/cyclingspeed.c
-@MAINTAINER_MODE_TRUE@am__append_12 = plugins/external-dummy.la
-@CLIENT_TRUE@am__append_13 = client/bluetoothctl
-@MONITOR_TRUE@am__append_14 = monitor/btmon
-@EXPERIMENTAL_TRUE@am__append_15 = emulator/btvirt emulator/b1ee \
+@SIXAXIS_TRUE@am__append_12 = plugins/sixaxis.la
+@MAINTAINER_MODE_TRUE@am__append_13 = plugins/external-dummy.la
+@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 \
@@ -121,41 +122,41 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \
 @EXPERIMENTAL_TRUE@    tools/btinfo tools/btattach tools/btsnoop \
 @EXPERIMENTAL_TRUE@    tools/btiotest tools/cltest \
 @EXPERIMENTAL_TRUE@    tools/mpris-player
-@TOOLS_TRUE@am__append_16 = tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
+@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@am__append_17 = tools/hciattach.1 tools/hciconfig.1 \
+@TOOLS_TRUE@am__append_18 = tools/hciattach.1 tools/hciconfig.1 \
 @TOOLS_TRUE@                   tools/hcitool.1 tools/hcidump.1 \
 @TOOLS_TRUE@                   tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
 @TOOLS_TRUE@                   tools/sdptool.1 tools/ciptool.1 tools/bccmd.1
 
-@TOOLS_FALSE@am__append_18 = tools/hciattach.1 tools/hciconfig.1 \
+@TOOLS_FALSE@am__append_19 = tools/hciattach.1 tools/hciconfig.1 \
 @TOOLS_FALSE@                  tools/hcitool.1 tools/hcidump.1 \
 @TOOLS_FALSE@                  tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
 @TOOLS_FALSE@                  tools/sdptool.1 tools/ciptool.1 tools/bccmd.1
 
 @HID2HCI_TRUE@udev_PROGRAMS = tools/hid2hci$(EXEEXT)
-@HID2HCI_TRUE@am__append_19 = tools/hid2hci.1
-@HID2HCI_FALSE@am__append_20 = tools/hid2hci.1
-@EXPERIMENTAL_TRUE@am__append_21 = tools/bdaddr.1
-@READLINE_TRUE@am__append_22 = attrib/gatttool \
+@HID2HCI_TRUE@am__append_20 = tools/hid2hci.1
+@HID2HCI_FALSE@am__append_21 = tools/hid2hci.1
+@EXPERIMENTAL_TRUE@am__append_22 = tools/bdaddr.1
+@READLINE_TRUE@am__append_23 = attrib/gatttool \
 @READLINE_TRUE@                        tools/obex-client-tool tools/obex-server-tool \
 @READLINE_TRUE@                        tools/bluetooth-player tools/obexctl
 
-@EXPERIMENTAL_TRUE@am__append_23 = profiles/iap/iapd
+@EXPERIMENTAL_TRUE@am__append_24 = profiles/iap/iapd
 @CUPS_TRUE@cups_PROGRAMS = profiles/cups/bluetooth$(EXEEXT)
-@EXPERIMENTAL_TRUE@am__append_24 = pcsuite
-@EXPERIMENTAL_TRUE@am__append_25 = obexd/plugins/pcsuite.c
-@OBEX_TRUE@am__append_26 = irmc pbap
-@OBEX_TRUE@am__append_27 = obexd/plugins/irmc.c obexd/plugins/pbap.c \
+@EXPERIMENTAL_TRUE@am__append_25 = pcsuite
+@EXPERIMENTAL_TRUE@am__append_26 = obexd/plugins/pcsuite.c
+@OBEX_TRUE@am__append_27 = irmc pbap
+@OBEX_TRUE@am__append_28 = obexd/plugins/irmc.c obexd/plugins/pbap.c \
 @OBEX_TRUE@    obexd/plugins/vcard.h obexd/plugins/vcard.c \
 @OBEX_TRUE@    obexd/plugins/phonebook.h \
 @OBEX_TRUE@    obexd/plugins/phonebook-dummy.c
-@ANDROID_TRUE@am__append_28 = android/system-emulator \
+@ANDROID_TRUE@am__append_29 = android/system-emulator \
 @ANDROID_TRUE@ android/bluetoothd android/haltest
-@ANDROID_TRUE@am__append_29 = android/libhal-internal.la
-@HID2HCI_TRUE@am__append_30 = $(rules_DATA)
+@ANDROID_TRUE@am__append_30 = android/libhal-internal.la
+@HID2HCI_TRUE@am__append_31 = $(rules_DATA)
 TESTS = $(am__EXEEXT_8)
 subdir = .
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -286,6 +287,16 @@ plugins_external_dummy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
        $(plugins_external_dummy_la_LDFLAGS) $(LDFLAGS) -o $@
 @MAINTAINER_MODE_TRUE@am_plugins_external_dummy_la_rpath = -rpath \
 @MAINTAINER_MODE_TRUE@ $(plugindir)
+plugins_sixaxis_la_LIBADD =
+am__plugins_sixaxis_la_SOURCES_DIST = plugins/sixaxis.c
+@SIXAXIS_TRUE@am_plugins_sixaxis_la_OBJECTS =  \
+@SIXAXIS_TRUE@ plugins/plugins_sixaxis_la-sixaxis.lo
+plugins_sixaxis_la_OBJECTS = $(am_plugins_sixaxis_la_OBJECTS)
+plugins_sixaxis_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+       $(plugins_sixaxis_la_CFLAGS) $(CFLAGS) \
+       $(plugins_sixaxis_la_LDFLAGS) $(LDFLAGS) -o $@
+@SIXAXIS_TRUE@am_plugins_sixaxis_la_rpath = -rpath $(plugindir)
 @CLIENT_TRUE@am__EXEEXT_1 = client/bluetoothctl$(EXEEXT)
 @MONITOR_TRUE@am__EXEEXT_2 = monitor/btmon$(EXEEXT)
 @TOOLS_TRUE@am__EXEEXT_3 = tools/hciattach$(EXEEXT) \
@@ -325,7 +336,7 @@ plugins_external_dummy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
 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-gdbus-client$(EXEEXT) \
+       unit/test-avdtp$(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) \
@@ -339,9 +350,11 @@ am__android_bluetoothd_SOURCES_DIST = android/main.c src/log.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/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
+       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
 @ANDROID_TRUE@am_android_bluetoothd_OBJECTS = android/main.$(OBJEXT) \
 @ANDROID_TRUE@ src/log.$(OBJEXT) src/sdpd-database.$(OBJEXT) \
 @ANDROID_TRUE@ src/sdpd-server.$(OBJEXT) \
@@ -352,9 +365,10 @@ am__android_bluetoothd_SOURCES_DIST = android/main.c src/log.c \
 @ANDROID_TRUE@ src/shared/mgmt.$(OBJEXT) \
 @ANDROID_TRUE@ android/bluetooth.$(OBJEXT) \
 @ANDROID_TRUE@ android/hidhost.$(OBJEXT) android/ipc.$(OBJEXT) \
-@ANDROID_TRUE@ android/a2dp.$(OBJEXT) android/socket.$(OBJEXT) \
-@ANDROID_TRUE@ android/pan.$(OBJEXT) btio/btio.$(OBJEXT) \
-@ANDROID_TRUE@ src/sdp-client.$(OBJEXT)
+@ANDROID_TRUE@ android/avdtp.$(OBJEXT) android/a2dp.$(OBJEXT) \
+@ANDROID_TRUE@ android/socket.$(OBJEXT) android/pan.$(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
@@ -583,8 +597,8 @@ am__src_bluetoothd_SOURCES_DIST = plugins/hostname.c plugins/wiimote.c \
        profiles/audio/avctp.h profiles/audio/avctp.c \
        profiles/audio/avrcp.h profiles/audio/avrcp.c \
        profiles/audio/player.h profiles/audio/player.c \
-       profiles/network/manager.c profiles/network/common.h \
-       profiles/network/common.c profiles/network/server.h \
+       profiles/network/manager.c profiles/network/bnep.h \
+       profiles/network/bnep.c profiles/network/server.h \
        profiles/network/server.c profiles/network/connection.h \
        profiles/network/connection.c profiles/input/manager.c \
        profiles/input/server.h profiles/input/server.c \
@@ -666,7 +680,7 @@ am__objects_14 = plugins/bluetoothd-hostname.$(OBJEXT) \
        profiles/audio/bluetoothd-avrcp.$(OBJEXT) \
        profiles/audio/bluetoothd-player.$(OBJEXT) \
        profiles/network/bluetoothd-manager.$(OBJEXT) \
-       profiles/network/bluetoothd-common.$(OBJEXT) \
+       profiles/network/bluetoothd-bnep.$(OBJEXT) \
        profiles/network/bluetoothd-server.$(OBJEXT) \
        profiles/network/bluetoothd-connection.$(OBJEXT) \
        profiles/input/bluetoothd-manager.$(OBJEXT) \
@@ -1010,6 +1024,11 @@ am__tools_smp_tester_SOURCES_DIST = tools/smp-tester.c monitor/bt.h \
 tools_smp_tester_OBJECTS = $(am_tools_smp_tester_OBJECTS)
 @EXPERIMENTAL_TRUE@tools_smp_tester_DEPENDENCIES =  \
 @EXPERIMENTAL_TRUE@    lib/libbluetooth-internal.la
+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_crc_OBJECTS = unit/test-crc.$(OBJEXT) \
        monitor/crc.$(OBJEXT)
 unit_test_crc_OBJECTS = $(am_unit_test_crc_OBJECTS)
@@ -1092,11 +1111,12 @@ SOURCES = $(profiles_sap_libsap_a_SOURCES) \
        $(lib_libbluetooth_internal_la_SOURCES) \
        $(lib_libbluetooth_la_SOURCES) \
        $(plugins_external_dummy_la_SOURCES) \
-       $(android_bluetoothd_SOURCES) $(android_haltest_SOURCES) \
-       $(android_system_emulator_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) \
+       $(plugins_sixaxis_la_SOURCES) $(android_bluetoothd_SOURCES) \
+       $(android_haltest_SOURCES) $(android_system_emulator_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) \
        $(profiles_cups_bluetooth_SOURCES) \
        $(profiles_iap_iapd_SOURCES) $(src_bluetoothd_SOURCES) \
        $(nodist_src_bluetoothd_SOURCES) tools/amptest.c \
@@ -1116,9 +1136,10 @@ SOURCES = $(profiles_sap_libsap_a_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_crc_SOURCES) \
-       $(unit_test_eir_SOURCES) $(unit_test_gdbus_client_SOURCES) \
-       $(unit_test_gobex_SOURCES) $(unit_test_gobex_apparam_SOURCES) \
+       $(tools_smp_tester_SOURCES) $(unit_test_avdtp_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) \
@@ -1130,6 +1151,7 @@ DIST_SOURCES = $(am__profiles_sap_libsap_a_SOURCES_DIST) \
        $(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_bluetoothd_SOURCES_DIST) \
        $(am__android_haltest_SOURCES_DIST) \
        $(am__android_system_emulator_SOURCES_DIST) \
@@ -1164,7 +1186,8 @@ DIST_SOURCES = $(am__profiles_sap_libsap_a_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) \
-       $(am__tools_smp_tester_SOURCES_DIST) $(unit_test_crc_SOURCES) \
+       $(am__tools_smp_tester_SOURCES_DIST) \
+       $(unit_test_avdtp_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) \
@@ -1363,25 +1386,27 @@ 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_29)
-dist_man_MANS = $(am__append_17) $(am__append_19)
+       gdbus/libgdbus-internal.la $(am__append_30)
+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_30)
+       $(am__append_31)
 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_18) \
-       $(am__append_20) $(am__append_21) obexd/src/obex.service.in \
+       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 \
-       tools/hid2hci.rules $(test_scripts) doc/assigned-numbers.txt \
-       doc/supported-features.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 doc/alert-api.txt \
-       doc/proximity-api.txt doc/heartrate-api.txt \
+       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) \
+       doc/assigned-numbers.txt doc/supported-features.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 \
+       doc/alert-api.txt doc/proximity-api.txt doc/heartrate-api.txt \
        doc/thermometer-api.txt doc/cyclingspeed-api.txt \
        doc/obex-api.txt doc/obex-agent-api.txt tools/magic.btsnoop
 include_HEADERS = $(am__append_1)
@@ -1401,7 +1426,7 @@ 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)
+plugin_LTLIBRARIES = $(am__append_12) $(am__append_13)
 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 \
@@ -1412,7 +1437,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:2:17
+@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:3:17
 @LIBRARY_TRUE@lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
 lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \
                                        $(extra_headers) $(extra_sources)
@@ -1450,8 +1475,8 @@ builtin_sources = plugins/hostname.c plugins/wiimote.c \
        profiles/audio/avctp.h profiles/audio/avctp.c \
        profiles/audio/avrcp.h profiles/audio/avrcp.c \
        profiles/audio/player.h profiles/audio/player.c \
-       profiles/network/manager.c profiles/network/common.h \
-       profiles/network/common.c profiles/network/server.h \
+       profiles/network/manager.c profiles/network/bnep.h \
+       profiles/network/bnep.c profiles/network/server.h \
        profiles/network/server.c profiles/network/connection.h \
        profiles/network/connection.c profiles/input/manager.c \
        profiles/input/server.h profiles/input/server.c \
@@ -1462,6 +1487,11 @@ builtin_sources = plugins/hostname.c plugins/wiimote.c \
        profiles/deviceinfo/deviceinfo.c $(am__append_11)
 builtin_nodist = 
 @EXPERIMENTAL_TRUE@profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+@SIXAXIS_TRUE@plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
+@SIXAXIS_TRUE@plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+@SIXAXIS_TRUE@                                         -no-undefined @UDEV_LIBS@
+
+@SIXAXIS_TRUE@plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@
 @MAINTAINER_MODE_TRUE@plugins_external_dummy_la_SOURCES = plugins/external-dummy.c
 @MAINTAINER_MODE_TRUE@plugins_external_dummy_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
 @MAINTAINER_MODE_TRUE@                             -no-undefined
@@ -1723,12 +1753,12 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \
 @SYSTEMD_TRUE@dbussessionbusdir = @DBUS_SESSIONBUSDIR@
 @SYSTEMD_TRUE@dbussessionbus_DATA = obexd/src/org.bluez.obex.service
 obex_plugindir = $(libdir)/obex/plugins
-obexd_builtin_modules = filesystem bluetooth $(am__append_24) opp ftp \
-       $(am__append_26) mas mns
+obexd_builtin_modules = filesystem bluetooth $(am__append_25) opp ftp \
+       $(am__append_27) mas mns
 obexd_builtin_sources = obexd/plugins/filesystem.c \
        obexd/plugins/filesystem.h obexd/plugins/bluetooth.c \
-       $(am__append_25) obexd/plugins/opp.c obexd/plugins/ftp.c \
-       obexd/plugins/ftp.h $(am__append_27) obexd/plugins/mas.c \
+       $(am__append_26) obexd/plugins/opp.c obexd/plugins/ftp.c \
+       obexd/plugins/ftp.h $(am__append_28) obexd/plugins/mas.c \
        obexd/src/map_ap.h obexd/plugins/messages.h \
        obexd/plugins/messages-dummy.c obexd/client/mns.c \
        obexd/src/map_ap.h obexd/client/map-event.h
@@ -1792,11 +1822,13 @@ nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files)
 @ANDROID_TRUE@                         android/bluetooth.h android/bluetooth.c \
 @ANDROID_TRUE@                         android/hidhost.h android/hidhost.c \
 @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/socket.h android/socket.c \
 @ANDROID_TRUE@                         android/pan.h android/pan.c \
 @ANDROID_TRUE@                         btio/btio.h btio/btio.c \
-@ANDROID_TRUE@                         src/sdp-client.h src/sdp-client.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 \
@@ -1854,7 +1886,7 @@ 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-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
@@ -1877,6 +1909,12 @@ unit_test_sdp_SOURCES = unit/test-sdp.c \
                                src/sdpd-service.c src/sdpd-request.c
 
 unit_test_sdp_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+unit_test_avdtp_SOURCES = unit/test-avdtp.c \
+                               src/shared/util.h src/shared/util.c \
+                               src/log.h src/log.c \
+                               android/avdtp.c android/avdtp.h
+
+unit_test_avdtp_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@
@@ -2123,6 +2161,10 @@ plugins/plugins_external_dummy_la-external-dummy.lo:  \
        plugins/$(am__dirstamp) plugins/$(DEPDIR)/$(am__dirstamp)
 plugins/external-dummy.la: $(plugins_external_dummy_la_OBJECTS) $(plugins_external_dummy_la_DEPENDENCIES) $(EXTRA_plugins_external_dummy_la_DEPENDENCIES) plugins/$(am__dirstamp)
        $(AM_V_CCLD)$(plugins_external_dummy_la_LINK) $(am_plugins_external_dummy_la_rpath) $(plugins_external_dummy_la_OBJECTS) $(plugins_external_dummy_la_LIBADD) $(LIBS)
+plugins/plugins_sixaxis_la-sixaxis.lo: plugins/$(am__dirstamp) \
+       plugins/$(DEPDIR)/$(am__dirstamp)
+plugins/sixaxis.la: $(plugins_sixaxis_la_OBJECTS) $(plugins_sixaxis_la_DEPENDENCIES) $(EXTRA_plugins_sixaxis_la_DEPENDENCIES) plugins/$(am__dirstamp)
+       $(AM_V_CCLD)$(plugins_sixaxis_la_LINK) $(am_plugins_sixaxis_la_rpath) $(plugins_sixaxis_la_OBJECTS) $(plugins_sixaxis_la_LIBADD) $(LIBS)
 install-binPROGRAMS: $(bin_PROGRAMS)
        @$(NORMAL_INSTALL)
        @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
@@ -2352,6 +2394,8 @@ android/hidhost.$(OBJEXT): android/$(am__dirstamp) \
        android/$(DEPDIR)/$(am__dirstamp)
 android/ipc.$(OBJEXT): android/$(am__dirstamp) \
        android/$(DEPDIR)/$(am__dirstamp)
+android/avdtp.$(OBJEXT): android/$(am__dirstamp) \
+       android/$(DEPDIR)/$(am__dirstamp)
 android/a2dp.$(OBJEXT): android/$(am__dirstamp) \
        android/$(DEPDIR)/$(am__dirstamp)
 android/socket.$(OBJEXT): android/$(am__dirstamp) \
@@ -2368,6 +2412,14 @@ btio/btio.$(OBJEXT): btio/$(am__dirstamp) \
        btio/$(DEPDIR)/$(am__dirstamp)
 src/sdp-client.$(OBJEXT): src/$(am__dirstamp) \
        src/$(DEPDIR)/$(am__dirstamp)
+profiles/network/$(am__dirstamp):
+       @$(MKDIR_P) profiles/network
+       @: > profiles/network/$(am__dirstamp)
+profiles/network/$(DEPDIR)/$(am__dirstamp):
+       @$(MKDIR_P) profiles/network/$(DEPDIR)
+       @: > profiles/network/$(DEPDIR)/$(am__dirstamp)
+profiles/network/bnep.$(OBJEXT): profiles/network/$(am__dirstamp) \
+       profiles/network/$(DEPDIR)/$(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)
@@ -2725,16 +2777,10 @@ profiles/audio/bluetoothd-avrcp.$(OBJEXT):  \
 profiles/audio/bluetoothd-player.$(OBJEXT):  \
        profiles/audio/$(am__dirstamp) \
        profiles/audio/$(DEPDIR)/$(am__dirstamp)
-profiles/network/$(am__dirstamp):
-       @$(MKDIR_P) profiles/network
-       @: > profiles/network/$(am__dirstamp)
-profiles/network/$(DEPDIR)/$(am__dirstamp):
-       @$(MKDIR_P) profiles/network/$(DEPDIR)
-       @: > profiles/network/$(DEPDIR)/$(am__dirstamp)
 profiles/network/bluetoothd-manager.$(OBJEXT):  \
        profiles/network/$(am__dirstamp) \
        profiles/network/$(DEPDIR)/$(am__dirstamp)
-profiles/network/bluetoothd-common.$(OBJEXT):  \
+profiles/network/bluetoothd-bnep.$(OBJEXT):  \
        profiles/network/$(am__dirstamp) \
        profiles/network/$(DEPDIR)/$(am__dirstamp)
 profiles/network/bluetoothd-server.$(OBJEXT):  \
@@ -3241,6 +3287,11 @@ unit/$(am__dirstamp):
 unit/$(DEPDIR)/$(am__dirstamp):
        @$(MKDIR_P) unit/$(DEPDIR)
        @: > unit/$(DEPDIR)/$(am__dirstamp)
+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-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)
@@ -3362,6 +3413,7 @@ mostlyclean-compile:
        -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/avdtp.$(OBJEXT)
        -rm -f android/bluetooth.$(OBJEXT)
        -rm -f android/client/android_haltest-haltest.$(OBJEXT)
        -rm -f android/client/android_haltest-history.$(OBJEXT)
@@ -3491,6 +3543,8 @@ mostlyclean-compile:
        -rm -f plugins/bluetoothd-wiimote.$(OBJEXT)
        -rm -f plugins/plugins_external_dummy_la-external-dummy.$(OBJEXT)
        -rm -f plugins/plugins_external_dummy_la-external-dummy.lo
+       -rm -f plugins/plugins_sixaxis_la-sixaxis.$(OBJEXT)
+       -rm -f plugins/plugins_sixaxis_la-sixaxis.lo
        -rm -f profiles/alert/bluetoothd-server.$(OBJEXT)
        -rm -f profiles/audio/bluetoothd-a2dp.$(OBJEXT)
        -rm -f profiles/audio/bluetoothd-avctp.$(OBJEXT)
@@ -3522,10 +3576,11 @@ mostlyclean-compile:
        -rm -f profiles/input/bluetoothd-manager.$(OBJEXT)
        -rm -f profiles/input/bluetoothd-server.$(OBJEXT)
        -rm -f profiles/input/bluetoothd-suspend-dummy.$(OBJEXT)
-       -rm -f profiles/network/bluetoothd-common.$(OBJEXT)
+       -rm -f profiles/network/bluetoothd-bnep.$(OBJEXT)
        -rm -f profiles/network/bluetoothd-connection.$(OBJEXT)
        -rm -f profiles/network/bluetoothd-manager.$(OBJEXT)
        -rm -f profiles/network/bluetoothd-server.$(OBJEXT)
+       -rm -f profiles/network/bnep.$(OBJEXT)
        -rm -f profiles/proximity/bluetoothd-immalert.$(OBJEXT)
        -rm -f profiles/proximity/bluetoothd-linkloss.$(OBJEXT)
        -rm -f profiles/proximity/bluetoothd-main.$(OBJEXT)
@@ -3655,6 +3710,7 @@ mostlyclean-compile:
        -rm -f tools/sdptool.$(OBJEXT)
        -rm -f tools/smp-tester.$(OBJEXT)
        -rm -f tools/ubcsp.$(OBJEXT)
+       -rm -f unit/test-avdtp.$(OBJEXT)
        -rm -f unit/test-crc.$(OBJEXT)
        -rm -f unit/test-eir.$(OBJEXT)
        -rm -f unit/test-gdbus-client.$(OBJEXT)
@@ -3681,6 +3737,7 @@ distclean-compile:
 @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)/avdtp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth.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@
@@ -3800,6 +3857,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-policy.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-wiimote.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/alert/$(DEPDIR)/bluetoothd-server.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po@am__quote@
@@ -3831,10 +3889,11 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-server.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-suspend-dummy.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-common.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-bnep.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-connection.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-manager.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bnep.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-immalert.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-linkloss.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-main.Po@am__quote@
@@ -3964,6 +4023,7 @@ 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-avdtp.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@
@@ -4052,6 +4112,13 @@ plugins/plugins_external_dummy_la-external-dummy.lo: plugins/external-dummy.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_external_dummy_la_CFLAGS) $(CFLAGS) -c -o plugins/plugins_external_dummy_la-external-dummy.lo `test -f 'plugins/external-dummy.c' || echo '$(srcdir)/'`plugins/external-dummy.c
 
+plugins/plugins_sixaxis_la-sixaxis.lo: plugins/sixaxis.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_sixaxis_la_CFLAGS) $(CFLAGS) -MT plugins/plugins_sixaxis_la-sixaxis.lo -MD -MP -MF plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Tpo -c -o plugins/plugins_sixaxis_la-sixaxis.lo `test -f 'plugins/sixaxis.c' || echo '$(srcdir)/'`plugins/sixaxis.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Tpo plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='plugins/sixaxis.c' object='plugins/plugins_sixaxis_la-sixaxis.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_sixaxis_la_CFLAGS) $(CFLAGS) -c -o plugins/plugins_sixaxis_la-sixaxis.lo `test -f 'plugins/sixaxis.c' || echo '$(srcdir)/'`plugins/sixaxis.c
+
 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
@@ -5116,19 +5183,19 @@ profiles/network/bluetoothd-manager.obj: profiles/network/manager.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 profiles/network/bluetoothd-manager.obj `if test -f 'profiles/network/manager.c'; then $(CYGPATH_W) 'profiles/network/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/manager.c'; fi`
 
-profiles/network/bluetoothd-common.o: profiles/network/common.c
-@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-common.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-common.Tpo -c -o profiles/network/bluetoothd-common.o `test -f 'profiles/network/common.c' || echo '$(srcdir)/'`profiles/network/common.c
-@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-common.Tpo profiles/network/$(DEPDIR)/bluetoothd-common.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='profiles/network/common.c' object='profiles/network/bluetoothd-common.o' libtool=no @AMDEPBACKSLASH@
+profiles/network/bluetoothd-bnep.o: profiles/network/bnep.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-bnep.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo -c -o profiles/network/bluetoothd-bnep.o `test -f 'profiles/network/bnep.c' || echo '$(srcdir)/'`profiles/network/bnep.c
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo profiles/network/$(DEPDIR)/bluetoothd-bnep.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='profiles/network/bnep.c' object='profiles/network/bluetoothd-bnep.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 profiles/network/bluetoothd-common.o `test -f 'profiles/network/common.c' || echo '$(srcdir)/'`profiles/network/common.c
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-bnep.o `test -f 'profiles/network/bnep.c' || echo '$(srcdir)/'`profiles/network/bnep.c
 
-profiles/network/bluetoothd-common.obj: profiles/network/common.c
-@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-common.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-common.Tpo -c -o profiles/network/bluetoothd-common.obj `if test -f 'profiles/network/common.c'; then $(CYGPATH_W) 'profiles/network/common.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/common.c'; fi`
-@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-common.Tpo profiles/network/$(DEPDIR)/bluetoothd-common.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='profiles/network/common.c' object='profiles/network/bluetoothd-common.obj' libtool=no @AMDEPBACKSLASH@
+profiles/network/bluetoothd-bnep.obj: profiles/network/bnep.c
+@am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-bnep.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo -c -o profiles/network/bluetoothd-bnep.obj `if test -f 'profiles/network/bnep.c'; then $(CYGPATH_W) 'profiles/network/bnep.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/bnep.c'; fi`
+@am__fastdepCC_TRUE@   $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo profiles/network/$(DEPDIR)/bluetoothd-bnep.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      $(AM_V_CC)source='profiles/network/bnep.c' object='profiles/network/bluetoothd-bnep.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 profiles/network/bluetoothd-common.obj `if test -f 'profiles/network/common.c'; then $(CYGPATH_W) 'profiles/network/common.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/common.c'; fi`
+@am__fastdepCC_FALSE@  $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-bnep.obj `if test -f 'profiles/network/bnep.c'; then $(CYGPATH_W) 'profiles/network/bnep.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/bnep.c'; fi`
 
 profiles/network/bluetoothd-server.o: profiles/network/server.c
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-server.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/network/bluetoothd-server.o `test -f 'profiles/network/server.c' || echo '$(srcdir)/'`profiles/network/server.c
@@ -6835,7 +6902,7 @@ $(lib_libbluetooth_la_OBJECTS): $(local_headers)
 
 lib/bluetooth/%.h: lib/%.h
        $(AM_V_at)$(MKDIR_P) lib/bluetooth
-       $(AM_V_GEN)$(LN_S) -f $(abs_top_builddir)/$< $@
+       $(AM_V_GEN)$(LN_S) -f "$(abs_top_builddir)"/$< $@
 
 clean-local:
        $(RM) -r lib/bluetooth
index 7c5f71d..6a1ddbf 100644 (file)
@@ -47,7 +47,7 @@ builtin_sources += profiles/audio/control.h profiles/audio/control.c \
 
 builtin_modules += network
 builtin_sources += profiles/network/manager.c \
-                       profiles/network/common.h profiles/network/common.c \
+                       profiles/network/bnep.h profiles/network/bnep.c \
                        profiles/network/server.h profiles/network/server.c \
                        profiles/network/connection.h \
                        profiles/network/connection.c
@@ -110,3 +110,11 @@ builtin_sources += profiles/heartrate/heartrate.c
 builtin_modules += cyclingspeed
 builtin_sources += profiles/cyclingspeed/cyclingspeed.c
 endif
+
+if SIXAXIS
+plugin_LTLIBRARIES += plugins/sixaxis.la
+plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
+plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+                                               -no-undefined @UDEV_LIBS@
+plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@
+endif
index 5bfa29d..2065852 100644 (file)
@@ -21,6 +21,8 @@ AC_DEFUN([COMPILER_FLAGS], [
                with_cflags="$with_cflags -Wredundant-decls"
                with_cflags="$with_cflags -Wcast-align"
                with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
+               with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
+               with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28"
        fi
        AC_SUBST([WARNING_CFLAGS], $with_cflags)
 ])
index 616a338..549613c 100644 (file)
@@ -25,6 +25,7 @@ LOCAL_SRC_FILES := \
        hidhost.c \
        socket.c \
        ipc.c ipc.h \
+       avdtp.c \
        a2dp.c \
        pan.c \
        ../src/log.c \
@@ -41,6 +42,7 @@ LOCAL_SRC_FILES := \
        ../lib/hci.c \
        ../btio/btio.c \
        ../src/sdp-client.c \
+       ../profiles/network/bnep.c \
 
 LOCAL_C_INCLUDES := \
        $(call include-path-for, glib) \
@@ -65,6 +67,7 @@ lib_headers := \
        sdp.h \
        rfcomm.h \
        sco.h \
+       bnep.h \
 
 $(shell mkdir -p $(LOCAL_PATH)/../lib/bluetooth)
 
index 4e0c9ed..df04762 100644 (file)
@@ -19,11 +19,13 @@ android_bluetoothd_SOURCES = android/main.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
+                               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@
 
@@ -83,4 +85,7 @@ android_haltest_LDFLAGS = -pthread
 
 endif
 
-EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README
+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
index 6c2c53f..68c3e9f 100644 (file)
@@ -82,9 +82,12 @@ 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 and type 'adapter init' in
-prompt to initialize HAL library. On Android required bluetoothd service will
-be started automatically. On Linux it is required to start android/bluetoothd
-manually before init command timeout. To deinitialize HAL library and stop
-daemon type 'adapter cleanup'. Type 'help' for more information. Tab completion
-is also supported.
+of both Android daemon and HAL library. Start it with '-n' parameter and type
+'bluetooth init' in prompt to initialize HAL library. Running without parameter
+will make haltest try to initialize all services after start. On Android
+required bluetoothd service will be started automatically. On Linux it is
+required to start android/bluetoothd manually before init command timeout or
+use provided android/system-emulator, which takes care of launching daemon
+automatically on HAL library initialization. To deinitialize HAL library and
+stop daemon type 'bluetooth cleanup'. Type 'help' for more information. Tab
+completion is also supported.
index 936c28e..107cbb8 100644 (file)
 #include "ipc.h"
 #include "utils.h"
 #include "bluetooth.h"
+#include "avdtp.h"
 
 #define L2CAP_PSM_AVDTP 0x19
 #define SVC_HINT_CAPTURING 0x08
 
-static int notification_sk = -1;
 static GIOChannel *server = NULL;
 static GSList *devices = NULL;
 static bdaddr_t adapter_addr;
@@ -58,6 +58,7 @@ struct a2dp_device {
        uint8_t         state;
        GIOChannel      *io;
        guint           watch;
+       struct avdtp    *session;
 };
 
 static int device_cmp(gconstpointer s, gconstpointer user_data)
@@ -70,6 +71,9 @@ static int device_cmp(gconstpointer s, gconstpointer user_data)
 
 static void a2dp_device_free(struct a2dp_device *dev)
 {
+       if (dev->session)
+               avdtp_unref(dev->session);
+
        if (dev->watch > 0)
                g_source_remove(dev->watch);
 
@@ -107,8 +111,13 @@ static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state)
        bdaddr2android(&dev->dst, ev.bdaddr);
        ev.state = state;
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_A2DP,
-                       HAL_EV_A2DP_CONN_STATE, sizeof(ev), &ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE, sizeof(ev),
+                                                                       &ev);
+
+       if (state != HAL_A2DP_STATE_DISCONNECTED)
+               return;
+
+       a2dp_device_free(dev);
 }
 
 static gboolean watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
@@ -117,8 +126,6 @@ static gboolean watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data)
 
        bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
 
-       a2dp_device_free(dev);
-
        return FALSE;
 }
 
@@ -126,23 +133,42 @@ static void signaling_connect_cb(GIOChannel *chan, GError *err,
                                                        gpointer user_data)
 {
        struct a2dp_device *dev = user_data;
+       uint16_t imtu, omtu;
+       GError *gerr = NULL;
+       int fd;
 
        if (err) {
                bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
                error("%s", err->message);
-               a2dp_device_free(dev);
                return;
        }
 
+       bt_io_get(chan, &gerr,
+                       BT_IO_OPT_IMTU, &imtu,
+                       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;
+       }
+
+       /* FIXME: Add proper version */
+       fd = g_io_channel_unix_get_fd(chan);
+       dev->session = avdtp_new(fd, imtu, omtu, 0x0100);
+
        dev->watch = g_io_add_watch(dev->io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
                                                                watch_cb, dev);
 
        bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED);
 }
 
-static uint8_t bt_a2dp_connect(struct hal_cmd_a2dp_connect *cmd, uint16_t len)
+static void bt_a2dp_connect(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_a2dp_connect *cmd = buf;
        struct a2dp_device *dev;
+       uint8_t status;
        char addr[18];
        bdaddr_t dst;
        GSList *l;
@@ -150,14 +176,13 @@ static uint8_t bt_a2dp_connect(struct hal_cmd_a2dp_connect *cmd, uint16_t len)
 
        DBG("");
 
-       if (len < sizeof(*cmd))
-               return HAL_STATUS_INVALID;
-
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (l)
-               return HAL_STATUS_FAILED;
+       if (l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        dev = a2dp_device_new(&dst);
        dev->io = bt_io_connect(signaling_connect_cb, dev, NULL, &err,
@@ -170,7 +195,8 @@ static uint8_t bt_a2dp_connect(struct hal_cmd_a2dp_connect *cmd, uint16_t len)
                error("%s", err->message);
                g_error_free(err);
                a2dp_device_free(dev);
-               return HAL_STATUS_FAILED;
+               status = HAL_STATUS_FAILED;
+               goto failed;
        }
 
        ba2str(&dev->dst, addr);
@@ -178,26 +204,29 @@ static uint8_t bt_a2dp_connect(struct hal_cmd_a2dp_connect *cmd, uint16_t len)
 
        bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING);
 
-       return HAL_STATUS_SUCCESS;
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status);
 }
 
-static uint8_t bt_a2dp_disconnect(struct hal_cmd_a2dp_connect *cmd,
-                                                               uint16_t len)
+static void bt_a2dp_disconnect(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_a2dp_connect *cmd = buf;
+       uint8_t status;
        struct a2dp_device *dev;
        GSList *l;
        bdaddr_t dst;
 
        DBG("");
 
-       if (len < sizeof(*cmd))
-               return HAL_STATUS_INVALID;
-
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (!l)
-               return HAL_STATUS_FAILED;
+       if (!l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        dev = l->data;
 
@@ -207,28 +236,19 @@ static uint8_t bt_a2dp_disconnect(struct hal_cmd_a2dp_connect *cmd,
 
        bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTING);
 
-       return HAL_STATUS_SUCCESS;
-}
-
-void bt_a2dp_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len)
-{
-       uint8_t status = HAL_STATUS_FAILED;
-
-       switch (opcode) {
-       case HAL_OP_A2DP_CONNECT:
-               status = bt_a2dp_connect(buf, len);
-               break;
-       case HAL_OP_A2DP_DISCONNECT:
-               status = bt_a2dp_disconnect(buf, len);
-               break;
-       default:
-               DBG("Unhandled command, opcode 0x%x", opcode);
-               break;
-       }
+       status = HAL_STATUS_SUCCESS;
 
-       ipc_send_rsp(sk, HAL_SERVICE_ID_A2DP, status);
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, status);
 }
 
+static const struct ipc_handler cmd_handlers[] = {
+       /* HAL_OP_A2DP_CONNECT */
+       { bt_a2dp_connect, false, sizeof(struct hal_cmd_a2dp_connect) },
+       /* HAL_OP_A2DP_DISCONNECT */
+       { bt_a2dp_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) },
+};
+
 static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 {
        struct a2dp_device *dev;
@@ -280,52 +300,52 @@ static sdp_record_t *a2dp_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);
 
        sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID);
-       svclass_id = sdp_list_append(0, &a2dp_uuid);
+       svclass_id = sdp_list_append(NULL, &a2dp_uuid);
        sdp_set_service_classes(record, svclass_id);
 
        sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
        profile[0].version = a2dp_ver;
-       pfseq = sdp_list_append(0, &profile[0]);
+       pfseq = sdp_list_append(NULL, &profile[0]);
        sdp_set_profile_descs(record, pfseq);
 
        sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
-       proto[0] = sdp_list_append(0, &l2cap_uuid);
+       proto[0] = sdp_list_append(NULL, &l2cap_uuid);
        psm = sdp_data_alloc(SDP_UINT16, &lp);
        proto[0] = sdp_list_append(proto[0], psm);
-       apseq = sdp_list_append(0, proto[0]);
+       apseq = sdp_list_append(NULL, proto[0]);
 
        sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID);
-       proto[1] = sdp_list_append(0, &avdtp_uuid);
+       proto[1] = sdp_list_append(NULL, &avdtp_uuid);
        version = sdp_data_alloc(SDP_UINT16, &avdtp_ver);
        proto[1] = sdp_list_append(proto[1], version);
        apseq = sdp_list_append(apseq, proto[1]);
 
-       aproto = sdp_list_append(0, apseq);
+       aproto = sdp_list_append(NULL, apseq);
        sdp_set_access_protos(record, aproto);
 
        features = sdp_data_alloc(SDP_UINT16, &feat);
        sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features);
 
-       sdp_set_info_attr(record, "Audio Source", 0, 0);
+       sdp_set_info_attr(record, "Audio Source", NULL, NULL);
 
-       free(psm);
-       free(version);
-       sdp_list_free(proto[0], 0);
-       sdp_list_free(proto[1], 0);
-       sdp_list_free(apseq, 0);
-       sdp_list_free(pfseq, 0);
-       sdp_list_free(aproto, 0);
-       sdp_list_free(root, 0);
-       sdp_list_free(svclass_id, 0);
+       sdp_data_free(psm);
+       sdp_data_free(version);
+       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;
 }
 
-bool bt_a2dp_register(int sk, const bdaddr_t *addr)
+bool bt_a2dp_register(const bdaddr_t *addr)
 {
        GError *err = NULL;
        sdp_record_t *rec;
@@ -346,27 +366,46 @@ bool bt_a2dp_register(int sk, const bdaddr_t *addr)
        }
 
        rec = a2dp_record();
+       if (!rec) {
+               error("Failed to allocate A2DP record");
+               goto fail;
+       }
+
        if (bt_adapter_add_record(rec, SVC_HINT_CAPTURING) < 0) {
-               error("Failed to register on A2DP record");
+               error("Failed to register A2DP record");
                sdp_record_free(rec);
-               g_io_channel_shutdown(server, TRUE, NULL);
-               g_io_channel_unref(server);
-               server = NULL;
-               return false;
+               goto fail;
        }
        record_id = rec->handle;
 
-       notification_sk = sk;
+       ipc_register(HAL_SERVICE_ID_A2DP, 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;
+}
+
+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("");
 
-       notification_sk = -1;
+       g_slist_foreach(devices, a2dp_device_disconnected, NULL);
+       devices = NULL;
+
 
+       ipc_unregister(HAL_SERVICE_ID_A2DP);
        bt_adapter_remove_record(record_id);
        record_id = 0;
 
index 3531618..7e9b2f6 100644 (file)
@@ -21,7 +21,5 @@
  *
  */
 
-void bt_a2dp_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len);
-
-bool bt_a2dp_register(int sk, const bdaddr_t *addr);
+bool bt_a2dp_register(const bdaddr_t *addr);
 void bt_a2dp_unregister(void);
diff --git a/android/avdtp.c b/android/avdtp.c
new file mode 100644 (file)
index 0000000..5ae3afc
--- /dev/null
@@ -0,0 +1,3269 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <glib.h>
+
+#include "log.h"
+#include "avdtp.h"
+
+#define AVDTP_PSM 25
+
+#define MAX_SEID 0x3E
+
+#ifndef MAX
+# define MAX(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#define AVDTP_DISCOVER                         0x01
+#define AVDTP_GET_CAPABILITIES                 0x02
+#define AVDTP_SET_CONFIGURATION                        0x03
+#define AVDTP_GET_CONFIGURATION                        0x04
+#define AVDTP_RECONFIGURE                      0x05
+#define AVDTP_OPEN                             0x06
+#define AVDTP_START                            0x07
+#define AVDTP_CLOSE                            0x08
+#define AVDTP_SUSPEND                          0x09
+#define AVDTP_ABORT                            0x0A
+#define AVDTP_SECURITY_CONTROL                 0x0B
+#define AVDTP_GET_ALL_CAPABILITIES             0x0C
+#define AVDTP_DELAY_REPORT                     0x0D
+
+#define AVDTP_PKT_TYPE_SINGLE                  0x00
+#define AVDTP_PKT_TYPE_START                   0x01
+#define AVDTP_PKT_TYPE_CONTINUE                        0x02
+#define AVDTP_PKT_TYPE_END                     0x03
+
+#define AVDTP_MSG_TYPE_COMMAND                 0x00
+#define AVDTP_MSG_TYPE_GEN_REJECT              0x01
+#define AVDTP_MSG_TYPE_ACCEPT                  0x02
+#define AVDTP_MSG_TYPE_REJECT                  0x03
+
+#define REQ_TIMEOUT 6
+#define ABORT_TIMEOUT 2
+#define DISCONNECT_TIMEOUT 1
+#define START_TIMEOUT 1
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_common_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+       uint8_t signal_id:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+       uint8_t no_of_packets;
+       uint8_t signal_id:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+       uint8_t message_type:2;
+       uint8_t packet_type:2;
+       uint8_t transaction:4;
+} __attribute__ ((packed));
+
+struct seid_info {
+       uint8_t rfa0:1;
+       uint8_t inuse:1;
+       uint8_t seid:6;
+       uint8_t rfa2:3;
+       uint8_t type:1;
+       uint8_t media_type:4;
+} __attribute__ ((packed));
+
+struct seid {
+       uint8_t rfa0:2;
+       uint8_t seid:6;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_common_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct avdtp_single_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+       uint8_t rfa0:2;
+       uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_start_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+       uint8_t no_of_packets;
+       uint8_t rfa0:2;
+       uint8_t signal_id:6;
+} __attribute__ ((packed));
+
+struct avdtp_continue_header {
+       uint8_t transaction:4;
+       uint8_t packet_type:2;
+       uint8_t message_type:2;
+} __attribute__ ((packed));
+
+struct seid_info {
+       uint8_t seid:6;
+       uint8_t inuse:1;
+       uint8_t rfa0:1;
+       uint8_t media_type:4;
+       uint8_t type:1;
+       uint8_t rfa2:3;
+} __attribute__ ((packed));
+
+struct seid {
+       uint8_t seid:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+/* packets */
+
+struct discover_resp {
+       struct seid_info seps[0];
+} __attribute__ ((packed));
+
+struct getcap_resp {
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct start_req {
+       struct seid first_seid;
+       struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct suspend_req {
+       struct seid first_seid;
+       struct seid other_seids[0];
+} __attribute__ ((packed));
+
+struct seid_rej {
+       uint8_t error;
+} __attribute__ ((packed));
+
+struct conf_rej {
+       uint8_t category;
+       uint8_t error;
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct seid_req {
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+} __attribute__ ((packed));
+
+struct setconf_req {
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+       uint8_t rfa1:2;
+       uint8_t int_seid:6;
+
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+       uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+
+       uint8_t serv_cap;
+       uint8_t serv_cap_len;
+
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+       uint8_t rfa0:2;
+       uint8_t acp_seid:6;
+       uint16_t delay;
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct seid_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+} __attribute__ ((packed));
+
+struct setconf_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+       uint8_t int_seid:6;
+       uint8_t rfa1:2;
+
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct stream_rej {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+       uint8_t error;
+} __attribute__ ((packed));
+
+struct reconf_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+
+       uint8_t serv_cap;
+       uint8_t serv_cap_len;
+
+       uint8_t caps[0];
+} __attribute__ ((packed));
+
+struct delay_req {
+       uint8_t acp_seid:6;
+       uint8_t rfa0:2;
+       uint16_t delay;
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+struct in_buf {
+       gboolean active;
+       int no_of_packets;
+       uint8_t transaction;
+       uint8_t message_type;
+       uint8_t signal_id;
+       uint8_t buf[1024];
+       uint8_t data_size;
+};
+
+struct pending_req {
+       uint8_t transaction;
+       uint8_t signal_id;
+       void *data;
+       size_t data_size;
+       struct avdtp_stream *stream; /* Set if the request targeted a stream */
+       guint timeout;
+       gboolean collided;
+};
+
+struct avdtp_remote_sep {
+       uint8_t seid;
+       uint8_t type;
+       uint8_t media_type;
+       struct avdtp_service_capability *codec;
+       gboolean delay_reporting;
+       GSList *caps; /* of type struct avdtp_service_capability */
+       struct avdtp_stream *stream;
+};
+
+struct avdtp_local_sep {
+       avdtp_state_t state;
+       struct avdtp_stream *stream;
+       struct seid_info info;
+       uint8_t codec;
+       gboolean delay_reporting;
+       GSList *caps;
+       struct avdtp_sep_ind *ind;
+       struct avdtp_sep_cfm *cfm;
+       void *user_data;
+};
+
+struct stream_callback {
+       avdtp_stream_state_cb cb;
+       void *user_data;
+       unsigned int id;
+};
+
+struct discover_callback {
+       unsigned int id;
+       avdtp_discover_cb_t cb;
+       void *user_data;
+};
+
+struct avdtp_stream {
+       GIOChannel *io;
+       uint16_t imtu;
+       uint16_t omtu;
+       struct avdtp *session;
+       struct avdtp_local_sep *lsep;
+       uint8_t rseid;
+       GSList *caps;
+       GSList *callbacks;
+       struct avdtp_service_capability *codec;
+       guint io_id;            /* Transport GSource ID */
+       guint timer;            /* Waiting for other side to close or open
+                                * the transport channel */
+       gboolean open_acp;      /* If we are in ACT role for Open */
+       gboolean close_int;     /* If we are in INT role for Close */
+       gboolean abort_int;     /* If we are in INT role for Abort */
+       guint start_timer;      /* Wait START command timer */
+       gboolean delay_reporting;
+       uint16_t delay;         /* AVDTP 1.3 Delay Reporting feature */
+       gboolean starting;      /* only valid while sep state == OPEN */
+};
+
+/* Structure describing an AVDTP connection between two devices */
+
+struct avdtp {
+       unsigned int ref;
+
+       uint16_t version;
+
+       struct avdtp_server *server;
+
+       guint auth_id;
+
+       GIOChannel *io;
+       guint io_id;
+
+       GSList *seps; /* Elements of type struct avdtp_remote_sep * */
+
+       GSList *streams; /* Elements of type struct avdtp_stream * */
+
+       GSList *req_queue; /* Elements of type struct pending_req * */
+       GSList *prio_queue; /* Same as req_queue but is processed before it */
+
+       struct avdtp_stream *pending_open;
+
+       uint16_t imtu;
+       uint16_t omtu;
+
+       struct in_buf in;
+
+       char *buf;
+
+       struct discover_callback *discover;
+       struct pending_req *req;
+};
+
+static GSList *lseps = NULL;
+
+static int send_request(struct avdtp *session, gboolean priority,
+                       struct avdtp_stream *stream, uint8_t signal_id,
+                       void *buffer, size_t size);
+static gboolean avdtp_parse_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       uint8_t transaction, uint8_t signal_id,
+                                       void *buf, int size);
+static gboolean avdtp_parse_rej(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       uint8_t transaction, uint8_t signal_id,
+                                       void *buf, int size);
+static int process_queue(struct avdtp *session);
+static void avdtp_sep_set_state(struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               avdtp_state_t state);
+
+static const char *avdtp_statestr(avdtp_state_t state)
+{
+       switch (state) {
+       case AVDTP_STATE_IDLE:
+               return "IDLE";
+       case AVDTP_STATE_CONFIGURED:
+               return "CONFIGURED";
+       case AVDTP_STATE_OPEN:
+               return "OPEN";
+       case AVDTP_STATE_STREAMING:
+               return "STREAMING";
+       case AVDTP_STATE_CLOSING:
+               return "CLOSING";
+       case AVDTP_STATE_ABORTING:
+               return "ABORTING";
+       default:
+               return "<unknown state>";
+       }
+}
+
+static gboolean try_send(int sk, void *data, size_t len)
+{
+       int err;
+
+       do {
+               err = send(sk, data, len, 0);
+       } while (err < 0 && errno == EINTR);
+
+       if (err < 0) {
+               error("send: %s (%d)", strerror(errno), errno);
+               return FALSE;
+       } else if ((size_t) err != len) {
+               error("try_send: complete buffer not sent (%d/%zu bytes)",
+                                                               err, len);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,
+                               uint8_t message_type, uint8_t signal_id,
+                               void *data, size_t len)
+{
+       unsigned int cont_fragments, sent;
+       struct avdtp_start_header start;
+       struct avdtp_continue_header cont;
+       int sock;
+
+       if (session->io == NULL) {
+               error("avdtp_send: session is closed");
+               return FALSE;
+       }
+
+       sock = g_io_channel_unix_get_fd(session->io);
+
+       /* Single packet - no fragmentation */
+       if (sizeof(struct avdtp_single_header) + len <= session->omtu) {
+               struct avdtp_single_header single;
+
+               memset(&single, 0, sizeof(single));
+
+               single.transaction = transaction;
+               single.packet_type = AVDTP_PKT_TYPE_SINGLE;
+               single.message_type = message_type;
+               single.signal_id = signal_id;
+
+               memcpy(session->buf, &single, sizeof(single));
+               memcpy(session->buf + sizeof(single), data, len);
+
+               return try_send(sock, session->buf, sizeof(single) + len);
+       }
+
+       /* Check if there is enough space to start packet */
+       if (session->omtu < sizeof(start)) {
+               error("No enough space to fragment packet");
+               return FALSE;
+       }
+
+       /* Count the number of needed fragments */
+       cont_fragments = (len - (session->omtu - sizeof(start))) /
+                                       (session->omtu - sizeof(cont)) + 1;
+
+       DBG("%zu bytes split into %d fragments", len, cont_fragments + 1);
+
+       /* Send the start packet */
+       memset(&start, 0, sizeof(start));
+       start.transaction = transaction;
+       start.packet_type = AVDTP_PKT_TYPE_START;
+       start.message_type = message_type;
+       start.no_of_packets = cont_fragments + 1;
+       start.signal_id = signal_id;
+
+       memcpy(session->buf, &start, sizeof(start));
+       memcpy(session->buf + sizeof(start), data,
+                                       session->omtu - sizeof(start));
+
+       if (!try_send(sock, session->buf, session->omtu))
+               return FALSE;
+
+       DBG("first packet with %zu bytes sent", session->omtu - sizeof(start));
+
+       sent = session->omtu - sizeof(start);
+
+       /* Send the continue fragments and the end packet */
+       while (sent < len) {
+               int left, to_copy;
+
+               left = len - sent;
+               if (left + sizeof(cont) > session->omtu) {
+                       cont.packet_type = AVDTP_PKT_TYPE_CONTINUE;
+                       to_copy = session->omtu - sizeof(cont);
+                       DBG("sending continue with %d bytes", to_copy);
+               } else {
+                       cont.packet_type = AVDTP_PKT_TYPE_END;
+                       to_copy = left;
+                       DBG("sending end with %d bytes", to_copy);
+               }
+
+               cont.transaction = transaction;
+               cont.message_type = message_type;
+
+               memcpy(session->buf, &cont, sizeof(cont));
+               memcpy(session->buf + sizeof(cont), data + sent, to_copy);
+
+               if (!try_send(sock, session->buf, to_copy + sizeof(cont)))
+                       return FALSE;
+
+               sent += to_copy;
+       }
+
+       return TRUE;
+}
+
+static void pending_req_free(void *data)
+{
+       struct pending_req *req = data;
+
+       if (req->timeout)
+               g_source_remove(req->timeout);
+       g_free(req->data);
+       g_free(req);
+}
+
+static void close_stream(struct avdtp_stream *stream)
+{
+       int sock;
+
+       if (stream->io == NULL)
+               return;
+
+       sock = g_io_channel_unix_get_fd(stream->io);
+
+       shutdown(sock, SHUT_RDWR);
+
+       g_io_channel_shutdown(stream->io, FALSE, NULL);
+
+       g_io_channel_unref(stream->io);
+       stream->io = NULL;
+}
+
+static gboolean stream_close_timeout(gpointer user_data)
+{
+       struct avdtp_stream *stream = user_data;
+
+       DBG("Timed out waiting for peer to close the transport channel");
+
+       stream->timer = 0;
+
+       close_stream(stream);
+
+       return FALSE;
+}
+
+static gboolean stream_open_timeout(gpointer user_data)
+{
+       struct avdtp_stream *stream = user_data;
+
+       DBG("Timed out waiting for peer to open the transport channel");
+
+       stream->timer = 0;
+
+       stream->session->pending_open = NULL;
+
+       avdtp_abort(stream->session, stream);
+
+       return FALSE;
+}
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id)
+{
+       err->category = category;
+
+       if (category == AVDTP_ERRNO)
+               err->err.posix_errno = id;
+       else
+               err->err.error_code = id;
+}
+
+uint8_t avdtp_error_category(struct avdtp_error *err)
+{
+       return err->category;
+}
+
+int avdtp_error_error_code(struct avdtp_error *err)
+{
+       assert(err->category != AVDTP_ERRNO);
+       return err->err.error_code;
+}
+
+int avdtp_error_posix_errno(struct avdtp_error *err)
+{
+       assert(err->category == AVDTP_ERRNO);
+       return err->err.posix_errno;
+}
+
+static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session,
+                                                       uint8_t rseid)
+{
+       GSList *l;
+
+       for (l = session->streams; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_stream *stream = l->data;
+
+               if (stream->rseid == rseid)
+                       return stream;
+       }
+
+       return NULL;
+}
+
+static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid)
+{
+       GSList *l;
+
+       for (l = seps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_remote_sep *sep = l->data;
+
+               if (sep->seid == seid)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+static void stream_free(void *data)
+{
+       struct avdtp_stream *stream = data;
+       struct avdtp_remote_sep *rsep;
+
+       stream->lsep->info.inuse = 0;
+       stream->lsep->stream = NULL;
+
+       rsep = find_remote_sep(stream->session->seps, stream->rseid);
+       if (rsep)
+               rsep->stream = NULL;
+
+       if (stream->timer)
+               g_source_remove(stream->timer);
+
+       if (stream->io)
+               close_stream(stream);
+
+       if (stream->io_id)
+               g_source_remove(stream->io_id);
+
+       g_slist_free_full(stream->callbacks, g_free);
+       g_slist_free_full(stream->caps, g_free);
+
+       g_free(stream);
+}
+
+static gboolean transport_cb(GIOChannel *chan, GIOCondition cond,
+                               gpointer data)
+{
+       struct avdtp_stream *stream = data;
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       if (stream->close_int && sep->cfm && sep->cfm->close)
+               sep->cfm->close(stream->session, sep, stream, NULL,
+                               sep->user_data);
+
+       if (!(cond & G_IO_NVAL))
+               close_stream(stream);
+
+       stream->io_id = 0;
+
+       if (!stream->abort_int)
+               avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE);
+
+       return FALSE;
+}
+
+static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
+                                       uint16_t imtu, uint16_t omtu)
+{
+       struct avdtp_stream *stream = session->pending_open;
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       session->pending_open = NULL;
+
+       if (stream->timer) {
+               g_source_remove(stream->timer);
+               stream->timer = 0;
+       }
+
+       if (io == NULL)
+               return;
+
+       if (stream->io == NULL)
+               stream->io = g_io_channel_ref(io);
+
+       stream->omtu = omtu;
+       stream->imtu = imtu;
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+       stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       (GIOFunc) transport_cb, stream);
+}
+
+static int pending_req_cmp(gconstpointer a, gconstpointer b)
+{
+       const struct pending_req *req = a;
+       const struct avdtp_stream *stream = b;
+
+       if (req->stream == stream)
+               return 0;
+
+       return -1;
+}
+
+static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream)
+{
+       GSList *l;
+       struct pending_req *req;
+
+       while ((l = g_slist_find_custom(session->prio_queue, stream,
+                                                       pending_req_cmp))) {
+               req = l->data;
+               pending_req_free(req);
+               session->prio_queue = g_slist_remove(session->prio_queue, req);
+       }
+
+       while ((l = g_slist_find_custom(session->req_queue, stream,
+                                                       pending_req_cmp))) {
+               req = l->data;
+               pending_req_free(req);
+               session->req_queue = g_slist_remove(session->req_queue, req);
+       }
+}
+
+static void handle_unanswered_req(struct avdtp *session,
+                                               struct avdtp_stream *stream)
+{
+       struct pending_req *req;
+       struct avdtp_local_sep *lsep;
+       struct avdtp_error err;
+
+       if (session->req->signal_id == AVDTP_ABORT) {
+               /* Avoid freeing the Abort request here */
+               DBG("handle_unanswered_req: Abort req, returning");
+               session->req->stream = NULL;
+               return;
+       }
+
+       req = session->req;
+       session->req = NULL;
+
+       avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+
+       lsep = stream->lsep;
+
+       switch (req->signal_id) {
+       case AVDTP_RECONFIGURE:
+               error("No reply to Reconfigure request");
+               if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+                       lsep->cfm->reconfigure(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_OPEN:
+               error("No reply to Open request");
+               if (lsep && lsep->cfm && lsep->cfm->open)
+                       lsep->cfm->open(session, lsep, stream, &err,
+                                       lsep->user_data);
+               break;
+       case AVDTP_START:
+               error("No reply to Start request");
+               if (lsep && lsep->cfm && lsep->cfm->start)
+                       lsep->cfm->start(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_SUSPEND:
+               error("No reply to Suspend request");
+               if (lsep && lsep->cfm && lsep->cfm->suspend)
+                       lsep->cfm->suspend(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_CLOSE:
+               error("No reply to Close request");
+               if (lsep && lsep->cfm && lsep->cfm->close)
+                       lsep->cfm->close(session, lsep, stream, &err,
+                                               lsep->user_data);
+               break;
+       case AVDTP_SET_CONFIGURATION:
+               error("No reply to SetConfiguration request");
+               if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+                       lsep->cfm->set_configuration(session, lsep, stream,
+                                                       &err, lsep->user_data);
+       }
+
+       pending_req_free(req);
+}
+
+static void avdtp_sep_set_state(struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               avdtp_state_t state)
+{
+       struct avdtp_stream *stream = sep->stream;
+       avdtp_state_t old_state;
+       struct avdtp_error err, *err_ptr = NULL;
+       GSList *l;
+
+       if (!stream) {
+               error("Error changing sep state: stream not available");
+               return;
+       }
+
+       if (sep->state == state) {
+               avdtp_error_init(&err, AVDTP_ERRNO, EIO);
+               DBG("stream state change failed: %s", avdtp_strerror(&err));
+               err_ptr = &err;
+       } else {
+               err_ptr = NULL;
+               DBG("stream state changed: %s -> %s",
+                               avdtp_statestr(sep->state),
+                               avdtp_statestr(state));
+       }
+
+       old_state = sep->state;
+       sep->state = state;
+
+       switch (state) {
+       case AVDTP_STATE_CONFIGURED:
+               if (sep->info.type == AVDTP_SEP_TYPE_SINK)
+                       avdtp_delay_report(session, stream, stream->delay);
+               break;
+       case AVDTP_STATE_OPEN:
+               stream->starting = FALSE;
+               break;
+       case AVDTP_STATE_STREAMING:
+               if (stream->start_timer) {
+                       g_source_remove(stream->start_timer);
+                       stream->start_timer = 0;
+               }
+               stream->open_acp = FALSE;
+               break;
+       case AVDTP_STATE_CLOSING:
+       case AVDTP_STATE_ABORTING:
+               if (stream->start_timer) {
+                       g_source_remove(stream->start_timer);
+                       stream->start_timer = 0;
+               }
+               break;
+       case AVDTP_STATE_IDLE:
+               if (stream->start_timer) {
+                       g_source_remove(stream->start_timer);
+                       stream->start_timer = 0;
+               }
+               if (session->pending_open == stream)
+                       handle_transport_connect(session, NULL, 0, 0);
+               if (session->req && session->req->stream == stream)
+                       handle_unanswered_req(session, stream);
+               /* Remove pending commands for this stream from the queue */
+               cleanup_queue(session, stream);
+               break;
+       default:
+               break;
+       }
+
+       l = stream->callbacks;
+       while (l != NULL) {
+               struct stream_callback *cb = l->data;
+               l = g_slist_next(l);
+               cb->cb(stream, old_state, state, err_ptr, cb->user_data);
+       }
+
+       if (state == AVDTP_STATE_IDLE &&
+                               g_slist_find(session->streams, stream)) {
+               session->streams = g_slist_remove(session->streams, stream);
+               stream_free(stream);
+       }
+}
+
+static void finalize_discovery(struct avdtp *session, int err)
+{
+       struct discover_callback *discover = session->discover;
+       struct avdtp_error avdtp_err;
+
+       if (!discover)
+               return;
+
+       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,
+                                                       discover->user_data);
+       g_free(discover);
+       session->discover = NULL;
+}
+
+static void release_stream(struct avdtp_stream *stream, struct avdtp *session)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       if (sep->cfm && sep->cfm->abort &&
+                               (sep->state != AVDTP_STATE_ABORTING ||
+                                                       stream->abort_int))
+               sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+}
+
+static void sep_free(gpointer data)
+{
+       struct avdtp_remote_sep *sep = data;
+
+       g_slist_free_full(sep->caps, g_free);
+       g_free(sep);
+}
+
+static void avdtp_free(void *data)
+{
+       struct avdtp *session = data;
+
+       DBG("%p", session);
+
+       g_slist_free_full(session->streams, stream_free);
+
+       if (session->io) {
+               g_io_channel_shutdown(session->io, FALSE, NULL);
+               g_io_channel_unref(session->io);
+       }
+
+       if (session->io_id) {
+               g_source_remove(session->io_id);
+               session->io_id = 0;
+       }
+
+       if (session->req)
+               pending_req_free(session->req);
+
+       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_free(session->buf);
+
+       g_free(session);
+}
+
+static void connection_lost(struct avdtp *session, int err)
+{
+       DBG("Disconnected: %s (%d)", strerror(err), err);
+
+       g_slist_foreach(session->streams, (GFunc) release_stream, session);
+       session->streams = NULL;
+
+       finalize_discovery(session, err);
+
+       if (session->ref > 0)
+               return;
+
+       avdtp_free(session);
+}
+
+void avdtp_unref(struct avdtp *session)
+{
+       if (!session)
+               return;
+
+       session->ref--;
+
+       DBG("%p: ref=%d", session, session->ref);
+
+       if (session->ref > 0)
+               return;
+
+       finalize_discovery(session, ECONNABORTED);
+
+       avdtp_free(session);
+}
+
+struct avdtp *avdtp_ref(struct avdtp *session)
+{
+       session->ref++;
+
+       DBG("%p: ref=%d", session, session->ref);
+
+       return session;
+}
+
+static struct avdtp_local_sep *find_local_sep_by_seid(uint8_t seid)
+{
+       GSList *l;
+
+       for (l = lseps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_local_sep *sep = l->data;
+
+               if (sep->info.seid == seid)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+                                               struct avdtp_local_sep *lsep)
+{
+       GSList *l;
+
+       if (lsep->info.inuse)
+               return NULL;
+
+       for (l = session->seps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_remote_sep *sep = l->data;
+               struct avdtp_service_capability *cap;
+               struct avdtp_media_codec_capability *codec_data;
+
+               /* Type must be different: source <-> sink */
+               if (sep->type == lsep->info.type)
+                       continue;
+
+               if (sep->media_type != lsep->info.media_type)
+                       continue;
+
+               if (!sep->codec)
+                       continue;
+
+               cap = sep->codec;
+               codec_data = (void *) cap->data;
+
+               if (codec_data->media_codec_type != lsep->codec)
+                       continue;
+
+               if (sep->stream == NULL)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+static GSList *caps_to_list(uint8_t *data, int size,
+                               struct avdtp_service_capability **codec,
+                               gboolean *delay_reporting)
+{
+       GSList *caps;
+       int processed;
+
+       if (delay_reporting)
+               *delay_reporting = FALSE;
+
+       for (processed = 0, caps = NULL; processed + 2 <= size;) {
+               struct avdtp_service_capability *cap;
+               uint8_t length, category;
+
+               category = data[0];
+               length = data[1];
+
+               if (processed + 2 + length > size) {
+                       error("Invalid capability data in getcap resp");
+                       break;
+               }
+
+               cap = g_malloc(sizeof(struct avdtp_service_capability) +
+                                       length);
+               memcpy(cap, data, 2 + length);
+
+               processed += 2 + length;
+               data += 2 + length;
+
+               caps = g_slist_append(caps, cap);
+
+               if (category == AVDTP_MEDIA_CODEC &&
+                               length >=
+                               sizeof(struct avdtp_media_codec_capability))
+                       *codec = cap;
+               else if (category == AVDTP_DELAY_REPORTING && delay_reporting)
+                       *delay_reporting = TRUE;
+       }
+
+       return caps;
+}
+
+static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction,
+                                                       uint8_t signal_id)
+{
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT,
+                                                       signal_id, NULL, 0);
+}
+
+static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
+                                                       void *buf, int size)
+{
+       GSList *l;
+       unsigned int rsp_size, sep_count, i;
+       struct seid_info *seps;
+       gboolean ret;
+
+       sep_count = g_slist_length(lseps);
+
+       if (sep_count == 0) {
+               uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
+               return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_DISCOVER, &err, sizeof(err));
+       }
+
+       rsp_size = sep_count * sizeof(struct seid_info);
+
+       seps = g_new0(struct seid_info, sep_count);
+
+       for (l = lseps, i = 0; l != NULL; l = l->next, i++) {
+               struct avdtp_local_sep *sep = l->data;
+
+               memcpy(&seps[i], &sep->info, sizeof(struct seid_info));
+       }
+
+       ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                               AVDTP_DISCOVER, seps, rsp_size);
+       g_free(seps);
+
+       return ret;
+}
+
+static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, unsigned int size,
+                                       gboolean get_all)
+{
+       GSList *l, *caps;
+       struct avdtp_local_sep *sep = NULL;
+       unsigned int rsp_size;
+       uint8_t err, buf[1024], *ptr = buf;
+       uint8_t cmd;
+
+       cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES;
+
+       if (size < sizeof(struct seid_req)) {
+               err = AVDTP_BAD_LENGTH;
+               goto failed;
+       }
+
+       sep = find_local_sep_by_seid(req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (!sep->ind->get_capability(session, sep, get_all, &caps,
+                                                       &err, sep->user_data))
+               goto failed;
+
+       for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (rsp_size + cap->length + 2 > sizeof(buf))
+                       break;
+
+               memcpy(ptr, cap, cap->length + 2);
+               rsp_size += cap->length + 2;
+               ptr += cap->length + 2;
+
+               g_free(cap);
+       }
+
+       g_slist_free(caps);
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd,
+                                                               buf, rsp_size);
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd,
+                                                       &err, sizeof(err));
+}
+
+static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream,
+                                               struct avdtp_error *err)
+{
+       struct conf_rej rej;
+       struct avdtp_local_sep *sep;
+
+       if (err != NULL) {
+               rej.error = AVDTP_UNSUPPORTED_CONFIGURATION;
+               rej.category = err->err.error_code;
+               avdtp_send(session, session->in.transaction,
+                               AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION,
+                               &rej, sizeof(rej));
+               return;
+       }
+
+       if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                       AVDTP_SET_CONFIGURATION, NULL, 0)) {
+               stream_free(stream);
+               return;
+       }
+
+       sep = stream->lsep;
+       sep->stream = stream;
+       sep->info.inuse = 1;
+       session->streams = g_slist_append(session->streams, stream);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+}
+
+static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction,
+                               struct setconf_req *req, unsigned int size)
+{
+       struct conf_rej rej;
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err, category = 0x00;
+       GSList *l;
+
+       if (size < sizeof(struct setconf_req)) {
+               error("Too short getcap request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (sep->stream) {
+               err = AVDTP_SEP_IN_USE;
+               goto failed;
+       }
+
+       stream = g_new0(struct avdtp_stream, 1);
+       stream->session = session;
+       stream->lsep = sep;
+       stream->rseid = req->int_seid;
+       stream->caps = caps_to_list(req->caps,
+                                       size - sizeof(struct setconf_req),
+                                       &stream->codec,
+                                       &stream->delay_reporting);
+
+       /* Verify that the Media Transport capability's length = 0. Reject otherwise */
+       for (l = stream->caps; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (cap->category == AVDTP_MEDIA_TRANSPORT && cap->length != 0) {
+                       err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT;
+                       goto failed_stream;
+               }
+       }
+
+       if (stream->delay_reporting && session->version < 0x0103)
+               session->version = 0x0103;
+
+       if (sep->ind && sep->ind->set_configuration) {
+               if (!sep->ind->set_configuration(session, sep, stream,
+                                                       stream->caps,
+                                                       setconf_cb,
+                                                       sep->user_data)) {
+                       err = AVDTP_UNSUPPORTED_CONFIGURATION;
+                       category = 0x00;
+                       goto failed_stream;
+               }
+       } else {
+               if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                       AVDTP_SET_CONFIGURATION, NULL, 0)) {
+                       stream_free(stream);
+                       return FALSE;
+               }
+
+               sep->stream = stream;
+               sep->info.inuse = 1;
+               session->streams = g_slist_append(session->streams, stream);
+
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+       }
+
+       return TRUE;
+
+failed_stream:
+       stream_free(stream);
+failed:
+       rej.error = err;
+       rej.category = category;
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_SET_CONFIGURATION, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, int size)
+{
+       GSList *l;
+       struct avdtp_local_sep *sep = NULL;
+       int rsp_size;
+       uint8_t err;
+       uint8_t buf[1024];
+       uint8_t *ptr = buf;
+
+       if (size < (int) sizeof(struct seid_req)) {
+               error("Too short getconf request");
+               return FALSE;
+       }
+
+       memset(buf, 0, sizeof(buf));
+
+       sep = find_local_sep_by_seid(req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+       if (!sep->stream || !sep->stream->caps) {
+               err = AVDTP_UNSUPPORTED_CONFIGURATION;
+               goto failed;
+       }
+
+       for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (rsp_size + cap->length + 2 > (int) sizeof(buf))
+                       break;
+
+               memcpy(ptr, cap, cap->length + 2);
+               rsp_size += cap->length + 2;
+               ptr += cap->length + 2;
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                               AVDTP_GET_CONFIGURATION, buf, rsp_size);
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_GET_CONFIGURATION, &err, sizeof(err));
+}
+
+static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, int size)
+{
+       struct conf_rej rej;
+
+       rej.error = AVDTP_NOT_SUPPORTED_COMMAND;
+       rej.category = 0x00;
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_RECONFIGURE, &rej, sizeof(rej));
+}
+
+static void check_seid_collision(struct pending_req *req, uint8_t id)
+{
+       struct seid_req *seid = req->data;
+
+       if (seid->acp_seid == id)
+               req->collided = TRUE;
+}
+
+static void check_start_collision(struct pending_req *req, uint8_t id)
+{
+       struct start_req *start = req->data;
+       struct seid *seid = &start->first_seid;
+       int count = 1 + req->data_size - sizeof(struct start_req);
+       int i;
+
+       for (i = 0; i < count; i++, seid++) {
+               if (seid->seid == id) {
+                       req->collided = TRUE;
+                       return;
+               }
+       }
+}
+
+static void check_suspend_collision(struct pending_req *req, uint8_t id)
+{
+       struct suspend_req *suspend = req->data;
+       struct seid *seid = &suspend->first_seid;
+       int count = 1 + req->data_size - sizeof(struct suspend_req);
+       int i;
+
+       for (i = 0; i < count; i++, seid++) {
+               if (seid->seid == id) {
+                       req->collided = TRUE;
+                       return;
+               }
+       }
+}
+
+static void avdtp_check_collision(struct avdtp *session, uint8_t cmd,
+                                       struct avdtp_stream *stream)
+{
+       struct pending_req *req = session->req;
+
+       if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT))
+               return;
+
+       if (cmd == AVDTP_ABORT)
+               cmd = req->signal_id;
+
+       switch (cmd) {
+       case AVDTP_OPEN:
+       case AVDTP_CLOSE:
+               check_seid_collision(req, stream->rseid);
+               break;
+       case AVDTP_START:
+               check_start_collision(req, stream->rseid);
+               break;
+       case AVDTP_SUSPEND:
+               check_suspend_collision(req, stream->rseid);
+               break;
+       }
+}
+
+static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
+                               struct seid_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err;
+
+       if (size < sizeof(struct seid_req)) {
+               error("Too short abort request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(req->acp_seid);
+       if (!sep) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (sep->state != AVDTP_STATE_CONFIGURED) {
+               err = AVDTP_BAD_STATE;
+               goto failed;
+       }
+
+       stream = sep->stream;
+
+       if (sep->ind && sep->ind->open) {
+               if (!sep->ind->open(session, sep, stream, &err,
+                                       sep->user_data))
+                       goto failed;
+       }
+
+       avdtp_check_collision(session, AVDTP_OPEN, stream);
+
+       if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_OPEN, NULL, 0))
+               return FALSE;
+
+       stream->open_acp = TRUE;
+       session->pending_open = stream;
+       stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+                                               stream_open_timeout,
+                                               stream);
+
+       return TRUE;
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_OPEN, &err, sizeof(err));
+}
+
+static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
+                               struct start_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       struct stream_rej rej;
+       struct seid *seid;
+       uint8_t err, failed_seid;
+       int seid_count, i;
+
+       if (size < sizeof(struct start_req)) {
+               error("Too short start request");
+               return FALSE;
+       }
+
+       seid_count = 1 + size - sizeof(struct start_req);
+
+       seid = &req->first_seid;
+
+       for (i = 0; i < seid_count; i++, seid++) {
+               failed_seid = seid->seid;
+
+               sep = find_local_sep_by_seid(req->first_seid.seid);
+               if (!sep || !sep->stream) {
+                       err = AVDTP_BAD_ACP_SEID;
+                       goto failed;
+               }
+
+               stream = sep->stream;
+
+               /* Also reject start cmd if state is not open */
+               if (sep->state != AVDTP_STATE_OPEN) {
+                       err = AVDTP_BAD_STATE;
+                       goto failed;
+               }
+               stream->starting = TRUE;
+
+               if (sep->ind && sep->ind->start) {
+                       if (!sep->ind->start(session, sep, stream, &err,
+                                               sep->user_data))
+                               goto failed;
+               }
+
+               avdtp_check_collision(session, AVDTP_START, stream);
+
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_START, NULL, 0);
+
+failed:
+       DBG("Rejecting (%d)", err);
+       memset(&rej, 0, sizeof(rej));
+       rej.acp_seid = failed_seid;
+       rej.error = err;
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_START, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction,
+                               struct seid_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err;
+
+       if (size < sizeof(struct seid_req)) {
+               error("Too short close request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(req->acp_seid);
+       if (!sep || !sep->stream) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       if (sep->state != AVDTP_STATE_OPEN &&
+                       sep->state != AVDTP_STATE_STREAMING) {
+               err = AVDTP_BAD_STATE;
+               goto failed;
+       }
+
+       stream = sep->stream;
+
+       if (sep->ind && sep->ind->close) {
+               if (!sep->ind->close(session, sep, stream, &err,
+                                       sep->user_data))
+                       goto failed;
+       }
+
+       avdtp_check_collision(session, AVDTP_CLOSE, stream);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+       if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_CLOSE, NULL, 0))
+               return FALSE;
+
+       stream->timer = g_timeout_add_seconds(REQ_TIMEOUT,
+                                       stream_close_timeout,
+                                       stream);
+
+       return TRUE;
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_CLOSE, &err, sizeof(err));
+}
+
+static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
+                               struct suspend_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       struct stream_rej rej;
+       struct seid *seid;
+       uint8_t err, failed_seid;
+       int seid_count, i;
+
+       if (size < sizeof(struct suspend_req)) {
+               error("Too short suspend request");
+               return FALSE;
+       }
+
+       seid_count = 1 + size - sizeof(struct suspend_req);
+
+       seid = &req->first_seid;
+
+       for (i = 0; i < seid_count; i++, seid++) {
+               failed_seid = seid->seid;
+
+               sep = find_local_sep_by_seid(req->first_seid.seid);
+               if (!sep || !sep->stream) {
+                       err = AVDTP_BAD_ACP_SEID;
+                       goto failed;
+               }
+
+               stream = sep->stream;
+
+               if (sep->state != AVDTP_STATE_STREAMING) {
+                       err = AVDTP_BAD_STATE;
+                       goto failed;
+               }
+
+               if (sep->ind && sep->ind->suspend) {
+                       if (!sep->ind->suspend(session, sep, stream, &err,
+                                               sep->user_data))
+                               goto failed;
+               }
+
+               avdtp_check_collision(session, AVDTP_SUSPEND, stream);
+
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_SUSPEND, NULL, 0);
+
+failed:
+       memset(&rej, 0, sizeof(rej));
+       rej.acp_seid = failed_seid;
+       rej.error = err;
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                               AVDTP_SUSPEND, &rej, sizeof(rej));
+}
+
+static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction,
+                               struct seid_req *req, unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       uint8_t err;
+       gboolean ret;
+
+       if (size < sizeof(struct seid_req)) {
+               error("Too short abort request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(req->acp_seid);
+       if (!sep || !sep->stream)
+               return TRUE;
+
+       if (sep->ind && sep->ind->abort)
+               sep->ind->abort(session, sep, sep->stream, &err,
+                                                       sep->user_data);
+
+       avdtp_check_collision(session, AVDTP_ABORT, sep->stream);
+
+       ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_ABORT, NULL, 0);
+       if (ret)
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+       return ret;
+}
+
+static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction,
+                                       struct seid_req *req, int size)
+{
+       return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL);
+}
+
+static gboolean avdtp_delayreport_cmd(struct avdtp *session,
+                                       uint8_t transaction,
+                                       struct delay_req *req,
+                                       unsigned int size)
+{
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       uint8_t err;
+
+       if (size < sizeof(struct delay_req)) {
+               error("Too short delay report request");
+               return FALSE;
+       }
+
+       sep = find_local_sep_by_seid(req->acp_seid);
+       if (!sep || !sep->stream) {
+               err = AVDTP_BAD_ACP_SEID;
+               goto failed;
+       }
+
+       stream = sep->stream;
+
+       if (sep->state != AVDTP_STATE_CONFIGURED &&
+                                       sep->state != AVDTP_STATE_STREAMING) {
+               err = AVDTP_BAD_STATE;
+               goto failed;
+       }
+
+       stream->delay = ntohs(req->delay);
+
+       if (sep->ind && sep->ind->delayreport) {
+               if (!sep->ind->delayreport(session, sep, stream->rseid,
+                                               stream->delay, &err,
+                                               sep->user_data))
+                       goto failed;
+       }
+
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
+                                               AVDTP_DELAY_REPORT, NULL, 0);
+
+failed:
+       return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+                                       AVDTP_DELAY_REPORT, &err, sizeof(err));
+}
+
+static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction,
+                               uint8_t signal_id, void *buf, int size)
+{
+       switch (signal_id) {
+       case AVDTP_DISCOVER:
+               DBG("Received DISCOVER_CMD");
+               return avdtp_discover_cmd(session, transaction, buf, size);
+       case AVDTP_GET_CAPABILITIES:
+               DBG("Received  GET_CAPABILITIES_CMD");
+               return avdtp_getcap_cmd(session, transaction, buf, size,
+                                                                       FALSE);
+       case AVDTP_GET_ALL_CAPABILITIES:
+               DBG("Received  GET_ALL_CAPABILITIES_CMD");
+               return avdtp_getcap_cmd(session, transaction, buf, size, TRUE);
+       case AVDTP_SET_CONFIGURATION:
+               DBG("Received SET_CONFIGURATION_CMD");
+               return avdtp_setconf_cmd(session, transaction, buf, size);
+       case AVDTP_GET_CONFIGURATION:
+               DBG("Received GET_CONFIGURATION_CMD");
+               return avdtp_getconf_cmd(session, transaction, buf, size);
+       case AVDTP_RECONFIGURE:
+               DBG("Received RECONFIGURE_CMD");
+               return avdtp_reconf_cmd(session, transaction, buf, size);
+       case AVDTP_OPEN:
+               DBG("Received OPEN_CMD");
+               return avdtp_open_cmd(session, transaction, buf, size);
+       case AVDTP_START:
+               DBG("Received START_CMD");
+               return avdtp_start_cmd(session, transaction, buf, size);
+       case AVDTP_CLOSE:
+               DBG("Received CLOSE_CMD");
+               return avdtp_close_cmd(session, transaction, buf, size);
+       case AVDTP_SUSPEND:
+               DBG("Received SUSPEND_CMD");
+               return avdtp_suspend_cmd(session, transaction, buf, size);
+       case AVDTP_ABORT:
+               DBG("Received ABORT_CMD");
+               return avdtp_abort_cmd(session, transaction, buf, size);
+       case AVDTP_SECURITY_CONTROL:
+               DBG("Received SECURITY_CONTROL_CMD");
+               return avdtp_secctl_cmd(session, transaction, buf, size);
+       case AVDTP_DELAY_REPORT:
+               DBG("Received DELAY_REPORT_CMD");
+               return avdtp_delayreport_cmd(session, transaction, buf, size);
+       default:
+               DBG("Received unknown request id %u", signal_id);
+               return avdtp_unknown_cmd(session, transaction, signal_id);
+       }
+}
+
+enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS };
+
+static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session,
+                                                       void *buf, size_t size)
+{
+       struct avdtp_common_header *header = buf;
+       struct avdtp_single_header *single = (void *) session->buf;
+       struct avdtp_start_header *start = (void *) session->buf;
+       void *payload;
+       gsize payload_size;
+
+       switch (header->packet_type) {
+       case AVDTP_PKT_TYPE_SINGLE:
+               if (size < sizeof(*single)) {
+                       error("Received too small single packet (%zu bytes)", size);
+                       return PARSE_ERROR;
+               }
+               if (session->in.active) {
+                       error("SINGLE: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+
+               payload = session->buf + sizeof(*single);
+               payload_size = size - sizeof(*single);
+
+               session->in.active = TRUE;
+               session->in.data_size = 0;
+               session->in.no_of_packets = 1;
+               session->in.transaction = header->transaction;
+               session->in.message_type = header->message_type;
+               session->in.signal_id = single->signal_id;
+
+               break;
+       case AVDTP_PKT_TYPE_START:
+               if (size < sizeof(*start)) {
+                       error("Received too small start packet (%zu bytes)", size);
+                       return PARSE_ERROR;
+               }
+               if (session->in.active) {
+                       error("START: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+
+               session->in.active = TRUE;
+               session->in.data_size = 0;
+               session->in.transaction = header->transaction;
+               session->in.message_type = header->message_type;
+               session->in.no_of_packets = start->no_of_packets;
+               session->in.signal_id = start->signal_id;
+
+               payload = session->buf + sizeof(*start);
+               payload_size = size - sizeof(*start);
+
+               break;
+       case AVDTP_PKT_TYPE_CONTINUE:
+               if (size < sizeof(struct avdtp_continue_header)) {
+                       error("Received too small continue packet (%zu bytes)",
+                                                                       size);
+                       return PARSE_ERROR;
+               }
+               if (!session->in.active) {
+                       error("CONTINUE: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+               if (session->in.transaction != header->transaction) {
+                       error("Continue transaction id doesn't match");
+                       return PARSE_ERROR;
+               }
+               if (session->in.no_of_packets <= 1) {
+                       error("Too few continue packets");
+                       return PARSE_ERROR;
+               }
+
+               payload = session->buf + sizeof(struct avdtp_continue_header);
+               payload_size = size - sizeof(struct avdtp_continue_header);
+
+               break;
+       case AVDTP_PKT_TYPE_END:
+               if (size < sizeof(struct avdtp_continue_header)) {
+                       error("Received too small end packet (%zu bytes)", size);
+                       return PARSE_ERROR;
+               }
+               if (!session->in.active) {
+                       error("END: Invalid AVDTP packet fragmentation");
+                       return PARSE_ERROR;
+               }
+               if (session->in.transaction != header->transaction) {
+                       error("End transaction id doesn't match");
+                       return PARSE_ERROR;
+               }
+               if (session->in.no_of_packets > 1) {
+                       error("Got an end packet too early");
+                       return PARSE_ERROR;
+               }
+
+               payload = session->buf + sizeof(struct avdtp_continue_header);
+               payload_size = size - sizeof(struct avdtp_continue_header);
+
+               break;
+       default:
+               error("Invalid AVDTP packet type 0x%02X", header->packet_type);
+               return PARSE_ERROR;
+       }
+
+       if (session->in.data_size + payload_size >
+                                       sizeof(session->in.buf)) {
+               error("Not enough incoming buffer space!");
+               return PARSE_ERROR;
+       }
+
+       memcpy(session->in.buf + session->in.data_size, payload, payload_size);
+       session->in.data_size += payload_size;
+
+       if (session->in.no_of_packets > 1) {
+               session->in.no_of_packets--;
+               DBG("Received AVDTP fragment. %d to go",
+                                               session->in.no_of_packets);
+               return PARSE_FRAGMENT;
+       }
+
+       session->in.active = FALSE;
+
+       return PARSE_SUCCESS;
+}
+
+static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
+                               gpointer data)
+{
+       struct avdtp *session = data;
+       struct avdtp_common_header *header;
+       ssize_t size;
+       int fd;
+
+       DBG("");
+
+       if (cond & G_IO_NVAL)
+               return FALSE;
+
+       header = (void *) session->buf;
+
+       if (cond & (G_IO_HUP | G_IO_ERR))
+               goto failed;
+
+       fd = g_io_channel_unix_get_fd(chan);
+       size = read(fd, session->buf, session->imtu);
+       if (size < 0) {
+               error("IO Channel read error");
+               goto failed;
+       }
+
+       if ((size_t) size < sizeof(struct avdtp_common_header)) {
+               error("Received too small packet (%zu bytes)", size);
+               goto failed;
+       }
+
+       switch (avdtp_parse_data(session, session->buf, size)) {
+       case PARSE_ERROR:
+               goto failed;
+       case PARSE_FRAGMENT:
+               return TRUE;
+       case PARSE_SUCCESS:
+               break;
+       }
+
+       if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+               if (!avdtp_parse_cmd(session, session->in.transaction,
+                                       session->in.signal_id,
+                                       session->in.buf,
+                                       session->in.data_size)) {
+                       error("Unable to handle command. Disconnecting");
+                       goto failed;
+               }
+
+               if (session->req && session->req->collided) {
+                       DBG("Collision detected");
+                       goto next;
+               }
+
+               return TRUE;
+       }
+
+       if (session->req == NULL) {
+               error("No pending request, ignoring message");
+               return TRUE;
+       }
+
+       if (header->transaction != session->req->transaction) {
+               error("Transaction label doesn't match");
+               return TRUE;
+       }
+
+       if (session->in.signal_id != session->req->signal_id) {
+               error("Response signal doesn't match");
+               return TRUE;
+       }
+
+       g_source_remove(session->req->timeout);
+       session->req->timeout = 0;
+
+       switch (header->message_type) {
+       case AVDTP_MSG_TYPE_ACCEPT:
+               if (!avdtp_parse_resp(session, session->req->stream,
+                                               session->in.transaction,
+                                               session->in.signal_id,
+                                               session->in.buf,
+                                               session->in.data_size)) {
+                       error("Unable to parse accept response");
+                       goto failed;
+               }
+               break;
+       case AVDTP_MSG_TYPE_REJECT:
+               if (!avdtp_parse_rej(session, session->req->stream,
+                                               session->in.transaction,
+                                               session->in.signal_id,
+                                               session->in.buf,
+                                               session->in.data_size)) {
+                       error("Unable to parse reject response");
+                       goto failed;
+               }
+               break;
+       case AVDTP_MSG_TYPE_GEN_REJECT:
+               error("Received a General Reject message");
+               break;
+       default:
+               error("Unknown message type 0x%02X", header->message_type);
+               break;
+       }
+
+next:
+       pending_req_free(session->req);
+       session->req = NULL;
+
+       process_queue(session);
+
+       return TRUE;
+
+failed:
+       connection_lost(session, EIO);
+
+       return FALSE;
+}
+
+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;
+
+       session = g_new0(struct avdtp, 1);
+       session->io = g_io_channel_unix_new(fd);
+       session->version = version;
+       session->imtu = imtu;
+       session->omtu = omtu;
+       session->buf = g_malloc0(MAX(session->imtu, session->omtu));
+
+       /* This watch should be low priority since otherwise the
+        * connect callback might be dispatched before the session
+        * callback if the kernel wakes us up at the same time for
+        * them. This could happen if a headset is very quick in
+        * sending the Start command after connecting the stream
+        * transport channel.
+        */
+       session->io_id = g_io_add_watch_full(session->io, G_PRIORITY_LOW, cond,
+                                               (GIOFunc) session_cb, session,
+                                               NULL);
+
+       return avdtp_ref(session);
+}
+
+static void queue_request(struct avdtp *session, struct pending_req *req,
+                       gboolean priority)
+{
+       if (priority)
+               session->prio_queue = g_slist_append(session->prio_queue, req);
+       else
+               session->req_queue = g_slist_append(session->req_queue, req);
+}
+
+static uint8_t req_get_seid(struct pending_req *req)
+{
+       if (req->signal_id == AVDTP_DISCOVER)
+               return 0;
+
+       return ((struct seid_req *) (req->data))->acp_seid;
+}
+
+static int cancel_request(struct avdtp *session, int err)
+{
+       struct pending_req *req;
+       struct seid_req sreq;
+       struct avdtp_local_sep *lsep;
+       struct avdtp_stream *stream;
+       uint8_t seid;
+       struct avdtp_error averr;
+
+       req = session->req;
+       session->req = NULL;
+
+       avdtp_error_init(&averr, AVDTP_ERRNO, err);
+
+       seid = req_get_seid(req);
+       if (seid)
+               stream = find_stream_by_rseid(session, seid);
+       else
+               stream = NULL;
+
+       if (stream) {
+               stream->abort_int = TRUE;
+               lsep = stream->lsep;
+       } else
+               lsep = NULL;
+
+       switch (req->signal_id) {
+       case AVDTP_RECONFIGURE:
+               error("Reconfigure: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->reconfigure)
+                       lsep->cfm->reconfigure(session, lsep, stream, &averr,
+                                               lsep->user_data);
+               break;
+       case AVDTP_OPEN:
+               error("Open: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->open)
+                       lsep->cfm->open(session, lsep, stream, &averr,
+                                       lsep->user_data);
+               break;
+       case AVDTP_START:
+               error("Start: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->start) {
+                       lsep->cfm->start(session, lsep, stream, &averr,
+                                               lsep->user_data);
+                       if (stream)
+                               stream->starting = FALSE;
+               }
+               break;
+       case AVDTP_SUSPEND:
+               error("Suspend: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->suspend)
+                       lsep->cfm->suspend(session, lsep, stream, &averr,
+                                               lsep->user_data);
+               break;
+       case AVDTP_CLOSE:
+               error("Close: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->close) {
+                       lsep->cfm->close(session, lsep, stream, &averr,
+                                               lsep->user_data);
+                       if (stream)
+                               stream->close_int = FALSE;
+               }
+               break;
+       case AVDTP_SET_CONFIGURATION:
+               error("SetConfiguration: %s (%d)", strerror(err), err);
+               if (lsep && lsep->cfm && lsep->cfm->set_configuration)
+                       lsep->cfm->set_configuration(session, lsep, stream,
+                                                       &averr, lsep->user_data);
+               goto failed;
+       case AVDTP_DISCOVER:
+               error("Discover: %s (%d)", strerror(err), err);
+               goto failed;
+       case AVDTP_GET_CAPABILITIES:
+               error("GetCapabilities: %s (%d)", strerror(err), err);
+               goto failed;
+       case AVDTP_ABORT:
+               error("Abort: %s (%d)", strerror(err), err);
+               goto failed;
+       }
+
+       if (!stream)
+               goto failed;
+
+       memset(&sreq, 0, sizeof(sreq));
+       sreq.acp_seid = seid;
+
+       err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq,
+                               sizeof(sreq));
+       if (err < 0) {
+               error("Unable to send abort request");
+               goto failed;
+       }
+
+       goto done;
+
+failed:
+       connection_lost(session, err);
+done:
+       pending_req_free(req);
+       return err;
+}
+
+static gboolean request_timeout(gpointer user_data)
+{
+       struct avdtp *session = user_data;
+
+       cancel_request(session, ETIMEDOUT);
+
+       return FALSE;
+}
+
+static int send_req(struct avdtp *session, gboolean priority,
+                       struct pending_req *req)
+{
+       static int transaction = 0;
+       int err;
+
+       if (session->req != NULL) {
+               queue_request(session, req, priority);
+               return 0;
+       }
+
+       req->transaction = transaction++;
+       transaction %= 16;
+
+       /* FIXME: Should we retry to send if the buffer
+       was not totally sent or in case of EINTR? */
+       if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,
+                               req->signal_id, req->data, req->data_size)) {
+               err = -EIO;
+               goto failed;
+       }
+
+       session->req = req;
+
+       req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ?
+                                       ABORT_TIMEOUT : REQ_TIMEOUT,
+                                       request_timeout,
+                                       session);
+       return 0;
+
+failed:
+       g_free(req->data);
+       g_free(req);
+       return err;
+}
+
+static int send_request(struct avdtp *session, gboolean priority,
+                       struct avdtp_stream *stream, uint8_t signal_id,
+                       void *buffer, size_t size)
+{
+       struct pending_req *req;
+
+       if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {
+               DBG("Unable to send requests while aborting");
+               return -EINVAL;
+       }
+
+       req = g_new0(struct pending_req, 1);
+       req->signal_id = signal_id;
+       req->data = g_malloc(size);
+       memcpy(req->data, buffer, size);
+       req->data_size = size;
+       req->stream = stream;
+
+       return send_req(session, priority, req);
+}
+
+static gboolean avdtp_discover_resp(struct avdtp *session,
+                                       struct discover_resp *resp, int size)
+{
+       int sep_count, i;
+       uint8_t getcap_cmd;
+       int ret = 0;
+       gboolean getcap_pending = FALSE;
+
+       if (session->version >= 0x0103)
+               getcap_cmd = AVDTP_GET_ALL_CAPABILITIES;
+       else
+               getcap_cmd = AVDTP_GET_CAPABILITIES;
+
+       sep_count = size / sizeof(struct seid_info);
+
+       for (i = 0; i < sep_count; i++) {
+               struct avdtp_remote_sep *sep;
+               struct avdtp_stream *stream;
+               struct seid_req req;
+
+               DBG("seid %d type %d media %d in use %d",
+                               resp->seps[i].seid, resp->seps[i].type,
+                               resp->seps[i].media_type, resp->seps[i].inuse);
+
+               stream = find_stream_by_rseid(session, resp->seps[i].seid);
+
+               sep = find_remote_sep(session->seps, resp->seps[i].seid);
+               if (!sep) {
+                       if (resp->seps[i].inuse && !stream)
+                               continue;
+                       sep = g_new0(struct avdtp_remote_sep, 1);
+                       session->seps = g_slist_append(session->seps, sep);
+               }
+
+               sep->stream = stream;
+               sep->seid = resp->seps[i].seid;
+               sep->type = resp->seps[i].type;
+               sep->media_type = resp->seps[i].media_type;
+
+               memset(&req, 0, sizeof(req));
+               req.acp_seid = sep->seid;
+
+               ret = send_request(session, TRUE, NULL, getcap_cmd,
+                                                       &req, sizeof(req));
+               if (ret < 0)
+                       break;
+               getcap_pending = TRUE;
+       }
+
+       if (!getcap_pending)
+               finalize_discovery(session, -ret);
+
+       return TRUE;
+}
+
+static gboolean avdtp_get_capabilities_resp(struct avdtp *session,
+                                               struct getcap_resp *resp,
+                                               unsigned int size)
+{
+       struct avdtp_remote_sep *sep;
+       uint8_t seid;
+
+       /* Check for minimum required packet size includes:
+        *   1. getcap resp header
+        *   2. media transport capability (2 bytes)
+        *   3. media codec capability type + length (2 bytes)
+        *   4. the actual media codec elements
+        * */
+       if (size < (sizeof(struct getcap_resp) + 4 +
+                               sizeof(struct avdtp_media_codec_capability))) {
+               error("Too short getcap resp packet");
+               return FALSE;
+       }
+
+       seid = ((struct seid_req *) session->req->data)->acp_seid;
+
+       sep = find_remote_sep(session->seps, seid);
+
+       DBG("seid %d type %d media %d", sep->seid,
+                                       sep->type, sep->media_type);
+
+       if (sep->caps) {
+               g_slist_free_full(sep->caps, g_free);
+               sep->caps = NULL;
+               sep->codec = NULL;
+               sep->delay_reporting = FALSE;
+       }
+
+       sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp),
+                                       &sep->codec, &sep->delay_reporting);
+
+       return TRUE;
+}
+
+static gboolean avdtp_set_configuration_resp(struct avdtp *session,
+                                               struct avdtp_stream *stream,
+                                               struct avdtp_single_header *resp,
+                                               int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       if (sep->cfm && sep->cfm->set_configuration)
+               sep->cfm->set_configuration(session, sep, stream, NULL,
+                                               sep->user_data);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
+       return TRUE;
+}
+
+static gboolean avdtp_reconfigure_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_single_header *resp, int size)
+{
+       return TRUE;
+}
+
+static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream,
+                               struct seid_rej *resp, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       session->pending_open = stream;
+
+       if (!stream->open_acp && sep->cfm && sep->cfm->open)
+               sep->cfm->open(session, sep, stream, NULL, sep->user_data);
+
+       return TRUE;
+}
+
+static gboolean avdtp_start_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       struct seid_rej *resp, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       /* We might be in STREAMING already if both sides send START_CMD at the
+        * same time and the one in SNK role doesn't reject it as it should */
+       if (sep->state != AVDTP_STATE_STREAMING)
+               avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
+
+       if (sep->cfm && sep->cfm->start)
+               sep->cfm->start(session, sep, stream, NULL, sep->user_data);
+
+       return TRUE;
+}
+
+static gboolean avdtp_close_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       struct seid_rej *resp, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
+
+       close_stream(stream);
+
+       return TRUE;
+}
+
+static gboolean avdtp_suspend_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       void *data, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
+
+       if (sep->cfm && sep->cfm->suspend)
+               sep->cfm->suspend(session, sep, stream, NULL, sep->user_data);
+
+       return TRUE;
+}
+
+static gboolean avdtp_abort_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       struct seid_rej *resp, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
+
+       if (sep->cfm && sep->cfm->abort)
+               sep->cfm->abort(session, sep, stream, NULL, sep->user_data);
+
+       avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
+
+       return TRUE;
+}
+
+static gboolean avdtp_delay_report_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       void *data, int size)
+{
+       struct avdtp_local_sep *sep = stream->lsep;
+
+       if (sep->cfm && sep->cfm->delay_report)
+               sep->cfm->delay_report(session, sep, stream, NULL, sep->user_data);
+
+       return TRUE;
+}
+
+static gboolean avdtp_parse_resp(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       uint8_t transaction, uint8_t signal_id,
+                                       void *buf, int size)
+{
+       struct pending_req *next;
+       const char *get_all = "";
+
+       if (session->prio_queue)
+               next = session->prio_queue->data;
+       else if (session->req_queue)
+               next = session->req_queue->data;
+       else
+               next = NULL;
+
+       switch (signal_id) {
+       case AVDTP_DISCOVER:
+               DBG("DISCOVER request succeeded");
+               return avdtp_discover_resp(session, buf, size);
+       case AVDTP_GET_ALL_CAPABILITIES:
+               get_all = "ALL_";
+       case AVDTP_GET_CAPABILITIES:
+               DBG("GET_%sCAPABILITIES request succeeded", get_all);
+               if (!avdtp_get_capabilities_resp(session, buf, size))
+                       return FALSE;
+               if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES ||
+                               next->signal_id == AVDTP_GET_ALL_CAPABILITIES)))
+                       finalize_discovery(session, 0);
+               return TRUE;
+       }
+
+       /* The remaining commands require an existing stream so bail out
+        * here if the stream got unexpectedly disconnected */
+       if (!stream) {
+               DBG("AVDTP: stream was closed while waiting for reply");
+               return TRUE;
+       }
+
+       switch (signal_id) {
+       case AVDTP_SET_CONFIGURATION:
+               DBG("SET_CONFIGURATION request succeeded");
+               return avdtp_set_configuration_resp(session, stream,
+                                                               buf, size);
+       case AVDTP_RECONFIGURE:
+               DBG("RECONFIGURE request succeeded");
+               return avdtp_reconfigure_resp(session, stream, buf, size);
+       case AVDTP_OPEN:
+               DBG("OPEN request succeeded");
+               return avdtp_open_resp(session, stream, buf, size);
+       case AVDTP_SUSPEND:
+               DBG("SUSPEND request succeeded");
+               return avdtp_suspend_resp(session, stream, buf, size);
+       case AVDTP_START:
+               DBG("START request succeeded");
+               return avdtp_start_resp(session, stream, buf, size);
+       case AVDTP_CLOSE:
+               DBG("CLOSE request succeeded");
+               return avdtp_close_resp(session, stream, buf, size);
+       case AVDTP_ABORT:
+               DBG("ABORT request succeeded");
+               return avdtp_abort_resp(session, stream, buf, size);
+       case AVDTP_DELAY_REPORT:
+               DBG("DELAY_REPORT request succeeded");
+               return avdtp_delay_report_resp(session, stream, buf, size);
+       }
+
+       error("Unknown signal id in accept response: %u", signal_id);
+       return TRUE;
+}
+
+static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size,
+                                       struct avdtp_error *err)
+{
+       if (size < sizeof(struct seid_rej)) {
+               error("Too small packet for seid_rej");
+               return FALSE;
+       }
+
+       avdtp_error_init(err, 0x00, rej->error);
+
+       return TRUE;
+}
+
+static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size,
+                               struct avdtp_error *err)
+{
+       if (size < sizeof(struct conf_rej)) {
+               error("Too small packet for conf_rej");
+               return FALSE;
+       }
+
+       avdtp_error_init(err, rej->category, rej->error);
+
+       return TRUE;
+}
+
+static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size,
+                                       struct avdtp_error *err,
+                                       uint8_t *acp_seid)
+{
+       if (size < sizeof(struct stream_rej)) {
+               error("Too small packet for stream_rej");
+               return FALSE;
+       }
+
+       avdtp_error_init(err, 0x00, rej->error);
+
+       if (acp_seid)
+               *acp_seid = rej->acp_seid;
+
+       return TRUE;
+}
+
+static gboolean avdtp_parse_rej(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       uint8_t transaction, uint8_t signal_id,
+                                       void *buf, int size)
+{
+       struct avdtp_error err;
+       uint8_t acp_seid;
+       struct avdtp_local_sep *sep = stream ? stream->lsep : NULL;
+
+       switch (signal_id) {
+       case AVDTP_DISCOVER:
+       case AVDTP_GET_CAPABILITIES:
+       case AVDTP_GET_ALL_CAPABILITIES:
+               if (!seid_rej_to_err(buf, size, &err))
+                       return FALSE;
+               error("%s request rejected: %s (%d)",
+                       signal_id == AVDTP_DISCOVER ? "DISCOVER" :
+                       signal_id == AVDTP_GET_CAPABILITIES ?
+                       "GET_CAPABILITIES" : "GET_ALL_CAPABILITIES",
+                       avdtp_strerror(&err), err.err.error_code);
+               if (session->discover) {
+                       session->discover->cb(session, session->seps, &err,
+                                               session->discover->user_data);
+                       g_free(session->discover);
+                       session->discover = NULL;
+               }
+               return TRUE;
+       case AVDTP_OPEN:
+               if (!seid_rej_to_err(buf, size, &err))
+                       return FALSE;
+               error("OPEN request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->open)
+                       sep->cfm->open(session, sep, stream, &err,
+                                       sep->user_data);
+               return TRUE;
+       case AVDTP_SET_CONFIGURATION:
+               if (!conf_rej_to_err(buf, size, &err))
+                       return FALSE;
+               error("SET_CONFIGURATION request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->set_configuration)
+                       sep->cfm->set_configuration(session, sep, stream,
+                                                       &err, sep->user_data);
+               return TRUE;
+       case AVDTP_GET_CONFIGURATION:
+               if (!seid_rej_to_err(buf, size, &err))
+                       return FALSE;
+               error("GET_CONFIGURATION request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->get_configuration)
+                       sep->cfm->get_configuration(session, sep, stream, &err,
+                                                               sep->user_data);
+               return TRUE;
+       case AVDTP_RECONFIGURE:
+               if (!conf_rej_to_err(buf, size, &err))
+                       return FALSE;
+               error("RECONFIGURE request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->reconfigure)
+                       sep->cfm->reconfigure(session, sep, stream, &err,
+                                               sep->user_data);
+               return TRUE;
+       case AVDTP_START:
+               if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+                       return FALSE;
+               error("START request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->start) {
+                       sep->cfm->start(session, sep, stream, &err,
+                                       sep->user_data);
+                       stream->starting = FALSE;
+               }
+               return TRUE;
+       case AVDTP_SUSPEND:
+               if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+                       return FALSE;
+               error("SUSPEND request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->suspend)
+                       sep->cfm->suspend(session, sep, stream, &err,
+                                               sep->user_data);
+               return TRUE;
+       case AVDTP_CLOSE:
+               if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+                       return FALSE;
+               error("CLOSE request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->close) {
+                       sep->cfm->close(session, sep, stream, &err,
+                                       sep->user_data);
+                       stream->close_int = FALSE;
+               }
+               return TRUE;
+       case AVDTP_ABORT:
+               if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+                       return FALSE;
+               error("ABORT request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->abort)
+                       sep->cfm->abort(session, sep, stream, &err,
+                                       sep->user_data);
+               return FALSE;
+       case AVDTP_DELAY_REPORT:
+               if (!stream_rej_to_err(buf, size, &err, &acp_seid))
+                       return FALSE;
+               error("DELAY_REPORT request rejected: %s (%d)",
+                               avdtp_strerror(&err), err.err.error_code);
+               if (sep && sep->cfm && sep->cfm->delay_report)
+                       sep->cfm->delay_report(session, sep, stream, &err,
+                                                       sep->user_data);
+               return TRUE;
+       default:
+               error("Unknown reject response signal id: %u", signal_id);
+               return TRUE;
+       }
+}
+
+struct avdtp_service_capability *avdtp_stream_get_codec(
+                                               struct avdtp_stream *stream)
+{
+       GSList *l;
+
+       for (l = stream->caps; l; l = l->next) {
+               struct avdtp_service_capability *cap = l->data;
+
+               if (cap->category == AVDTP_MEDIA_CODEC)
+                       return cap;
+       }
+
+       return NULL;
+}
+
+static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+                                       struct avdtp_service_capability *cap)
+{
+       GSList *l;
+       struct avdtp_service_capability *stream_cap;
+
+       for (l = stream->caps; l; l = g_slist_next(l)) {
+               stream_cap = l->data;
+
+               if (stream_cap->category != cap->category ||
+                       stream_cap->length != cap->length)
+                       continue;
+
+               if (memcmp(stream_cap->data, cap->data, cap->length) == 0)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+                                       GSList *caps)
+{
+       for (; caps; caps = g_slist_next(caps)) {
+               struct avdtp_service_capability *cap = caps->data;
+
+               if (!avdtp_stream_has_capability(stream, cap))
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+                                               struct avdtp_stream *stream)
+{
+       GSList *l;
+
+       for (l = stream->session->seps; l; l = l->next) {
+               struct avdtp_remote_sep *sep = l->data;
+
+               if (sep->seid == stream->rseid)
+                       return sep;
+       }
+
+       return NULL;
+}
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+                                               size_t imtu, size_t omtu)
+{
+       GIOChannel *io;
+
+       if (stream != stream->session->pending_open)
+               return FALSE;
+
+       io = g_io_channel_unix_new(fd);
+
+       handle_transport_connect(stream->session, io, imtu, omtu);
+
+       g_io_channel_unref(io);
+
+       return TRUE;
+
+}
+
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+                                       uint16_t *imtu, uint16_t *omtu,
+                                       GSList **caps)
+{
+       if (stream->io == NULL)
+               return FALSE;
+
+       if (sock)
+               *sock = g_io_channel_unix_get_fd(stream->io);
+
+       if (omtu)
+               *omtu = stream->omtu;
+
+       if (imtu)
+               *imtu = stream->imtu;
+
+       if (caps)
+               *caps = stream->caps;
+
+       return TRUE;
+}
+
+static int process_queue(struct avdtp *session)
+{
+       GSList **queue, *l;
+       struct pending_req *req;
+
+       if (session->req)
+               return 0;
+
+       if (session->prio_queue)
+               queue = &session->prio_queue;
+       else
+               queue = &session->req_queue;
+
+       if (!*queue)
+               return 0;
+
+       l = *queue;
+       req = l->data;
+
+       *queue = g_slist_remove(*queue, req);
+
+       return send_req(session, FALSE, req);
+}
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
+{
+       return sep->codec;
+}
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+                                                       void *data, int length)
+{
+       struct avdtp_service_capability *cap;
+
+       if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING)
+               return NULL;
+
+       cap = g_malloc(sizeof(struct avdtp_service_capability) + length);
+       cap->category = category;
+       cap->length = length;
+       memcpy(cap->data, data, length);
+
+       return cap;
+}
+
+static gboolean process_discover(gpointer data)
+{
+       struct avdtp *session = data;
+
+       session->discover->id = 0;
+
+       finalize_discovery(session, 0);
+
+       return FALSE;
+}
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+                       void *user_data)
+{
+       int err;
+
+       if (session->discover)
+               return -EBUSY;
+
+       session->discover = g_new0(struct discover_callback, 1);
+
+       if (session->seps) {
+               session->discover->cb = cb;
+               session->discover->user_data = user_data;
+               session->discover->id = g_idle_add(process_discover, session);
+               return 0;
+       }
+
+       err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);
+       if (err == 0) {
+               session->discover->cb = cb;
+               session->discover->user_data = user_data;
+       }
+
+       return err;
+}
+
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+                               struct avdtp_stream *stream,
+                               unsigned int id)
+{
+       GSList *l;
+       struct stream_callback *cb;
+
+       if (!stream)
+               return FALSE;
+
+       for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) {
+               struct stream_callback *tmp = l->data;
+               if (tmp && tmp->id == id) {
+                       cb = tmp;
+                       break;
+               }
+       }
+
+       if (!cb)
+               return FALSE;
+
+       stream->callbacks = g_slist_remove(stream->callbacks, cb);
+       g_free(cb);
+
+       return TRUE;
+}
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       avdtp_stream_state_cb cb, void *data)
+{
+       struct stream_callback *stream_cb;
+       static unsigned int id = 0;
+
+       stream_cb = g_new(struct stream_callback, 1);
+       stream_cb->cb = cb;
+       stream_cb->user_data = data;
+       stream_cb->id = ++id;
+
+       stream->callbacks = g_slist_append(stream->callbacks, stream_cb);
+
+       return stream_cb->id;
+}
+
+int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream)
+{
+       struct seid_req req;
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+
+       return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION,
+                                                       &req, sizeof(req));
+}
+
+static void copy_capabilities(gpointer data, gpointer user_data)
+{
+       struct avdtp_service_capability *src_cap = data;
+       struct avdtp_service_capability *dst_cap;
+       GSList **l = user_data;
+
+       dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data,
+                                       src_cap->length);
+
+       *l = g_slist_append(*l, dst_cap);
+}
+
+int avdtp_set_configuration(struct avdtp *session,
+                               struct avdtp_remote_sep *rsep,
+                               struct avdtp_local_sep *lsep,
+                               GSList *caps,
+                               struct avdtp_stream **stream)
+{
+       struct setconf_req *req;
+       struct avdtp_stream *new_stream;
+       unsigned char *ptr;
+       int err, caps_len;
+       struct avdtp_service_capability *cap;
+       GSList *l;
+
+       if (!(lsep && rsep))
+               return -EINVAL;
+
+       DBG("%p: int_seid=%u, acp_seid=%u", session,
+                       lsep->info.seid, rsep->seid);
+
+       new_stream = g_new0(struct avdtp_stream, 1);
+       new_stream->session = session;
+       new_stream->lsep = lsep;
+       new_stream->rseid = rsep->seid;
+
+       if (rsep->delay_reporting && lsep->delay_reporting) {
+               struct avdtp_service_capability *delay_reporting;
+
+               delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING,
+                                                               NULL, 0);
+               caps = g_slist_append(caps, delay_reporting);
+               new_stream->delay_reporting = TRUE;
+       }
+
+       g_slist_foreach(caps, copy_capabilities, &new_stream->caps);
+
+       /* Calculate total size of request */
+       for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) {
+               cap = l->data;
+               caps_len += cap->length + 2;
+       }
+
+       req = g_malloc0(sizeof(struct setconf_req) + caps_len);
+
+       req->int_seid = lsep->info.seid;
+       req->acp_seid = rsep->seid;
+
+       /* Copy the capabilities into the request */
+       for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) {
+               cap = l->data;
+               memcpy(ptr, cap, cap->length + 2);
+               ptr += cap->length + 2;
+       }
+
+       err = send_request(session, FALSE, new_stream,
+                               AVDTP_SET_CONFIGURATION, req,
+                               sizeof(struct setconf_req) + caps_len);
+       if (err < 0)
+               stream_free(new_stream);
+       else {
+               lsep->info.inuse = 1;
+               lsep->stream = new_stream;
+               rsep->stream = new_stream;
+               session->streams = g_slist_append(session->streams, new_stream);
+               if (stream)
+                       *stream = new_stream;
+       }
+
+       g_free(req);
+
+       return err;
+}
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream)
+{
+       struct seid_req req;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state > AVDTP_STATE_CONFIGURED)
+               return -EINVAL;
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+
+       return send_request(session, FALSE, stream, AVDTP_OPEN,
+                                                       &req, sizeof(req));
+}
+
+static gboolean start_timeout(gpointer user_data)
+{
+       struct avdtp_stream *stream = user_data;
+       struct avdtp *session = stream->session;
+
+       stream->open_acp = FALSE;
+
+       if (avdtp_start(session, stream) < 0)
+               error("wait_timeout: avdtp_start failed");
+
+       stream->start_timer = 0;
+
+       return FALSE;
+}
+
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
+{
+       struct start_req req;
+       int ret;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state != AVDTP_STATE_OPEN)
+               return -EINVAL;
+
+       /* Recommendation 12:
+        *  If the RD has configured and opened a stream it is also responsible
+        *  to start the streaming via GAVDP_START.
+        */
+       if (stream->open_acp) {
+               /* If timer already active wait it */
+               if (stream->start_timer)
+                       return 0;
+
+               stream->start_timer = g_timeout_add_seconds(START_TIMEOUT,
+                                                               start_timeout,
+                                                               stream);
+               return 0;
+       }
+
+       if (stream->close_int == TRUE) {
+               error("avdtp_start: rejecting start since close is initiated");
+               return -EINVAL;
+       }
+
+       if (stream->starting == TRUE) {
+               DBG("stream already started");
+               return -EINPROGRESS;
+       }
+
+       memset(&req, 0, sizeof(req));
+       req.first_seid.seid = stream->rseid;
+
+       ret = send_request(session, FALSE, stream, AVDTP_START,
+                                                       &req, sizeof(req));
+       if (ret == 0)
+               stream->starting = TRUE;
+
+       return ret;
+}
+
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+               gboolean immediate)
+{
+       struct seid_req req;
+       int ret;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state < AVDTP_STATE_OPEN)
+               return -EINVAL;
+
+       if (stream->close_int == TRUE) {
+               error("avdtp_close: rejecting since close is already initiated");
+               return -EINVAL;
+       }
+
+       if (immediate && session->req && stream == session->req->stream)
+               return avdtp_abort(session, stream);
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+
+       ret = send_request(session, FALSE, stream, AVDTP_CLOSE,
+                                                       &req, sizeof(req));
+       if (ret == 0)
+               stream->close_int = TRUE;
+
+       return ret;
+}
+
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream)
+{
+       struct seid_req req;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int)
+               return -EINVAL;
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+
+       return send_request(session, FALSE, stream, AVDTP_SUSPEND,
+                                                       &req, sizeof(req));
+}
+
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
+{
+       struct seid_req req;
+       int ret;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state == AVDTP_STATE_ABORTING)
+               return -EINVAL;
+
+       if (session->req && session->req->timeout > 0 &&
+                                               stream == session->req->stream)
+               return cancel_request(session, ECANCELED);
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+
+       ret = send_request(session, TRUE, stream, AVDTP_ABORT,
+                                                       &req, sizeof(req));
+       if (ret == 0)
+               stream->abort_int = TRUE;
+
+       return ret;
+}
+
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+                                                       uint16_t delay)
+{
+       struct delay_req req;
+
+       if (!g_slist_find(session->streams, stream))
+               return -EINVAL;
+
+       if (stream->lsep->state != AVDTP_STATE_CONFIGURED &&
+                               stream->lsep->state != AVDTP_STATE_STREAMING)
+               return -EINVAL;
+
+       if (!stream->delay_reporting || session->version < 0x0103)
+               return -EINVAL;
+
+       stream->delay = delay;
+
+       memset(&req, 0, sizeof(req));
+       req.acp_seid = stream->rseid;
+       req.delay = htons(delay);
+
+       return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT,
+                                                       &req, sizeof(req));
+}
+
+struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type,
+                                               uint8_t codec_type,
+                                               gboolean delay_reporting,
+                                               struct avdtp_sep_ind *ind,
+                                               struct avdtp_sep_cfm *cfm,
+                                               void *user_data)
+{
+       struct avdtp_local_sep *sep;
+
+       if (g_slist_length(lseps) > MAX_SEID)
+               return NULL;
+
+       sep = g_new0(struct avdtp_local_sep, 1);
+
+       sep->state = AVDTP_STATE_IDLE;
+       sep->info.seid = g_slist_length(lseps) + 1;
+       sep->info.type = type;
+       sep->info.media_type = media_type;
+       sep->codec = codec_type;
+       sep->ind = ind;
+       sep->cfm = cfm;
+       sep->user_data = user_data;
+       sep->delay_reporting = TRUE;
+
+       DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
+                       sep->info.type, sep->codec, sep->info.seid);
+       lseps = g_slist_append(lseps, sep);
+
+       return sep;
+}
+
+int avdtp_unregister_sep(struct avdtp_local_sep *sep)
+{
+       if (!sep)
+               return -EINVAL;
+
+       lseps = g_slist_remove(lseps, sep);
+
+       if (sep->stream)
+               release_stream(sep->stream, sep->stream->session);
+
+       DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
+                       sep->info.type, sep->codec, sep->info.seid);
+
+       g_free(sep);
+
+       return 0;
+}
+
+const char *avdtp_strerror(struct avdtp_error *err)
+{
+       if (err->category == AVDTP_ERRNO)
+               return strerror(err->err.posix_errno);
+
+       switch(err->err.error_code) {
+       case AVDTP_BAD_HEADER_FORMAT:
+               return "Bad Header Format";
+       case AVDTP_BAD_LENGTH:
+               return "Bad Packet Length";
+       case AVDTP_BAD_ACP_SEID:
+               return "Bad Acceptor SEID";
+       case AVDTP_SEP_IN_USE:
+               return "Stream End Point in Use";
+       case AVDTP_SEP_NOT_IN_USE:
+               return "Stream End Point Not in Use";
+       case AVDTP_BAD_SERV_CATEGORY:
+               return "Bad Service Category";
+       case AVDTP_BAD_PAYLOAD_FORMAT:
+               return "Bad Payload format";
+       case AVDTP_NOT_SUPPORTED_COMMAND:
+               return "Command Not Supported";
+       case AVDTP_INVALID_CAPABILITIES:
+               return "Invalid Capabilities";
+       case AVDTP_BAD_RECOVERY_TYPE:
+               return "Bad Recovery Type";
+       case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT:
+               return "Bad Media Transport Format";
+       case AVDTP_BAD_RECOVERY_FORMAT:
+               return "Bad Recovery Format";
+       case AVDTP_BAD_ROHC_FORMAT:
+               return "Bad Header Compression Format";
+       case AVDTP_BAD_CP_FORMAT:
+               return "Bad Content Protection Format";
+       case AVDTP_BAD_MULTIPLEXING_FORMAT:
+               return "Bad Multiplexing Format";
+       case AVDTP_UNSUPPORTED_CONFIGURATION:
+               return "Configuration not supported";
+       case AVDTP_BAD_STATE:
+               return "Bad State";
+       default:
+               return "Unknown error";
+       }
+}
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep)
+{
+       return sep->state;
+}
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream)
+{
+       return g_slist_find(session->streams, stream) ? TRUE : FALSE;
+}
diff --git a/android/avdtp.h b/android/avdtp.h
new file mode 100644 (file)
index 0000000..9760875
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2006-2010  Nokia Corporation
+ *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+struct avdtp;
+struct avdtp_stream;
+struct avdtp_local_sep;
+struct avdtp_remote_sep;
+struct avdtp_error {
+       uint8_t category;
+       union {
+               uint8_t error_code;
+               int posix_errno;
+       } err;
+};
+
+/* SEP capability categories */
+#define AVDTP_MEDIA_TRANSPORT                  0x01
+#define AVDTP_REPORTING                                0x02
+#define AVDTP_RECOVERY                         0x03
+#define AVDTP_CONTENT_PROTECTION               0x04
+#define AVDTP_HEADER_COMPRESSION               0x05
+#define AVDTP_MULTIPLEXING                     0x06
+#define AVDTP_MEDIA_CODEC                      0x07
+#define AVDTP_DELAY_REPORTING                  0x08
+#define AVDTP_ERRNO                            0xff
+
+/* AVDTP error definitions */
+#define AVDTP_BAD_HEADER_FORMAT                        0x01
+#define AVDTP_BAD_LENGTH                       0x11
+#define AVDTP_BAD_ACP_SEID                     0x12
+#define AVDTP_SEP_IN_USE                       0x13
+#define AVDTP_SEP_NOT_IN_USE                   0x14
+#define AVDTP_BAD_SERV_CATEGORY                        0x17
+#define AVDTP_BAD_PAYLOAD_FORMAT               0x18
+#define AVDTP_NOT_SUPPORTED_COMMAND            0x19
+#define AVDTP_INVALID_CAPABILITIES             0x1A
+#define AVDTP_BAD_RECOVERY_TYPE                        0x22
+#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT       0x23
+#define AVDTP_BAD_RECOVERY_FORMAT              0x25
+#define AVDTP_BAD_ROHC_FORMAT                  0x26
+#define AVDTP_BAD_CP_FORMAT                    0x27
+#define AVDTP_BAD_MULTIPLEXING_FORMAT          0x28
+#define AVDTP_UNSUPPORTED_CONFIGURATION                0x29
+#define AVDTP_BAD_STATE                                0x31
+
+/* SEP types definitions */
+#define AVDTP_SEP_TYPE_SOURCE                  0x00
+#define AVDTP_SEP_TYPE_SINK                    0x01
+
+/* Media types definitions */
+#define AVDTP_MEDIA_TYPE_AUDIO                 0x00
+#define AVDTP_MEDIA_TYPE_VIDEO                 0x01
+#define AVDTP_MEDIA_TYPE_MULTIMEDIA            0x02
+
+typedef enum {
+       AVDTP_STATE_IDLE,
+       AVDTP_STATE_CONFIGURED,
+       AVDTP_STATE_OPEN,
+       AVDTP_STATE_STREAMING,
+       AVDTP_STATE_CLOSING,
+       AVDTP_STATE_ABORTING,
+} avdtp_state_t;
+
+struct avdtp_service_capability {
+       uint8_t category;
+       uint8_t length;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+struct avdtp_media_codec_capability {
+       uint8_t rfa0:4;
+       uint8_t media_type:4;
+       uint8_t media_codec_type;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+struct avdtp_media_codec_capability {
+       uint8_t media_type:4;
+       uint8_t rfa0:4;
+       uint8_t media_codec_type;
+       uint8_t data[0];
+} __attribute__ ((packed));
+
+#else
+#error "Unknown byte order"
+#endif
+
+typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream,
+                                       avdtp_state_t old_state,
+                                       avdtp_state_t new_state,
+                                       struct avdtp_error *err,
+                                       void *user_data);
+
+typedef void (*avdtp_set_configuration_cb) (struct avdtp *session,
+                                               struct avdtp_stream *stream,
+                                               struct avdtp_error *err);
+
+/* Callbacks for when a reply is received to a command that we sent */
+struct avdtp_sep_cfm {
+       void (*set_configuration) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_error *err,
+                                       void *user_data);
+       void (*get_configuration) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       struct avdtp_stream *stream,
+                                       struct avdtp_error *err,
+                                       void *user_data);
+       void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data);
+       void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data);
+       void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data);
+       void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data);
+       void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data);
+       void (*reconfigure) (struct avdtp *session,
+                               struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data);
+       void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data);
+};
+
+/* Callbacks for indicating when we received a new command. The return value
+ * indicates whether the command should be rejected or accepted */
+struct avdtp_sep_ind {
+       gboolean (*get_capability) (struct avdtp *session,
+                                       struct avdtp_local_sep *sep,
+                                       gboolean get_all,
+                                       GSList **caps, uint8_t *err,
+                                       void *user_data);
+       gboolean (*set_configuration) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       struct avdtp_stream *stream,
+                                       GSList *caps,
+                                       avdtp_set_configuration_cb cb,
+                                       void *user_data);
+       gboolean (*get_configuration) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       uint8_t *err, void *user_data);
+       gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data);
+       gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data);
+       gboolean (*suspend) (struct avdtp *session,
+                               struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data);
+       gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data);
+       void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream, uint8_t *err,
+                               void *user_data);
+       gboolean (*reconfigure) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       uint8_t *err, void *user_data);
+       gboolean (*delayreport) (struct avdtp *session,
+                                       struct avdtp_local_sep *lsep,
+                                       uint8_t rseid, uint16_t delay,
+                                       uint8_t *err, void *user_data);
+};
+
+typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
+                                       struct avdtp_error *err, void *user_data);
+
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+
+void avdtp_unref(struct avdtp *session);
+struct avdtp *avdtp_ref(struct avdtp *session);
+
+struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
+                                                       void *data, int size);
+
+struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);
+
+int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
+                       void *user_data);
+
+gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream);
+
+unsigned int avdtp_stream_add_cb(struct avdtp *session,
+                                       struct avdtp_stream *stream,
+                                       avdtp_stream_state_cb cb, void *data);
+gboolean avdtp_stream_remove_cb(struct avdtp *session,
+                               struct avdtp_stream *stream,
+                               unsigned int id);
+
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+                                               size_t imtu, size_t omtu);
+gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
+                                       uint16_t *imtu, uint16_t *omtu,
+                                       GSList **caps);
+struct avdtp_service_capability *avdtp_stream_get_codec(
+                                               struct avdtp_stream *stream);
+gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
+                                       GSList *caps);
+struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
+                                               struct avdtp_stream *stream);
+
+int avdtp_set_configuration(struct avdtp *session,
+                               struct avdtp_remote_sep *rsep,
+                               struct avdtp_local_sep *lsep,
+                               GSList *caps,
+                               struct avdtp_stream **stream);
+
+int avdtp_get_configuration(struct avdtp *session,
+                               struct avdtp_stream *stream);
+
+int avdtp_open(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_start(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_close(struct avdtp *session, struct avdtp_stream *stream,
+               gboolean immediate);
+int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream);
+int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
+                                                       uint16_t delay);
+
+struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type,
+                                               uint8_t codec_type,
+                                               gboolean delay_reporting,
+                                               struct avdtp_sep_ind *ind,
+                                               struct avdtp_sep_cfm *cfm,
+                                               void *user_data);
+
+/* Find a matching pair of local and remote SEP ID's */
+struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
+                                               struct avdtp_local_sep *lsep);
+
+int avdtp_unregister_sep(struct avdtp_local_sep *sep);
+
+avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep);
+
+void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id);
+const char *avdtp_strerror(struct avdtp_error *err);
+uint8_t avdtp_error_category(struct avdtp_error *err);
+int avdtp_error_error_code(struct avdtp_error *err);
+int avdtp_error_posix_errno(struct avdtp_error *err);
index 83f20e2..a3145cb 100644 (file)
@@ -60,8 +60,6 @@
 
 static uint16_t option_index = MGMT_INDEX_NONE;
 
-static int notification_sk = -1;
-
 #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 */
@@ -132,8 +130,8 @@ static void adapter_name_changed(const uint8_t *name)
        ev->props[0].len = len;
        memcpy(ev->props->val, name, len);
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
+                                                       sizeof(buf), ev);
 }
 
 static void adapter_set_name(const uint8_t *name)
@@ -173,8 +171,8 @@ static void powered_changed(void)
 
        DBG("%u", ev.state);
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_STATE_CHANGED,
+                                                       sizeof(ev), &ev);
 }
 
 static uint8_t settings2scan_mode(void)
@@ -210,8 +208,8 @@ static void scan_mode_changed(void)
 
        DBG("mode %u", *mode);
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
+                                                       sizeof(buf), buf);
 }
 
 static void adapter_class_changed(void)
@@ -226,8 +224,8 @@ static void adapter_class_changed(void)
        ev->props[0].len = sizeof(uint32_t);
        memcpy(ev->props->val, &adapter.dev_class, sizeof(uint32_t));
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
+                                                       sizeof(buf), buf);
 }
 
 static void settings_changed(uint32_t settings)
@@ -327,8 +325,8 @@ static void send_bond_state_change(const bdaddr_t *addr, uint8_t status,
        ev.state = state;
        bdaddr2android(addr, ev.bdaddr);
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_BOND_STATE_CHANGED,
+                                                       sizeof(ev), &ev);
 }
 
 static void cache_device_name(const bdaddr_t *addr, const char *name)
@@ -408,8 +406,8 @@ static void remote_uuids_callback(struct browse_req *req)
        ev->props[0].len = sizeof(uint128_t) * g_slist_length(req->uuids);
        fill_uuids(req->uuids, ev->props[0].val);
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                               HAL_EV_REMOTE_DEVICE_PROPS, len, ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS,
+                                                               len, ev);
 
        g_free(ev);
 }
@@ -450,6 +448,7 @@ static void update_records(struct browse_req *req, sdp_list_t *recs)
                        memcpy(&uuid128, tmp, sizeof(uuid_t));
                        break;
                default:
+                       sdp_list_free(svcclass, free);
                        continue;
                }
 
@@ -589,8 +588,10 @@ static void send_remote_device_name_prop(const bdaddr_t *bdaddr)
 
        /* Use cached name or bdaddr string */
        name = get_device_name(bdaddr);
-       if (!name)
+       if (!name) {
+               ba2str(bdaddr, dst);
                name = dst;
+       }
 
        ev_len = BASELEN_REMOTE_DEV_PROP + strlen(name);
        ev = g_malloc0(ev_len);
@@ -602,8 +603,8 @@ static void send_remote_device_name_prop(const bdaddr_t *bdaddr)
        ev->props[0].len = strlen(name);
        memcpy(&ev->props[0].val, name, strlen(name));
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_REMOTE_DEVICE_PROPS, sizeof(ev), ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS,
+                                                               ev_len, ev);
 
        g_free(ev);
 }
@@ -636,8 +637,8 @@ static void pin_code_request_callback(uint16_t index, uint16_t length,
        memset(&hal_ev, 0, sizeof(hal_ev));
        bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST,
-                                               sizeof(hal_ev), &hal_ev, -1);
+       ipc_send_notif(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,
@@ -654,8 +655,8 @@ static void send_ssp_request(const bdaddr_t *addr, uint8_t variant,
        ev.pairing_variant = variant;
        ev.passkey = passkey;
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST,
-                                                       sizeof(ev), &ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST,
+                                                       sizeof(ev), &ev);
 }
 
 static void user_confirm_request_callback(uint16_t index, uint16_t length,
@@ -757,8 +758,8 @@ static void mgmt_discovering_event(uint16_t index, uint16_t length,
                cp.state = HAL_DISCOVERY_STATE_STOPPED;
        }
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp), &cp, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH,
+                       HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp), &cp);
 }
 
 static void confirm_device_name(const bdaddr_t *addr, uint8_t addr_type)
@@ -774,131 +775,98 @@ static void confirm_device_name(const bdaddr_t *addr, uint8_t addr_type)
                error("Failed to send confirm name request");
 }
 
-static int fill_device_props(struct hal_property *prop, bdaddr_t *addr,
-                                       uint32_t cod, int8_t rssi, char *name)
-{
-       uint8_t num_props = 0;
 
-       /* fill Class of Device */
-       if (cod) {
-               prop->type = HAL_PROP_DEVICE_CLASS;
-               prop->len = sizeof(cod);
-               memcpy(prop->val, &cod, prop->len);
-               prop = ((void *) prop) + sizeof(*prop) + sizeof(cod);
-               num_props++;
-       }
-
-       /* fill RSSI */
-       if (rssi) {
-               prop->type = HAL_PROP_DEVICE_RSSI;
-               prop->len = sizeof(rssi);
-               memcpy(prop->val, &rssi, prop->len);
-               prop = ((void *) prop) + sizeof(*prop) + sizeof(rssi);
-               num_props++;
-       }
+static int fill_hal_prop(void *buf, uint8_t type, uint16_t len,
+                                                       const void *val)
+{
+       struct hal_property *prop = buf;
 
-       /* fill name */
-       if (name) {
-               prop->type = HAL_PROP_DEVICE_NAME;
-               prop->len = strlen(name);
-               memcpy(prop->val, name, prop->len);
-               prop = ((void *) prop) + sizeof(*prop) + prop->len;
-               num_props++;
-       }
+       prop->type = type;
+       prop->len = len;
+       memcpy(prop->val, val, len);
 
-       return num_props;
+       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)
 {
-       bool is_new_dev = false;
-       size_t props_size = 0;
-       size_t buff_size = 0;
-       void *buf;
+       uint8_t buf[BLUEZ_HAL_MTU];
+       bool new_dev = false;
        struct eir_data eir;
-       GSList *l;
-       bdaddr_t *remote = NULL;
-       int err;
+       uint8_t *num_prop;
+       uint8_t opcode;
+       int size = 0;
 
+       memset(buf, 0, sizeof(buf));
        memset(&eir, 0, sizeof(eir));
 
-       err = eir_parse(&eir, data, data_len);
-       if (err < 0) {
-               error("Error parsing EIR data: %s (%d)", strerror(-err), -err);
-               return;
-       }
-
-       l = g_slist_find_custom(found_devices, bdaddr, bdaddr_cmp);
-       if (l)
-               remote = l->data;
+       eir_parse(&eir, data, data_len);
 
-       if (!remote) {
+       if (!g_slist_find_custom(found_devices, bdaddr, bdaddr_cmp)) {
+               bdaddr_t *new_bdaddr;
                char addr[18];
 
-               remote = g_new0(bdaddr_t, 1);
-               bacpy(remote, bdaddr);
+               new_bdaddr = g_new0(bdaddr_t, 1);
+               bacpy(new_bdaddr, bdaddr);
 
-               found_devices = g_slist_prepend(found_devices, remote);
-               is_new_dev = true;
+               found_devices = g_slist_prepend(found_devices, new_bdaddr);
 
-               ba2str(remote, addr);
+               ba2str(new_bdaddr, addr);
                DBG("New device found: %s", addr);
-       }
-
-       props_size += sizeof(struct hal_property) + sizeof(eir.class);
-       props_size += sizeof(struct hal_property) + sizeof(rssi);
 
-       if (eir.name) {
-               props_size += sizeof(struct hal_property) + strlen(eir.name);
-               cache_device_name(remote, eir.name);
+               new_dev = true;
        }
 
-       if (is_new_dev) {
-               struct hal_ev_device_found *ev = NULL;
-               struct hal_property *prop = NULL;
+       if (new_dev) {
+               struct hal_ev_device_found *ev = (void *) buf;
+               bdaddr_t android_bdaddr;
 
-               /* with new device we also send bdaddr prop */
-               props_size += sizeof(struct hal_property) + sizeof(eir.addr);
+               size += sizeof(*ev);
 
-               buff_size = sizeof(struct hal_ev_device_found) + props_size;
-               buf = g_new0(char, buff_size);
-               ev = buf;
-               prop = ev->props;
+               num_prop = &ev->num_props;
+               opcode = HAL_EV_DEVICE_FOUND;
 
-               /* fill first prop with bdaddr */
-               prop->type = HAL_PROP_DEVICE_ADDR;
-               prop->len = sizeof(bdaddr_t);
-               bdaddr2android(bdaddr, prop->val);
-               prop = ((void *) prop) + sizeof(*prop) + sizeof(bdaddr_t);
-               ev->num_props += 1;
+               bdaddr2android(bdaddr, &android_bdaddr);
 
-               /* fill eir, name, and cod props */
-               ev->num_props += fill_device_props(prop, remote, eir.class,
-                                                               rssi, eir.name);
-
-               ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                               HAL_EV_DEVICE_FOUND, buff_size, ev, -1);
-               g_free(buf);
+               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 = NULL;
+               struct hal_ev_remote_device_props *ev = (void *) buf;
 
-               buff_size = sizeof(*ev) + props_size;
-               buf = g_new0(char, buff_size);
-               ev = buf;
+               size += sizeof(*ev);
 
-               ev->num_props = fill_device_props(ev->props, remote, eir.class,
-                                                               rssi, eir.name);
+               num_prop = &ev->num_props;
+               opcode = HAL_EV_REMOTE_DEVICE_PROPS;
 
                ev->status = HAL_STATUS_SUCCESS;
                bdaddr2android(bdaddr, ev->bdaddr);
+       }
+
+       if (eir.class) {
+               size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
+                                               sizeof(eir.class), &eir.class);
+               (*num_prop)++;
+       }
+
+       if (rssi) {
+               size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
+                                                       sizeof(rssi), &rssi);
+               (*num_prop)++;
+       }
+
+       if (eir.name) {
+               cache_device_name(bdaddr, eir.name);
 
-               ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                               HAL_EV_REMOTE_DEVICE_PROPS, buff_size, ev, -1);
-               g_free(buf);
+               size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
+                                               strlen(eir.name), eir.name);
+               (*num_prop)++;
        }
 
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, opcode, size, buf);
+
        if (confirm) {
                char addr[18];
 
@@ -940,8 +908,8 @@ static void mgmt_device_found_event(uint16_t index, uint16_t length,
        flags = btohl(ev->flags);
 
        ba2str(&ev->addr.bdaddr, addr);
-       DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u eir %u",
-                               index, addr, ev->rssi, flags, eir_len, *eir);
+       DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u",
+                               index, addr, ev->rssi, flags, eir_len);
 
        confirm_name = flags & MGMT_DEV_FOUND_CONFIRM_NAME;
 
@@ -967,8 +935,8 @@ 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(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev, -1);
+       ipc_send_notif(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,
@@ -986,8 +954,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(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ACL_STATE_CHANGED,
+                                               sizeof(hal_ev), &hal_ev);
 }
 
 static uint8_t status_mgmt2hal(uint8_t mgmt)
@@ -1160,7 +1128,7 @@ static void uuid16_to_uint128(uint16_t uuid, uint128_t *u128)
        ntoh128(&uuid128.value.uuid128, u128);
 }
 
-static bool get_uuids(void)
+static uint8_t get_uuids(void)
 {
        struct hal_ev_adapter_props_changed *ev;
        GSList *list = adapter.uuids;
@@ -1193,10 +1161,10 @@ static bool get_uuids(void)
                p += sizeof(uint128_t);
        }
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
+                                                       sizeof(buf), ev);
 
-       return true;
+       return HAL_STATUS_SUCCESS;
 }
 
 static void remove_uuid_complete(uint8_t status, uint16_t length,
@@ -1210,9 +1178,7 @@ static void remove_uuid_complete(uint8_t status, uint16_t length,
 
        mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
 
-       /* send notification only if bluetooth service is registered */
-       if (notification_sk >= 0)
-               get_uuids();
+       get_uuids();
 }
 
 static void remove_uuid(uint16_t uuid)
@@ -1238,9 +1204,7 @@ static void add_uuid_complete(uint8_t status, uint16_t length,
 
        mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
 
-       /* send notification only if bluetooth service is registered */
-       if (notification_sk >= 0)
-               get_uuids();
+       get_uuids();
 }
 
 static void add_uuid(uint8_t svc_hint, uint16_t uuid)
@@ -1392,7 +1356,7 @@ static void set_adapter_name_complete(uint8_t status, uint16_t length,
        adapter_set_name(rp->name);
 }
 
-static uint8_t set_adapter_name(uint8_t *name, uint16_t len)
+static uint8_t set_adapter_name(const uint8_t *name, uint16_t len)
 {
        struct mgmt_cp_set_local_name cp;
 
@@ -1409,8 +1373,17 @@ static uint8_t set_adapter_name(uint8_t *name, uint16_t len)
        return HAL_STATUS_FAILED;
 }
 
-static uint8_t set_discoverable_timeout(uint8_t *timeout)
+static uint8_t set_discoverable_timeout(const void *buf, uint16_t len)
 {
+       const uint32_t *timeout = buf;
+
+       if (len != sizeof(*timeout)) {
+               error("Invalid set disc timeout size (%u bytes), terminating",
+                                                                       len);
+               raise(SIGTERM);
+               return HAL_STATUS_FAILED;
+       }
+
        /* Android handles discoverable timeout in Settings app.
         * There is no need to use kernel feature for that.
         * Just need to store this value here */
@@ -1713,7 +1686,7 @@ static bool set_discoverable(uint8_t mode, uint16_t timeout)
        return false;
 }
 
-static void get_address(void)
+static uint8_t get_address(void)
 {
        uint8_t buf[BASELEN_PROP_CHANGED + sizeof(bdaddr_t)];
        struct hal_ev_adapter_props_changed *ev = (void *) buf;
@@ -1725,67 +1698,69 @@ static void get_address(void)
        ev->props[0].len = sizeof(bdaddr_t);
        bdaddr2android(&adapter.bdaddr, ev->props[0].val);
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
+                                                       sizeof(buf), buf);
+
+       return HAL_STATUS_SUCCESS;
 }
 
-static bool get_name(void)
+static uint8_t get_name(void)
 {
        if (!adapter.name)
-               return false;
+               return HAL_STATUS_FAILED;
 
        adapter_name_changed((uint8_t *) adapter.name);
 
-       return true;
+       return HAL_STATUS_SUCCESS;
 }
 
 
-static bool get_class(void)
+static uint8_t get_class(void)
 {
        DBG("");
 
        adapter_class_changed();
 
-       return true;
+       return HAL_STATUS_SUCCESS;
 }
 
-static bool get_type(void)
+static uint8_t get_type(void)
 {
        DBG("Not implemented");
 
        /* TODO: Add implementation */
 
-       return false;
+       return HAL_STATUS_FAILED;
 }
 
-static bool get_service(void)
+static uint8_t get_service(void)
 {
        DBG("Not implemented");
 
        /* TODO: Add implementation */
 
-       return false;
+       return HAL_STATUS_FAILED;
 }
 
-static bool get_scan_mode(void)
+static uint8_t get_scan_mode(void)
 {
        DBG("");
 
        scan_mode_changed();
 
-       return true;
+       return HAL_STATUS_SUCCESS;
 }
 
-static bool get_devices(void)
+static uint8_t get_devices(void)
 {
        DBG("Not implemented");
 
        /* TODO: Add implementation */
 
-       return false;
+       return HAL_STATUS_FAILED;
 }
 
-static bool get_discoverable_timeout(void)
+static uint8_t get_discoverable_timeout(void)
 {
        struct hal_ev_adapter_props_changed *ev;
        uint8_t buf[BASELEN_PROP_CHANGED + sizeof(uint32_t)];
@@ -1801,39 +1776,51 @@ static bool get_discoverable_timeout(void)
        memcpy(&ev->props[0].val, &adapter.discoverable_timeout,
                                                        sizeof(uint32_t));
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH,
-                       HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED,
+                                                       sizeof(buf), ev);
 
-       return true;
+       return HAL_STATUS_SUCCESS;
 }
 
-static bool get_property(void *buf, uint16_t len)
+static void handle_get_adapter_prop_cmd(const void *buf, uint16_t len)
 {
-       struct hal_cmd_get_adapter_prop *cmd = buf;
+       const struct hal_cmd_get_adapter_prop *cmd = buf;
+       uint8_t status;
 
        switch (cmd->type) {
        case HAL_PROP_ADAPTER_ADDR:
-               get_address();
-               return true;
+               status = get_address();
+               break;
        case HAL_PROP_ADAPTER_NAME:
-               return get_name();
+               status = get_name();
+               break;
        case HAL_PROP_ADAPTER_UUIDS:
-               return get_uuids();
+               status = get_uuids();
+               break;
        case HAL_PROP_ADAPTER_CLASS:
-               return get_class();
+               status = get_class();
+               break;
        case HAL_PROP_ADAPTER_TYPE:
-               return get_type();
+               status = get_type();
+               break;
        case HAL_PROP_ADAPTER_SERVICE_REC:
-               return get_service();
+               status = get_service();
+               break;
        case HAL_PROP_ADAPTER_SCAN_MODE:
-               return get_scan_mode();
+               status = get_scan_mode();
+               break;
        case HAL_PROP_ADAPTER_BONDED_DEVICES:
-               return get_devices();
+               status = get_devices();
+               break;
        case HAL_PROP_ADAPTER_DISC_TIMEOUT:
-               return get_discoverable_timeout();
+               status = get_discoverable_timeout();
+               break;
        default:
-               return false;
+               status = HAL_STATUS_FAILED;
+               break;
        }
+
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, status);
 }
 
 static void get_properties(void)
@@ -1889,11 +1876,18 @@ static bool stop_discovery(void)
        return false;
 }
 
-static uint8_t set_scan_mode(void *buf, uint16_t len)
+static uint8_t set_scan_mode(const void *buf, uint16_t len)
 {
-       uint8_t *mode = buf;
+       const uint8_t *mode = buf;
        bool conn, disc, cur_conn, cur_disc;
 
+       if (len != sizeof(*mode)) {
+               error("Invalid set scan mode size (%u bytes), terminating",
+                                                               len);
+               raise(SIGTERM);
+               return HAL_STATUS_FAILED;
+       }
+
        cur_conn = adapter.current_settings & MGMT_SETTING_CONNECTABLE;
        cur_disc = adapter.current_settings & MGMT_SETTING_DISCOVERABLE;
 
@@ -1931,7 +1925,7 @@ static uint8_t set_scan_mode(void *buf, uint16_t len)
                        return HAL_STATUS_FAILED;
        }
 
-       if (cur_disc != disc) {
+       if (cur_disc != disc && conn) {
                if (!set_discoverable(disc ? 0x01 : 0x00, 0))
                        return HAL_STATUS_FAILED;
        }
@@ -1945,21 +1939,35 @@ done:
        return HAL_STATUS_DONE;
 }
 
-static uint8_t set_property(void *buf, uint16_t len)
+static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len)
 {
-       struct hal_cmd_set_adapter_prop *cmd = buf;
+       const struct hal_cmd_set_adapter_prop *cmd = buf;
+       uint8_t status;
+
+       if (len != sizeof(*cmd) + cmd->len) {
+               error("Invalid set adapter prop cmd (0x%x), terminating",
+                                                               cmd->type);
+               raise(SIGTERM);
+               return;
+       }
 
        switch (cmd->type) {
        case HAL_PROP_ADAPTER_SCAN_MODE:
-               return set_scan_mode(cmd->val, cmd->len);
+               status = set_scan_mode(cmd->val, cmd->len);
+               break;
        case HAL_PROP_ADAPTER_NAME:
-               return set_adapter_name(cmd->val, cmd->len);
+               status = set_adapter_name(cmd->val, cmd->len);
+               break;
        case HAL_PROP_ADAPTER_DISC_TIMEOUT:
-               return set_discoverable_timeout(cmd->val);
+               status = set_discoverable_timeout(cmd->val, cmd->len);
+               break;
        default:
                DBG("Unhandled property type 0x%x", cmd->type);
-               return HAL_STATUS_FAILED;
+               status = HAL_STATUS_FAILED;
+               break;
        }
+
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, status);
 }
 
 static void pair_device_complete(uint8_t status, uint16_t length,
@@ -1978,9 +1986,10 @@ static void pair_device_complete(uint8_t status, uint16_t length,
                                                        HAL_BOND_STATE_NONE);
 }
 
-static bool create_bond(void *buf, uint16_t len)
+static void handle_create_bond_cmd(const void *buf, uint16_t len)
 {
-       struct hal_cmd_create_bond *cmd = buf;
+       const struct hal_cmd_create_bond *cmd = buf;
+       uint8_t status;
        struct mgmt_cp_pair_device cp;
 
        cp.io_cap = DEFAULT_IO_CAPABILITY;
@@ -1988,25 +1997,36 @@ static bool create_bond(void *buf, uint16_t len)
        android2bdaddr(cmd->bdaddr, &cp.addr.bdaddr);
 
        if (mgmt_send(mgmt_if, MGMT_OP_PAIR_DEVICE, adapter.index, sizeof(cp),
-                               &cp, pair_device_complete, NULL, NULL) == 0)
-               return false;
+                               &cp, pair_device_complete, NULL, NULL) == 0) {
+               status = HAL_STATUS_FAILED;
+               goto fail;
+       }
+
+       status = HAL_STATUS_SUCCESS;
 
        set_device_bond_state(&cp.addr.bdaddr, HAL_STATUS_SUCCESS,
                                                HAL_BOND_STATE_BONDING);
 
-       return true;
+fail:
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, status);
 }
 
-static bool cancel_bond(void *buf, uint16_t len)
+static void handle_cancel_bond_cmd(const void *buf, uint16_t len)
 {
-       struct hal_cmd_cancel_bond *cmd = buf;
+       const struct hal_cmd_cancel_bond *cmd = buf;
        struct mgmt_addr_info cp;
+       uint8_t status;
 
        cp.type = BDADDR_BREDR;
        android2bdaddr(cmd->bdaddr, &cp.bdaddr);
 
-       return mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index,
-                                       sizeof(cp), &cp, NULL, NULL, NULL) > 0;
+       if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index,
+                                       sizeof(cp), &cp, NULL, NULL, NULL) > 0)
+               status = HAL_STATUS_SUCCESS;
+       else
+               status = HAL_STATUS_FAILED;
+
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, status);
 }
 
 static void unpair_device_complete(uint8_t status, uint16_t length,
@@ -2023,23 +2043,30 @@ static void unpair_device_complete(uint8_t status, uint16_t length,
                                                        HAL_BOND_STATE_NONE);
 }
 
-static bool remove_bond(void *buf, uint16_t len)
+static void handle_remove_bond_cmd(const void *buf, uint16_t len)
 {
-       struct hal_cmd_remove_bond *cmd = buf;
+       const struct hal_cmd_remove_bond *cmd = buf;
        struct mgmt_cp_unpair_device cp;
+       uint8_t status;
 
        cp.disconnect = 1;
        cp.addr.type = BDADDR_BREDR;
        android2bdaddr(cmd->bdaddr, &cp.addr.bdaddr);
 
-       return mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
+       if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index,
                                sizeof(cp), &cp, unpair_device_complete,
-                               NULL, NULL) > 0;
+                               NULL, NULL) > 0)
+               status = HAL_STATUS_SUCCESS;
+       else
+               status = HAL_STATUS_FAILED;
+
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, status);
 }
 
-static uint8_t pin_reply(void *buf, uint16_t len)
+static void handle_pin_reply_cmd(const void *buf, uint16_t len)
 {
-       struct hal_cmd_pin_reply *cmd = buf;
+       const struct hal_cmd_pin_reply *cmd = buf;
+       uint8_t status;
        bdaddr_t bdaddr;
        char addr[18];
 
@@ -2048,8 +2075,10 @@ static uint8_t pin_reply(void *buf, uint16_t len)
 
        DBG("%s accept %u pin_len %u", addr, cmd->accept, cmd->pin_len);
 
-       if (!cmd->accept && cmd->pin_len)
-               return HAL_STATUS_INVALID;
+       if (!cmd->accept && cmd->pin_len) {
+               status = HAL_STATUS_INVALID;
+               goto failed;
+       }
 
        if (cmd->accept) {
                struct mgmt_cp_pin_code_reply rp;
@@ -2062,8 +2091,10 @@ static uint8_t pin_reply(void *buf, uint16_t len)
                memcpy(rp.pin_code, cmd->pin_code, rp.pin_len);
 
                if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_REPLY, adapter.index,
-                               sizeof(rp), &rp, NULL, NULL, NULL) == 0)
-                       return HAL_STATUS_FAILED;
+                               sizeof(rp), &rp, NULL, NULL, NULL) == 0) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
        } else {
                struct mgmt_cp_pin_code_neg_reply rp;
 
@@ -2072,11 +2103,15 @@ static uint8_t pin_reply(void *buf, uint16_t len)
 
                if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_NEG_REPLY,
                                                adapter.index, sizeof(rp), &rp,
-                                               NULL, NULL, NULL) == 0)
-                       return HAL_STATUS_FAILED;
+                                               NULL, NULL, NULL) == 0) {
+                       status = HAL_STATUS_FAILED;
+                       goto failed;
+               }
        }
 
-       return HAL_STATUS_SUCCESS;
+       status = HAL_STATUS_SUCCESS;
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, status);
 }
 
 static uint8_t user_confirm_reply(const bdaddr_t *bdaddr, bool accept)
@@ -2133,11 +2168,11 @@ static uint8_t user_passkey_reply(const bdaddr_t *bdaddr, bool accept,
        return HAL_STATUS_SUCCESS;
 }
 
-static uint8_t ssp_reply(void *buf, uint16_t len)
+static void handle_ssp_reply_cmd(const void *buf, uint16_t len)
 {
-       struct hal_cmd_ssp_reply *cmd = buf;
-       uint8_t status;
+       const struct hal_cmd_ssp_reply *cmd = buf;
        bdaddr_t bdaddr;
+       uint8_t status;
        char addr[18];
 
        /* TODO should parameters sanity be verified here? */
@@ -2164,150 +2199,226 @@ static uint8_t ssp_reply(void *buf, uint16_t len)
                break;
        }
 
-       return status;
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, status);
 }
 
-static uint8_t get_remote_services(void *buf, uint16_t len)
+static void handle_get_remote_services_cmd(const void *buf, uint16_t len)
 {
-       struct hal_cmd_get_remote_services *cmd = buf;
+       const struct hal_cmd_get_remote_services *cmd = buf;
+       uint8_t status;
        bdaddr_t addr;
 
        android2bdaddr(&cmd->bdaddr, &addr);
 
-       return browse_remote_sdp(&addr);
+       status = browse_remote_sdp(&addr);
+
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES,
+                                                                       status);
 }
 
-void bt_bluetooth_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len)
+static void handle_enable_cmd(const void *buf, uint16_t len)
 {
-       uint8_t status = HAL_STATUS_FAILED;
+       uint8_t status;
 
-       switch (opcode) {
-       case HAL_OP_ENABLE:
-               /* Framework expects all properties to be emitted while
-                * enabling adapter */
-               get_properties();
+       /* Framework expects all properties to be emitted while
+        * enabling adapter */
+       get_properties();
 
-               if (adapter.current_settings & MGMT_SETTING_POWERED) {
-                       status = HAL_STATUS_DONE;
-                       goto error;
-               }
+       if (adapter.current_settings & MGMT_SETTING_POWERED) {
+               status = HAL_STATUS_DONE;
+               goto failed;
+       }
 
-               if (!set_mode(MGMT_OP_SET_POWERED, 0x01))
-                       goto error;
+       if (!set_mode(MGMT_OP_SET_POWERED, 0x01)) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
-               break;
-       case HAL_OP_DISABLE:
-               if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
-                       status = HAL_STATUS_DONE;
-                       goto error;
-               }
+       status = HAL_STATUS_SUCCESS;
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status);
+}
 
-               if (!set_mode(MGMT_OP_SET_POWERED, 0x00))
-                       goto error;
+static void handle_disable_cmd(const void *buf, uint16_t len)
+{
+       uint8_t status;
 
-               break;
-       case HAL_OP_GET_ADAPTER_PROPS:
-               get_properties();
+       if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+               status = HAL_STATUS_DONE;
+               goto failed;
+       }
 
-               break;
-       case HAL_OP_GET_ADAPTER_PROP:
-               if (!get_property(buf, len))
-                       goto error;
+       if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
-               break;
-       case HAL_OP_SET_ADAPTER_PROP:
-               status = set_property(buf, len);
-               if (status != HAL_STATUS_SUCCESS && status != HAL_STATUS_DONE)
-                       goto error;
+       status = HAL_STATUS_SUCCESS;
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status);
+}
 
-               break;
-       case HAL_OP_CREATE_BOND:
-               if (!create_bond(buf, len))
-                       goto error;
+static void handle_get_adapter_props_cmd(const void *buf, uint16_t len)
+{
+       get_properties();
 
-               break;
-       case HAL_OP_CANCEL_BOND:
-               if (!cancel_bond(buf, len))
-                       goto error;
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS,
+                                                       HAL_STATUS_SUCCESS);
+}
 
-               break;
-       case HAL_OP_REMOVE_BOND:
-               if (!remove_bond(buf, len))
-                       goto error;
+static void handle_get_remote_device_props_cmd(const void *buf, uint16_t len)
+{
+       /* TODO */
 
-               break;
-       case HAL_OP_PIN_REPLY:
-               status = pin_reply(buf, len);
-               if (status != HAL_STATUS_SUCCESS)
-                       goto error;
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROPS,
+                                                       HAL_STATUS_FAILED);
+}
 
-               break;
-       case HAL_OP_SSP_REPLY:
-               status = ssp_reply(buf, len);
-               if (status != HAL_STATUS_SUCCESS)
-                       goto error;
-               break;
-       case HAL_OP_START_DISCOVERY:
-               if (adapter.discovering) {
-                       status = HAL_STATUS_DONE;
-                       goto error;
-               }
+static void handle_get_remote_device_prop_cmd(const void *buf, uint16_t len)
+{
+       /* TODO */
 
-               if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
-                       status = HAL_STATUS_NOT_READY;
-                       goto error;
-               }
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROP,
+                                                       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;
+       uint8_t status;
+
+       if (len != sizeof(*cmd) + cmd->len) {
+               error("Invalid set remote device prop cmd (0x%x), terminating",
+                                                               cmd->type);
+               raise(SIGTERM);
+               return;
+       }
 
-               if (!start_discovery())
-                       goto error;
+       /* TODO */
 
+       switch (cmd->type) {
+       default:
+               DBG("Unhandled property type 0x%x", cmd->type);
+               status = HAL_STATUS_FAILED;
                break;
-       case HAL_OP_CANCEL_DISCOVERY:
-               if (!adapter.discovering) {
-                       status = HAL_STATUS_DONE;
-                       goto error;
-               }
+       }
 
-               if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
-                       status = HAL_STATUS_NOT_READY;
-                       goto error;
-               }
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_REMOTE_DEVICE_PROP,
+                                                                       status);
+}
 
-               if (!stop_discovery())
-                       goto error;
+static void handle_get_remote_service_rec_cmd(const void *buf, uint16_t len)
+{
+       /* TODO */
 
-               break;
-       case HAL_OP_GET_REMOTE_SERVICES:
-               status = get_remote_services(buf, len);
-               if (status != HAL_STATUS_SUCCESS)
-                       goto error;
-               break;
-       default:
-               DBG("Unhandled command, opcode 0x%x", opcode);
-               goto error;
+       ipc_send_rsp(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;
        }
 
-       ipc_send(sk, HAL_SERVICE_ID_BLUETOOTH, opcode, 0, NULL, -1);
-       return;
+       if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+               status = HAL_STATUS_NOT_READY;
+               goto failed;
+       }
 
-error:
-       error("Error handling command 0x%02x status %u", opcode, status);
+       if (!start_discovery()) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
-       ipc_send_rsp(sk, HAL_SERVICE_ID_BLUETOOTH, status);
+       status = HAL_STATUS_SUCCESS;
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, status);
 }
 
-bool bt_bluetooth_register(int sk)
+static void handle_cancel_discovery_cmd(const void *buf, uint16_t len)
 {
-       DBG("");
+       uint8_t status;
 
-       notification_sk = sk;
+       if (!adapter.discovering) {
+               status = HAL_STATUS_DONE;
+               goto failed;
+       }
 
-       return true;
+       if (!(adapter.current_settings & MGMT_SETTING_POWERED)) {
+               status = HAL_STATUS_NOT_READY;
+               goto failed;
+       }
+
+       if (!stop_discovery()) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, status);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+       /* HAL_OP_ENABLE */
+       { handle_enable_cmd, false, 0 },
+       /* HAL_OP_DISABLE */
+       { handle_disable_cmd, false, 0 },
+       /* HAL_OP_GET_ADAPTER_PROPS */
+       { handle_get_adapter_props_cmd, false, 0 },
+       /* HAL_OP_GET_ADAPTER_PROP */
+       { handle_get_adapter_prop_cmd, false,
+                               sizeof(struct hal_cmd_get_adapter_prop) },
+       /* HAL_OP_SET_ADAPTER_PROP */
+       { handle_set_adapter_prop_cmd, true,
+                               sizeof(struct hal_cmd_set_adapter_prop) },
+       /* HAL_OP_GET_REMOTE_DEVICE_PROPS */
+       { handle_get_remote_device_props_cmd, false,
+                       sizeof(struct hal_cmd_get_remote_device_props) },
+       /* HAL_OP_GET_REMOTE_DEVICE_PROP */
+       { handle_get_remote_device_prop_cmd, false,
+                               sizeof(struct hal_cmd_get_remote_device_prop) },
+       /* HAL_OP_SET_REMOTE_DEVICE_PROP */
+       { handle_set_remote_device_prop_cmd, true,
+                               sizeof(struct hal_cmd_set_remote_device_prop) },
+       /* HAL_OP_GET_REMOTE_SERVICE_REC */
+       { handle_get_remote_service_rec_cmd, false,
+                               sizeof(struct hal_cmd_get_remote_service_rec) },
+       /* HAL_OP_GET_REMOTE_SERVICES */
+       { handle_get_remote_services_cmd, false,
+                               sizeof(struct hal_cmd_get_remote_services) },
+       /* HAL_OP_START_DISCOVERY */
+       { handle_start_discovery_cmd, false, 0 },
+       /* HAL_OP_CANCEL_DISCOVERY */
+       { handle_cancel_discovery_cmd, false, 0 },
+       /* HAL_OP_CREATE_BOND */
+       { handle_create_bond_cmd, false, sizeof(struct hal_cmd_create_bond) },
+       /* HAL_OP_REMOVE_BOND */
+       { handle_remove_bond_cmd, false, sizeof(struct hal_cmd_remove_bond) },
+       /* HAL_OP_CANCEL_BOND */
+       {handle_cancel_bond_cmd, false, sizeof(struct hal_cmd_cancel_bond) },
+       /* HAL_OP_PIN_REPLY */
+       { 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) },
+};
+
+void bt_bluetooth_register(void)
+{
+       DBG("");
+
+       ipc_register(HAL_SERVICE_ID_BLUETOOTH, cmd_handlers,
+                                               G_N_ELEMENTS(cmd_handlers));
 }
 
 void bt_bluetooth_unregister(void)
 {
        DBG("");
 
-       notification_sk = -1;
+       ipc_unregister(HAL_SERVICE_ID_CORE);
 }
index 44b8e9e..86872ee 100644 (file)
@@ -31,7 +31,7 @@ void bt_bluetooth_cleanup(void);
 
 void bt_bluetooth_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len);
 
-bool bt_bluetooth_register(int sk);
+void bt_bluetooth_register(void);
 void bt_bluetooth_unregister(void);
 
 int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint);
index 2cd06e8..5394a5d 100644 (file)
@@ -35,6 +35,10 @@ ENDMAP
 static int listen_fd[MAX_LISTEN_FD];
 static int listen_fd_count;
 
+static const char * const uuids[] = {
+       "00001101", "00001105", "0000112f", NULL
+};
+
 /*
  * This function reads data from file descriptor and
  * prints it to the user
@@ -130,6 +134,7 @@ static void read_accepted(int fd)
 
        memset(&msg, 0, sizeof(msg));
        memset(&iv, 0, sizeof(iv));
+       memset(cmsgbuf, 0, sizeof(cmsgbuf));
 
        iv.iov_base = &cs;
        iv.iov_len = sizeof(cs);
@@ -192,6 +197,9 @@ static void listen_c(int argc, const char **argv, enum_func *enum_func,
        if (argc == 3) {
                *user = TYPE_ENUM(btsock_type_t);
                *enum_func = enum_defines;
+       } else if (argc == 5) {
+               *user = (void *) uuids;
+               *enum_func = enum_strings;
        }
 }
 
@@ -263,6 +271,9 @@ static void connect_c(int argc, const char **argv, enum_func *enum_func,
        } else if (argc == 4) {
                *user = TYPE_ENUM(btsock_type_t);
                *enum_func = enum_defines;
+       } else if (argc == 5) {
+               *user = (void *) uuids;
+               *enum_func = enum_strings;
        }
 }
 
@@ -327,9 +338,9 @@ static void connect_p(int argc, const char **argv)
 /* Methods available in btsock_interface_t */
 static struct method methods[] = {
        STD_METHODCH(listen,
-                       "<sock_type> <srvc_name> <uuid> [<channle>] [<flags>]"),
+                       "<sock_type> <srvc_name> <uuid> [<channel>] [<flags>]"),
        STD_METHODCH(connect,
-                       "<addr> <sock_type> <uuid> <channle> [<flags>]"),
+                       "<addr> <sock_type> <uuid> <channel> [<flags>]"),
        END_METHOD
 };
 
index e9fadb7..c898995 100644 (file)
@@ -31,7 +31,7 @@ static bool interface_ready(void)
        return cbs != NULL;
 }
 
-static void handle_conn_state(void *buf)
+static void handle_conn_state(void *buf, uint16_t len)
 {
        struct hal_ev_a2dp_conn_state *ev = buf;
 
@@ -40,7 +40,7 @@ static void handle_conn_state(void *buf)
                                                (bt_bdaddr_t *) (ev->bdaddr));
 }
 
-static void handle_audio_state(void *buf)
+static void handle_audio_state(void *buf, uint16_t len)
 {
        struct hal_ev_a2dp_audio_state *ev = buf;
 
@@ -48,24 +48,20 @@ static void handle_audio_state(void *buf)
                cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr));
 }
 
-/* will be called from notification thread context */
-void bt_notify_a2dp(uint8_t opcode, void *buf, uint16_t len)
-{
-       if (!interface_ready())
-               return;
-
-       switch (opcode) {
-       case HAL_EV_A2DP_CONN_STATE:
-               handle_conn_state(buf);
-               break;
-       case HAL_EV_A2DP_AUDIO_STATE:
-               handle_audio_state(buf);
-               break;
-       default:
-               DBG("Unhandled callback opcode=0x%x", opcode);
-               break;
-       }
-}
+/* 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,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_a2dp_conn_state),
+       },
+       {       /* HAL_EV_A2DP_AUDIO_STATE */
+               .handler = handle_audio_state,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_a2dp_audio_state),
+       },
+};
 
 static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr)
 {
@@ -100,15 +96,29 @@ static bt_status_t disconnect(bt_bdaddr_t *bd_addr)
 static bt_status_t init(btav_callbacks_t *callbacks)
 {
        struct hal_cmd_register_module cmd;
+       int ret;
 
        DBG("");
 
+       if (interface_ready())
+               return BT_STATUS_DONE;
+
        cbs = callbacks;
 
+       hal_ipc_register(HAL_SERVICE_ID_A2DP, ev_handlers,
+                               sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
        cmd.service_id = HAL_SERVICE_ID_A2DP;
 
-       return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+       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_A2DP);
+       }
+
+       return ret;
 }
 
 static void cleanup()
@@ -126,6 +136,8 @@ static void cleanup()
 
        hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
                                        sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       hal_ipc_unregister(HAL_SERVICE_ID_A2DP);
 }
 
 static btav_interface_t iface = {
index 078d537..7cac15c 100644 (file)
 
 static const bt_callbacks_t *bt_hal_cbacks = NULL;
 
-#define create_enum_prop(prop, hal_prop, type) do { \
-       type *pe = malloc(sizeof(type)); \
-       prop.val = pe; \
-       prop.len = sizeof(*pe); \
-       *pe = *((uint8_t *) (hal_prop->val)); \
+#define enum_prop_to_hal(prop, hal_prop, type) do { \
+       static type e; \
+       prop.val = &e; \
+       prop.len = sizeof(e); \
+       e = *((uint8_t *) (hal_prop->val)); \
 } while (0)
 
-static void handle_adapter_state_changed(void *buf)
+#define enum_prop_from_hal(prop, hal_len, hal_val, enum_type) do { \
+       enum_type e; \
+       if (prop->len != sizeof(e)) { \
+               error("invalid HAL property %u (%u vs %zu), aborting ", \
+                                       prop->type, prop->len, sizeof(e)); \
+               exit(EXIT_FAILURE); \
+       } \
+       memcpy(&e, prop->val, sizeof(e)); \
+       *((uint8_t *) hal_val) = e; /* enums are mapped to 1 byte */ \
+       *hal_len = 1; \
+} while (0)
+
+static void handle_adapter_state_changed(void *buf, uint16_t len)
 {
        struct hal_ev_adapter_state_changed *ev = buf;
 
@@ -46,105 +58,109 @@ static void handle_adapter_state_changed(void *buf)
 }
 
 static void adapter_props_to_hal(bt_property_t *send_props,
-                                       struct hal_property *hal_prop,
-                                       uint8_t num_props, void *buff_end)
+                                       struct hal_property *prop,
+                                       uint8_t num_props, uint16_t len)
 {
-       void *p = hal_prop;
+       void *buf = prop;
        uint8_t i;
 
        for (i = 0; i < num_props; i++) {
-               if (p + sizeof(*hal_prop) + hal_prop->len > buff_end) {
-                       error("invalid adapter properties event, aborting");
+               if (sizeof(*prop) + prop->len > len) {
+                       error("invalid adapter properties(%zu > %u), aborting",
+                                       sizeof(*prop) + prop->len, len);
                        exit(EXIT_FAILURE);
                }
 
-               send_props[i].type = hal_prop->type;
+               send_props[i].type = prop->type;
 
-               switch (hal_prop->type) {
+               switch (prop->type) {
                case HAL_PROP_ADAPTER_TYPE:
-                       create_enum_prop(send_props[i], hal_prop,
+                       enum_prop_to_hal(send_props[i], prop,
                                                        bt_device_type_t);
                        break;
                case HAL_PROP_ADAPTER_SCAN_MODE:
-                       create_enum_prop(send_props[i], hal_prop,
+                       enum_prop_to_hal(send_props[i], prop,
                                                        bt_scan_mode_t);
                        break;
                case HAL_PROP_ADAPTER_SERVICE_REC:
                default:
-                       send_props[i].len = hal_prop->len;
-                       send_props[i].val = hal_prop->val;
+                       send_props[i].len = prop->len;
+                       send_props[i].val = prop->val;
                        break;
                }
 
                DBG("prop[%d]: %s", i, btproperty2str(&send_props[i]));
+
+               len -= sizeof(*prop) + prop->len;
+               buf += sizeof(*prop) + prop->len;
+               prop = buf;
        }
+
+       if (!len)
+               return;
+
+       error("invalid adapter properties (%u bytes left), aborting", len);
+       exit(EXIT_FAILURE);
 }
 
-static void adapter_hal_props_cleanup(bt_property_t *props, uint8_t num)
+static void adapter_prop_from_hal(const bt_property_t *property, uint8_t *type,
+                                               uint16_t *len, void *val)
 {
-       uint8_t i;
+       /* type match IPC type */
+       *type = property->type;
 
-       for (i = 0; i < num; i++) {
-               switch (props[i].type) {
-               case HAL_PROP_ADAPTER_TYPE:
-               case HAL_PROP_ADAPTER_SCAN_MODE:
-                       free(props[i].val);
-                       break;
-               default:
-                       break;
-               }
+       switch(property->type) {
+       case HAL_PROP_ADAPTER_SCAN_MODE:
+               enum_prop_from_hal(property, len, val, bt_scan_mode_t);
+               break;
+       default:
+               *len = property->len;
+               memcpy(val, property->val, property->len);
+               break;
        }
 }
 
 static void device_props_to_hal(bt_property_t *send_props,
-                                       struct hal_property *hal_prop,
-                                       uint8_t num_props, void *buff_end)
+                               struct hal_property *prop, uint8_t num_props,
+                               uint16_t len)
 {
-       void *p = hal_prop;
+       void *buf = prop;
        uint8_t i;
 
        for (i = 0; i < num_props; i++) {
-               if (p + sizeof(*hal_prop) + hal_prop->len > buff_end) {
-                       error("invalid adapter properties event, aborting");
+               if (sizeof(*prop) + prop->len > len) {
+                       error("invalid device properties (%zu > %u), aborting",
+                                       sizeof(*prop) + prop->len, len);
                        exit(EXIT_FAILURE);
                }
 
-               send_props[i].type = hal_prop->type;
+               send_props[i].type = prop->type;
 
-               switch (hal_prop->type) {
+               switch (prop->type) {
                case HAL_PROP_DEVICE_TYPE:
-                       create_enum_prop(send_props[i], hal_prop,
+                       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:
                default:
-                       send_props[i].len = hal_prop->len;
-                       send_props[i].val = hal_prop->val;
+                       send_props[i].len = prop->len;
+                       send_props[i].val = prop->val;
                        break;
                }
 
-               p += sizeof(*hal_prop) + hal_prop->len;
-               hal_prop = p;
+               len -= sizeof(*prop) + prop->len;
+               buf += sizeof(*prop) + prop->len;
+               prop = buf;
 
                DBG("prop[%d]: %s", i, btproperty2str(&send_props[i]));
        }
-}
-
 
-static void device_hal_props_cleanup(bt_property_t *props, uint8_t num)
-{
-       uint8_t i;
+       if (!len)
+               return;
 
-       for (i = 0; i < num; i++) {
-               switch (props[i].type) {
-               case HAL_PROP_DEVICE_TYPE:
-                       free(props[i].val);
-                       break;
-               default:
-                       break;
-               }
-       }
+       error("invalid device properties (%u bytes left), aborting", len);
+       exit(EXIT_FAILURE);
 }
 
 static void handle_adapter_props_changed(void *buf, uint16_t len)
@@ -157,14 +173,13 @@ static void handle_adapter_props_changed(void *buf, uint16_t len)
        if (!bt_hal_cbacks->adapter_properties_cb)
                return;
 
-       adapter_props_to_hal(props, ev->props, ev->num_props, buf + len);
+       len -= sizeof(*ev);
+       adapter_props_to_hal(props, ev->props, ev->num_props, len);
 
        bt_hal_cbacks->adapter_properties_cb(ev->status, ev->num_props, props);
-
-       adapter_hal_props_cleanup(props, ev->num_props);
 }
 
-static void handle_bond_state_change(void *buf)
+static void handle_bond_state_change(void *buf, uint16_t len)
 {
        struct hal_ev_bond_state_changed *ev = buf;
        bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
@@ -176,7 +191,7 @@ static void handle_bond_state_change(void *buf)
                                                                ev->state);
 }
 
-static void handle_pin_request(void *buf)
+static void handle_pin_request(void *buf, uint16_t len)
 {
        struct hal_ev_pin_request *ev = buf;
        /* Those are declared as packed, so it's safe to assign pointers */
@@ -189,7 +204,7 @@ static void handle_pin_request(void *buf)
                bt_hal_cbacks->pin_request_cb(addr, name, ev->class_of_dev);
 }
 
-static void handle_ssp_request(void *buf)
+static void handle_ssp_request(void *buf, uint16_t len)
 {
        struct hal_ev_ssp_request *ev = buf;
        /* Those are declared as packed, so it's safe to assign pointers */
@@ -221,7 +236,7 @@ static bool interface_ready(void)
        return bt_hal_cbacks != NULL;
 }
 
-static void handle_discovery_state_changed(void *buf)
+static void handle_discovery_state_changed(void *buf, uint16_t len)
 {
        struct hal_ev_discovery_state_changed *ev = buf;
 
@@ -241,11 +256,10 @@ static void handle_device_found(void *buf, uint16_t len)
        if (!bt_hal_cbacks->device_found_cb)
                return;
 
-       device_props_to_hal(props, ev->props, ev->num_props, buf + len);
+       len -= sizeof(*ev);
+       device_props_to_hal(props, ev->props, ev->num_props, len);
 
        bt_hal_cbacks->device_found_cb(ev->num_props, props);
-
-       device_hal_props_cleanup(props, ev->num_props);
 }
 
 static void handle_device_state_changed(void *buf, uint16_t len)
@@ -258,16 +272,15 @@ static void handle_device_state_changed(void *buf, uint16_t len)
        if (!bt_hal_cbacks->remote_device_properties_cb)
                return;
 
-       device_props_to_hal(props, ev->props, ev->num_props, buf + len);
+       len -= sizeof(*ev);
+       device_props_to_hal(props, ev->props, ev->num_props, len);
 
        bt_hal_cbacks->remote_device_properties_cb(ev->status,
                                                (bt_bdaddr_t *)ev->bdaddr,
                                                ev->num_props, props);
-
-       device_hal_props_cleanup(props, ev->num_props);
 }
 
-static void handle_acl_state_changed(void *buf)
+static void handle_acl_state_changed(void *buf, uint16_t len)
 {
        struct hal_ev_acl_state_changed *ev = buf;
        bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr;
@@ -279,47 +292,78 @@ static void handle_acl_state_changed(void *buf)
                                                                ev->state);
 }
 
-/* will be called from notification thread context */
-void bt_notify_adapter(uint8_t opcode, void *buf, uint16_t len)
+static void handle_dut_mode_receive(void *buf, uint16_t len)
 {
-       if (!interface_ready())
-               return;
+       struct hal_ev_dut_mode_receive *ev = buf;
 
-       DBG("opcode 0x%x", opcode);
+       DBG("");
 
-       switch (opcode) {
-       case HAL_EV_ADAPTER_STATE_CHANGED:
-               handle_adapter_state_changed(buf);
-               break;
-       case HAL_EV_ADAPTER_PROPS_CHANGED:
-               handle_adapter_props_changed(buf, len);
-               break;
-       case HAL_EV_DISCOVERY_STATE_CHANGED:
-               handle_discovery_state_changed(buf);
-               break;
-       case HAL_EV_DEVICE_FOUND:
-               handle_device_found(buf, len);
-               break;
-       case HAL_EV_REMOTE_DEVICE_PROPS:
-               handle_device_state_changed(buf, len);
-               break;
-       case HAL_EV_BOND_STATE_CHANGED:
-               handle_bond_state_change(buf);
-               break;
-       case HAL_EV_PIN_REQUEST:
-               handle_pin_request(buf);
-               break;
-       case HAL_EV_SSP_REQUEST:
-               handle_ssp_request(buf);
-               break;
-       case HAL_EV_ACL_STATE_CHANGED:
-               handle_acl_state_changed(buf);
-               break;
-       default:
-               DBG("Unhandled callback opcode=0x%x", opcode);
-               break;
+       if (len != sizeof(*ev) + ev->len) {
+               error("invalid dut mode receive event (%u), aborting", len);
+               exit(EXIT_FAILURE);
        }
-}
+
+       if (bt_hal_cbacks->dut_mode_recv_cb)
+               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 const struct hal_ipc_handler ev_handlers[] = {
+       {       /* HAL_EV_ADAPTER_STATE_CHANGED */
+               .handler = handle_adapter_state_changed,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_adapter_state_changed)
+       },
+       {       /* HAL_EV_ADAPTER_PROPS_CHANGED */
+               .handler = handle_adapter_props_changed,
+               .var_len = true,
+               .data_len = sizeof(struct hal_ev_adapter_props_changed) +
+                                               sizeof(struct hal_property),
+       },
+       {       /* HAL_EV_REMOTE_DEVICE_PROPS */
+               .handler = handle_device_state_changed,
+               .var_len = true,
+               .data_len = sizeof(struct hal_ev_remote_device_props) +
+                                               sizeof(struct hal_property),
+       },
+       {       /* HAL_EV_DEVICE_FOUND */
+               .handler = handle_device_found,
+               .var_len = true,
+               .data_len = sizeof(struct hal_ev_device_found) +
+                                               sizeof(struct hal_property),
+       },
+       {       /* HAL_EV_DISCOVERY_STATE_CHANGED */
+               .handler = handle_discovery_state_changed,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_discovery_state_changed),
+       },
+       {       /* HAL_EV_PIN_REQUEST */
+               .handler = handle_pin_request,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_pin_request),
+       },
+       {       /* HAL_EV_SSP_REQUEST */
+               .handler = handle_ssp_request,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_ssp_request),
+       },
+       {       /* HAL_EV_BOND_STATE_CHANGED */
+               .handler = handle_bond_state_change,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_bond_state_changed),
+       },
+       {       /* HAL_EV_ACL_STATE_CHANGED */
+               .handler = handle_acl_state_changed,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_acl_state_changed),
+       },
+       {       /* HAL_EV_DUT_MODE_RECEIVE */
+               .handler = handle_dut_mode_receive,
+               .var_len = true,
+               .data_len = sizeof(struct hal_ev_dut_mode_receive),
+       },
+};
 
 static int init(bt_callbacks_t *callbacks)
 {
@@ -329,10 +373,13 @@ static int init(bt_callbacks_t *callbacks)
        DBG("");
 
        if (interface_ready())
-               return BT_STATUS_SUCCESS;
+               return BT_STATUS_DONE;
 
        bt_hal_cbacks = callbacks;
 
+       hal_ipc_register(HAL_SERVICE_ID_BLUETOOTH, ev_handlers,
+                               sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
        if (!hal_ipc_init()) {
                bt_hal_cbacks = NULL;
                return BT_STATUS_FAIL;
@@ -361,6 +408,9 @@ static int init(bt_callbacks_t *callbacks)
 fail:
        hal_ipc_cleanup();
        bt_hal_cbacks = NULL;
+
+       hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
+
        return status;
 }
 
@@ -396,6 +446,8 @@ static void cleanup(void)
        hal_ipc_cleanup();
 
        bt_hal_cbacks = NULL;
+
+       hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
 }
 
 static int get_adapter_properties(void)
@@ -418,21 +470,6 @@ static int get_adapter_property(bt_property_type_t type)
        if (!interface_ready())
                return BT_STATUS_NOT_READY;
 
-       switch (type) {
-       case BT_PROPERTY_BDNAME:
-       case BT_PROPERTY_BDADDR:
-       case BT_PROPERTY_UUIDS:
-       case BT_PROPERTY_CLASS_OF_DEVICE:
-       case BT_PROPERTY_TYPE_OF_DEVICE:
-       case BT_PROPERTY_SERVICE_RECORD:
-       case BT_PROPERTY_ADAPTER_SCAN_MODE:
-       case BT_PROPERTY_ADAPTER_BONDED_DEVICES:
-       case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
-               break;
-       default:
-               return BT_STATUS_PARM_INVALID;
-       }
-
        /* type match IPC type */
        cmd.type = type;
 
@@ -450,66 +487,90 @@ static int set_adapter_property(const bt_property_t *property)
        if (!interface_ready())
                return BT_STATUS_NOT_READY;
 
-       switch (property->type) {
-       case BT_PROPERTY_BDNAME:
-       case BT_PROPERTY_ADAPTER_SCAN_MODE:
-       case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
-               break;
-       default:
-               return BT_STATUS_PARM_INVALID;
-       }
-
-       /* type match IPC type */
-       cmd->type = property->type;
-       cmd->len = property->len;
-       memcpy(cmd->val, property->val, property->len);
+       adapter_prop_from_hal(property, &cmd->type, &cmd->len, cmd->val);
 
        return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP,
-                                       sizeof(buf), cmd, 0, NULL, NULL);
+                               sizeof(*cmd) + cmd->len, cmd, 0, NULL, NULL);
 }
 
 static int get_remote_device_properties(bt_bdaddr_t *remote_addr)
 {
+       struct hal_cmd_get_remote_device_props cmd;
+
        DBG("bdaddr: %s", bdaddr2str(remote_addr));
 
        if (!interface_ready())
                return BT_STATUS_NOT_READY;
 
-       return BT_STATUS_UNSUPPORTED;
+       memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+                                       HAL_OP_GET_REMOTE_DEVICE_PROPS,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
 }
 
 static int get_remote_device_property(bt_bdaddr_t *remote_addr,
                                                bt_property_type_t type)
 {
+       struct hal_cmd_get_remote_device_prop cmd;
+
        DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr),
                                                bt_property_type_t2str(type));
 
        if (!interface_ready())
                return BT_STATUS_NOT_READY;
 
-       return BT_STATUS_UNSUPPORTED;
+       memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+
+       /* type match IPC type */
+       cmd.type = type;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+                                       HAL_OP_GET_REMOTE_DEVICE_PROP,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
 }
 
 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];
+
        DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr),
-                                               btproperty2str(property));
+                               bt_property_type_t2str(property->type));
 
        if (!interface_ready())
                return BT_STATUS_NOT_READY;
 
-       return BT_STATUS_UNSUPPORTED;
+       cmd = (void *) buf;
+
+       memcpy(cmd->bdaddr, remote_addr, sizeof(cmd->bdaddr));
+
+       /* type match IPC type */
+       cmd->type = property->type;
+       cmd->len = property->len;
+       memcpy(cmd->val, property->val, property->len);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+                                       HAL_OP_SET_REMOTE_DEVICE_PROP,
+                                       sizeof(buf), cmd, 0, NULL, NULL);
 }
 
 static int get_remote_service_record(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid)
 {
+       struct hal_cmd_get_remote_service_rec cmd;
+
        DBG("bdaddr: %s", bdaddr2str(remote_addr));
 
        if (!interface_ready())
                return BT_STATUS_NOT_READY;
 
-       return BT_STATUS_UNSUPPORTED;
+       memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr));
+       memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH,
+                                       HAL_OP_GET_REMOTE_SERVICE_REC,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
 }
 
 static int get_remote_services(bt_bdaddr_t *remote_addr)
@@ -638,7 +699,7 @@ static int ssp_reply(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant,
 
 static const void *get_profile_interface(const char *profile_id)
 {
-       DBG("%s: %s", __func__, profile_id);
+       DBG("%s", profile_id);
 
        if (!interface_ready())
                return NULL;
@@ -660,22 +721,35 @@ static const void *get_profile_interface(const char *profile_id)
 
 static int dut_mode_configure(uint8_t enable)
 {
-       DBG("");
+       struct hal_cmd_dut_mode_conf cmd;
+
+       DBG("enable %u", enable);
 
        if (!interface_ready())
                return BT_STATUS_NOT_READY;
 
-       return BT_STATUS_UNSUPPORTED;
+       cmd.enable = enable;
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF,
+                                       sizeof(cmd), &cmd, 0, NULL, NULL);
 }
 
 static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t len)
 {
-       DBG("");
+       uint8_t cmd_buf[sizeof(struct hal_cmd_dut_mode_send) + len];
+       struct hal_cmd_dut_mode_send *cmd = (void *) cmd_buf;
+
+       DBG("opcode %u len %u", opcode, len);
 
        if (!interface_ready())
                return BT_STATUS_NOT_READY;
 
-       return BT_STATUS_UNSUPPORTED;
+       cmd->opcode = opcode;
+       cmd->len = len;
+       memcpy(cmd->data, buf, cmd->len);
+
+       return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND,
+                                       sizeof(cmd_buf), cmd, 0, NULL, NULL);
 }
 
 static const bt_interface_t bluetooth_if = {
index 2ce17a3..6a6b682 100644 (file)
@@ -32,7 +32,7 @@ static bool interface_ready(void)
        return cbacks != NULL;
 }
 
-static void handle_conn_state(void *buf)
+static void handle_conn_state(void *buf, uint16_t len)
 {
        struct hal_ev_hidhost_conn_state *ev = buf;
 
@@ -41,7 +41,7 @@ static void handle_conn_state(void *buf)
                                                                ev->state);
 }
 
-static void handle_info(void *buf)
+static void handle_info(void *buf, uint16_t len)
 {
        struct hal_ev_hidhost_info *ev = buf;
        bthh_hid_info_t info;
@@ -60,7 +60,7 @@ static void handle_info(void *buf)
                cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info);
 }
 
-static void handle_proto_mode(void *buf)
+static void handle_proto_mode(void *buf, uint16_t len)
 {
        struct hal_ev_hidhost_proto_mode *ev = buf;
 
@@ -69,16 +69,21 @@ static void handle_proto_mode(void *buf)
                                                        ev->status, ev->mode);
 }
 
-static void handle_get_report(void *buf)
+static void handle_get_report(void *buf, uint16_t len)
 {
        struct hal_ev_hidhost_get_report *ev = buf;
 
+       if (len != sizeof(*ev) + ev->len) {
+               error("invalid get report event, aborting");
+               exit(EXIT_FAILURE);
+       }
+
        if (cbacks->get_report_cb)
                cbacks->get_report_cb((bt_bdaddr_t *) ev->bdaddr, ev->status,
                                                        ev->data, ev->len);
 }
 
-static void handle_virtual_unplug(void *buf)
+static void handle_virtual_unplug(void *buf, uint16_t len)
 {
        struct hal_ev_hidhost_virtual_unplug *ev = buf;
 
@@ -87,33 +92,35 @@ static void handle_virtual_unplug(void *buf)
                                                                ev->status);
 }
 
-/* will be called from notification thread context */
-void bt_notify_hidhost(uint8_t opcode, void *buf, uint16_t len)
-{
-       if (!interface_ready())
-               return;
-
-       switch (opcode) {
-       case HAL_EV_HIDHOST_CONN_STATE:
-               handle_conn_state(buf);
-               break;
-       case HAL_EV_HIDHOST_INFO:
-               handle_info(buf);
-               break;
-       case HAL_EV_HIDHOST_PROTO_MODE:
-               handle_proto_mode(buf);
-               break;
-       case HAL_EV_HIDHOST_GET_REPORT:
-               handle_get_report(buf);
-               break;
-       case HAL_EV_HIDHOST_VIRTUAL_UNPLUG:
-               handle_virtual_unplug(buf);
-               break;
-       default:
-               DBG("Unhandled callback opcode=0x%x", opcode);
-               break;
-       }
-}
+/* 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,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_hidhost_conn_state)
+       },
+       {       /* HAL_EV_HIDHOST_INFO */
+               .handler = handle_info,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_hidhost_info),
+       },
+       {       /* HAL_EV_HIDHOST_PROTO_MODE */
+               .handler = handle_proto_mode,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_hidhost_proto_mode),
+       },
+       {       /* HAL_EV_HIDHOST_GET_REPORT */
+               .handler = handle_get_report,
+               .var_len = true,
+               .data_len = sizeof(struct hal_ev_hidhost_get_report),
+       },
+       {       /* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */
+               .handler = handle_virtual_unplug,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_hidhost_virtual_unplug),
+       },
+};
 
 static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr)
 {
@@ -356,16 +363,30 @@ static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data)
 static bt_status_t init(bthh_callbacks_t *callbacks)
 {
        struct hal_cmd_register_module cmd;
+       int ret;
 
        DBG("");
 
+       if (interface_ready())
+               return BT_STATUS_DONE;
+
        /* store reference to user callbacks */
        cbacks = callbacks;
 
+       hal_ipc_register(HAL_SERVICE_ID_HIDHOST, ev_handlers,
+                               sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
        cmd.service_id = HAL_SERVICE_ID_HIDHOST;
 
-       return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+       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_HIDHOST);
+       }
+
+       return ret;
 }
 
 static void cleanup(void)
@@ -383,6 +404,8 @@ static void cleanup(void)
 
        hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
                                        sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST);
 }
 
 static bthh_interface_t hidhost_if = {
index 026e245..b19704a 100644 (file)
@@ -43,26 +43,86 @@ static pthread_mutex_t cmd_sk_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static pthread_t notif_th = 0;
 
-static void notification_dispatch(struct hal_hdr *msg, int fd)
+struct service_handler {
+       const struct hal_ipc_handler *handler;
+       uint8_t size;
+};
+
+static struct service_handler services[HAL_SERVICE_ID_MAX + 1];
+
+void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers,
+                                                               uint8_t size)
 {
-       switch (msg->service_id) {
-       case HAL_SERVICE_ID_BLUETOOTH:
-               bt_notify_adapter(msg->opcode, msg->payload, msg->len);
-               break;
-       case HAL_SERVICE_ID_HIDHOST:
-               bt_notify_hidhost(msg->opcode, msg->payload, msg->len);
-               break;
-       case HAL_SERVICE_ID_A2DP:
-               bt_notify_a2dp(msg->opcode, msg->payload, msg->len);
-               break;
-       case HAL_SERVICE_ID_PAN:
-               bt_notify_pan(msg->opcode, msg->payload, msg->len);
-               break;
-       default:
-               DBG("Unhandled notification service=%d opcode=0x%x",
+       services[service].handler = handlers;
+       services[service].size = size;
+}
+
+void hal_ipc_unregister(uint8_t service)
+{
+       services[service].handler = NULL;
+       services[service].size = 0;
+}
+
+static void handle_msg(void *buf, ssize_t len)
+{
+       struct hal_hdr *msg = buf;
+       const struct hal_ipc_handler *handler;
+       uint8_t opcode;
+
+       if (len < (ssize_t) sizeof(*msg)) {
+               error("IPC: message too small (%zd bytes), aborting", len);
+               exit(EXIT_FAILURE);
+       }
+
+       if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
+               error("IPC: message malformed (%zd bytes), aborting", len);
+               exit(EXIT_FAILURE);
+       }
+
+       /* if service is valid */
+       if (msg->service_id > HAL_SERVICE_ID_MAX) {
+               error("IPC: unknown service (0x%x), aborting",
+                                                       msg->service_id);
+               exit(EXIT_FAILURE);
+       }
+
+       /* if service is registered */
+       if (!services[msg->service_id].handler) {
+               error("IPC: unregistered service (0x%x), aborting",
+                                                       msg->service_id);
+               exit(EXIT_FAILURE);
+       }
+
+       /* if opcode fit valid range */
+       if (msg->opcode < HAL_MINIMUM_EVENT) {
+               error("IPC: invalid opcode for service 0x%x (0x%x), aborting",
                                                msg->service_id, msg->opcode);
-               break;
+               exit(EXIT_FAILURE);
        }
+
+       /* opcode is used as table offset and must be adjusted as events start
+        * with HAL_MINIMUM_EVENT offset */
+       opcode = msg->opcode - HAL_MINIMUM_EVENT;
+
+       /* if opcode is valid */
+       if (opcode >= services[msg->service_id].size) {
+               error("IPC: invalid opcode for service 0x%x (0x%x), aborting",
+                                               msg->service_id, msg->opcode);
+               exit(EXIT_FAILURE);
+       }
+
+       handler = &services[msg->service_id].handler[opcode];
+
+       /* if payload size is valid */
+       if ((handler->var_len && handler->data_len > msg->len) ||
+                       (!handler->var_len && handler->data_len != msg->len)) {
+               error("IPC: message size invalid for service 0x%x opcode 0x%x "
+                               "(%u bytes), aborting",
+                               msg->service_id, msg->opcode, msg->len);
+               exit(EXIT_FAILURE);
+       }
+
+       handler->handler(msg->payload, msg->len);
 }
 
 static void *notification_handler(void *data)
@@ -72,7 +132,6 @@ static void *notification_handler(void *data)
        struct cmsghdr *cmsg;
        char cmsgbuf[CMSG_SPACE(sizeof(int))];
        char buf[BLUEZ_HAL_MTU];
-       struct hal_hdr *ev = (void *) buf;
        ssize_t ret;
        int fd;
 
@@ -83,7 +142,7 @@ static void *notification_handler(void *data)
                memset(buf, 0, sizeof(buf));
                memset(cmsgbuf, 0, sizeof(cmsgbuf));
 
-               iv.iov_base = ev;
+               iv.iov_base = buf;
                iv.iov_len = sizeof(buf);
 
                msg.msg_iov = &iv;
@@ -108,24 +167,6 @@ static void *notification_handler(void *data)
                        exit(EXIT_FAILURE);
                }
 
-               if (ret < (ssize_t) sizeof(*ev)) {
-                       error("Too small notification (%zd bytes), aborting",
-                                                                       ret);
-                       exit(EXIT_FAILURE);
-               }
-
-               if (ev->opcode < HAL_MINIMUM_EVENT) {
-                       error("Invalid notification (0x%x), aborting",
-                                                       ev->opcode);
-                       exit(EXIT_FAILURE);
-               }
-
-               if (ret != (ssize_t) (sizeof(*ev) + ev->len)) {
-                       error("Malformed notification(%zd bytes), aborting",
-                                                                       ret);
-                       exit(EXIT_FAILURE);
-               }
-
                fd = -1;
 
                /* Receive auxiliary data in msg */
@@ -138,7 +179,7 @@ static void *notification_handler(void *data)
                        }
                }
 
-               notification_dispatch(ev, fd);
+               handle_msg(buf, ret);
        }
 
        close(notif_sk);
@@ -315,6 +356,12 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
                exit(EXIT_FAILURE);
        }
 
+       /* socket was shutdown */
+       if (ret == 0) {
+               error("Command socket closed, aborting");
+               exit(EXIT_FAILURE);
+       }
+
        memset(&msg, 0, sizeof(msg));
        memset(&cmd, 0, sizeof(cmd));
 
@@ -367,6 +414,17 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
 
        if (cmd.opcode == HAL_OP_STATUS) {
                struct hal_status *s = rsp;
+
+               if (sizeof(*s) != cmd.len) {
+                       error("Invalid status length, aborting");
+                       exit(EXIT_FAILURE);
+               }
+
+               if (s->code == HAL_STATUS_SUCCESS) {
+                       error("Invalid success status response, aborting");
+                       exit(EXIT_FAILURE);
+               }
+
                return s->code;
        }
 
index ea53e1c..2fbf30f 100644 (file)
  *
  */
 
+struct hal_ipc_handler {
+       void (*handler) (void *buf, uint16_t len);
+       bool var_len;
+       size_t data_len;
+};
+
 bool hal_ipc_init(void);
 void hal_ipc_cleanup(void);
 
 int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param,
                                        size_t *rsp_len, void *rsp, int *fd);
+
+void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers,
+                                                               uint8_t size);
+void hal_ipc_unregister(uint8_t service);
index 44fd5c8..80c4a25 100644 (file)
@@ -232,6 +232,8 @@ struct hal_cmd_sock_connect {
        uint8_t  flags;
 } __attribute__((packed));
 
+/* Bluetooth HID Host HAL API */
+
 #define HAL_OP_HIDHOST_CONNECT         0x01
 struct hal_cmd_hidhost_connect {
        uint8_t bdaddr[6];
index 2bc560e..8c0f8d8 100644 (file)
@@ -31,7 +31,7 @@ static bool interface_ready(void)
        return cbs != NULL;
 }
 
-static void handle_conn_state(void *buf)
+static void handle_conn_state(void *buf, uint16_t len)
 {
        struct hal_ev_pan_conn_state *ev = buf;
 
@@ -41,7 +41,7 @@ static void handle_conn_state(void *buf)
                                        ev->local_role, ev->remote_role);
 }
 
-static void handle_ctrl_state(void *buf)
+static void handle_ctrl_state(void *buf, uint16_t len)
 {
        struct hal_ev_pan_ctrl_state *ev = buf;
 
@@ -50,23 +50,20 @@ static void handle_ctrl_state(void *buf)
                                        ev->local_role, (char *)ev->name);
 }
 
-void bt_notify_pan(uint8_t opcode, void *buf, uint16_t len)
-{
-       if (!interface_ready())
-               return;
-
-       switch (opcode) {
-       case HAL_EV_PAN_CONN_STATE:
-               handle_conn_state(buf);
-               break;
-       case HAL_EV_PAN_CTRL_STATE:
-               handle_ctrl_state(buf);
-               break;
-       default:
-               DBG("Unhandled callback opcode=0x%x", opcode);
-               break;
-       }
-}
+/* 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,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_pan_ctrl_state),
+       },
+       {       /* HAL_EV_PAN_CONN_STATE */
+               .handler = handle_conn_state,
+               .var_len = false,
+               .data_len = sizeof(struct hal_ev_pan_conn_state),
+       },
+};
 
 static bt_status_t pan_enable(int local_role)
 {
@@ -138,15 +135,29 @@ static bt_status_t pan_disconnect(const bt_bdaddr_t *bd_addr)
 static bt_status_t pan_init(const btpan_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_PAN, ev_handlers,
+                               sizeof(ev_handlers)/sizeof(ev_handlers[0]));
+
        cmd.service_id = HAL_SERVICE_ID_PAN;
 
-       return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE,
+       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_PAN);
+       }
+
+       return ret;
 }
 
 static void pan_cleanup()
@@ -164,6 +175,8 @@ static void pan_cleanup()
 
        hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE,
                                        sizeof(cmd), &cmd, 0, NULL, NULL);
+
+       hal_ipc_unregister(HAL_SERVICE_ID_PAN);
 }
 
 static btpan_interface_t pan_if = {
index b7bc88e..f45be30 100644 (file)
@@ -34,12 +34,17 @@ static bt_status_t sock_listen_rfcomm(const char *service_name,
 
        DBG("");
 
+       memset(&cmd, 0, sizeof(cmd));
+
        cmd.flags = flags;
        cmd.type = BTSOCK_RFCOMM;
        cmd.channel = chan;
-       memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
-       memset(cmd.name, 0, sizeof(cmd.name));
-       memcpy(cmd.name, service_name, strlen(service_name));
+
+       if (uuid)
+               memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+       if (service_name)
+               memcpy(cmd.name, service_name, strlen(service_name));
 
        return hal_ipc_cmd(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN,
                                sizeof(cmd), &cmd, NULL, NULL, sock);
@@ -55,8 +60,8 @@ static bt_status_t sock_listen(btsock_type_t type, const char *service_name,
                return BT_STATUS_PARM_INVALID;
        }
 
-       DBG("uuid %s chan %d sock %p type %d service_name %s",
-                       btuuid2str(uuid), chan, sock, type, service_name);
+       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:
@@ -77,23 +82,28 @@ static bt_status_t sock_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type,
        struct hal_cmd_sock_connect cmd;
 
        if ((!uuid && chan <= 0) || !bdaddr || !sock) {
-               error("Invalid params: bd_addr %p, uuid %s, chan %d, sock %p",
-                                       bdaddr, btuuid2str(uuid), chan, sock);
+               error("Invalid params: bd_addr %s, uuid %s, chan %d, sock %p",
+                       bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock);
                return BT_STATUS_PARM_INVALID;
        }
 
-       DBG("uuid %s chan %d sock %p type %d", btuuid2str(uuid), chan, sock,
-                                                                       type);
+       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;
        cmd.type = type;
        cmd.channel = chan;
-       memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
+       if (uuid)
+               memcpy(cmd.uuid, uuid, sizeof(cmd.uuid));
+
        memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr));
 
        return hal_ipc_cmd(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT,
index 4f44d98..e3c0c60 100644 (file)
@@ -33,6 +33,9 @@ const char *bt_uuid_t2str(const uint8_t *uuid, char *buf)
        unsigned int i;
        int is_bt;
 
+       if (!uuid)
+               return strcpy(buf, "NULL");
+
        is_bt = !memcmp(&uuid[4], &BT_BASE_UUID[4], HAL_UUID_LEN - 4);
 
        for (i = 0; i < HAL_UUID_LEN; i++) {
@@ -167,6 +170,9 @@ const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf)
 {
        const uint8_t *p = bd_addr->address;
 
+       if (!bd_addr)
+               return strcpy(buf, "NULL");
+
        snprintf(buf, MAX_ADDR_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x",
                                        p[0], p[1], p[2], p[3], p[4], p[5]);
 
index 72090fe..b475411 100644 (file)
@@ -26,9 +26,5 @@ bthh_interface_t *bt_get_hidhost_interface(void);
 btpan_interface_t *bt_get_pan_interface(void);
 btav_interface_t *bt_get_a2dp_interface(void);
 
-void bt_notify_adapter(uint8_t opcode, void *buf, uint16_t len);
 void bt_thread_associate(void);
 void bt_thread_disassociate(void);
-void bt_notify_hidhost(uint8_t opcode, void *buf, uint16_t len);
-void bt_notify_a2dp(uint8_t opcode, void *buf, uint16_t len);
-void bt_notify_pan(uint8_t opcode, void *buf, uint16_t len);
index f5a607c..8bfdfed 100644 (file)
@@ -78,7 +78,6 @@
 
 static bdaddr_t adapter_addr;
 
-static int notification_sk = -1;
 static GIOChannel *ctrl_io = NULL;
 static GIOChannel *intr_io = NULL;
 static GSList *devices = NULL;
@@ -297,8 +296,8 @@ static void bt_hid_notify_state(struct hid_device *dev, uint8_t state)
        bdaddr2android(&dev->dst, ev.bdaddr);
        ev.state = state;
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST,
-                       HAL_EV_HIDHOST_CONN_STATE, sizeof(ev), &ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_CONN_STATE,
+                                                       sizeof(ev), &ev);
 }
 
 static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond,
@@ -356,8 +355,8 @@ static void bt_hid_notify_proto_mode(struct hid_device *dev, uint8_t *buf,
                ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL;
        }
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST,
-                       HAL_EV_HIDHOST_PROTO_MODE, sizeof(ev), &ev, -1);
+       ipc_send_notif(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,
@@ -399,8 +398,8 @@ static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf,
        }
 
 send:
-       ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST,
-                               HAL_EV_HIDHOST_GET_REPORT, ev_len, ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_GET_REPORT,
+                                                               ev_len, ev);
        g_free(ev);
 }
 
@@ -424,8 +423,8 @@ static void bt_hid_notify_virtual_unplug(struct hid_device *dev,
                ev.status = HAL_HIDHOST_STATUS_OK;
        }
 
-       ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST,
-                       HAL_EV_HIDHOST_VIRTUAL_UNPLUG, sizeof(ev), &ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_VIRTUAL_UNPLUG,
+                                                       sizeof(ev), &ev);
 
 }
 
@@ -509,21 +508,22 @@ 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(notification_sk, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO,
-                                                       sizeof(ev), &ev, -1);
+       ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO, sizeof(ev),
+                                                                       &ev);
 }
 
 static int uhid_create(struct hid_device *dev)
 {
        GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_NVAL;
-       GIOChannel *io;
        struct uhid_event ev;
+       GIOChannel *io;
+       int err;
 
        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));
-               bt_hid_notify_state(dev, HAL_HIDHOST_STATE_NO_HID);
-               return -errno;
+               return err;
        }
 
        memset(&ev, 0, sizeof(ev));
@@ -538,11 +538,11 @@ static int uhid_create(struct hid_device *dev)
        ev.u.create.rd_data = dev->rd_data;
 
        if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0) {
+               err = -errno;
                error("Failed to create uHID device: %s", strerror(errno));
                close(dev->uhid_fd);
                dev->uhid_fd = -1;
-               bt_hid_notify_state(dev, HAL_HIDHOST_STATE_NO_HID);
-               return -errno;
+               return err;
        }
 
        io = g_io_channel_unix_new(dev->uhid_fd);
@@ -559,16 +559,20 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
                                                        gpointer user_data)
 {
        struct hid_device *dev = user_data;
+       uint8_t state;
 
        DBG("");
 
        if (conn_err) {
                error("%s", conn_err->message);
+               state = HAL_HIDHOST_STATE_FAILED;
                goto failed;
        }
 
-       if (uhid_create(dev) < 0)
+       if (uhid_create(dev) < 0) {
+               state = HAL_HIDHOST_STATE_NO_HID;
                goto failed;
+       }
 
        dev->intr_watch = g_io_add_watch(dev->intr_io,
                                G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
@@ -579,6 +583,7 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
        return;
 
 failed:
+       bt_hid_notify_state(dev, state);
        hid_device_free(dev);
 }
 
@@ -715,10 +720,11 @@ fail:
        hid_device_free(dev);
 }
 
-static uint8_t bt_hid_connect(struct hal_cmd_hidhost_connect *cmd,
-                                                               uint16_t len)
+static void bt_hid_connect(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_hidhost_connect *cmd = buf;
        struct hid_device *dev;
+       uint8_t status;
        char addr[18];
        bdaddr_t dst;
        GSList *l;
@@ -726,14 +732,13 @@ static uint8_t bt_hid_connect(struct hal_cmd_hidhost_connect *cmd,
 
        DBG("");
 
-       if (len < sizeof(*cmd))
-               return HAL_STATUS_INVALID;
-
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (l)
-               return HAL_STATUS_FAILED;
+       if (l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        dev = g_new0(struct hid_device, 1);
        bacpy(&dev->dst, &dst);
@@ -747,32 +752,36 @@ static uint8_t bt_hid_connect(struct hal_cmd_hidhost_connect *cmd,
                                        hid_sdp_search_cb, dev, NULL) < 0) {
                error("Failed to search sdp details");
                hid_device_free(dev);
-               return HAL_STATUS_FAILED;
+               status = HAL_STATUS_FAILED;
+               goto failed;
        }
 
        devices = g_slist_append(devices, dev);
        bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING);
 
-       return HAL_STATUS_SUCCESS;
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, status);
 }
 
-static uint8_t bt_hid_disconnect(struct hal_cmd_hidhost_disconnect *cmd,
-                                                               uint16_t len)
+static void bt_hid_disconnect(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_hidhost_disconnect *cmd = buf;
        struct hid_device *dev;
+       uint8_t status;
        GSList *l;
        bdaddr_t dst;
 
        DBG("");
 
-       if (len < sizeof(*cmd))
-               return HAL_STATUS_INVALID;
-
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (!l)
-               return HAL_STATUS_FAILED;
+       if (!l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        dev = l->data;
 
@@ -785,33 +794,38 @@ static uint8_t bt_hid_disconnect(struct hal_cmd_hidhost_disconnect *cmd,
 
        bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
 
-       return HAL_STATUS_SUCCESS;
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, status);
 }
 
-static uint8_t bt_hid_virtual_unplug(struct hal_cmd_hidhost_virtual_unplug *cmd,
-                                                               uint16_t len)
+static void bt_hid_virtual_unplug(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_hidhost_virtual_unplug *cmd = buf;
        struct hid_device *dev;
        GSList *l;
+       uint8_t status;
        bdaddr_t dst;
        uint8_t hdr;
        int fd;
 
        DBG("");
 
-       if (len < sizeof(*cmd))
-               return HAL_STATUS_INVALID;
-
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (!l)
-               return HAL_STATUS_FAILED;
+       if (!l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        dev = l->data;
 
-       if (!(dev->ctrl_io))
-               return HAL_STATUS_FAILED;
+       if (!(dev->ctrl_io)) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        hdr = HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG;
 
@@ -820,7 +834,8 @@ static uint8_t bt_hid_virtual_unplug(struct hal_cmd_hidhost_virtual_unplug *cmd,
        if (write(fd, &hdr, sizeof(hdr)) < 0) {
                error("error writing virtual unplug command: %s (%d)",
                                                strerror(errno), errno);
-               return HAL_STATUS_FAILED;
+               status = HAL_STATUS_FAILED;
+               goto failed;
        }
 
        /* Wait either channels to HUP */
@@ -832,10 +847,14 @@ static uint8_t bt_hid_virtual_unplug(struct hal_cmd_hidhost_virtual_unplug *cmd,
 
        bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING);
 
-       return HAL_STATUS_SUCCESS;
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_VIRTUAL_UNPLUG,
+                                                                       status);
 }
 
-static uint8_t bt_hid_info(struct hal_cmd_hidhost_set_info *cmd, uint16_t len)
+static void bt_hid_info(const void *buf, uint16_t len)
 {
        /* Data from hal_cmd_hidhost_set_info is usefull only when we create
         * UHID device. Once device is created all the transactions will be
@@ -843,33 +862,36 @@ static uint8_t bt_hid_info(struct hal_cmd_hidhost_set_info *cmd, uint16_t len)
         * once device is created with HID internals. */
        DBG("Not supported");
 
-       return HAL_STATUS_UNSUPPORTED;
+       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO,
+                                                       HAL_STATUS_UNSUPPORTED);
 }
 
-static uint8_t bt_hid_get_protocol(struct hal_cmd_hidhost_get_protocol *cmd,
-                                                               uint16_t len)
+static void bt_hid_get_protocol(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_hidhost_get_protocol *cmd = buf;
        struct hid_device *dev;
        GSList *l;
        bdaddr_t dst;
        int fd;
        uint8_t hdr;
+       uint8_t status;
 
        DBG("");
 
-       if (len < sizeof(*cmd))
-               return HAL_STATUS_INVALID;
-
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (!l)
-               return HAL_STATUS_FAILED;
+       if (!l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        dev = l->data;
 
-       if (dev->boot_dev)
-               return HAL_STATUS_UNSUPPORTED;
+       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);
@@ -877,37 +899,45 @@ static uint8_t bt_hid_get_protocol(struct hal_cmd_hidhost_get_protocol *cmd,
        if (write(fd, &hdr, sizeof(hdr)) < 0) {
                error("error writing device_get_protocol: %s (%d)",
                                                strerror(errno), errno);
-               return HAL_STATUS_FAILED;
+               status = HAL_STATUS_FAILED;
+               goto failed;
        }
 
        dev->last_hid_msg = HID_MSG_GET_PROTOCOL;
-       return HAL_STATUS_SUCCESS;
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_PROTOCOL,
+                                                                       status);
 }
 
-static uint8_t bt_hid_set_protocol(struct hal_cmd_hidhost_set_protocol *cmd,
-                                                               uint16_t len)
+static void bt_hid_set_protocol(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_hidhost_set_protocol *cmd = buf;
        struct hid_device *dev;
        GSList *l;
        bdaddr_t dst;
        int fd;
        uint8_t hdr;
+       uint8_t status;
 
        DBG("");
 
-       if (len < sizeof(*cmd))
-               return HAL_STATUS_INVALID;
-
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (!l)
-               return HAL_STATUS_FAILED;
+       if (!l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        dev = l->data;
 
-       if (dev->boot_dev)
-               return HAL_STATUS_UNSUPPORTED;
+       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);
@@ -915,39 +945,47 @@ static uint8_t bt_hid_set_protocol(struct hal_cmd_hidhost_set_protocol *cmd,
        if (write(fd, &hdr, sizeof(hdr)) < 0) {
                error("error writing device_set_protocol: %s (%d)",
                                                strerror(errno), errno);
-               return HAL_STATUS_FAILED;
+               status = HAL_STATUS_FAILED;
+               goto failed;
        }
 
        dev->last_hid_msg = HID_MSG_SET_PROTOCOL;
-       return HAL_STATUS_SUCCESS;
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_PROTOCOL,
+                                                                       status);
 }
 
-static uint8_t bt_hid_get_report(struct hal_cmd_hidhost_get_report *cmd,
-                                                               uint16_t len)
+static void bt_hid_get_report(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_hidhost_get_report *cmd = buf;
        struct hid_device *dev;
        GSList *l;
        bdaddr_t dst;
        int fd;
        uint8_t *req;
        uint8_t req_size;
+       uint8_t status;
 
        DBG("");
 
-       if (len < sizeof(*cmd))
-               return HAL_STATUS_INVALID;
-
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (!l)
-               return HAL_STATUS_FAILED;
+       if (!l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        dev = l->data;
        req_size = (cmd->buf_size > 0) ? 4 : 2;
        req = g_try_malloc0(req_size);
-       if (!req)
-               return HAL_STATUS_NOMEM;
+       if (!req) {
+               status = HAL_STATUS_NOMEM;
+               goto failed;
+       }
 
        req[0] = HID_MSG_GET_REPORT | cmd->type;
        req[1] = cmd->id;
@@ -963,44 +1001,60 @@ static uint8_t bt_hid_get_report(struct hal_cmd_hidhost_get_report *cmd,
                error("error writing hid_get_report: %s (%d)",
                                                strerror(errno), errno);
                g_free(req);
-               return HAL_STATUS_FAILED;
+               status = HAL_STATUS_FAILED;
+               goto failed;
        }
 
        dev->last_hid_msg = HID_MSG_GET_REPORT;
        g_free(req);
-       return HAL_STATUS_SUCCESS;
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, status);
 }
 
-static uint8_t bt_hid_set_report(struct hal_cmd_hidhost_set_report *cmd,
-                                                               uint16_t len)
+static void bt_hid_set_report(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_hidhost_set_report *cmd = buf;
        struct hid_device *dev;
        GSList *l;
        bdaddr_t dst;
        int i, fd;
        uint8_t *req;
        uint8_t req_size;
+       uint8_t status;
 
        DBG("");
 
-       if (len < sizeof(*cmd))
-               return HAL_STATUS_INVALID;
+       if (len != sizeof(*cmd) + cmd->len) {
+               error("Invalid hid set report size (%u bytes), terminating",
+                                                                       len);
+               raise(SIGTERM);
+               return;
+       }
 
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (!l)
-               return HAL_STATUS_FAILED;
+       if (!l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        dev = l->data;
 
-       if (!(dev->ctrl_io))
-               return HAL_STATUS_FAILED;
+       if (!(dev->ctrl_io)) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        req_size = 1 + (cmd->len / 2);
        req = g_try_malloc0(req_size);
-       if (!req)
-               return HAL_STATUS_NOMEM;
+       if (!req) {
+               status = HAL_STATUS_NOMEM;
+               goto failed;
+       }
 
        req[0] = HID_MSG_SET_REPORT | cmd->type;
        /* Report data coming to HAL is in ascii format, HAL sends
@@ -1014,44 +1068,60 @@ static uint8_t bt_hid_set_report(struct hal_cmd_hidhost_set_report *cmd,
                error("error writing hid_set_report: %s (%d)",
                                                strerror(errno), errno);
                g_free(req);
-               return HAL_STATUS_FAILED;
+               status = HAL_STATUS_FAILED;
+               goto failed;
        }
 
        dev->last_hid_msg = HID_MSG_SET_REPORT;
        g_free(req);
-       return HAL_STATUS_SUCCESS;
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, status);
 }
 
-static uint8_t bt_hid_send_data(struct hal_cmd_hidhost_send_data *cmd,
-                                                               uint16_t len)
+static void bt_hid_send_data(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_hidhost_send_data *cmd = buf;
        struct hid_device *dev;
        GSList *l;
        bdaddr_t dst;
        int i, fd;
        uint8_t *req;
        uint8_t req_size;
+       uint8_t status;
 
        DBG("");
 
-       if (len < sizeof(*cmd))
-               return HAL_STATUS_INVALID;
+       if (len != sizeof(*cmd) + cmd->len) {
+               error("Invalid hid send data size (%u bytes), terminating",
+                                                                       len);
+               raise(SIGTERM);
+               return;
+       }
 
        android2bdaddr(&cmd->bdaddr, &dst);
 
        l = g_slist_find_custom(devices, &dst, device_cmp);
-       if (!l)
-               return HAL_STATUS_FAILED;
+       if (!l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        dev = l->data;
 
-       if (!(dev->intr_io))
-               return HAL_STATUS_FAILED;
+       if (!(dev->intr_io)) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
 
        req_size = 1 + (cmd->len / 2);
        req = g_try_malloc0(req_size);
-       if (!req)
-               return HAL_STATUS_NOMEM;
+       if (!req) {
+               status = HAL_STATUS_NOMEM;
+               goto failed;
+       }
 
        req[0] = HID_MSG_DATA | HID_DATA_TYPE_OUTPUT;
        /* Report data coming to HAL is in ascii format, HAL sends
@@ -1065,53 +1135,42 @@ static uint8_t bt_hid_send_data(struct hal_cmd_hidhost_send_data *cmd,
                error("error writing data to HID device: %s (%d)",
                                                strerror(errno), errno);
                g_free(req);
-               return HAL_STATUS_FAILED;
+               status = HAL_STATUS_FAILED;
+               goto failed;
        }
 
        g_free(req);
-       return HAL_STATUS_SUCCESS;
-}
-
-void bt_hid_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len)
-{
-       uint8_t status = HAL_STATUS_FAILED;
 
-       switch (opcode) {
-       case HAL_OP_HIDHOST_CONNECT:
-               status = bt_hid_connect(buf, len);
-               break;
-       case HAL_OP_HIDHOST_DISCONNECT:
-               status = bt_hid_disconnect(buf, len);
-               break;
-       case HAL_OP_HIDHOST_VIRTUAL_UNPLUG:
-               status = bt_hid_virtual_unplug(buf, len);
-               break;
-       case HAL_OP_HIDHOST_SET_INFO:
-               status = bt_hid_info(buf, len);
-               break;
-       case HAL_OP_HIDHOST_GET_PROTOCOL:
-               status = bt_hid_get_protocol(buf, len);
-               break;
-       case HAL_OP_HIDHOST_SET_PROTOCOL:
-               status = bt_hid_set_protocol(buf, len);
-               break;
-       case HAL_OP_HIDHOST_GET_REPORT:
-               status = bt_hid_get_report(buf, len);
-               break;
-       case HAL_OP_HIDHOST_SET_REPORT:
-               status = bt_hid_set_report(buf, len);
-               break;
-       case HAL_OP_HIDHOST_SEND_DATA:
-               status = bt_hid_send_data(buf, len);
-               break;
-       default:
-               DBG("Unhandled command, opcode 0x%x", opcode);
-               break;
-       }
+       status = HAL_STATUS_SUCCESS;
 
-       ipc_send_rsp(sk, HAL_SERVICE_ID_HIDHOST, status);
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, status);
 }
 
+static const struct ipc_handler cmd_handlers[] = {
+       /* HAL_OP_HIDHOST_CONNECT */
+       { bt_hid_connect, false, sizeof(struct hal_cmd_hidhost_connect) },
+       /* HAL_OP_HIDHOST_DISCONNECT */
+       { bt_hid_disconnect, false, sizeof(struct hal_cmd_hidhost_disconnect) },
+       /* HAL_OP_HIDHOST_VIRTUAL_UNPLUG */
+       { bt_hid_virtual_unplug, false,
+                               sizeof(struct hal_cmd_hidhost_virtual_unplug) },
+       /* HAL_OP_HIDHOST_SET_INFO */
+       { bt_hid_info, true, sizeof(struct hal_cmd_hidhost_set_info) },
+       /* HAL_OP_HIDHOST_GET_PROTOCOL */
+       { bt_hid_get_protocol, false,
+                               sizeof(struct hal_cmd_hidhost_get_protocol) },
+       /* HAL_OP_HIDHOST_SET_PROTOCOL */
+       { bt_hid_set_protocol, false,
+                               sizeof(struct hal_cmd_hidhost_get_protocol) },
+       /* HAL_OP_HIDHOST_GET_REPORT */
+       { bt_hid_get_report, false, sizeof(struct hal_cmd_hidhost_get_report) },
+       /* HAL_OP_HIDHOST_SET_REPORT */
+       { bt_hid_set_report, true, sizeof(struct hal_cmd_hidhost_set_report) },
+       /* HAL_OP_HIDHOST_SEND_DATA */
+       { bt_hid_send_data, true, sizeof(struct hal_cmd_hidhost_send_data)  },
+};
+
 static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
 {
        struct hid_device *dev;
@@ -1184,7 +1243,7 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
        }
 }
 
-bool bt_hid_register(int sk, const bdaddr_t *addr)
+bool bt_hid_register(const bdaddr_t *addr)
 {
        GError *err = NULL;
 
@@ -1210,21 +1269,35 @@ bool bt_hid_register(int sk, const bdaddr_t *addr)
                                BT_IO_OPT_INVALID);
        if (!intr_io) {
                error("Failed to listen on intr channel: %s", err->message);
-               g_io_channel_unref(ctrl_io);
                g_error_free(err);
+
+               g_io_channel_shutdown(ctrl_io, TRUE, NULL);
+               g_io_channel_unref(ctrl_io);
+               ctrl_io = NULL;
+
                return false;
        }
 
-       notification_sk = sk;
+       ipc_register(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("");
 
-       notification_sk = -1;
+       g_slist_foreach(devices, free_hid_devices, NULL);
+       devices = NULL;
 
        if (ctrl_io) {
                g_io_channel_shutdown(ctrl_io, TRUE, NULL);
@@ -1237,4 +1310,6 @@ void bt_hid_unregister(void)
                g_io_channel_unref(intr_io);
                intr_io = NULL;
        }
+
+       ipc_unregister(HAL_SERVICE_ID_HIDHOST);
 }
index 688086a..ea14446 100644 (file)
@@ -21,7 +21,5 @@
  *
  */
 
-void bt_hid_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len);
-
-bool bt_hid_register(int sk, const bdaddr_t *addr);
+bool bt_hid_register(const bdaddr_t *addr);
 void bt_hid_unregister(void);
index 729f157..6f940cd 100644 (file)
 #include <stdint.h>
 #include <string.h>
 #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.h"
 #include "log.h"
 
-void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
+struct service_handler {
+       const struct ipc_handler *handler;
+       uint8_t size;
+};
+
+static struct service_handler services[HAL_SERVICE_ID_MAX + 1];
+
+static GIOChannel *cmd_io = NULL;
+static GIOChannel *notif_io = NULL;
+
+static void ipc_handle_msg(const void *buf, ssize_t len)
+{
+       const struct hal_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;
+       }
+
+       if (len != (ssize_t) (sizeof(*msg) + msg->len)) {
+               error("IPC: message malformed (%zd bytes), terminating", len);
+               raise(SIGTERM);
+               return;
+       }
+
+       /* 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 service is registered */
+       if (!services[msg->service_id].handler) {
+               error("IPC: unregistered service (0x%x), terminating",
+                                                       msg->service_id);
+               raise(SIGTERM);
+               return;
+       }
+
+       /* 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;
+       }
+
+       /* opcode is table offset + 1 */
+       handler = &services[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;
+       }
+
+       handler->handler(msg->payload, msg->len);
+}
+
+static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       char buf[BLUEZ_HAL_MTU];
+       ssize_t ret;
+       int fd;
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               info("IPC: command socket closed, terminating");
+               goto fail;
+       }
+
+       fd = g_io_channel_unix_get_fd(io);
+
+       ret = read(fd, buf, sizeof(buf));
+       if (ret < 0) {
+               error("IPC: command read failed, terminating (%s)",
+                                                       strerror(errno));
+               goto fail;
+       }
+
+       ipc_handle_msg(buf, ret);
+       return TRUE;
+
+fail:
+       raise(SIGTERM);
+       return FALSE;
+}
+
+static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       info("IPC: notification socket closed, terminating");
+       raise(SIGTERM);
+
+       return FALSE;
+}
+
+static GIOChannel *connect_hal(GIOFunc connect_cb)
+{
+       struct sockaddr_un addr;
+       GIOCondition cond;
+       GIOChannel *io;
+       int sk;
+
+       sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
+       if (sk < 0) {
+               error("IPC: failed to create socket: %d (%s)", errno,
+                                                       strerror(errno));
+               return NULL;
+       }
+
+       io = g_io_channel_unix_new(sk);
+
+       g_io_channel_set_close_on_unref(io, TRUE);
+       g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+
+       memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
+
+       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;
+       }
+
+       cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+       g_io_add_watch(io, cond, connect_cb, NULL);
+
+       return io;
+}
+
+static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       DBG("");
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               error("IPC: notification socket connect failed, terminating");
+               raise(SIGTERM);
+               return FALSE;
+       }
+
+       cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+       g_io_add_watch(io, cond, notif_watch_cb, NULL);
+
+       cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
+
+       g_io_add_watch(cmd_io, cond, cmd_watch_cb, NULL);
+
+       info("IPC: successfully connected");
+
+       return FALSE;
+}
+
+static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       DBG("");
+
+       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+               error("IPC: command socket connect failed, terminating");
+               raise(SIGTERM);
+               return FALSE;
+       }
+
+       notif_io = connect_hal(notif_connect_cb);
+       if (!notif_io)
+               raise(SIGTERM);
+
+       return FALSE;
+}
+
+void ipc_init(void)
+{
+       cmd_io = connect_hal(cmd_connect_cb);
+       if (!cmd_io)
+               raise(SIGTERM);
+}
+
+void ipc_cleanup(void)
+{
+       if (cmd_io) {
+               g_io_channel_shutdown(cmd_io, TRUE, NULL);
+               g_io_channel_unref(cmd_io);
+               cmd_io = NULL;
+       }
+
+       if (notif_io) {
+               g_io_channel_shutdown(notif_io, TRUE, NULL);
+               g_io_channel_unref(notif_io);
+               notif_io = NULL;
+       }
+}
+
+static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
                                                        void *param, int fd)
 {
        struct msghdr msg;
@@ -47,6 +259,7 @@ void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
 
        memset(&msg, 0, sizeof(msg));
        memset(&m, 0, sizeof(m));
+       memset(cmsgbuf, 0, sizeof(cmsgbuf));
 
        m.service_id = service_id;
        m.opcode = opcode;
@@ -80,11 +293,49 @@ void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
        }
 }
 
-void ipc_send_rsp(int sk, uint8_t service_id, uint8_t status)
+void ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status)
 {
        struct hal_status s;
+       int sk;
+
+       sk = g_io_channel_unix_get_fd(cmd_io);
+
+       if (status == HAL_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);
 }
+
+void ipc_send_rsp_full(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,
+                                                               param, fd);
+}
+
+void ipc_send_notif(uint8_t service_id, uint8_t opcode,  uint16_t len,
+                                                               void *param)
+{
+       if (!notif_io)
+               return;
+
+       ipc_send(g_io_channel_unix_get_fd(notif_io), service_id, opcode, len,
+                                                               param, -1);
+}
+
+void ipc_register(uint8_t service, const struct ipc_handler *handlers,
+                                                               uint8_t size)
+{
+       services[service].handler = handlers;
+       services[service].size = size;
+}
+
+void ipc_unregister(uint8_t service)
+{
+       services[service].handler = NULL;
+       services[service].size = 0;
+}
index cf0f3d6..6cd102b 100644 (file)
  *
  */
 
-void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len,
+struct ipc_handler {
+       void (*handler) (const void *buf, uint16_t len);
+       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_rsp(int sk, uint8_t service_id, uint8_t status);
+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);
index a4f5e84..5210b4b 100644 (file)
@@ -36,8 +36,6 @@
 #include <unistd.h>
 
 #include <sys/signalfd.h>
-#include <sys/socket.h>
-#include <sys/un.h>
 
 #include <glib.h>
 
@@ -69,68 +67,73 @@ static bdaddr_t adapter_bdaddr;
 
 static GMainLoop *event_loop;
 
-static GIOChannel *hal_cmd_io = NULL;
-static GIOChannel *hal_notif_io = NULL;
-
 static bool services[HAL_SERVICE_ID_MAX + 1] = { false };
 
-static void service_register(void *buf, uint16_t len)
+static void service_register(const void *buf, uint16_t len)
 {
-       struct hal_cmd_register_module *m = buf;
-       int sk = g_io_channel_unix_get_fd(hal_notif_io);
+       const struct hal_cmd_register_module *m = buf;
+       uint8_t status;
 
-       if (m->service_id > HAL_SERVICE_ID_MAX || services[m->service_id])
+       if (m->service_id > HAL_SERVICE_ID_MAX || services[m->service_id]) {
+               status = HAL_STATUS_FAILED;
                goto failed;
+       }
 
        switch (m->service_id) {
        case HAL_SERVICE_ID_BLUETOOTH:
-               if (!bt_bluetooth_register(sk))
-                       goto failed;
+               bt_bluetooth_register();
 
                break;
        case HAL_SERVICE_ID_SOCK:
-               if (!bt_socket_register(sk, &adapter_bdaddr))
-                       goto failed;
+               bt_socket_register(&adapter_bdaddr);
 
                break;
        case HAL_SERVICE_ID_HIDHOST:
-               if (!bt_hid_register(sk, &adapter_bdaddr))
+               if (!bt_hid_register(&adapter_bdaddr)) {
+                       status = HAL_STATUS_FAILED;
                        goto failed;
+               }
 
                break;
        case HAL_SERVICE_ID_A2DP:
-               if (!bt_a2dp_register(sk, &adapter_bdaddr))
+               if (!bt_a2dp_register(&adapter_bdaddr)) {
+                       status = HAL_STATUS_FAILED;
                        goto failed;
+               }
 
                break;
        case HAL_SERVICE_ID_PAN:
-               if (!bt_pan_register(sk, &adapter_bdaddr))
+               if (!bt_pan_register(&adapter_bdaddr)) {
+                       status = HAL_STATUS_FAILED;
                        goto failed;
+               }
 
                break;
        default:
                DBG("service %u not supported", m->service_id);
+               status = HAL_STATUS_FAILED;
                goto failed;
        }
 
        services[m->service_id] = true;
 
-       ipc_send(g_io_channel_unix_get_fd(hal_cmd_io), HAL_SERVICE_ID_CORE,
-                                       HAL_OP_REGISTER_MODULE, 0, NULL, -1);
+       status = HAL_STATUS_SUCCESS;
 
        info("Service ID=%u registered", m->service_id);
-       return;
+
 failed:
-       ipc_send_rsp(g_io_channel_unix_get_fd(hal_cmd_io),
-                               HAL_SERVICE_ID_CORE, HAL_STATUS_FAILED);
+       ipc_send_rsp(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, status);
 }
 
-static void service_unregister(void *buf, uint16_t len)
+static void service_unregister(const void *buf, uint16_t len)
 {
-       struct hal_cmd_unregister_module *m = buf;
+       const struct hal_cmd_unregister_module *m = buf;
+       uint8_t status;
 
-       if (m->service_id > HAL_SERVICE_ID_MAX || !services[m->service_id])
+       if (m->service_id > HAL_SERVICE_ID_MAX || !services[m->service_id]) {
+               status = HAL_STATUS_FAILED;
                goto failed;
+       }
 
        switch (m->service_id) {
        case HAL_SERVICE_ID_BLUETOOTH:
@@ -152,36 +155,26 @@ static void service_unregister(void *buf, uint16_t len)
                /* 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;
        }
 
        services[m->service_id] = false;
 
-       ipc_send(g_io_channel_unix_get_fd(hal_cmd_io), HAL_SERVICE_ID_CORE,
-                                       HAL_OP_UNREGISTER_MODULE, 0, NULL, -1);
+       status = HAL_STATUS_SUCCESS;
 
        info("Service ID=%u unregistered", m->service_id);
-       return;
+
 failed:
-       ipc_send_rsp(g_io_channel_unix_get_fd(hal_cmd_io),
-                               HAL_SERVICE_ID_CORE, HAL_STATUS_FAILED);
+       ipc_send_rsp(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, status);
 }
 
-static void handle_service_core(uint8_t opcode, void *buf, uint16_t len)
-{
-       switch (opcode) {
-       case HAL_OP_REGISTER_MODULE:
-               service_register(buf, len);
-               break;
-       case HAL_OP_UNREGISTER_MODULE:
-               service_unregister(buf, len);
-               break;
-       default:
-               ipc_send_rsp(g_io_channel_unix_get_fd(hal_cmd_io),
-                               HAL_SERVICE_ID_CORE, HAL_STATUS_FAILED);
-               break;
-       }
-}
+static const struct ipc_handler cmd_handlers[] = {
+       /* HAL_OP_REGISTER_MODULE */
+       { service_register, false, sizeof(struct hal_cmd_register_module) },
+       /* HAL_OP_UNREGISTER_MODULE */
+       { service_unregister, false, sizeof(struct hal_cmd_unregister_module) },
+};
 
 static void bluetooth_stopped(void)
 {
@@ -211,169 +204,6 @@ static void stop_bluetooth(void)
        g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, quit_eventloop, NULL);
 }
 
-static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
-                                                       gpointer user_data)
-{
-       char buf[BLUEZ_HAL_MTU];
-       struct hal_hdr *msg = (void *) buf;
-       ssize_t ret;
-       int fd;
-
-       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               info("HAL command socket closed, terminating");
-               goto fail;
-       }
-
-       fd = g_io_channel_unix_get_fd(io);
-
-       ret = read(fd, buf, sizeof(buf));
-       if (ret < 0) {
-               error("HAL command read failed, terminating (%s)",
-                                                       strerror(errno));
-               goto fail;
-       }
-
-       if (ret < (ssize_t) sizeof(*msg)) {
-               error("HAL command too small, terminating (%zd)", ret);
-               goto fail;
-       }
-
-       if (ret != (ssize_t) (sizeof(*msg) + msg->len)) {
-               error("Malformed HAL command (%zd bytes), terminating", ret);
-               goto fail;
-       }
-
-       DBG("service_id %u opcode %u len %u", msg->service_id, msg->opcode,
-                                                               msg->len);
-
-       if (msg->service_id > HAL_SERVICE_ID_MAX ||
-                                               !services[msg->service_id]) {
-               error("HAL command for unregistered service %u, terminating",
-                                                       msg->service_id);
-               goto fail;
-       }
-
-       switch (msg->service_id) {
-       case HAL_SERVICE_ID_CORE:
-               handle_service_core(msg->opcode, msg->payload, msg->len);
-               break;
-       case HAL_SERVICE_ID_BLUETOOTH:
-               bt_bluetooth_handle_cmd(fd, msg->opcode, msg->payload,
-                                                               msg->len);
-               break;
-       case HAL_SERVICE_ID_HIDHOST:
-               bt_hid_handle_cmd(fd, msg->opcode, msg->payload, msg->len);
-               break;
-       case HAL_SERVICE_ID_SOCK:
-               bt_sock_handle_cmd(fd, msg->opcode, msg->payload, msg->len);
-               break;
-       case HAL_SERVICE_ID_A2DP:
-               bt_a2dp_handle_cmd(fd, msg->opcode, msg->payload, msg->len);
-               break;
-       case HAL_SERVICE_ID_PAN:
-               bt_pan_handle_cmd(fd, msg->opcode, msg->payload, msg->len);
-               break;
-       default:
-               ipc_send_rsp(fd, msg->service_id, HAL_STATUS_FAILED);
-               break;
-       }
-
-       return TRUE;
-
-fail:
-       stop_bluetooth();
-       return FALSE;
-}
-
-static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond,
-                                                       gpointer user_data)
-{
-       info("HAL notification socket closed, terminating");
-       stop_bluetooth();
-
-       return FALSE;
-}
-
-static GIOChannel *connect_hal(GIOFunc connect_cb)
-{
-       struct sockaddr_un addr;
-       GIOCondition cond;
-       GIOChannel *io;
-       int sk;
-
-       sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
-       if (sk < 0) {
-               error("Failed to create socket: %d (%s)", errno,
-                                                       strerror(errno));
-               return NULL;
-       }
-
-       io = g_io_channel_unix_new(sk);
-
-       g_io_channel_set_close_on_unref(io, TRUE);
-       g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-
-       memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH));
-
-       if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               error("Failed to connect HAL socket: %d (%s)", errno,
-                                                       strerror(errno));
-               g_io_channel_unref(io);
-               return NULL;
-       }
-
-       cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-
-       g_io_add_watch(io, cond, connect_cb, NULL);
-
-       return io;
-}
-
-static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond,
-                                                       gpointer user_data)
-{
-       DBG("");
-
-       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               stop_bluetooth();
-               return FALSE;
-       }
-
-       cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-
-       g_io_add_watch(io, cond, notif_watch_cb, NULL);
-
-       cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
-
-       g_io_add_watch(hal_cmd_io, cond, cmd_watch_cb, NULL);
-
-       info("Successfully connected to HAL");
-
-       return FALSE;
-}
-
-static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond,
-                                                       gpointer user_data)
-{
-       DBG("");
-
-       if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
-               stop_bluetooth();
-               return FALSE;
-       }
-
-       hal_notif_io = connect_hal(notif_connect_cb);
-       if (!hal_notif_io) {
-               error("Cannot connect to HAL, terminating");
-               stop_bluetooth();
-       }
-
-       return FALSE;
-}
-
 static void adapter_ready(int err, const bdaddr_t *addr)
 {
        if (err < 0) {
@@ -390,11 +220,7 @@ static void adapter_ready(int err, const bdaddr_t *addr)
 
        info("Adapter initialized");
 
-       hal_cmd_io = connect_hal(cmd_connect_cb);
-       if (!hal_cmd_io) {
-               error("Cannot connect to HAL, terminating");
-               stop_bluetooth();
-       }
+       ipc_init();
 }
 
 static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
@@ -477,18 +303,35 @@ static GOptionEntry options[] = {
        { NULL }
 };
 
-static void cleanup_hal_connection(void)
+static void cleanup_services(void)
 {
-       if (hal_cmd_io) {
-               g_io_channel_shutdown(hal_cmd_io, TRUE, NULL);
-               g_io_channel_unref(hal_cmd_io);
-               hal_cmd_io = NULL;
-       }
+       int i;
+
+       DBG("");
 
-       if (hal_notif_io) {
-               g_io_channel_shutdown(hal_notif_io, TRUE, NULL);
-               g_io_channel_unref(hal_notif_io);
-               hal_notif_io = NULL;
+       for (i = HAL_SERVICE_ID_BLUETOOTH; i < HAL_SERVICE_ID_MAX; i++) {
+               if (!services[i])
+                       continue;
+
+               switch (i) {
+               case HAL_SERVICE_ID_BLUETOOTH:
+                       bt_bluetooth_unregister();
+                       break;
+               case HAL_SERVICE_ID_SOCK:
+                       bt_socket_unregister();
+                       break;
+               case HAL_SERVICE_ID_HIDHOST:
+                       bt_hid_unregister();
+                       break;
+               case HAL_SERVICE_ID_A2DP:
+                       bt_a2dp_unregister();
+                       break;
+               case HAL_SERVICE_ID_PAN:
+                       bt_pan_unregister();
+                       break;
+               }
+
+               services[i] = false;
        }
 }
 
@@ -501,7 +344,11 @@ static bool set_capabilities(void)
        header.version = _LINUX_CAPABILITY_VERSION;
        header.pid = 0;
 
+       /* 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.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;
@@ -531,9 +378,6 @@ int main(int argc, char *argv[])
        GError *err = NULL;
        guint signal;
 
-       /* Core Service (ID=0) should always be considered registered */
-       services[0] = true;
-
        context = g_option_context_new(NULL);
        g_option_context_add_main_entries(context, options, NULL);
 
@@ -554,40 +398,60 @@ int main(int argc, char *argv[])
                exit(EXIT_SUCCESS);
        }
 
-       event_loop = g_main_loop_new(NULL, FALSE);
        signal = setup_signalfd();
        if (!signal)
                return EXIT_FAILURE;
 
        __btd_log_init("*", 0);
 
-       if (!set_capabilities())
+       if (!set_capabilities()) {
+               __btd_log_cleanup();
+               g_source_remove(signal);
                return EXIT_FAILURE;
+       }
 
        bluetooth_start_timeout = g_timeout_add_seconds(STARTUP_GRACE_SECONDS,
                                                        quit_eventloop, NULL);
        if (bluetooth_start_timeout == 0) {
                error("Failed to init startup timeout");
+               __btd_log_cleanup();
+               g_source_remove(signal);
                return EXIT_FAILURE;
        }
 
-       if (!bt_bluetooth_start(option_index, adapter_ready))
+       if (!bt_bluetooth_start(option_index, adapter_ready)) {
+               __btd_log_cleanup();
+               g_source_remove(bluetooth_start_timeout);
+               g_source_remove(signal);
                return EXIT_FAILURE;
+       }
 
        /* 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);
+
        g_main_loop_run(event_loop);
 
        g_source_remove(signal);
 
-       cleanup_hal_connection();
+       if (bluetooth_start_timeout > 0)
+               g_source_remove(bluetooth_start_timeout);
+
+       cleanup_services();
+
+       ipc_cleanup();
        stop_sdp_server();
        bt_bluetooth_cleanup();
        g_main_loop_unref(event_loop);
 
+       ipc_unregister(HAL_SERVICE_ID_CORE);
+
        info("Exit");
 
        __btd_log_cleanup();
index 46b3700..6b098b2 100644 (file)
 #include <fcntl.h>
 #include <glib.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 "profiles/network/bnep.h"
+
 #include "log.h"
 #include "pan.h"
 #include "hal-msg.h"
 #include "ipc.h"
+#include "utils.h"
+#include "bluetooth.h"
+
+static bdaddr_t adapter_addr;
+GSList *devices = NULL;
+uint8_t local_role = HAL_PAN_ROLE_NONE;
 
-static int notification_sk = -1;
+struct pan_device {
+       char            iface[16];
+       bdaddr_t        dst;
+       uint8_t         conn_state;
+       uint8_t         role;
+       GIOChannel      *io;
+       guint           watch;
+};
 
-static uint8_t bt_pan_enable(struct hal_cmd_pan_enable *cmd, uint16_t len)
+static int device_cmp(gconstpointer s, gconstpointer user_data)
 {
-       DBG("Not Implemented");
+       const struct pan_device *dev = s;
+       const bdaddr_t *dst = user_data;
 
-       return HAL_STATUS_FAILED;
+       return bacmp(&dev->dst, dst);
 }
 
-static uint8_t bt_pan_get_role(void *cmd, uint16_t len)
+static void pan_device_free(struct pan_device *dev)
 {
-       DBG("Not Implemented");
+       local_role = HAL_PAN_ROLE_NONE;
+
+       if (dev->watch > 0) {
+               g_source_remove(dev->watch);
+               dev->watch = 0;
+       }
+
+       if (dev->io) {
+               g_io_channel_unref(dev->io);
+               dev->io = NULL;
+       }
+
+       devices = g_slist_remove(devices, dev);
+       g_free(dev);
+}
+
+static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state)
+{
+       struct hal_ev_pan_conn_state ev;
+       char addr[18];
+
+       if (dev->conn_state == state)
+               return;
 
-       return HAL_STATUS_FAILED;
+       dev->conn_state = state;
+       ba2str(&dev->dst, addr);
+       DBG("device %s state %u", addr, state);
+
+       bdaddr2android(&dev->dst, ev.bdaddr);
+       ev.state = state;
+       ev.local_role = local_role;
+       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);
 }
 
-static uint8_t bt_pan_connect(struct hal_cmd_pan_connect *cmd, uint16_t len)
+static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state)
 {
-       DBG("Not Implemented");
+       struct hal_ev_pan_ctrl_state ev;
+
+       DBG("");
+
+       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));
 
-       return HAL_STATUS_FAILED;
+       ipc_send_notif(HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE, sizeof(ev),
+                                                                       &ev);
 }
 
-static uint8_t bt_pan_disconnect(struct hal_cmd_pan_connect *cmd, uint16_t len)
+static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+                                                               gpointer data)
 {
-       DBG("Not Implemented");
+       struct pan_device *dev = data;
+
+       DBG("%s disconnected", dev->iface);
 
-       return HAL_STATUS_FAILED;
+       bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+       pan_device_free(dev);
+
+       return FALSE;
 }
 
-void bt_pan_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len)
+static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data)
 {
-       uint8_t status = HAL_STATUS_FAILED;
+       struct pan_device *dev = data;
 
-       switch (opcode) {
-       case HAL_OP_PAN_ENABLE:
-               status = bt_pan_enable(buf, len);
-               break;
-       case HAL_OP_PAN_GET_ROLE:
-               status = bt_pan_get_role(buf, len);
+       DBG("");
+
+       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;
+       }
+
+       memcpy(dev->iface, iface, sizeof(dev->iface));
+
+       DBG("%s connected", dev->iface);
+
+       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;
+       int perr, sk;
+
+       DBG("");
+
+       if (err) {
+               error("%s", err->message);
+               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;
+       sk = g_io_channel_unix_get_fd(dev->io);
+
+       perr = bnep_connect(sk, src, dst, bnep_conn_cb, dev);
+       if (perr < 0) {
+               error("bnep connect req failed: %s", strerror(-perr));
+               goto fail;
+       }
+
+       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)
+{
+       const struct hal_cmd_pan_connect *cmd = buf;
+       struct pan_device *dev;
+       uint8_t status;
+       bdaddr_t dst;
+       char addr[18];
+       GSList *l;
+       GError *gerr = NULL;
+
+       DBG("");
+
+       switch (cmd->local_role) {
+       case HAL_PAN_ROLE_NAP:
+               if (cmd->remote_role != HAL_PAN_ROLE_PANU) {
+                       status = HAL_STATUS_UNSUPPORTED;
+                       goto failed;
+               }
                break;
-       case HAL_OP_PAN_CONNECT:
-               status = bt_pan_connect(buf, len);
+       case HAL_PAN_ROLE_PANU:
+               if (cmd->remote_role != HAL_PAN_ROLE_NAP &&
+                                       cmd->remote_role != HAL_PAN_ROLE_PANU) {
+                       status = HAL_STATUS_UNSUPPORTED;
+                       goto failed;
+               }
                break;
-       case HAL_OP_PAN_DISCONNECT:
-               status = bt_pan_disconnect(buf, len);
+       default:
+               status = HAL_STATUS_UNSUPPORTED;
+               goto failed;
+       }
+
+       android2bdaddr(&cmd->bdaddr, &dst);
+
+       l = g_slist_find_custom(devices, &dst, device_cmp);
+       if (l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       dev = g_new0(struct pan_device, 1);
+       bacpy(&dev->dst, &dst);
+       local_role = cmd->local_role;
+       dev->role = cmd->remote_role;
+
+       ba2str(&dev->dst, addr);
+       DBG("connecting to %s %s", addr, dev->iface);
+
+       dev->io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+                                       BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                                       BT_IO_OPT_DEST_BDADDR, &dev->dst,
+                                       BT_IO_OPT_PSM, BNEP_PSM,
+                                       BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+                                       BT_IO_OPT_OMTU, BNEP_MTU,
+                                       BT_IO_OPT_IMTU, BNEP_MTU,
+                                       BT_IO_OPT_INVALID);
+       if (!dev->io) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               g_free(dev);
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       devices = g_slist_append(devices, dev);
+       bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING);
+
+       status =  HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status);
+}
+
+static void bt_pan_disconnect(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_pan_disconnect *cmd = buf;
+       struct pan_device *dev;
+       uint8_t status;
+       GSList *l;
+       bdaddr_t dst;
+
+       DBG("");
+
+       android2bdaddr(&cmd->bdaddr, &dst);
+
+       l = g_slist_find_custom(devices, &dst, device_cmp);
+       if (!l) {
+               status = HAL_STATUS_FAILED;
+               goto failed;
+       }
+
+       dev = l->data;
+
+       if (dev->watch) {
+               g_source_remove(dev->watch);
+               dev->watch = 0;
+       }
+
+       bnep_if_down(dev->iface);
+       bnep_kill_connection(&dst);
+
+       bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
+       pan_device_free(dev);
+
+       status = HAL_STATUS_SUCCESS;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, status);
+}
+
+static void bt_pan_enable(const void *buf, uint16_t len)
+{
+       const struct hal_cmd_pan_enable *cmd = buf;
+       uint8_t status;
+
+       switch (cmd->local_role) {
+       case HAL_PAN_ROLE_PANU:
+       case HAL_PAN_ROLE_NAP:
+               DBG("Not Implemented");
+               status  = HAL_STATUS_FAILED;
                break;
        default:
-               DBG("Unhandled command, opcode 0x%x", opcode);
+               status = HAL_STATUS_UNSUPPORTED;
                break;
        }
 
-       ipc_send_rsp(sk, HAL_SERVICE_ID_PAN, status);
+       ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status);
 }
 
-bool bt_pan_register(int sk, const bdaddr_t *addr)
+static void bt_pan_get_role(const void *buf, uint16_t len)
 {
+       struct hal_rsp_pan_get_role rsp;
+
        DBG("");
 
-       notification_sk = sk;
+       rsp.local_role = local_role;
+       ipc_send_rsp_full(HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, sizeof(rsp),
+                                                               &rsp, -1);
+}
+
+static const struct ipc_handler cmd_handlers[] = {
+       /* HAL_OP_PAN_ENABLE */
+       { bt_pan_enable, false, sizeof(struct hal_cmd_pan_enable) },
+       /* HAL_OP_PAN_GET_ROLE */
+       { bt_pan_get_role, false, 0 },
+       /* HAL_OP_PAN_CONNECT */
+       { bt_pan_connect, false, sizeof(struct hal_cmd_pan_connect) },
+       /* HAL_OP_PAN_DISCONNECT */
+       { bt_pan_disconnect, false, sizeof(struct hal_cmd_pan_disconnect) },
+};
+
+bool bt_pan_register(const bdaddr_t *addr)
+{
+       int err;
+
+       DBG("");
+
+       bacpy(&adapter_addr, addr);
+
+       err = bnep_init();
+       if (err) {
+               error("bnep init failed");
+               return false;
+       }
+
+       ipc_register(HAL_SERVICE_ID_PAN, cmd_handlers,
+                                               G_N_ELEMENTS(cmd_handlers));
 
        return true;
 }
@@ -103,5 +364,7 @@ void bt_pan_unregister(void)
 {
        DBG("");
 
-       notification_sk = -1;
+       bnep_cleanup();
+
+       ipc_unregister(HAL_SERVICE_ID_PAN);
 }
index 2430378..3178d88 100644 (file)
@@ -21,7 +21,5 @@
  *
  */
 
-void bt_pan_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len);
-
-bool bt_pan_register(int sk, const bdaddr_t *addr);
+bool bt_pan_register(const bdaddr_t *addr);
 void bt_pan_unregister(void);
diff --git a/android/pics-did.txt b/android/pics-did.txt
new file mode 100644 (file)
index 0000000..e8c914a
--- /dev/null
@@ -0,0 +1,42 @@
+DID PICS for the PTS tool.
+
+* - 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
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_DID_1_1   True            Specification ID (M)
+TSPC_DID_1_2   True            Vendor ID (M)
+TSPC_DID_1_3   True            Product ID (M)
+TSPC_DID_1_4   True            Version (M)
+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.
diff --git a/android/pics-gap.txt b/android/pics-gap.txt
new file mode 100644 (file)
index 0000000..cd274e8
--- /dev/null
@@ -0,0 +1,714 @@
+GAP PICS for the PTS tool.
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Device Configuration
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_0_1   False           BR/EDR (C.1)
+TSPC_GAP_0_2   False           LE (C.2)
+TSPC_GAP_0_3   True (*)        BR/EDR/LE (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if ('End Product' or 'Host Subsystem') and ('BR Host' or
+       'BR/HS Host') are Supported ('End Product' or 'Host Subsystem' with 'BR'
+       or 'BR/HS Host' CC), otherwise excluded. Optional for
+       'Component (Tested)' or 'Component (Non-Tested)'.
+C.2: Mandatory if ('End Product' or 'Host Subsystem') and ('LE Host') are
+       Supported (End Product or Host Subsystem with LE Host CC),
+       otherwise excluded.  Optional for 'Component (Tested)' or
+       'Component (Non-Tested)'.
+C.3: Mandatory if ('End Product' or 'Host Subsystem') and ('BR/LE Host' or
+       'BR/HS/LE Host') are Supported (End Product or Host Subsystem with
+       BR/LE or BR/HS/LE Host CC), otherwise excluded.
+       Optional for 'Component (Tested)' or 'Component (Non-tested)'.
+Note - Only one transport shall be supported.
+-------------------------------------------------------------------------------
+
+
+               Version Configuration
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_0A_1  False           Core Specification Addendum 3 (CSA3),
+                                       GAP Connection Parameters Changes,
+                                       Authentication and Lost Bond Changes,
+                                       Private Addressing Changes,
+                                       Dual Mode Addressing Changes,
+                                       Adopted 24 July 2012 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Modes
+-------------------------------------------------------------------------------
+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_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)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_0_2 is supported, otherwise Optional.
+C.2: Mandatory if TSPC_GAP_3_5 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+               Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_2_1   True (*)        Authentication procedure (C.1)
+TSPC_GAP_2_2   True (*)        Support of LMP-Authentication (M)
+TSPC_GAP_2_3   True (*)        Initiate LMP-Authentication (C.5)
+TSPC_GAP_2_4   False           Security mode 1 (C.2)
+TSPC_GAP_2_5   True (*)        Security mode 2 (O)
+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)
+-------------------------------------------------------------------------------
+C.1: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6) is supported, otherwise
+       Optional.
+Note 1: The Authentication Procedure in item GAP, TSPC_GAP_2_1 is the one
+       described in Fig. 5.1 on page 198 in the GAP Profile Specification and
+       not the LMP-Authenticaion.
+C.2: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional.
+C.5: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6 or TSPC_GAP_2_7) is supported,
+       otherwise Optional.
+C.4: Mandatory if (Core Spec 2.1 or later) is supported, otherwise Excluded.
+Note 2. If a Core 2.0 and earlier design claims to support secure communcation
+       it should support either Security mode 2 or 3.
+Note 3. A Core 2.1 or later device shall always support secure communication
+       in Security Mode 4, and shall use that mode to connect with another
+       Core 2.1 or later device. It shall use Security Mode 2 only for
+       backward compatibility purposes with Core 2.0 and earlier devices.
+       Security Mode 1 is excluded for Core 2.1 or later devices based on
+       condition C.2.
+C.6: If TSPC_GAP_2_7 is supported then at least one of (TSPC_GAP_2_8 or
+       TSPC_GAP_2_9 or TSPC_GAP_2_10) is Mandatory, otherwise Excluded.
+C.7: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+               Idle Mode Procedures
+-------------------------------------------------------------------------------
+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_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)
+TSPC_GAP_3_6   True (*)        Initiation of dedicated bonding (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of TSPC_GAP_3_1 or TSPC_GAP_3_2 if
+       TSPC_GAP_3_5 is supported, otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+               Establishment Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_4_1   True            Support link establishment as initiator (M)
+TSPC_GAP_4_2   True            Support link establishment as acceptor (M)
+TSPC_GAP_4_3   True (*)        Support channel establishment as initiator (O)
+TSPC_GAP_4_4   True            Support channel establishment as acceptor (M)
+TSPC_GAP_4_5   True (*)        Support connection establishment as initiator
+                                       (O)
+TSPC_GAP_4_6   True (*)        Support connection establishment as acceptor
+                                       (O)
+-------------------------------------------------------------------------------
+
+
+               LE Roles
+-------------------------------------------------------------------------------
+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)
+-------------------------------------------------------------------------------
+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
+       PTS is checking this precondition also in some BR/EDR/LE tests.
+-------------------------------------------------------------------------------
+
+
+               Broadcaster Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_6_1   False (*)       Broadcaster: Transmitter (M)
+TSPC_GAP_6_2   False           Broadcaster: Receiver (O)
+-------------------------------------------------------------------------------
+
+
+               Broadcaster Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_7_1   False (*)       Broadcaster: Standby (M)
+TSPC_GAP_7_2   False (*)       Broadcaster: Advertising (M)
+-------------------------------------------------------------------------------
+
+
+               Broadcaster Link Layer Advertising Event Types
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_8_1   False (*)       Broadcaster: Non-Connectable Undirected Event
+                                       (M)
+TSPC_GAP_8_2   False           Broadcaster: Scannable Undirected Event (O)
+-------------------------------------------------------------------------------
+
+
+               Broadcaster Link Layer Advertising Data Types
+-------------------------------------------------------------------------------
+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_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)
+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)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_SM_2_4 (OOB supported) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Broadcaster Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_9_1   False (*)       Broadcaster: Non-Connectable Mode
+-------------------------------------------------------------------------------
+
+
+               Broadcaster Broadcasting and Observing Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_10_1  False (*)       Broadcaster: Broadcast Mode
+TSPC_GAP_11_1  False           Broadcaster: Privacy Feature
+TSPC_GAP_11_2  False           Broadcaster: Resolvable Private Address
+                                       Generation Procedure
+-------------------------------------------------------------------------------
+
+
+               Observer Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_12_1  True (#)        Observer: Receiver
+TSPC_GAP_12_2  False           Observer: Transmitter
+-------------------------------------------------------------------------------
+
+
+               Observer Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_13_1  True (#)        Observer: Standby
+TSPC_GAP_13_2  True (#)        Observer: Scanning
+-------------------------------------------------------------------------------
+
+
+               Observer Link Layer Scanning Types
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_14_1  True (#)        Observer: Passive Scanning
+TSPC_GAP_14_2  False           Observer: Active Scanning
+-------------------------------------------------------------------------------
+
+
+               Observer Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_15_1  True (#)        Observer: Non-Connectable Mode
+-------------------------------------------------------------------------------
+
+
+               Observer Broadcasting and Observing Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_16_1  True (#)        Observer: Observation Procedure
+-------------------------------------------------------------------------------
+
+
+               Observer Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_17_1  False           Observer: Privacy Feature (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
+                                       Procedure (C.2)
+TSPC_GAP_17_4  False           Observer: Resolvable Private Address Generation
+                                       Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_17_1 and TSPC_GAP_14_2 (Active Scanning) are
+       supported and TSPC_GAP_17_4 (Resolvable Private Address Generation
+       Procedure) is Not Supported; Optional if CSA3 or later and
+       TSPC_GAP_17_4 are supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_17_1 is supported, otherwise Excluded.
+C.3: Mandatory if CSA3 or later and TSPC_GAP_17_1 and TSPC_GAP_14_2
+       (Active Scanning) are supported and TSPC_GAP_17_2 (Non-Resolvable
+       Private Address Generation Procedure) is not supported; Optional if
+       CSA3 or later and TSPC_GAP_17_2 (Non-Resolvable Private Address
+       Generation Procedure) are supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Peripheral Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_18_1  False           Peripheral: Transmitter
+TSPC_GAP_18_2  False           Peripheral: Receiver
+-------------------------------------------------------------------------------
+
+
+               Peripheral Link Layer States
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_19_1  False           Peripheral: Standby
+TSPC_GAP_19_2  False           Peripheral: Advertising
+TSPC_GAP_19_3  False           Peripheral: Connection, Slave Role
+-------------------------------------------------------------------------------
+
+
+               Peripheral Link Layer Advertising Event Types
+-------------------------------------------------------------------------------
+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_3  False           Peripheral: Non-Connectable Undirected Event
+TSPC_GAP_20_4  False           Peripheral: Scannable Undirected Event
+-------------------------------------------------------------------------------
+
+
+               Peripheral Link Layer Advertising Data Types
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_20A_1 False           AD Type-Service UUID (C.1)
+TSPC_GAP_20A_2 False           AD Type-Local Name (C.1)
+TSPC_GAP_20A_3 False           AD Type-Flags (C.2)
+TSPC_GAP_20A_4 False           AD Type-Manufacturer Specific Data (C.1)
+TSPC_GAP_20A_5 False           AD Type-TX Power Level (C.1)
+TSPC_GAP_20A_6 False           AD Type-Security Manager Out of Band (OOB) (C.3)
+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)
+-------------------------------------------------------------------------------
+C.1: Optional if (TSPC_GAP_20_1 or TSPC_GAP_20_3 or TSPC_GAP_20_4) is
+       supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_22_2 (Limited Discoverable Mode) or TSPC_GAP_22_3
+       (General Discoverable Mode) is supported, otherwise Optional.
+C.3: Optional if (TSPC_GAP_20_1 (Connectable Undirected Event) or TSPC_GAP_20_3
+       (Non-Connectable Undirected Event) or TSPC_GAP_20_4
+       (Scannable Undirected Event)) and TSPC_SM_2_4 (OOB supported) are
+       supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Peripheral Link Layer Control Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_21_1  False (*)       Peripheral: Connection Update Procedure (M)
+TSPC_GAP_21_2  False (*)       Peripheral: Channel Map Update Procedure (M)
+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)
+-------------------------------------------------------------------------------
+
+
+               Peripheral Discovery Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_22_1  False           Peripheral: Non-Discoverable Mode (C.2)
+TSPC_GAP_22_2  False           Peripheral: Limited Discoverable Mode (C.1)
+TSPC_GAP_22_3  False           Peripheral: General Discoverable Mode (C.1)
+TSPC_GAP_22_4  False           Peripheral: Name Discovery Procedure (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if (TSPC_GAP_5_3 OR TSPC_GAP_42_2), otherwise Excluded.
+C.2: Mandatory if (TSPC_GAP_5_3 or TSPC_GAP_42_1) is supported,
+       otherwise Excluded.
+C.3: Optional if TSPC_GAP_5_3 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Peripheral Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_23_1  False           Peripheral: Non-Connectable Mode (C.1)
+TSPC_GAP_23_2  False           Peripheral: Directed Connectable Mode (O)
+TSPC_GAP_23_3  False           Peripheral: Undirected Connectable Mode (M)
+TSPC_GAP_23_4  False           Peripheral: Connection Parameter Update
+                                       Procedure (O)
+TSPC_GAP_23_5  False           Peripheral: Terminate Connection Procedure (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_5_3 (LE Only â€“ Peripheral role) OR TSPC_GAP_42_3
+       (BR/EDR/LE â€“ Non-Connectable Mode) OR TSPC_GAP_42_4
+       (BR/EDR/LE â€“ Connectable Mode) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Peripheral Bonding Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_24_1  False           Peripheral: Non-Bondable Mode (M)
+TSPC_GAP_24_2  False           Peripheral: Bondable Mode (C.1)
+TSPC_GAP_24_3  False           Peripheral: Bonding Procedure  (C.2)
+TSPC_GAP_24_4  False           Peripheral: Multiple Bonds (C.3)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_5_3 (LE Only â€“ Peripheral role) OR (TSPC_GAP_38_3
+       (BR/EDR/LE â€“ Peripheral role) AND NOT TSPC_GAP_42_6 (BR.EDR/LE -
+       Bondable Mode)) is supported, Mandatory if TSPC_GAP_42_6
+       (BR/EDR/LE â€“ Bondable Mode) is supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_24_2 (Bondable Mode) is supported, otherwise Excluded
+-------------------------------------------------------------------------------
+
+
+               Peripheral Security Aspects Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_25_1  False           Peripheral: Security Mode (O)
+TSPC_GAP_25_2  False           Peripheral: Security Mode 2 (O)
+TSPC_GAP_25_3  False           Peripheral: Authentication Procedure (C.2)
+TSPC_GAP_25_4  False           Peripheral: Authorization Procedure (O)
+TSPC_GAP_25_5  False           Peripheral: Connection Data Signing Procedure
+                                       (O)
+TSPC_GAP_25_6  False           Peripheral: Authenticate Signed Data Procedure
+                                       (O)
+TSPC_GAP_25_7  False           Peripheral: Authenticated Pairing
+                                       (LE security mode 1 level 3) (C.1)
+TSPC_GAP_25_8  False           Peripheral: Unauthenticated Pairing
+                                       (LE security mode 1 level 2) (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_25_1 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported,
+       otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+               Peripheral Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_26_1  False           Peripheral: Privacy Feature (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)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Peripheral GAP Characteristics
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_27_1  False (*)       Peripheral: Device Name (M)
+TSPC_GAP_27_2  False (*)       Peripheral: Appearance (M)
+TSPC_GAP_27_3  False           Peripheral: Peripheral Privacy Flag (C.1)
+TSPC_GAP_27_4  False           Peripheral: Reconnection Address (C.2)
+TSPC_GAP_27_5  False           Peripheral: Peripheral Preferred Connection
+                                       Parameters (O)
+TSPC_GAP_27_6  False           Peripheral: Writeable Device Name (O)
+TSPC_GAP_27_7  False           Peripheral: Writeable Appearance (O)
+TSPC_GAP_27_8  False           Peripheral: Writeable Peripheral Privacy Flag
+                               (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded.
+C.2: Optional if TSPC_GAP_26_1 and TSPC_GAP_27_3 are supported,
+       otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Central Physical Layer
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_28_1  True (*#)       Central: Transmitter (M)
+TSPC_GAP_28_2  True (*#)       Central: Receiver (M)
+-------------------------------------------------------------------------------
+
+
+               Central Link Layer States
+-------------------------------------------------------------------------------
+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)
+-------------------------------------------------------------------------------
+
+
+               Central Link Layer Scanning Types
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+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)
+       is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Central Link Layer Control Procedures
+-------------------------------------------------------------------------------
+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)
+-------------------------------------------------------------------------------
+
+
+               Central Discovery Modes and Procedures
+-------------------------------------------------------------------------------
+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)
+-------------------------------------------------------------------------------
+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,
+       otherwise Excluded.
+C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_4) is supported,
+       otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Central Connection Modes and Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_33_1  True (*#)       Central: Auto Connection Establishment
+                                       Procedure (C.3)
+TSPC_GAP_33_2  True (*#)       Central: General Connection Establishment
+                                       Procedure (C.1)
+TSPC_GAP_33_3  True (*#)       Central: Selective Connection Establishment
+                                       Procedure (C.3)
+TSPC_GAP_33_4  True (*#)       Central: Direct Connectin Establishment
+                                       Procedure (C.2)
+TSPC_GAP_33_5  True (*#)       Central: Connection Parameter Update Procedure
+                                       (C.2)
+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
+       supported, otherwise Optional.
+C.2: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported,
+       otherwise Excluded.
+C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported,
+       otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Central Bonding Modes and Procedures
+-------------------------------------------------------------------------------
+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)
+-------------------------------------------------------------------------------
+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.
+-------------------------------------------------------------------------------
+
+
+               Central Security Features
+-------------------------------------------------------------------------------
+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
+                                       (LE security mode 1 level 3) (C.1)
+TSPC_GAP_35_8  False           Central: Unauthenticated Pairing
+                                       (LE security mode 1 level 2) (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Central Privacy Feature
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_36_1  False           Central: Privacy Feature (C.3)
+TSPC_GAP_36_2  False           Central: Non-Resolvable Private Address
+                                       Generation Procedure (C.1)
+TSPC_GAP_36_3  False           Central: Resolvable Private Address Resolution
+                                       Procedure (C.2)
+TSPC_GAP_36_4  False           Central: Write to Privacy Characteristic
+                                       (Enable/Disable Privacy) (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_GAP_36_1 and TSPC_GAP_30_2 are supported,
+       otherwise Excluded.
+C.2: Mandatory if TSPC_GAP_36_1 is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Central GAP Characteristics
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_37_1  True (*#)       Central: Device Name (M)
+TSPC_GAP_37_2  True (*#)       Central: Appearance (M)
+-------------------------------------------------------------------------------
+
+
+               BR/EDR/LE Roles
+-------------------------------------------------------------------------------
+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_3  False           BR/EDR/LE: Peripheral (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
+configurations, see 'LE Roles' table for role declarations.
+-------------------------------------------------------------------------------
+
+
+               Central BR/EDR/LE Modes
+-------------------------------------------------------------------------------
+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_5  False           Central BR/EDR/LE: Non-Bondable Mode (C.4)
+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,
+       otherwise Excluded.
+C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Central BR/EDR/LE Idle Mode Procedures
+-------------------------------------------------------------------------------
+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)
+-------------------------------------------------------------------------------
+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.
+C.3: Mandatory if (TSPC_GAP_3_1 or TSPC_GAP_3_2) is supported over BR/EDR,
+       otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_3_3 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if (TSPC_GAP_4_1 or TSPC_GAP_4_2) is supported over BR/EDR,
+       otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Central BR/EDR/LE Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_41_1  True (#)        Central BR/EDR/LE: Security Aspects (M)
+-------------------------------------------------------------------------------
+
+
+               Peripheral BR/EDR/LE Modes
+-------------------------------------------------------------------------------
+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)
+-------------------------------------------------------------------------------
+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,
+       otherwise Excluded.
+C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded.
+C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded.
+C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Peripheral BR/EDR/LE Security Aspects
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_GAP_43_1  False (*)       Peripheral BR/EDR/LE: Security Aspects (M)
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+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
+-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
+No special PIXIT settings required. All should be set according to Tester's
+test environment.
diff --git a/android/pics-hid.txt b/android/pics-hid.txt
new file mode 100644 (file)
index 0000000..ba50626
--- /dev/null
@@ -0,0 +1,290 @@
+HID PICS for the PTS tool.
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_1_1   True (*)        Role: Host, Report protocol (O.1)
+TSPC_HID_1_2   False           Role: HID Role (O.1)
+TSPC_HID_1_3   False           Role: Host, Boot protocol (O.1)
+-------------------------------------------------------------------------------
+O.1: It is Mandatory to support One of these roles.
+-------------------------------------------------------------------------------
+
+
+               Application Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_2_1   True            Host: Establish HID connection (M.1)
+TSPC_HID_2_2   True            Host: Accept HID connection (M.1)
+TSPC_HID_2_3   True            Host: Terminate HID connection (M.1)
+TSPC_HID_2_4   True            Host: Accept termination of HID connection (M.1)
+TSPC_HID_2_5   True            Host: Support for virtual cables (M.1)
+TSPC_HID_2_6   True            Host: HID initiated connection (M.1)
+TSPC_HID_2_7   True            Host: Host initiated connection (M.1)
+TSPC_HID_2_8   True            Host: Host data transfer to HID (C.1)
+TSPC_HID_2_9   True            Host: HID data transfer to Host (C.1)
+TSPC_HID_2_10  False           Host: Boot mode data transfer to Host (C.2)
+TSPC_HID_2_11  False           Host : Boot mode data transfer to HID (C.2)
+TSPC_HID_2_12  False           Host : Support for Application to send
+                                       GET_Report (O)
+TSPC_HID_2_13  False           Host : Support for Application to send
+                                       SET_REPORT (O)
+TSPC_HID_2_14  False           Host : Support for sending HCI_CONTROL with
+                                       VIRTUAL_CABLE_UNPLUG (C.3)
+TSPC_HID_2_15  False           Host : Support for receiving HCI_CONTROL with
+                                       VIRTUAL_CABLE_UNPLUG (C.2)
+-------------------------------------------------------------------------------
+M.1: Mandatory to support IF (TSPC_HID_1_1) supported.
+C.1: Optional for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory
+       for Host Role (TSPC_HID_1_1).
+C.2: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional.
+C.3: Optional IF (TSPC_HID_2_5) supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+               Device to Host Transfers
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_3_1   False           Host : Data reports larger than host MTU on
+                                       Control channel (O)
+TSPC_HID_3_2   True (*)        Host : Data reports larger than host MTU on
+                                       Interrupt channel (C.1)
+TSPC_HID_3_3   True (*)        Host : Data reports to host (C.1)
+TSPC_HID_3_4   False           Host : Boot mode reports to host (C.2)
+-------------------------------------------------------------------------------
+C.1: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory if
+       (TSPC_HID_2_12), otherwise Optional.
+C.2: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional.
+-------------------------------------------------------------------------------
+
+
+               Host to Device Transfers
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_4_1   False           Host : Data reports larger than device MTU on
+                                       Control channel (C.1)
+TSPC_HID_4_2   False           Host : Data reports larger than device MTU on
+                                       Interrupt channel (C.1)
+TSPC_HID_4_3   True (*)        Host : Data reports to device (C.2)
+TSPC_HID_4_4   False           Host : Boot mode reports to device (O)
+-------------------------------------------------------------------------------
+C.1: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional
+C.2: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory for
+       Host Role (TSPC_HID_1_1).
+-------------------------------------------------------------------------------
+
+
+               HID Control Commands
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_5_1   False           Host : Set_Protocol command (C.1)
+TSPC_HID_5_2   False           Host : Get_Protocol command (C.1)
+TSPC_HID_5_3   False           Host : Set_Idle command (O)
+TSPC_HID_5_4   False           Host : Get_Idle command (O)
+TSPC_HID_5_5   False (*)       Host : Set_Report command (M.1)
+TSPC_HID_5_6   False (*)       Host : Get_Report command (M.2)
+-------------------------------------------------------------------------------
+M.1: Mandatory IF (TSPC_HID_1_1) supported AND (TSPC_HID_2_13) supported.
+C.1: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional.
+       If either Set_Protocol or Get_Protocol supported, both are Mandatory.
+M.2: Mandatory IF (TSPC_HID_1_1) Supported AND (TSPC_HID_2_12) Supported
+-------------------------------------------------------------------------------
+
+
+               Host Link Manager Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_6_1   False           Host : Initiate Authentication before
+                                       connection completed (C.1)
+TSPC_HID_6_2   False           Host : Initiate Authentication after connection
+                                       completed (C.1)
+TSPC_HID_6_3   False           Host : Initiate pairing before connection
+                                       completed (C.2)
+TSPC_HID_6_4   False           Host : Initiate pairing after connection
+                                       completed (C.2)
+TSPC_HID_6_5   False           Host : Encryption (O)
+TSPC_HID_6_6   False           Host : Initiate encryption (C.3)
+TSPC_HID_6_7   False           Host : Accept encryption requests (C.3)
+TSPC_HID_6_8   True            Host : Role switch (Master/Slave) (M.1)
+TSPC_HID_6_9   True            Host : Request Master Slave switch (M.1)
+TSPC_HID_6_10  True            Host : Accept Master Slave switch requests (M.1)
+TSPC_HID_6_11  False           Host : Hold mode (O)
+TSPC_HID_6_12  True            Host : Sniff mode (M.1)
+TSPC_HID_6_13  False           Host : Park mode (O)
+-------------------------------------------------------------------------------
+C.1: If Host Authentication supported, both (TSPC_HID_6_1) AND (TSPC_HID_6_2)
+       must be supported.
+C.2: If Pairing supported both (TSPC_HID_6_3) AND (TSPC_HID_6_4) must
+       be supported.
+M.1: Mandatory IF (TSPC_HID_1_1) supported.
+C.3: Mandatory IF (TSPC_HID_6_5) encryption supported.
+-------------------------------------------------------------------------------
+
+
+               Host Link Control Requirements
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_7_1   True            Host : Supports inquiry, 79 channel (M.1)
+TSPC_HID_7_2   False (*)       Host : Supports inquiry scan, 79 channel (X)
+-------------------------------------------------------------------------------
+M.1: Mandatory to support IF (TSPC_HID_1_1) supported.
+X: Feature should not be used by a Host, but can be supported in LM.
+-------------------------------------------------------------------------------
+
+
+               HID Device Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_8_1   False           Hid : Pointing HID (O.1)
+TSPC_HID_8_2   False           Hid : Keyboard HID (O.1)
+TSPC_HID_8_3   False           Hid : Identification HID (O.1)
+TSPC_HID_8_4   False           Hid : Other HID (O.1)
+-------------------------------------------------------------------------------
+O.1: It is Mandatory to support One of these roles IF (TSPC_HID_1_2)
+       is selected
+-------------------------------------------------------------------------------
+
+
+               HID Application Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_9_1   False           Hid : Establish HID connection (O)
+TSPC_HID_9_2   False (*)       Hid : Accept HID connection (M.1)
+TSPC_HID_9_3   False           Hid : Terminate HID connection (O)
+TSPC_HID_9_4   False (*)       Hid : Accept Termination of HID connection (M.1)
+TSPC_HID_9_5   False           Hid : Support for virtual cables (O)
+TSPC_HID_9_6   False           Hid : HID initiated reconnection (C.1)
+TSPC_HID_9_7   False           Hid : Host initiated reconnection (C.1)
+TSPC_HID_9_8   False           Hid : Host data transfer to HID (C.2)
+TSPC_HID_9_9   False           Hid : HID data transfer to Host (C.2)
+TSPC_HID_9_10  False           Hid : HID Boot mode data transfer to Host (C.3)
+TSPC_HID_9_11  False           Hid : Host Boot mode data transfer to HID (C.4)
+TSPC_HID_9_12  False           Hid : Output reports declared (C.4)
+TSPC_HID_9_13  False           Hid : Input reports declared (C.3)
+TSPC_HID_9_14  False           Hid : Feature reports declared (O)
+TSPC_HID_9_15  False           Hid : Support for sending HCI_CONTROL with
+                                       VIRTUAL_CABLE_UNPLUG (C.5)
+TSPC_HID_9_16  False           Hid : Support for receiving HCI_CONTROL with
+                                       VIRTUAL_CABLE_UNPLUG (C.5)
+-------------------------------------------------------------------------------
+M.1: Mandatory IF (TSPC_HID_1_2) supported.
+C.1: One of these is Mandatory IF (TSPC_HID_9_5) is supported
+       (SDP attribute 0x204=True)
+C.2: One of these is Mandatory.
+C.3: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is selected
+C.4: Mandatory IF (TSPC_HID_8_2) is supported (for status indicators)
+C.5: Optional IF (TSPC_HID_9_2) supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+               Device to Host Transfers
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_10_1  False           Hid : Data reports larger than host MTU on
+                                       Control channel (O)
+TSPC_HID_10_2  False           Hid : Data reports larger than host MTU on
+                                       Interrupt channel (O)
+TSPC_HID_10_3  False           Hid : Data reports to host (O)
+TSPC_HID_10_4  False           Hid : Boot mode reports to host (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is supported.
+       Optional for other HIDs.
+-------------------------------------------------------------------------------
+
+
+               Host to Device Transfers
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_11_1  False           Hid : Data reports larger than device MTU on
+                                       Control channel (O)
+TSPC_HID_11_2  False           Hid : Data reports larger than device MTU on
+                                       Interrupt channel (O)
+TSPC_HID_11_3  False           Hid : Data reports to device (O)
+TSPC_HID_11_4  False           Hid : Boot mode reports to device (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (TSPC_HID_8_2) is supported. Optional for other HIDs.
+-------------------------------------------------------------------------------
+
+
+               HID Control Commands
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_12_1  False           Hid : Set_Protocol command (C.1)
+TSPC_HID_12_2  False           Hid : Get_Protocol command (C.1)
+TSPC_HID_12_3  False           Hid : Set_Idle command (C.2)
+TSPC_HID_12_4  False           Hid : Get_Idle command (C.2)
+TSPC_HID_12_5  False           Hid : Set_Report command (C.3)
+TSPC_HID_12_6  False           Hid : Get_Report command (C.4)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is supported.
+       Optional for other HIDs. If either Set_Protocol or Get_Protocol
+       supported, both are Mandatory.
+C.2: Mandatory IF (TSPC_HID_8_2) Keyboard is selected. Optional for other HIDs.
+C.3: Mandatory IF (TSPC_HID_9_12) or (TSPC_HID_9_14) supported.
+C.4: Mandatory IF (TSPC_HID_9_13) or (TSPC_HID_9_14) supported
+-------------------------------------------------------------------------------
+
+
+               HID Link Manager Procedures
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_13_1  False           Hid : Host initiated Authentication before
+                                       connection completed (C.1)
+TSPC_HID_13_2  False           Hid : Host initiated Authentication after
+                                       connection completed (C.1)
+TSPC_HID_13_3  False           Hid : Initiate pairing before connection
+                                       completed (X)
+TSPC_HID_13_4  False           Hid : Initiate pairing after connection
+                                       completed (X)
+TSPC_HID_13_5  False           Hid : Encryption (C.1)
+TSPC_HID_13_6  False           Hid : Initiate encryption (O)
+TSPC_HID_13_7  False           Hid : Accept encryption requests (C.2)
+TSPC_HID_13_8  False           Hid : Role switch (Master/Slave) (C.3)
+TSPC_HID_13_9  False           Hid : Request Master Slave switch (O)
+TSPC_HID_13_10 False           Hid : Accept Master Slave switch requests (C.3)
+TSPC_HID_13_11 False           Hid : Hold mode (O)
+TSPC_HID_13_12 False           Hid : Sniff mode (O)
+TSPC_HID_13_13 False           Hid : Park mode (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory IF (TSPC_HID_8_2) OR (TSPC_HID_8_3) is selected. Optional
+       for other HIDs.
+C.2: Mandatory IF (TSPC_HID_13_5) supported.
+C.3: Mandatory IF (TSPC_HID_9_6) is supported.
+X: Feature should not be used by a HID device, but can be supported in LM.
+-------------------------------------------------------------------------------
+
+
+               HID Link Control Requirements
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_HID_14_1  False           Hid : Supports inquiry, 79 channel (O)
+TSPC_HID_14_2  False (*)       Hid : Supports inquiry scan, 79 channel (M.1)
+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-opp.txt b/android/pics-opp.txt
new file mode 100644 (file)
index 0000000..11e61ff
--- /dev/null
@@ -0,0 +1,189 @@
+OPP PICS for the PTS tool.
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_OPP_1_1   True (*)        Role: Object Push Client.
+TSPC_OPP_1_2   True (*)        Role: Object Push Server.
+-------------------------------------------------------------------------------
+C.1: It is Mandatory to Support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+               Client Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_OPP_1b_1  True            Client supports OPP version 1.1. (C.1)
+TSPC_OPP_1b_2  False           Client supports OPP version 1.2. (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+               Client Application Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_OPP_2_1   True            Client: Perform Service Discovery request.
+                                       (M.1)
+TSPC_OPP_2_2   True            Client: Authentication/PIN exchange supported.
+                                       (M.1)
+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_8   True (*)        Client: Support content formats other than those
+                                       declared in TSPC_OPP_2_44 through
+                                       TSPC_OPP_2_7. (O)
+TSPC_OPP_2_8a  False           Client: Support specific set of other content
+                                       formats. (C.4)
+TSPC_OPP_2_8b  True (*)        Client: Support all content formats. (C.4)
+TSPC_OPP_2_9   True (*)        Client: Push multiple vCard objects. (O)
+TSPC_OPP_2_9a  False           Client: Push multiple vCard objects using
+                                       different PUT operations. (C.5)
+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
+                                       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_11  False           Client: Push multiple vMsg objects. (O)
+TSPC_OPP_2_11a False           Client: Push multiple vMsg objects using
+                                       different PUT operations. (C.7)
+TSPC_OPP_2_11b False           Client: Push multiple vMsg objects using the
+                                       same PUT operation. (C.7)
+TSPC_OPP_2_12  False           Client: Push multiple vNote objects. (O)
+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)
+-------------------------------------------------------------------------------
+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
+       is supported. Otherwise, both items are excluded.
+C.5: Mandatory to support at least one of TSPC_OPP_2_9a and TSPC_OPP_2_9b if
+       TSPC_OPP_2_9 is supported. Otherwise, both items are excluded.
+C.6: Mandatory to support at least one of TSPC_OPP_2_10a and TSPC_OPP_2_10b if
+       TSPC_OPP_2_10 is supported. Otherwise, both items are excluded.
+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.
+-------------------------------------------------------------------------------
+
+
+               Server Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_OPP_2b_1  True            Server supports OPP version 1.1.
+TSPC_OPP_2b_2  False           Server supports OPP version 1.2.
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the profile versions.
+-------------------------------------------------------------------------------
+
+
+               Server Application Features
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_OPP_3_1   True            Server: Provide information on supported
+                                       contents type on service discovery
+                                       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_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_8   True (*)        Server: Support content formats other than those
+                                       declared in TSPC_OPP_3_4 through
+                                       TSPC_OPP_3_7. (O)
+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_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_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_18  False           Server: Business card exchange reject. (O)
+-------------------------------------------------------------------------------
+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.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.
+-------------------------------------------------------------------------------
+
+
+               Additional OPP Capabilities
+-------------------------------------------------------------------------------
+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_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: 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.
diff --git a/android/pics-pan.txt b/android/pics-pan.txt
new file mode 100644 (file)
index 0000000..406b6a9
--- /dev/null
@@ -0,0 +1,151 @@
+PAN PICS for the PTS tool.
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+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_1a_6  False           BNEP: Network Protocol Filter Message
+                                       Transmission (O)
+TSPC_PAN_1a_7  False           BNEP: Multicast Address Filter Message
+                                       Transmission (O)
+-------------------------------------------------------------------------------
+O.1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+               Network Access Point Application Features
+-------------------------------------------------------------------------------
+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_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
+                                       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_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_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)
+TSPC_PAN_2_12  False           NAP: Support IPv6 (C.3)
+TSPC_PAN_2_13  False           NAP: Support ping client for IPv6 (O)
+TSPC_PAN_2_14  False           NAP: Support DNS/LLMNR Resolver for IPv6 (O)
+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_19  False           NAP: Support at least three PANUs (O)
+TSPC_PAN_2_20  False           NAP: Support at least two PANUs (O)
+-------------------------------------------------------------------------------
+Note that support for IP-related features only applies to the PAN interface of
+       the NAP (i.e. If the IP stack is accessible by PANUs).
+C.1: Network Access Point devices MUST support either (TSPC_PAN_2_3)
+       OR (TSPC_PAN_2_4).
+C.2: Mandatory to support IF any IPv4-based transport protocol OR
+       (TSPC_PAN_2_7-11) is supported, ELSE Optional.
+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.6: Mandatory if item (TSPC_PAN_2_12) supported
+-------------------------------------------------------------------------------
+
+
+               Group Ad-hoc Network Application Features
+                       (GN Application Features)
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PAN_3_1   False (*)       GN: Support BNEP (M)
+TSPC_PAN_3_2   False (*)       GN: Support BNEP Forwarding (M)
+TSPC_PAN_3_3   False           GN: Support BNEP Packet Filtering (O)
+TSPC_PAN_3_4   False           GN: Support IPv4 (C.1)
+TSPC_PAN_3_5   False           GN: Support ping client for IPv4 (O)
+TSPC_PAN_3_6   False           GN: Support DHCP Client for IPv4 (O)
+TSPC_PAN_3_7   False           GN: Support DNS/LLMNR Resolver for IPv4 (O)
+TSPC_PAN_3_7a  False (*)       GN: Support LLMNR Sender for IPv4 (C.3)
+TSPC_PAN_3_7b  False           GN: Support LLMNR Responder for IPv4 (O)
+TSPC_PAN_3_8   False           GN: Support HTTP Client for IPv4 (O)
+TSPC_PAN_3_9   False           GN: Support WAP Client for IPv4 (O)
+TSPC_PAN_3_10  False           GN: Support IPv6 (C.2)
+TSPC_PAN_3_11  False           GN: Support ping client for IPv6 (O)
+TSPC_PAN_3_12  False           GN: Support DNS/LLMNR Resolver for IPv6 (O)
+TSPC_PAN_3_12a False (*)       GN: Support LLMNR Sender for IPv6 (C.4)
+TSPC_PAN_3_12b False           GN: Support LLMNR Responder for IPv6 (O)
+TSPC_PAN_3_13  False           GN: Support HTTP Client for IPv6 (O)
+TSPC_PAN_3_14  False           GN: Support WAP Client for IPv6 (O)
+TSPC_PAN_3_15  False (*)       GN: Supports Connectable Mode (M)
+TSPC_PAN_3_16  False (*)       GN: GN Service Record (M)
+TSPC_PAN_3_17  False           GN: Support at least three PANUs (O)
+TSPC_PAN_3_18  False           GN: Support at least two PANUs (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support IF any IPv4-based transport protocol OR
+       (TSPC_PAN_3_5-9) is supported, ELSE Optional.
+C.2: Mandatory to support IF any IPv6-based transport protocol OR
+       (TSPC_PAN_3_11-14) is supported, ELSE Optional.
+C.3: Mandatory to support IF (TSPC_PAN_3_4) is supported.
+C.4: Mandatory to support if (TSPC_PAN_3_10) is supported.
+-------------------------------------------------------------------------------
+
+
+               PAN User Application Features
+-------------------------------------------------------------------------------
+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_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_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)
+TSPC_PAN_4_8   False           PANU: Support IPv6 (C.1)
+TSPC_PAN_4_9   False           PANU: Support ping client for IPv6 (O)
+TSPC_PAN_4_10  False           PANU: Support DNS/LLMNR Resolver for IPv6 (O)
+TSPC_PAN_4_10a False (*)       PANU: Support LLMNR Sender for IPv6 (C.3)
+TSPC_PAN_4_10b False           PANU: Support LLMNR Responder for IPv6 (O)
+TSPC_PAN_4_11  False           PANU: Support HTTP Client for IPv6 (O)
+TSPC_PAN_4_12  False           PANU: Support WAP Client for IPv6 (O)
+TSPC_PAN_4_13  False           PANU: Support connections to multi-user
+                                       NAPs/GNs (O)
+TSPC_PAN_4_14  False           PANU: Supports Connectable Mode (O)
+TSPC_PAN_4_15  False           PANU: PANU Service Record (O)
+TSPC_ALL       False           Turns on all the test cases
+-------------------------------------------------------------------------------
+C.1: PAN User devices must support at least One of items (TSPC_PAN_4_2) or
+       (TSPC_PAN_4_8).
+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.
diff --git a/android/pics-pbap.txt b/android/pics-pbap.txt
new file mode 100644 (file)
index 0000000..2af5fd8
--- /dev/null
@@ -0,0 +1,446 @@
+PBAP PICS for the PTS tool.
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+               Major Profile Version (X.Y)
+-------------------------------------------------------------------------------
+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)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support one and only one major profile version.
+-------------------------------------------------------------------------------
+
+
+               Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_1_1  False           Role: PCE (C.1)
+TSPC_PBAP_1_2  True (*)        Role: PSE (C.1)
+-------------------------------------------------------------------------------
+C1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+               Supported features (PCE)
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_2_1  False (*)       PCE: Phone Book Download (C.1)
+TSPC_PBAP_2_2  False (*)       PCE: Phone Book Browsing (C.1)
+TSPC_PBAP_2_3  False (*)       PCE: Session Management (M)
+TSPC_PBAP_2_4  False           PCE: Able to Request Size of the Phonebook (O)
+TSPC_PBAP_2_5  False           PCE: Database Identifier (C.2)
+TSPC_PBAP_2_6  False           PCE: Folder Version Counters (C.2)
+TSPC_PBAP_2_7  False           PCE: vCard Selecting (C.2)
+TSPC_PBAP_2_7a False           PCE: Able to send vCardSelector (C.2)
+TSPC_PBAP_2_7b False           PCE: Able to send vCardSelectorOperator (C.2)
+TSPC_PBAP_2_8  False (*)       PCE: Enhanced Missed Calls (C.4)
+TSPC_PBAP_2_8a False (*)       PCE: Able to reset the missed Calls (C.2)
+TSPC_PBAP_2_9  False           PCE: X-BT-UCI vCard Field (C.2)
+TSPC_PBAP_2_9a False           PCE: Able to request X-BT-UCI Field (C.2)
+TSPC_PBAP_2_10 False           PCE: X-BT-UID vCard Field (C.2)
+TSPC_PBAP_2_10a        False           PCE: Referencing Contacts (C.2)
+TSPC_PBAP_2_12 False           PCE: Contact Image Default Format (C.2)
+TSPC_PBAP_2_12a        False           PCE: Able to request Contact Images (C.2)
+TSPC_PBAP_2_13 False           PCE: Supported Phonebook Objects (C.3)
+TSPC_PBAP_2_13a        False (*)       PCE: Telecom/pb (C.3)
+TSPC_PBAP_2_13b        False           PCE: Telecom/ich (C.3)
+TSPC_PBAP_2_13c        False           PCE: Telecom/och (C.3)
+TSPC_PBAP_2_13d        False (*)       PCE: Telecom/mch (C.3)
+TSPC_PBAP_2_13e        False (*)       PCE: Telecom/cch (C.3)
+TSPC_PBAP_2_13f        False           PCE: Telecom/spd (C.3)
+TSPC_PBAP_2_13g        False           PCE: Telecom/fav (C.3)
+TSPC_PBAP_2_13h        False           PCE: SIM1/Telecom/pb (C.3)
+TSPC_PBAP_2_13i        False           PCE: SIM1/Telecom/ich (C.3)
+TSPC_PBAP_2_13j        False           PCE: SIM1/Telecom/och (C.3)
+TSPC_PBAP_2_13k        False           PCE: SIM1/Telecom/mch (C.3)
+TSPC_PBAP_2_13l        False           PCE: SIM1/Telecom/cch (C.3)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to support at least one of the defined features.
+C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+C.3: Mandatory to support at least one of the listed phonebook objects .
+C.4: Optional if TSPC_PBAP_0_3 (PBAP 1.2) and any of the mch or cch folders
+       (13d,13e,13k,13l) are supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               Supported Phone Book Download functions (PCE)
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_3_1  False (*)       PCE: Pull Phone Book (C.1)
+-------------------------------------------------------------------------------
+C1: Mandatory for PCE if Phone Book Download (TSPC_PBAP_2_1) is supported,
+       otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+               Supported Phone Book Browsing functions (PCE)
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_4_1  False (*)       PCE: Set Phone Book (C.1)
+TSPC_PBAP_4_2  False (*)       PCE: Pull vCard Listing (C.1)
+TSPC_PBAP_4_3  False (*)       PCE: Pull vCard Entry (C.1)
+-------------------------------------------------------------------------------
+C1: Mandatory for PCE if Phone Book Browsing TSPC_PBAP_2_2 is supported,
+       otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+               Used vCard formats (PCE)
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_5_1  False (*)       PCE: vCard 2.1 (C.1)
+TSPC_PBAP_5_2  False (*)       PCE: vCard 3.0 (C.1)
+-------------------------------------------------------------------------------
+C1: It is mandatory to support at least one of the defined versions if PCE
+       supported.
+-------------------------------------------------------------------------------
+
+
+               OBEX Functions for PCE
+-------------------------------------------------------------------------------
+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_5  False (*)       PCE: SetPath (C.1)
+TSPC_PBAP_6_6  False           PCE: Support for OBEX authentication initiation
+                                       (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_PBAP_2_2 (Phone Book Browsing) is supported,
+       otherwise Excluded.
+C.2: Optional to support initiation if TSPC_PBAP_0_1 (PBAP 1.0) or
+       TSPC_PBAP_0_2 (PBAP 1.1) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               PCE OBEX Header Support
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_7_1  False (*)       PCE: Name (M)
+TSPC_PBAP_7_2  False (*)       PCE: Type (M)
+TSPC_PBAP_7_3  False (*)       PCE: Body (M)
+TSPC_PBAP_7_4  False (*)       PCE: End of Body (M)
+TSPC_PBAP_7_5  False (*)       PCE: Target (M)
+TSPC_PBAP_7_6  False (*)       PCE: Who (M)
+TSPC_PBAP_7_7  False (*)       PCE: Connection ID (M)
+TSPC_PBAP_7_8  False (*)       PCE: Authentication Challenge (M)
+TSPC_PBAP_7_9  False (*)       PCE: Authentication Response (M)
+TSPC_PBAP_7_10 False (*)       PCE: Application Parameters (M)
+TSPC_PBAP_7_11 False           PCE: Single Response Mode (C.1)
+TSPC_PBAP_7_12 False           PCE: Single Response Mode Parameter
+                                       (ability to parse) (C.1)
+TSPC_PBAP_7_13 False           PCE: Single Response Mode Parameter
+                                       (ability to send) (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               OBEX Error Codes for PCE
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_8_1  False (*)       PCE: Bad Request (M)
+TSPC_PBAP_8_2  False (*)       PCE: Not Implemented (M)
+TSPC_PBAP_8_3  False (*)       PCE: Unauthorized (M)
+TSPC_PBAP_8_4  False (*)       PCE: Precondition Failed (M)
+TSPC_PBAP_8_5  False (*)       PCE: Not Found (M)
+TSPC_PBAP_8_6  False (*)       PCE: Not Acceptable (M)
+TSPC_PBAP_8_7  False (*)       PCE: Service Unavailable (M)
+TSPC_PBAP_8_8  False (*)       PCE: Forbidden (M)
+-------------------------------------------------------------------------------
+
+
+               Supported features ( PSE )
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_9_1  True            PSE: Phone Book Download (M)
+TSPC_PBAP_9_2  True            PSE: Phone Book Browsing (M)
+TSPC_PBAP_9_3  True            PSE: Session Management (M)
+TSPC_PBAP_9_4  True            PSE: Able to request the size of the Phonebook
+                                       (M)
+TSPC_PBAP_9_5  False           PSE: Database Identifier (C.1)
+TSPC_PBAP_9_5a False           PSE: Able to keep a persistent Database
+                                       Identifier (C.2)
+TSPC_PBAP_9_5b False           PSE: Able to regenerate a Database Identifier
+                                       (C.2)
+TSPC_PBAP_9_6  False           PSE: Folder Version Counters (C.1)
+TSPC_PBAP_9_6a False           PSE: Able to Insert or Remove Entries (C.2)
+TSPC_PBAP_9_6b False           PSE: Able to Modify contact primary Fields (C.2)
+TSPC_PBAP_9_6c False           PSE: Able to Modify contact secondary Fields
+                                       (C.2)
+TSPC_PBAP_9_7  False (*)       PSE: vCard Selecting (C.1)
+TSPC_PBAP_9_8  False (*)       PSE: Enhanced Missed Calls (C.4)
+TSPC_PBAP_9_9  False           PSE: X-BT-UCI vCard Field (C.2)
+TSPC_PBAP_9_10 False           PSE: X-BT-UID vCard Field (C.2)
+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_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)
+TSPC_PBAP_9_13i        False           PSE: SIM1/Telecom/ich (O)
+TSPC_PBAP_9_13j        False           PSE: SIM1/Telecom/och (O)
+TSPC_PBAP_9_13k        False (*)       PSE: SIM1/Telecom/mch (O)
+TSPC_PBAP_9_13l        False           PSE: SIM1/Telecom/cch (O)
+TSPC_PBAP_9_14 False           PSE: Deleted Handles Behavior
+TSPC_PBAP_9_14a        False (*)       PSE: Error reporting (C.5)
+TSPC_PBAP_9_14b        False           PSE: Change tracking (C.5)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+C.3: Optional if TSPC_PBAP_9_10 (X-BT-UID vCard Property) is supported,
+       otherwise Excluded.
+C.4: Optional if TSPC_PBAP_0_3 (PBAP 1.2) and any of the mch or cch folders
+       (13d,13e,13k,13l) are supported, otherwise Excluded.
+C.5: It is mandatory to support at least one of the defined deleted handles
+       behaviors.
+-------------------------------------------------------------------------------
+
+
+               Supported Phone Book Download functions ( PSE )
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_10_1 True            PSE: Pull Phone Book (M)
+TSPC_PBAP_10_2 False           PSE: Call History Function (O)
+-------------------------------------------------------------------------------
+
+
+               Supported Phone Book Browsing functions ( PSE )
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_11_1 True            PSE: Set Phone Book (M)
+TSPC_PBAP_11_2 True            PSE: Pull vCard Listing (M)
+TSPC_PBAP_11_3 True            PSE: Pull vCard Entry (M)
+-------------------------------------------------------------------------------
+
+
+               Used vCard formats (PSE)
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_12_1 True            PSE: vCard 2.1 (M)
+TSPC_PBAP_12_2 True            PSE: vCard 3.0 (M)
+-------------------------------------------------------------------------------
+
+
+               OBEX Functions for PSE
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_13_1 True            PSE: Connect (M)
+TSPC_PBAP_13_2 True            PSE: Disconnect (M)
+TSPC_PBAP_13_3 True            PSE: Get (M)
+TSPC_PBAP_13_4 True            PSE: Abort (M)
+TSPC_PBAP_13_5 True            PSE: SetPath (M)
+TSPC_PBAP_13_6 False           PSE: Support for OBEX authentication initiation
+                                       (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional to support initiation if TSPC_PBAP_0_1 (PBAP 1.0) or
+       TSPC_PBAP_0_2 (PBAP 1.1) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               PSE OBEX Header Support
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_14_1 True            PSE: Name (M)
+TSPC_PBAP_14_2 True            PSE: Type (M)
+TSPC_PBAP_14_3 True            PSE: Body (M)
+TSPC_PBAP_14_4 True            PSE: End of Body (M)
+TSPC_PBAP_14_5 True            PSE: Target (M)
+TSPC_PBAP_14_6 True            PSE: Who (M)
+TSPC_PBAP_14_7 True            PSE: Connection ID (M)
+TSPC_PBAP_14_8 True            PSE: Authentication Challenge (M)
+TSPC_PBAP_14_9 True            PSE: Authentication Response (M)
+TSPC_PBAP_14_10        True            PSE: Application Parameters (M)
+TSPC_PBAP_14_11        False           PSE: Single Response Mode (C.1)
+TSPC_PBAP_14_12        False           PSE: Single Response Mode Parameter
+                                       (ability to parse) (C.1)
+TSPC_PBAP_14_13        False           PSE: Single Response Mode Parameter
+                                       (ability to send) (C.2)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               OBEX Error Codes for PSE
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_15_1 True            PSE: Bad Request (M)
+TSPC_PBAP_15_2 True            PSE: Not Implemented (M)
+TSPC_PBAP_15_3 True (*)        PSE: Unauthorized (O)
+TSPC_PBAP_15_4 True (*)        PSE: Precondition Failed (C.1)
+TSPC_PBAP_15_5 True            PSE: Not Found (M)
+TSPC_PBAP_15_6 True (*)        PSE: Not Acceptable (O)
+TSPC_PBAP_15_7 True            PSE: Service Unavailable (M)
+TSPC_PBAP_15_8 True (*)        PSE: Forbidden (O)
+-------------------------------------------------------------------------------
+C.1: Mandatory if TSPC_PBAP_9_14a (Error reporting) is supported, otherwise
+       Optional.
+-------------------------------------------------------------------------------
+
+
+               GAP Modes for PCE
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_16_1 False (*)       PCE: General discoverable mode (M)
+TSPC_PBAP_16_2 False (*)       PCE: Pairable mode (M)
+-------------------------------------------------------------------------------
+
+
+               GAP Modes for PSE
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_17_1 True            PSE: General discoverable mode (M)
+TSPC_PBAP_17_2 True            PSE: Pairable mode (M)
+-------------------------------------------------------------------------------
+
+
+               GAP Security Modes for PCE
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_18_1 False (*)       PCE: Authentication Procedure (M)
+TSPC_PBAP_18_2 False (*)       PCE: Initiate LMP-Authentication (M)
+TSPC_PBAP_18_3 False           PCE: Security mode 1 (C.1)
+TSPC_PBAP_18_4 False           PCE: Security mode 2 (C.1)
+TSPC_PBAP_18_5 False           PCE: Security mode 3 (C.1)
+TSPC_PBAP_18_6 False           PCE: Security mode 4 (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one of TSPC_PBAP_18_4, TSPC_PBAP_18_5 or TSPC_PBAP_18_6
+       (security mode 2, 3, or 4) shall be supported.
+-------------------------------------------------------------------------------
+
+
+               GAP Security Modes for PSE
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_19_1 True            PSE: Authentication Procedure (M)
+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)
+-------------------------------------------------------------------------------
+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.
+C.2: Excluded in PSE.
+-------------------------------------------------------------------------------
+
+
+               GAP Idle Modes for PSE
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_21_1 True            PSE: Initiation of General Inquiry (M)
+TSPC_PBAP_21_2 False           PSE: Initiation of Limited Inquiry (O)
+-------------------------------------------------------------------------------
+
+
+               SDP Attributes (PCE)
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_22_1 False (*)       PCE: BluetoothProfileDescriptorList (M)
+-------------------------------------------------------------------------------
+
+               SDP Attributes (PSE)
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_23_1 True            PSE: ProtocolDescriptorList (M)
+TSPC_PBAP_23_2 True            PSE: BluetoothProfileDescriptorList (M)
+-------------------------------------------------------------------------------
+
+
+               Additional PBAP Capabilities
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_24_1 False           PCE: Retrieve Large Phone Book (C.1)
+TSPC_PBAP_24_2 False           PSE: Transfer Large Phone Book (C.2)
+TSPC_PBAP_24_3 False           PCE: Retrieve Empty Phone Book (C.1)
+TSPC_PBAP_24_4 False           PSE: Transfer Empty Phone Book (C.2)
+TSPC_PBAP_24_5 False           PSE: Return Phonebook â€“ Limit number of entries
+                                       (C.2)
+TSPC_PBAP_24_6 False           PSE: Return vCard listing â€“ Limit number of
+                                       entries  (C.2)
+TSPC_PBAP_24_7 False           PSE: Phone Book Order (C.2)
+TSPC_PBAP_24_8 False           PSE: Call stack timestamps (C.3)
+TSPC_PBAP_24_9 False           PSE: No User Interaction (C.2)
+TSPC_PBAP_24_10        False           PSE: Special Character Handling  (C.2)
+-------------------------------------------------------------------------------
+C.1: Optional if TSPC_PBAP_2_1 is supported, otherwise excluded.
+C.2: Optional if TSPC_PBAP_1_2 is supported, otherwise excluded.
+C.3: Optional if TSPC_PBAP_10_2 is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+               GOEP 2.0 or later Features (PCE)
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+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)
+TSPC_PBAP_25_5 False (*)       PCE: Send OBEX SRMP header (C.1)
+TSPC_PBAP_25_6 False           PCE: Receive OBEX SRMP header (M)
+-------------------------------------------------------------------------------
+C.1: Optional to support if TSPC_PBAP_25_4 (OBEX SRM) is supported,
+       otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+               GOEP 2.0 or later Features (PSE)
+-------------------------------------------------------------------------------
+Parameter Name Selected        Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_26_1 False (*)       PSE: GOEP v2.0 or later (M)
+TSPC_PBAP_26_2 False (*)       PSE: GOEP v2 Backwards Compatibility (M)
+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
+-------------------------------------------------------------------------------
+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.
index c283c5f..9020874 100644 (file)
 
 #include <glib.h>
 #include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
 
 #include "lib/bluetooth.h"
+#include "btio/btio.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "src/sdp-client.h"
+#include "src/sdpd.h"
+
+#include "bluetooth.h"
 #include "log.h"
 #include "hal-msg.h"
 #include "hal-ipc.h"
 #include "ipc.h"
+#include "utils.h"
 #include "socket.h"
 
+#define SPP_DEFAULT_CHANNEL    3
+#define OPP_DEFAULT_CHANNEL    9
+#define PBAP_DEFAULT_CHANNEL   15
+#define MAS_DEFAULT_CHANNEL    16
+
+#define SVC_HINT_OBEX 0x10
+
+static bdaddr_t adapter_addr;
+
+/* Simple list of RFCOMM server sockets */
+GList *servers = NULL;
+
+/* Simple list of RFCOMM connected sockets */
+GList *connections = NULL;
+
+struct rfcomm_sock {
+       int fd;         /* descriptor for communication with Java framework */
+       int real_sock;  /* real RFCOMM socket */
+       int channel;    /* RFCOMM channel */
+
+       guint rfcomm_watch;
+       guint stack_watch;
+
+       bdaddr_t dst;
+       uint32_t service_handle;
 
-static int handle_listen(void *buf)
+       const struct profile_info *profile;
+};
+
+static struct rfcomm_sock *create_rfsock(int sock, int *hal_fd)
 {
-       DBG("Not implemented");
+       int fds[2] = {-1, -1};
+       struct rfcomm_sock *rfsock;
+
+       if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) {
+               error("socketpair(): %s", strerror(errno));
+               *hal_fd = -1;
+               return NULL;
+       }
+
+       rfsock = g_new0(struct rfcomm_sock, 1);
+       rfsock->fd = fds[0];
+       *hal_fd = fds[1];
+       rfsock->real_sock = sock;
 
-       return -1;
+       return rfsock;
 }
 
-static int handle_connect(void *buf)
+static void cleanup_rfsock(gpointer data)
 {
-       DBG("Not implemented");
+       struct rfcomm_sock *rfsock = data;
+
+       DBG("rfsock: %p fd %d real_sock %d chan %u",
+               rfsock, rfsock->fd, rfsock->real_sock, rfsock->channel);
+
+       if (rfsock->fd >= 0)
+               if (close(rfsock->fd) < 0)
+                       error("close() fd %d failed: %s", rfsock->fd,
+                                                       strerror(errno));
+
+       if (rfsock->real_sock >= 0)
+               if (close(rfsock->real_sock) < 0)
+                       error("close() fd %d: failed: %s", rfsock->real_sock,
+                                                       strerror(errno));
+
+       if (rfsock->rfcomm_watch > 0)
+               if (!g_source_remove(rfsock->rfcomm_watch))
+                       error("rfcomm_watch source was not found");
 
-       return -1;
+       if (rfsock->stack_watch > 0)
+               if (!g_source_remove(rfsock->stack_watch))
+                       error("stack_watch source was not found");
+
+       if (rfsock->service_handle)
+               bt_adapter_remove_record(rfsock->service_handle);
+
+       g_free(rfsock);
 }
 
-void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len)
+static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name)
 {
-       int fd;
+       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;
+       sdp_data_t *channel;
+       sdp_record_t *record;
 
-       switch (opcode) {
-       case HAL_OP_SOCK_LISTEN:
-               fd = handle_listen(buf);
-               if (fd < 0)
-                       break;
+       record = sdp_record_alloc();
+       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(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
+       svclass_id = sdp_list_append(NULL, &opush_uuid);
+       sdp_set_service_classes(record, svclass_id);
+
+       sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
+       profile[0].version = 0x0100;
+       pfseq = sdp_list_append(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]);
+
+       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_uuid16_create(&obex_uuid, OBEX_UUID);
+       proto[2] = sdp_list_append(NULL, &obex_uuid);
+       apseq = sdp_list_append(apseq, proto[2]);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       for (i = 0; i < sizeof(formats); i++) {
+               dtds[i] = &dtd;
+               values[i] = &formats[i];
+       }
+       sflist = sdp_seq_alloc(dtds, values, sizeof(formats));
+       sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist);
+
+       if (svc_name)
+               service_name = svc_name;
+
+       sdp_set_info_attr(record, service_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);
+       sdp_list_free(svclass_id, NULL);
+
+       return record;
+}
+
+static sdp_record_t *create_pbap_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 dtd = SDP_UINT8;
+       sdp_data_t *sflist;
+       sdp_record_t *record;
+
+       record = sdp_record_alloc();
+       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);
+       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]);
+
+       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_uuid16_create(&obex_uuid, OBEX_UUID);
+       proto[2] = sdp_list_append(NULL, &obex_uuid);
+       apseq = sdp_list_append(apseq, proto[2]);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sflist = sdp_data_alloc(dtd, formats);
+       sdp_attr_add(record, SDP_ATTR_SUPPORTED_REPOSITORIES, sflist);
+
+       if (svc_name)
+               service_name = svc_name;
+
+       sdp_set_info_attr(record, service_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);
+       sdp_list_free(svclass_id, NULL);
+
+       return record;
+}
+
+static sdp_record_t *create_spp_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_record_t *record;
+
+       record = sdp_record_alloc();
+       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(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
+       svclass_id = sdp_list_append(NULL, &sp_uuid);
+       sdp_set_service_classes(record, svclass_id);
+
+       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_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]);
+
+       aproto = sdp_list_append(NULL, apseq);
+       sdp_set_access_protos(record, aproto);
+
+       sdp_add_lang_attr(record);
+
+       if (svc_name)
+               service_name = svc_name;
+
+       sdp_set_info_attr(record, service_name, "BlueZ", "COM Port");
+
+       sdp_set_url_attr(record, "http://www.bluez.org/",
+                       "http://www.bluez.org/", "http://www.bluez.org/");
+
+       sdp_set_service_id(record, sp_uuid);
+       sdp_set_service_ttl(record, 0xffff);
+       sdp_set_service_avail(record, 0xff);
+       sdp_set_record_state(record, 0x00001234);
+
+       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);
+
+       return record;
+}
+
+static const struct profile_info {
+       uint8_t         uuid[16];
+       uint8_t         channel;
+       uint8_t         svc_hint;
+       BtIOSecLevel    sec_level;
+       sdp_record_t *  (*create_record)(uint8_t chan, const char *svc_name);
+} profiles[] = {
+       {
+               .uuid = {
+                       0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+               },
+               .channel = PBAP_DEFAULT_CHANNEL,
+               .svc_hint = SVC_HINT_OBEX,
+               .sec_level = BT_IO_SEC_MEDIUM,
+               .create_record = create_pbap_record
+       }, {
+               .uuid = {
+                       0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+                 },
+               .channel = OPP_DEFAULT_CHANNEL,
+               .svc_hint = SVC_HINT_OBEX,
+               .sec_level = BT_IO_SEC_LOW,
+               .create_record = create_opp_record
+       }, {
+               .uuid = {
+                       0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+               },
+               .channel = MAS_DEFAULT_CHANNEL,
+               .svc_hint = 0,
+               .sec_level = BT_IO_SEC_MEDIUM,
+               .create_record = NULL
+       }, {
+               .uuid = {
+                       0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00,
+                       0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
+               },
+               .channel = SPP_DEFAULT_CHANNEL,
+               .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)
+{
+       sdp_record_t *record;
+
+       if (!profile || !profile->create_record)
+               return 0;
+
+       record = profile->create_record(profile->channel, svc_name);
+       if (!record)
+               return 0;
+
+       if (bt_adapter_add_record(record, profile->svc_hint) < 0) {
+               error("Failed to register on SDP record");
+               sdp_record_free(record);
+               return 0;
+       }
+
+       return record->handle;
+}
+
+static int bt_sock_send_fd(int sock_fd, const void *buf, int len, int send_fd)
+{
+       ssize_t ret;
+       struct msghdr msg;
+       struct cmsghdr *cmsg;
+       struct iovec iv;
+       char cmsgbuf[CMSG_SPACE(sizeof(int))];
+
+       DBG("len %d sock_fd %d send_fd %d", len, sock_fd, send_fd);
+
+       if (sock_fd == -1 || send_fd == -1)
+               return -1;
+
+       memset(&msg, 0, sizeof(msg));
+       memset(cmsgbuf, 0, sizeof(cmsgbuf));
+
+       msg.msg_control = cmsgbuf;
+       msg.msg_controllen = sizeof(cmsgbuf);
+
+       cmsg = CMSG_FIRSTHDR(&msg);
+       cmsg->cmsg_level = SOL_SOCKET;
+       cmsg->cmsg_type = SCM_RIGHTS;
+       cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
+
+       memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd));
+
+       iv.iov_base = (unsigned char *) buf;
+       iv.iov_len = len;
+
+       msg.msg_iov = &iv;
+       msg.msg_iovlen = 1;
+
+       ret = sendmsg(sock_fd, &msg, MSG_NOSIGNAL);
+       if (ret < 0) {
+               error("sendmsg(): sock_fd %d send_fd %d: %s",
+                                       sock_fd, send_fd, strerror(errno));
+               return ret;
+       }
+
+       return ret;
+}
+
+static const struct profile_info *get_profile_by_uuid(const uint8_t *uuid)
+{
+       unsigned int i;
+
+       for (i = 0; i < G_N_ELEMENTS(profiles); i++) {
+               if (!memcmp(profiles[i].uuid, uuid, 16))
+                       return &profiles[i];
+       }
+
+       return NULL;
+}
+
+static int try_write_all(int fd, unsigned char *buf, int len)
+{
+       int sent = 0;
+
+       while (len > 0) {
+               int written;
+
+               written = write(fd, buf, len);
+               if (written < 0) {
+                       if (errno == EINTR || errno == EAGAIN)
+                               continue;
+                       return -1;
+               }
+
+               if (!written)
+                       return 0;
+
+               len -= written; buf += written; sent += written;
+       }
+
+       return sent;
+}
+
+static gboolean sock_stack_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) {
+               DBG("Socket %d hang up", g_io_channel_unix_get_fd(io));
+               goto fail;
+       }
+
+       if (cond & (G_IO_ERR | G_IO_NVAL)) {
+               error("Socket error: sock %d cond %d",
+                                       g_io_channel_unix_get_fd(io), cond);
+               goto fail;
+       }
+
+       len = read(rfsock->fd, buf, sizeof(buf));
+       if (len <= 0) {
+               error("read(): %s", strerror(errno));
+               /* Read again */
+               return TRUE;
+       }
+
+       sent = try_write_all(rfsock->real_sock, buf, len);
+       if (sent < 0) {
+               error("write(): %s", strerror(errno));
+               goto fail;
+       }
+
+       return TRUE;
+fail:
+       connections = g_list_remove(connections, rfsock);
+       cleanup_rfsock(rfsock);
+
+       return FALSE;
+}
+
+static gboolean sock_rfcomm_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) {
+               DBG("Socket %d hang up", g_io_channel_unix_get_fd(io));
+               goto fail;
+       }
+
+       if (cond & (G_IO_ERR | G_IO_NVAL)) {
+               error("Socket error: sock %d cond %d",
+                                       g_io_channel_unix_get_fd(io), cond);
+               goto fail;
+       }
 
-               ipc_send(sk, HAL_SERVICE_ID_SOCK, opcode, 0, NULL, fd);
+       len = read(rfsock->real_sock, buf, sizeof(buf));
+       if (len <= 0) {
+               error("read(): %s", strerror(errno));
+               /* Read again */
+               return TRUE;
+       }
+
+       sent = try_write_all(rfsock->fd, buf, len);
+       if (sent < 0) {
+               error("write(): %s", strerror(errno));
+               goto fail;
+       }
+
+       return TRUE;
+fail:
+       connections = g_list_remove(connections, rfsock);
+       cleanup_rfsock(rfsock);
+
+       return FALSE;
+}
+
+static bool sock_send_accept(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr,
+                                                       int fd_accepted)
+{
+       struct hal_sock_connect_signal cmd;
+       int len;
+
+       DBG("");
+
+       cmd.size = sizeof(cmd);
+       bdaddr2android(bdaddr, cmd.bdaddr);
+       cmd.channel = rfsock->channel;
+       cmd.status = 0;
+
+       len = bt_sock_send_fd(rfsock->fd, &cmd, sizeof(cmd), fd_accepted);
+       if (len != sizeof(cmd)) {
+               error("Error sending accept signal");
+               return false;
+       }
+
+       return true;
+}
+
+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;
+       GError *gerr = NULL;
+       bdaddr_t dst;
+       char address[18];
+       int sock_acc;
+       int hal_fd;
+       guint id;
+       GIOCondition cond;
+
+       if (err) {
+               error("%s", err->message);
                return;
-       case HAL_OP_SOCK_CONNECT:
-               fd = handle_connect(buf);
-               if (fd < 0)
-                       break;
+       }
+
+       bt_io_get(io, &gerr,
+                       BT_IO_OPT_DEST_BDADDR, &dst,
+                       BT_IO_OPT_INVALID);
+       if (gerr) {
+               error("%s", gerr->message);
+               g_error_free(gerr);
+               g_io_channel_shutdown(io, TRUE, NULL);
+               return;
+       }
 
-               ipc_send(sk, HAL_SERVICE_ID_SOCK, opcode, 0, NULL, fd);
+       ba2str(&dst, address);
+       DBG("Incoming connection from %s rfsock %p", address, rfsock);
+
+       sock_acc = g_io_channel_unix_get_fd(io);
+       rfsock_acc = create_rfsock(sock_acc, &hal_fd);
+       if (!rfsock_acc) {
+               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);
+
+       if (!sock_send_accept(rfsock, &dst, hal_fd)) {
+               cleanup_rfsock(rfsock_acc);
                return;
-       default:
-               DBG("Unhandled command, opcode 0x%x", opcode);
-               break;
        }
 
-       ipc_send_rsp(sk, HAL_SERVICE_ID_SOCK, HAL_STATUS_FAILED);
+       connections = g_list_append(connections, rfsock_acc);
+
+       /* 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);
+
+       rfsock_acc->stack_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);
+       g_io_channel_set_close_on_unref(io, FALSE);
+
+       rfsock_acc->rfcomm_watch = id;
+
+       DBG("rfsock %p rfsock_acc %p stack_watch %d rfcomm_watch %d",
+               rfsock, rfsock_acc, rfsock_acc->stack_watch,
+               rfsock_acc->rfcomm_watch);
 }
 
-bool bt_socket_register(int sk, const bdaddr_t *addr)
+static void handle_listen(const void *buf, uint16_t len)
 {
+       const struct hal_cmd_sock_listen *cmd = buf;
+       const struct profile_info *profile;
+       struct rfcomm_sock *rfsock = NULL;
+       BtIOSecLevel sec_level;
+       GIOChannel *io;
+       GError *err = NULL;
+       int hal_fd = -1;
+       int chan;
+
        DBG("");
 
+       profile = get_profile_by_uuid(cmd->uuid);
+       if (!profile) {
+               if (!cmd->channel)
+                       goto failed;
+
+               chan = cmd->channel;
+               sec_level = BT_IO_SEC_MEDIUM;
+       } else {
+               chan = profile->channel;
+               sec_level = profile->sec_level;
+       }
+
+       DBG("rfcomm channel %d svc_name %s", chan, cmd->name);
+
+       rfsock = create_rfsock(-1, &hal_fd);
+       if (!rfsock)
+               goto failed;
+
+       io = bt_io_listen(accept_cb, NULL, rfsock, NULL, &err,
+                               BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+                               BT_IO_OPT_CHANNEL, chan,
+                               BT_IO_OPT_SEC_LEVEL, sec_level,
+                               BT_IO_OPT_INVALID);
+       if (!io) {
+               error("Failed listen: %s", err->message);
+               g_error_free(err);
+               goto failed;
+       }
+
+       rfsock->real_sock = g_io_channel_unix_get_fd(io);
+
+       g_io_channel_unref(io);
+
+       DBG("real_sock %d fd %d hal_fd %d", rfsock->real_sock, rfsock->fd,
+                                                               hal_fd);
+
+       if (write(rfsock->fd, &chan, sizeof(chan)) != sizeof(chan)) {
+               error("Error sending RFCOMM channel");
+               goto failed;
+       }
+
+       rfsock->service_handle = sdp_service_register(profile, cmd->name);
+
+       servers = g_list_append(servers, rfsock);
+
+       ipc_send_rsp_full(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, 0, NULL,
+                                                                       hal_fd);
+       close(hal_fd);
+       return;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN,
+                                                       HAL_STATUS_FAILED);
+
+       if (rfsock)
+               cleanup_rfsock(rfsock);
+
+       if (hal_fd >= 0)
+               close(hal_fd);
+}
+
+static bool sock_send_connect(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr)
+{
+       struct hal_sock_connect_signal cmd;
+       int len;
+
+       DBG("");
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.size = sizeof(cmd);
+       bdaddr2android(bdaddr, cmd.bdaddr);
+       cmd.channel = rfsock->channel;
+       cmd.status = 0;
+
+       len = write(rfsock->fd, &cmd, sizeof(cmd));
+       if (len < 0) {
+               error("%s", strerror(errno));
+               return false;
+       }
+
+       if (len != sizeof(cmd)) {
+               error("Error sending connect signal");
+               return false;
+       }
+
        return true;
 }
 
+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;
+       char address[18];
+       guint id;
+       GIOCondition cond;
+
+       if (err) {
+               error("%s", err->message);
+               goto fail;
+       }
+
+       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));
+
+       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);
+
+       rfsock->stack_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);
+       g_io_channel_set_close_on_unref(io, FALSE);
+
+       rfsock->rfcomm_watch = id;
+
+       return;
+fail:
+       connections = g_list_remove(connections, rfsock);
+       cleanup_rfsock(rfsock);
+}
+
+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("");
+
+       if (err < 0) {
+               error("Unable to get SDP record: %s", strerror(-err));
+               goto fail;
+       }
+
+       if (!recs || !recs->data) {
+               error("No SDP records found");
+               goto fail;
+       }
+
+       for (list = recs; list != NULL; list = list->next) {
+               sdp_record_t *rec = list->data;
+               sdp_list_t *protos;
+
+               if (sdp_get_access_protos(rec, &protos) < 0) {
+                       error("Unable to get proto list");
+                       goto fail;
+               }
+
+               chan = 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 (chan)
+                       break;
+       }
+
+       if (chan <= 0) {
+               error("Could not get RFCOMM channel %d", chan);
+               goto fail;
+       }
+
+       DBG("Got RFCOMM channel %d", chan);
+
+       if (rfsock->profile)
+               sec_level = rfsock->profile->sec_level;
+
+       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;
+       }
+
+       if (write(rfsock->fd, &chan, sizeof(chan)) != sizeof(chan)) {
+               error("Error sending RFCOMM channel");
+               goto fail;
+       }
+
+       rfsock->real_sock = g_io_channel_unix_get_fd(io);
+       rfsock->channel = chan;
+       connections = g_list_append(connections, rfsock);
+
+       g_io_channel_unref(io);
+
+       return;
+fail:
+       connections = g_list_remove(connections, rfsock);
+       cleanup_rfsock(rfsock);
+}
+
+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;
+
+       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);
+
+       if (bt_search_service(&adapter_addr, &rfsock->dst, &uuid,
+                                       sdp_search_cb, rfsock, NULL) < 0) {
+               error("Failed to search SDP records");
+               cleanup_rfsock(rfsock);
+               goto failed;
+       }
+
+       ipc_send_rsp_full(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT, 0, NULL,
+                                                                       hal_fd);
+       close(hal_fd);
+       return;
+
+failed:
+       ipc_send_rsp(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT,
+                                                       HAL_STATUS_FAILED);
+
+       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) },
+};
+
+void bt_socket_register(const bdaddr_t *addr)
+{
+       DBG("");
+
+       bacpy(&adapter_addr, addr);
+       ipc_register(HAL_SERVICE_ID_SOCK, cmd_handlers,
+                                               G_N_ELEMENTS(cmd_handlers));
+}
+
 void bt_socket_unregister(void)
 {
        DBG("");
+
+       g_list_free_full(connections, cleanup_rfsock);
+       g_list_free_full(servers, cleanup_rfsock);
+
+       ipc_unregister(HAL_SERVICE_ID_SOCK);
 }
index 7aa5574..5150b89 100644 (file)
  *
  */
 
+struct hal_sock_connect_signal {
+       short   size;
+       uint8_t bdaddr[6];
+       int     channel;
+       int     status;
+} __attribute__((packed));
+
 void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len);
 
-bool bt_socket_register(int sk, const bdaddr_t *addr);
+void bt_socket_register(const bdaddr_t *addr);
 void bt_socket_unregister(void);
index 0dd1510..ebc85c6 100644 (file)
@@ -538,6 +538,26 @@ static void cmd_devices(const char *arg)
        }
 }
 
+static void cmd_paired_devices(const char *arg)
+{
+       GList *list;
+
+       for (list = g_list_first(dev_list); list; list = g_list_next(list)) {
+               GDBusProxy *proxy = list->data;
+               DBusMessageIter iter;
+               dbus_bool_t paired;
+
+               if (g_dbus_proxy_get_property(proxy, "Paired", &iter) == FALSE)
+                       continue;
+
+               dbus_message_iter_get_basic(&iter, &paired);
+               if (!paired)
+                       continue;
+
+               print_device(proxy, NULL);
+       }
+}
+
 static void generic_callback(const DBusError *error, void *user_data)
 {
        char *str = user_data;
@@ -1047,6 +1067,8 @@ static const struct {
        { "select",       "<ctrl>",   cmd_select, "Select default controller",
                                                        ctrl_generator },
        { "devices",      NULL,       cmd_devices, "List available devices" },
+       { "paired-devices", NULL,     cmd_paired_devices,
+                                       "List paired devices"},
        { "system-alias", "<name>",   cmd_system_alias },
        { "reset-alias",  NULL,       cmd_reset_alias },
        { "power",        "<on/off>", cmd_power, "Set controller power" },
@@ -1197,12 +1219,16 @@ done:
 static gboolean input_handler(GIOChannel *channel, GIOCondition condition,
                                                        gpointer user_data)
 {
+       if (condition & G_IO_IN) {
+               rl_callback_read_char();
+               return TRUE;
+       }
+
        if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
                g_main_loop_quit(main_loop);
                return FALSE;
        }
 
-       rl_callback_read_char();
        return TRUE;
 }
 
index 76c63e7..7e58a43 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.11.
+# Generated by GNU Autoconf 2.69 for bluez 5.12.
 #
 #
 # 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.11'
-PACKAGE_STRING='bluez 5.11'
+PACKAGE_VERSION='5.12'
+PACKAGE_STRING='bluez 5.12'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -636,6 +636,8 @@ LIBOBJS
 ANDROID_FALSE
 ANDROID_TRUE
 CONFIGDIR
+SIXAXIS_FALSE
+SIXAXIS_TRUE
 EXPERIMENTAL_FALSE
 EXPERIMENTAL_TRUE
 DATAFILES_FALSE
@@ -833,6 +835,7 @@ with_systemdsystemunitdir
 with_systemduserunitdir
 enable_datafiles
 enable_experimental
+enable_sixaxis
 enable_android
 '
       ac_precious_vars='build_alias
@@ -1397,7 +1400,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.11 to adapt to many kinds of systems.
+\`configure' configures bluez 5.12 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1467,7 +1470,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of bluez 5.11:";;
+     short | recursive ) echo "Configuration of bluez 5.12:";;
    esac
   cat <<\_ACEOF
 
@@ -1501,6 +1504,7 @@ Optional Features:
   --disable-systemd       disable systemd integration
   --disable-datafiles     do not install configuration and data files
   --enable-experimental   enable experimental plugins (SAP, NFC, ...)
+  --enable-sixaxis        enable sixaxis plugin
   --enable-android        enable BlueZ for Android
 
 Optional Packages:
@@ -1615,7 +1619,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-bluez configure 5.11
+bluez configure 5.12
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1980,7 +1984,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.11, which was
+It was created by bluez $as_me 5.12, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2835,7 +2839,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='bluez'
- VERSION='5.11'
+ VERSION='5.12'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -4596,6 +4600,8 @@ fi
                with_cflags="$with_cflags -Wredundant-decls"
                with_cflags="$with_cflags -Wcast-align"
                with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
+               with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
+               with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28"
        fi
        WARNING_CFLAGS=$with_cflags
 
@@ -13560,6 +13566,21 @@ else
 fi
 
 
+# Check whether --enable-sixaxis was given.
+if test "${enable_sixaxis+set}" = set; then :
+  enableval=$enable_sixaxis; enable_sixaxis=${enableval}
+fi
+
+ if test "${enable_sixaxis}" = "yes" &&
+                                        test "${enable_udev}" != "no"; then
+  SIXAXIS_TRUE=
+  SIXAXIS_FALSE='#'
+else
+  SIXAXIS_TRUE='#'
+  SIXAXIS_FALSE=
+fi
+
+
 if (test "${prefix}" = "NONE"); then
                if (test "$localstatedir" = '${prefix}/var'); then
                localstatedir='/var'
@@ -13794,6 +13815,10 @@ if test -z "${EXPERIMENTAL_TRUE}" && test -z "${EXPERIMENTAL_FALSE}"; then
   as_fn_error $? "conditional \"EXPERIMENTAL\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${SIXAXIS_TRUE}" && test -z "${SIXAXIS_FALSE}"; then
+  as_fn_error $? "conditional \"SIXAXIS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${ANDROID_TRUE}" && test -z "${ANDROID_FALSE}"; then
   as_fn_error $? "conditional \"ANDROID\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -14195,7 +14220,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.11, which was
+This file was extended by bluez $as_me 5.12, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -14261,7 +14286,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.11
+bluez config.status 5.12
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
index 949846e..18d0b55 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(bluez, 5.11)
+AC_INIT(bluez, 5.12)
 
 AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
                                        tar-pax no-dist-gzip dist-xz])
@@ -216,6 +216,11 @@ AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental],
                                        [enable_experimental=${enableval}])
 AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes")
 
+AC_ARG_ENABLE(sixaxis, AC_HELP_STRING([--enable-sixaxis],
+               [enable sixaxis plugin]), [enable_sixaxis=${enableval}])
+AM_CONDITIONAL(SIXAXIS, test "${enable_sixaxis}" = "yes" &&
+                                        test "${enable_udev}" != "no")
+
 if (test "${prefix}" = "NONE"); then
        dnl no prefix and no localstatedir, so default to /var
        if (test "$localstatedir" = '${prefix}/var'); then
index 6201780..2d9fa3c 100644 (file)
@@ -85,6 +85,7 @@ Methods               void Connect()
 
                        Possible errors: org.bluez.Error.InvalidArguments
                                         org.bluez.Error.Failed
+                                        org.bluez.Error.AlreadyExists
                                         org.bluez.Error.AuthenticationCanceled
                                         org.bluez.Error.AuthenticationFailed
                                         org.bluez.Error.AuthenticationRejected
index 5f04bd1..d0dff74 100644 (file)
@@ -108,6 +108,7 @@ struct btdev {
        uint8_t  le_scan_enable;
        uint8_t  le_filter_dup;
        uint8_t  le_adv_enable;
+       uint8_t  le_ltk[16];
 
        uint16_t sync_train_interval;
        uint32_t sync_train_timeout;
@@ -833,29 +834,26 @@ static void conn_request(struct btdev *btdev, const uint8_t *bdaddr)
 {
        struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
 
-       if (remote) {
-               if (remote->scan_enable & 0x02) {
-                       struct bt_hci_evt_conn_request cr;
+       if (remote && remote->scan_enable & 0x02) {
+               struct bt_hci_evt_conn_request cr;
 
-                       memcpy(cr.bdaddr, btdev->bdaddr, 6);
-                       memcpy(cr.dev_class, btdev->dev_class, 3);
-                       cr.link_type = 0x01;
+               memcpy(cr.bdaddr, btdev->bdaddr, 6);
+               memcpy(cr.dev_class, btdev->dev_class, 3);
+               cr.link_type = 0x01;
 
-                       send_event(remote, BT_HCI_EVT_CONN_REQUEST,
-                                                       &cr, sizeof(cr));
-               } else
-                       conn_complete(btdev, bdaddr, BT_HCI_ERR_PAGE_TIMEOUT);
-       } else
-               conn_complete(btdev, bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+               send_event(remote, BT_HCI_EVT_CONN_REQUEST, &cr, sizeof(cr));
+       } else {
+               conn_complete(btdev, bdaddr, BT_HCI_ERR_PAGE_TIMEOUT);
+       }
 }
 
 static void disconnect_complete(struct btdev *btdev, uint16_t handle,
                                                        uint8_t reason)
 {
        struct bt_hci_evt_disconnect_complete dc;
-       struct btdev *remote;
+       struct btdev *remote = btdev->conn;
 
-       if (!btdev) {
+       if (!remote) {
                dc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
                dc.handle = cpu_to_le16(handle);
                dc.reason = 0x00;
@@ -869,8 +867,6 @@ static void disconnect_complete(struct btdev *btdev, uint16_t handle,
        dc.handle = cpu_to_le16(handle);
        dc.reason = reason;
 
-       remote = btdev->conn;
-
        btdev->conn = NULL;
        remote->conn = NULL;
 
@@ -1030,6 +1026,54 @@ static void le_set_scan_enable_complete(struct btdev *btdev)
        }
 }
 
+static void le_start_encrypt_complete(struct btdev *btdev)
+{
+       char buf[1 + sizeof(struct bt_hci_evt_le_long_term_key_request)];
+       struct bt_hci_evt_le_long_term_key_request *ev = (void *) &buf[1];
+       struct btdev *remote = btdev->conn;
+
+       if (!remote) {
+               cmd_status(btdev, BT_HCI_ERR_UNKNOWN_CONN_ID,
+                                               BT_HCI_CMD_LE_START_ENCRYPT);
+               return;
+       }
+
+       cmd_status(btdev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_START_ENCRYPT);
+
+       memset(buf, 0, sizeof(buf));
+       buf[0] = BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST;
+       ev->handle = cpu_to_le16(42);
+
+       send_event(remote, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf));
+}
+
+static void le_encrypt_complete(struct btdev *btdev)
+{
+       struct bt_hci_evt_encrypt_change ev;
+       struct bt_hci_rsp_le_ltk_req_reply rp;
+       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_REPLY, &rp,
+                                                       sizeof(rp));
+               return;
+       }
+
+       rp.status = BT_HCI_ERR_SUCCESS;
+       cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_REPLY, &rp, sizeof(rp));
+
+       memset(&ev, 0, sizeof(ev));
+       ev.handle = cpu_to_le16(42);
+       ev.encr_mode = 0x01;
+
+       send_event(btdev, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev));
+       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)
 {
@@ -1058,6 +1102,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
        const struct bt_hci_cmd_setup_sync_conn *ssc;
        const struct bt_hci_cmd_le_set_adv_enable *lsae;
        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;
        const struct bt_hci_cmd_read_local_amp_assoc *rlaa_cmd;
        struct bt_hci_rsp_read_default_link_policy rdlp;
        struct bt_hci_rsp_read_stored_link_key rslk;
@@ -1826,6 +1872,22 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode,
                cmd_complete(btdev, opcode, &lr, sizeof(lr));
                break;
 
+       case BT_HCI_CMD_LE_START_ENCRYPT:
+               if (btdev->type == BTDEV_TYPE_BREDR)
+                       goto unsupported;
+               lse = data;
+               memcpy(btdev->le_ltk, lse->ltk, 16);
+               le_start_encrypt_complete(btdev);
+               break;
+
+       case BT_HCI_CMD_LE_LTK_REQ_REPLY:
+               if (btdev->type == BTDEV_TYPE_BREDR)
+                       goto unsupported;
+               llrr = data;
+               memcpy(btdev->le_ltk, llrr->ltk, 16);
+               le_encrypt_complete(btdev);
+               break;
+
        case BT_HCI_CMD_SETUP_SYNC_CONN:
                if (btdev->type == BTDEV_TYPE_LE)
                        goto unsupported;
index 0c5836f..10e7a05 100644 (file)
@@ -975,6 +975,48 @@ static bool l2cap_conn_param_rsp(struct bthost *bthost, struct btconn *conn,
        return true;
 }
 
+static bool l2cap_le_conn_req(struct bthost *bthost, struct btconn *conn,
+                               uint8_t ident, const void *data, uint16_t len)
+{
+       const struct bt_l2cap_pdu_le_conn_req *req = data;
+       struct bt_l2cap_pdu_le_conn_rsp rsp;
+       uint16_t psm;
+
+       if (len < sizeof(*req))
+               return false;
+
+       psm = le16_to_cpu(req->psm);
+
+       memset(&rsp, 0, sizeof(rsp));
+
+       rsp.mtu = 23;
+       rsp.mps = 23;
+       rsp.credits = 1;
+
+       if (bthost->server_psm && bthost->server_psm == psm)
+               rsp.dcid = cpu_to_le16(conn->next_cid++);
+       else
+               rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */
+
+       l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_LE_CONN_RSP, ident, &rsp,
+                                                               sizeof(rsp));
+
+       return true;
+}
+
+static bool l2cap_le_conn_rsp(struct bthost *bthost, struct btconn *conn,
+                               uint8_t ident, const void *data, uint16_t len)
+{
+       const struct bt_l2cap_pdu_le_conn_rsp *rsp = data;
+
+       if (len < sizeof(*rsp))
+               return false;
+
+       bthost_add_l2cap_conn(bthost, conn, 0, le16_to_cpu(rsp->dcid));
+
+       return true;
+}
+
 static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn,
                                                const void *data, uint16_t len)
 {
@@ -997,6 +1039,11 @@ static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn,
                                                data + sizeof(*hdr), hdr_len);
                break;
 
+       case BT_L2CAP_PDU_DISCONN_REQ:
+               ret = l2cap_disconn_req(bthost, conn, hdr->ident,
+                                               data + sizeof(*hdr), hdr_len);
+               break;
+
        case BT_L2CAP_PDU_CONN_PARAM_REQ:
                ret = l2cap_conn_param_req(bthost, conn, hdr->ident,
                                                data + sizeof(*hdr), hdr_len);
@@ -1007,6 +1054,16 @@ static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn,
                                                data + sizeof(*hdr), hdr_len);
                break;
 
+       case BT_L2CAP_PDU_LE_CONN_REQ:
+               ret = l2cap_le_conn_req(bthost, conn, hdr->ident,
+                                               data + sizeof(*hdr), hdr_len);
+               break;
+
+       case BT_L2CAP_PDU_LE_CONN_RSP:
+               ret = l2cap_le_conn_rsp(bthost, conn, hdr->ident,
+                                               data + sizeof(*hdr), hdr_len);
+               break;
+
        default:
                printf("Unknown L2CAP code 0x%02x\n", hdr->code);
                ret = false;
@@ -1159,6 +1216,20 @@ void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable)
        send_command(bthost, BT_HCI_CMD_LE_SET_ADV_ENABLE, &enable, 1);
 }
 
+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);
+
+       send_command(bthost, BT_HCI_CMD_LE_START_ENCRYPT, &cmd, sizeof(cmd));
+}
+
 void bthost_set_server_psm(struct bthost *bthost, uint16_t psm)
 {
        bthost->server_psm = psm;
index 32b10f9..474ada9 100644 (file)
@@ -72,6 +72,9 @@ void bthost_write_scan_enable(struct bthost *bthost, uint8_t scan);
 
 void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable);
 
+void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle,
+                                                       const uint8_t ltk[16]);
+
 void bthost_set_server_psm(struct bthost *bthost, uint16_t psm);
 
 void bthost_start(struct bthost *bthost);
index 5598ccf..17ee2c1 100644 (file)
@@ -793,6 +793,30 @@ const char *bt_compidtostr(int compid)
                return "Atus BV";
        case 266:
                return "Codegate Ltd.";
+       case 267:
+               return "ERi, Inc.";
+       case 268:
+               return "Transducers Direct, LLC";
+       case 269:
+               return "Fujitsu Ten Limited";
+       case 270:
+               return "Audi AG";
+       case 271:
+               return "HiSilicon Technologies Co., Ltd.";
+       case 272:
+               return "Nippon Seiki Co., Ltd.";
+       case 273:
+               return "Steelseries ApS";
+       case 274:
+               return "vyzybl Inc.";
+       case 275:
+               return "Openbrain Technologies, Co., Ltd.";
+       case 276:
+               return "Xensr";
+       case 277:
+               return "e.solutions";
+       case 278:
+               return "1OAK Technologies";
        case 65535:
                return "internal use";
        default:
index 012fde4..61c1f9a 100644 (file)
@@ -77,6 +77,13 @@ struct bt_security {
 #define BT_FLUSHABLE_OFF       0
 #define BT_FLUSHABLE_ON                1
 
+#define BT_POWER               9
+struct bt_power {
+       uint8_t force_active;
+};
+#define BT_POWER_FORCE_ACTIVE_OFF 0
+#define BT_POWER_FORCE_ACTIVE_ON  1
+
 #define BT_CHANNEL_POLICY      10
 
 /* BR/EDR only (default policy)
@@ -109,6 +116,9 @@ struct bt_voice {
        uint16_t setting;
 };
 
+#define BT_SNDMTU              12
+#define BT_RCVMTU              13
+
 #define BT_VOICE_TRANSPARENT                   0x0003
 #define BT_VOICE_CVSD_16BIT                    0x0060
 
index 6e37836..005578a 100644 (file)
--- a/lib/hci.c
+++ b/lib/hci.c
@@ -649,6 +649,7 @@ static hci_map ver_map[] = {
        { "2.1",        0x04 },
        { "3.0",        0x05 },
        { "4.0",        0x06 },
+       { "4.1",        0x07 },
        { NULL }
 };
 
index 2aec950..849ed86 100644 (file)
@@ -290,8 +290,8 @@ struct bt_hci_cmd_setup_sync_conn {
        uint16_t pkt_type;
 } __attribute__ ((packed));
 
-#define BT_HCI_CMD_ACCEPT_SYNC_CONN            0x0429
-struct bt_hci_cmd_accept_sync_conn {
+#define BT_HCI_CMD_ACCEPT_SYNC_CONN_REQUEST    0x0429
+struct bt_hci_cmd_accept_sync_conn_request {
        uint8_t  bdaddr[6];
        uint32_t tx_bandwidth;
        uint32_t rx_bandwidth;
@@ -301,8 +301,8 @@ struct bt_hci_cmd_accept_sync_conn {
        uint16_t pkt_type;
 } __attribute__ ((packed));
 
-#define BT_HCI_CMD_REJECT_SYNC_CONN            0x042a
-struct bt_hci_cmd_reject_sync_conn {
+#define BT_HCI_CMD_REJECT_SYNC_CONN_REQUEST    0x042a
+struct bt_hci_cmd_reject_sync_conn_request {
        uint8_t  bdaddr[6];
        uint8_t  reason;
 } __attribute__ ((packed));
@@ -411,6 +411,73 @@ struct bt_hci_cmd_flow_spec_modify {
        uint8_t  rx_flow_spec[16];
 } __attribute__ ((packed));
 
+#define BT_HCI_CMD_TRUNCATED_PAGE              0x043f
+struct bt_hci_cmd_truncated_page {
+       uint8_t  bdaddr[6];
+       uint8_t  pscan_rep_mode;
+       uint16_t clock_offset;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_TRUNCATED_PAGE_CANCEL       0x0440
+struct bt_hci_cmd_truncated_page_cancel {
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_SLAVE_BROADCAST         0x0441
+struct bt_hci_cmd_set_slave_broadcast {
+       uint8_t  enable;
+       uint8_t  lt_addr;
+       uint8_t  lpo_allowed;
+       uint16_t pkt_type;
+       uint16_t min_interval;
+       uint16_t max_interval;
+       uint16_t timeout;
+} __attribute__ ((packed));
+struct bt_hci_rsp_set_slave_broadcast {
+       uint8_t  status;
+       uint8_t  lt_addr;
+       uint16_t interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_SLAVE_BROADCAST_RECEIVE 0x0442
+struct bt_hci_cmd_set_slave_broadcast_receive {
+       uint8_t  enable;
+       uint8_t  bdaddr[6];
+       uint8_t  lt_addr;
+       uint16_t interval;
+       uint32_t offset;
+       uint32_t instant;
+       uint16_t timeout;
+       uint8_t  accuracy;
+       uint8_t  skip;
+       uint16_t pkt_type;
+       uint8_t  map[10];
+} __attribute__ ((packed));
+struct bt_hci_rsp_set_slave_broadcast_receive {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+       uint8_t  lt_addr;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_START_SYNC_TRAIN            0x0443
+
+#define BT_HCI_CMD_RECEIVE_SYNC_TRAIN          0x0444
+struct bt_hci_cmd_receive_sync_train {
+       uint8_t  bdaddr[6];
+       uint16_t timeout;
+       uint16_t window;
+       uint16_t interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REMOTE_OOB_EXT_DATA_REQUEST_REPLY   0x0445
+struct bt_hci_cmd_remote_oob_ext_data_request_reply {
+       uint8_t  bdaddr[6];
+       uint8_t  hash192[16];
+       uint8_t  randomizer192[16];
+       uint8_t  hash256[16];
+       uint8_t  randomizer256[16];
+} __attribute__ ((packed));
+
 #define BT_HCI_CMD_HOLD_MODE                   0x0801
 struct bt_hci_cmd_hold_mode {
        uint16_t handle;
@@ -1002,6 +1069,35 @@ struct bt_hci_cmd_write_le_host_supported {
        uint8_t  simultaneous;
 } __attribute__ ((packed));
 
+#define BT_HCI_CMD_SET_RESERVED_LT_ADDR                0x0c74
+struct bt_hci_cmd_set_reserved_lt_addr {
+       uint8_t  lt_addr;
+} __attribute__ ((packed));
+struct bt_hci_rsp_set_reserved_lt_addr {
+       uint8_t  status;
+       uint8_t  lt_addr;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_DELETE_RESERVED_LT_ADDR     0x0c75
+struct bt_hci_cmd_delete_reserved_lt_addr {
+       uint8_t  lt_addr;
+} __attribute__ ((packed));
+struct bt_hci_rsp_delete_reserved_lt_addr {
+       uint8_t  status;
+       uint8_t  lt_addr;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_SLAVE_BROADCAST_DATA    0x0c76
+struct bt_hci_cmd_set_slave_broadcast_data {
+       uint8_t  lt_addr;
+       uint8_t  fragment;
+       uint8_t  length;
+} __attribute__ ((packed));
+struct bt_hci_rsp_set_slave_broadcast_data {
+       uint8_t  status;
+       uint8_t  lt_addr;
+} __attribute__ ((packed));
+
 #define BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS      0x0c77
 struct bt_hci_rsp_read_sync_train_params {
        uint8_t  status;
@@ -1010,6 +1106,58 @@ struct bt_hci_rsp_read_sync_train_params {
        uint8_t  service_data;
 } __attribute__ ((packed));
 
+#define BT_HCI_CMD_WRITE_SYNC_TRAIN_PARAMS     0x0c78
+struct bt_hci_cmd_write_sync_train_params {
+       uint16_t min_interval;
+       uint16_t max_interval;
+       uint32_t timeout;
+       uint8_t  service_data;
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_sync_train_params {
+       uint8_t  status;
+       uint16_t interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_SECURE_CONN_SUPPORT    0x0c79
+struct bt_hci_rsp_read_secure_conn_support {
+       uint8_t  status;
+       uint8_t  support;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT   0x0c7a
+struct bt_hci_cmd_write_secure_conn_support {
+       uint8_t support;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_AUTH_PAYLOAD_TIMEOUT   0x0c7b
+struct bt_hci_cmd_read_auth_payload_timeout {
+       uint16_t handle;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_auth_payload_timeout {
+       uint8_t  status;
+       uint16_t handle;
+       uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_AUTH_PAYLOAD_TIMEOUT  0x0c7c
+struct bt_hci_cmd_write_auth_payload_timeout {
+       uint16_t handle;
+       uint16_t timeout;
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_auth_payload_timeout {
+       uint8_t  status;
+       uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA     0x0c7d
+struct bt_hci_rsp_read_local_oob_ext_data {
+       uint8_t  status;
+       uint8_t  hash192[16];
+       uint8_t  randomizer192[16];
+       uint8_t  hash256[16];
+       uint8_t  randomizer256[16];
+} __attribute__ ((packed));
+
 #define BT_HCI_CMD_READ_LOCAL_VERSION          0x1001
 struct bt_hci_rsp_read_local_version {
        uint8_t  status;
@@ -1830,6 +1978,58 @@ struct bt_hci_evt_amp_status_change {
        uint8_t  amp_status;
 } __attribute__ ((packed));
 
+#define BT_HCI_EVT_SYNC_TRAIN_COMPLETE         0x4f
+struct bt_hci_evt_sync_train_complete {
+       uint8_t  status;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SYNC_TRAIN_RECEIVED         0x50
+struct bt_hci_evt_sync_train_received {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+       uint32_t offset;
+       uint8_t  map[10];
+       uint8_t  lt_addr;
+       uint32_t instant;
+       uint16_t interval;
+       uint8_t  service_data;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SLAVE_BROADCAST_RECEIVE     0x51
+struct bt_hci_evt_slave_broadcast_receive {
+       uint8_t  bdaddr[6];
+       uint8_t  lt_addr;
+       uint32_t clock;
+       uint32_t offset;
+       uint8_t  status;
+       uint8_t  fragment;
+       uint8_t  length;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SLAVE_BROADCAST_TIMEOUT     0x52
+struct bt_hci_evt_slave_broadcast_timeout {
+       uint8_t  bdaddr[6];
+       uint8_t  lt_addr;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE     0x53
+struct bt_hci_evt_truncated_page_complete {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_SLAVE_PAGE_RESPONSE_TIMEOUT 0x54
+
+#define BT_HCI_EVT_SLAVE_BROADCAST_CHANNEL_MAP_CHANGE  0x55
+struct bt_hci_evt_slave_broadcast_channel_map_change {
+       uint8_t  map[10];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_AUTH_PAYLOAD_TIMEOUT_EXPIRED        0x57
+struct bt_hci_evt_auth_payload_timeout_expired {
+       uint16_t handle;
+} __attribute__ ((packed));
+
 #define BT_HCI_EVT_LE_CONN_COMPLETE            0x01
 struct bt_hci_evt_le_conn_complete {
        uint8_t  status;
@@ -2009,6 +2209,30 @@ struct bt_l2cap_pdu_conn_param_rsp {
        uint16_t result;
 } __attribute__ ((packed));
 
+#define BT_L2CAP_PDU_LE_CONN_REQ       0x14
+struct bt_l2cap_pdu_le_conn_req {
+       uint16_t psm;
+       uint16_t scid;
+       uint16_t mtu;
+       uint16_t mps;
+       uint16_t credits;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_LE_CONN_RSP       0x15
+struct bt_l2cap_pdu_le_conn_rsp {
+       uint16_t dcid;
+       uint16_t mtu;
+       uint16_t mps;
+       uint16_t credits;
+       uint16_t result;
+} __attribute__ ((packed));
+
+#define BT_L2CAP_PDU_LE_FLOWCTL_CREDS  0x16
+struct bt_l2cap_pdu_le_flowctl_creds {
+       uint16_t cid;
+       uint16_t credits;
+} __attribute__ ((packed));
+
 struct bt_l2cap_hdr_connless {
        uint16_t psm;
 } __attribute__ ((packed));
index ad6e64b..c215882 100644 (file)
@@ -321,6 +321,12 @@ static void print_conn_result(uint16_t result)
        case 0x0004:
                str = "Connection refused - no resources available";
                break;
+       case 0x0005:
+               str = "Insufficient Authentication";
+               break;
+       case 0x0006:
+               str = "Insufficient Authorization";
+               break;
        default:
                str = "Reserved";
                break;
@@ -1002,6 +1008,40 @@ static void sig_conn_param_rsp(const struct l2cap_frame *frame)
        print_conn_param_result(pdu->result);
 }
 
+static void sig_le_conn_req(const struct l2cap_frame *frame)
+{
+       const struct bt_l2cap_pdu_le_conn_req *pdu = frame->data;
+
+       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));
+
+       assign_scid(frame, btohs(pdu->scid), btohs(pdu->psm), 0);
+}
+
+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_conn_result(pdu->result);
+
+       /*assign_dcid(frame, btohs(pdu->dcid), btohs(pdu->scid));*/
+}
+
+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));
+}
+
 struct sig_opcode_data {
        uint8_t opcode;
        const char *str;
@@ -1051,10 +1091,20 @@ static const struct sig_opcode_data bredr_sig_opcode_table[] = {
 static const struct sig_opcode_data le_sig_opcode_table[] = {
        { 0x01, "Command Reject",
                        sig_cmd_reject, 2, false },
+       { 0x06, "Disconnection Request",
+                       sig_disconn_req, 4, true },
+       { 0x07, "Disconnection Response",
+                       sig_disconn_rsp, 4, true },
        { 0x12, "Connection Parameter Update Request",
                        sig_conn_param_req, 8, true },
        { 0x13, "Connection Parameter Update Response",
                        sig_conn_param_rsp, 2, true },
+       { 0x14, "LE Connection Request",
+                       sig_le_conn_req, 10, true },
+       { 0x15, "LE Connection Response",
+                       sig_le_conn_rsp, 10, true },
+       { 0x16, "LE Flow Control Credit",
+                       sig_le_flowctl_creds, 4, true },
        { },
 };
 
index e8b7a7a..1a66572 100644 (file)
@@ -483,6 +483,8 @@ static const struct llcp_data llcp_table[] = {
        { 0x0b, "LL_PAUSE_ENC_RSP",         null_pdu,         0, true },
        { 0x0c, "LL_VERSION_IND",           version_ind,      5, true },
        { 0x0d, "LL_REJECT_IND",            reject_ind,       1, true },
+       { 0x12, "LL_PING_REQ",              null_pdu,         0, true },
+       { 0x13, "LL_PING_RSP",              null_pdu,         0, true },
        { }
 };
 
index 0efa60b..6ec7d9b 100644 (file)
@@ -66,6 +66,7 @@
 #define COLOR_HCI_ACLDATA              COLOR_CYAN
 #define COLOR_HCI_SCODATA              COLOR_YELLOW
 
+#define COLOR_UNKNOWN_ERROR            COLOR_WHITE_BG
 #define COLOR_UNKNOWN_FEATURE_BIT      COLOR_WHITE_BG
 #define COLOR_UNKNOWN_EVENT_MASK       COLOR_WHITE_BG
 #define COLOR_UNKNOWN_LE_STATES                COLOR_WHITE_BG
@@ -345,6 +346,8 @@ static const struct {
        { 0x3d, "Connection Terminated due to MIC Failure"              },
        { 0x3e, "Connection Failed to be Established"                   },
        { 0x3f, "MAC Connection Failed"                                 },
+       { 0x40, "Coarse Clock Adjustment Rejected "
+               "but Will Try to Adjust Using Clock Dragging"           },
        { }
 };
 
@@ -352,19 +355,24 @@ static void print_error(const char *label, uint8_t error)
 {
        const char *str = "Unknown";
        const char *color_on, *color_off;
+       bool unknown = true;
        int i;
 
        for (i = 0; error2str_table[i].str; i++) {
                if (error2str_table[i].error == error) {
                        str = error2str_table[i].str;
+                       unknown = false;
                        break;
                }
        }
 
        if (use_color()) {
-               if (error)
-                       color_on = COLOR_RED;
-               else
+               if (error) {
+                       if (unknown)
+                               color_on = COLOR_UNKNOWN_ERROR;
+                       else
+                               color_on = COLOR_RED;
+               } else
                        color_on = COLOR_GREEN;
                color_off = COLOR_OFF;
        } else {
@@ -455,6 +463,11 @@ static void print_addr_type(const char *label, uint8_t addr_type)
        print_field("%s: %s (0x%2.2x)", label, str, addr_type);
 }
 
+static void print_lt_addr(uint8_t lt_addr)
+{
+       print_field("LT address: %d", lt_addr);
+}
+
 static void print_handle(uint16_t handle)
 {
        print_field("Handle: %d", btohs(handle));
@@ -1002,7 +1015,6 @@ static void print_afh_mode(uint8_t mode)
        case 0x01:
                str = "Enabled";
                break;
-               break;
        default:
                str = "Reserved";
                break;
@@ -1049,6 +1061,31 @@ static void print_ssp_debug_mode(uint8_t mode)
        print_field("Debug mode: %s (0x%2.2x)", str, mode);
 }
 
+static void print_secure_conn_support(uint8_t support)
+{
+       const char *str;
+
+       switch (support) {
+       case 0x00:
+               str = "Disabled";
+               break;
+       case 0x01:
+               str = "Enabled";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Support: %s (0x%2.2x)", str, support);
+}
+
+static void print_auth_payload_timeout(uint16_t timeout)
+{
+       print_field("Timeout: %d msec (0x%4.4x)",
+                                       btohs(timeout) * 10, btohs(timeout));
+}
+
 static void print_pscan_rep_mode(uint8_t pscan_rep_mode)
 {
        const char *str;
@@ -1138,6 +1175,31 @@ static void print_clock_accuracy(uint16_t accuracy)
                                btohs(accuracy) * 0.3125, btohs(accuracy));
 }
 
+static void print_broadcast_fragment(uint8_t fragment)
+{
+       const char *str;
+
+       switch (fragment) {
+       case 0x00:
+               str = "Continuation fragment";
+               break;
+       case 0x01:
+               str = "Starting fragment";
+               break;
+       case 0x02:
+               str = "Ending fragment";
+               break;
+       case 0x03:
+               str = "No fragmentation";
+               break;
+       default:
+               str = "Reserved";
+               break;
+       }
+
+       print_field("Fragment: %s (0x%2.2x)", str, fragment);
+}
+
 static void print_link_type(uint8_t link_type)
 {
        const char *str;
@@ -1203,6 +1265,9 @@ static void print_encr_mode_change(uint8_t encr_mode, uint16_t handle)
                        break;
                }
                break;
+       case 0x02:
+               str = "Enabled with AES-CCM";
+               break;
        default:
                str = "Reserved";
                break;
@@ -1283,14 +1348,20 @@ static void print_key_type(uint8_t key_type)
                str = "Debug Combination key";
                break;
        case 0x04:
-               str = "Unauthenticated Combination key";
+               str = "Unauthenticated Combination key from P-192";
                break;
        case 0x05:
-               str = "Authenticated Combination key";
+               str = "Authenticated Combination key from P-192";
                break;
        case 0x06:
                str = "Changed Combination key";
                break;
+       case 0x07:
+               str = "Unauthenticated Combination key from P-256";
+               break;
+       case 0x08:
+               str = "Authenticated Combination key from P-256";
+               break;
        default:
                str = "Reserved";
                break;
@@ -1388,7 +1459,13 @@ static void print_oob_data(uint8_t oob_data)
                str = "Authentication data not present";
                break;
        case 0x01:
-               str = "Authentication data present";
+               str = "P-192 authentication data present";
+               break;
+       case 0x02:
+               str = "P-256 authentication data present";
+               break;
+       case 0x03:
+               str = "P-192 and P-256 authentication data present";
                break;
        default:
                str = "Reserved";
@@ -1898,6 +1975,7 @@ static const struct features_data features_page1[] = {
        {  0, "Secure Simple Pairing (Host Support)"    },
        {  1, "LE Supported (Host)"                     },
        {  2, "Simultaneous LE and BR/EDR (Host)"       },
+       {  3, "Secure Connections (Host Support)"       },
        { }
 };
 
@@ -1907,11 +1985,20 @@ static const struct features_data features_page2[] = {
        {  2, "Synchronization Train"                   },
        {  3, "Synchronization Scan"                    },
        {  4, "Inquiry Response Notification Event"     },
+       {  5, "Generalized interlaced scan"             },
+       {  6, "Coarse Clock Adjustment"                 },
+       {  8, "Secure Connections (Controller Support)" },
+       {  9, "Ping"                                    },
+       { 11, "Train nudging"                           },
        { }
 };
 
 static const struct features_data features_le[] = {
        {  0, "LE Encryption"                           },
+       {  1, "Connection Parameter Request Procedure"  },
+       {  2, "Extended Reject Indication"              },
+       {  3, "Slave-initiated Features Exchange"       },
+       {  4, "LE Ping"                                 },
        { }
 };
 
@@ -2180,6 +2267,7 @@ static const struct {
        { 20, "Slave Page Response Timeout"                             },
        { 21, "Connectionless Slave Broadcast Channel Map Change"       },
        { 22, "Inquiry Response Notification"                           },
+       { 23, "Authenticated Payload Timeout Expired"                   },
        { }
 };
 
@@ -2211,11 +2299,12 @@ static const struct {
        uint8_t bit;
        const char *str;
 } events_le_table[] = {
-       {  0, "LE Connection Complete"          },
-       {  1, "LE Advertising Report"           },
-       {  2, "LE Connection Update Complete"   },
-       {  3, "LE Read Remote Used Features"    },
-       {  4, "LE Long Term Key Request"        },
+       {  0, "LE Connection Complete"                  },
+       {  1, "LE Advertising Report"                   },
+       {  2, "LE Connection Update Complete"           },
+       {  3, "LE Read Remote Used Features"            },
+       {  4, "LE Long Term Key Request"                },
+       {  5, "LE Remote Connection Parameter Request"  },
        { }
 };
 
@@ -3106,9 +3195,9 @@ static void setup_sync_conn_cmd(const void *data, uint8_t size)
        print_pkt_type(cmd->pkt_type);
 }
 
-static void accept_sync_conn_cmd(const void *data, uint8_t size)
+static void accept_sync_conn_request_cmd(const void *data, uint8_t size)
 {
-       const struct bt_hci_cmd_accept_sync_conn *cmd = data;
+       const struct bt_hci_cmd_accept_sync_conn_request *cmd = data;
 
        print_bdaddr(cmd->bdaddr);
        print_field("Transmit bandwidth: %d", btohl(cmd->tx_bandwidth));
@@ -3119,9 +3208,9 @@ static void accept_sync_conn_cmd(const void *data, uint8_t size)
        print_pkt_type(cmd->pkt_type);
 }
 
-static void reject_sync_conn_cmd(const void *data, uint8_t size)
+static void reject_sync_conn_request_cmd(const void *data, uint8_t size)
 {
-       const struct bt_hci_cmd_reject_sync_conn *cmd = data;
+       const struct bt_hci_cmd_reject_sync_conn_request *cmd = data;
 
        print_bdaddr(cmd->bdaddr);
        print_reason(cmd->reason);
@@ -3271,6 +3360,91 @@ static void flow_spec_modify_cmd(const void *data, uint8_t size)
        print_flow_spec("RX", cmd->rx_flow_spec);
 }
 
+static void truncated_page_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_truncated_page *cmd = data;
+
+       print_bdaddr(cmd->bdaddr);
+       print_pscan_rep_mode(cmd->pscan_rep_mode);
+       print_clock_offset(cmd->clock_offset);
+}
+
+static void truncated_page_cancel_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_truncated_page_cancel *cmd = data;
+
+       print_bdaddr(cmd->bdaddr);
+}
+
+static void set_slave_broadcast_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_set_slave_broadcast *cmd = data;
+
+       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_pkt_type(cmd->pkt_type);
+       print_slot_625("Min interval", cmd->min_interval);
+       print_slot_625("Max interval", cmd->max_interval);
+       print_slot_625("Supervision timeout", cmd->timeout);
+}
+
+static void set_slave_broadcast_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_set_slave_broadcast *rsp = data;
+
+       print_status(rsp->status);
+       print_lt_addr(rsp->lt_addr);
+       print_interval(rsp->interval);
+}
+
+static void set_slave_broadcast_receive_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_set_slave_broadcast_receive *cmd = data;
+
+       print_field("Enable: 0x%2.2x", cmd->enable);
+       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_slot_625("Supervision timeout", cmd->timeout);
+       print_field("Remote timing accuracy: %d ppm", cmd->accuracy);
+       print_field("Skip: 0x%2.2x", cmd->skip);
+       print_pkt_type(cmd->pkt_type);
+       print_channel_map(cmd->map);
+}
+
+static void set_slave_broadcast_receive_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_set_slave_broadcast_receive *rsp = data;
+
+       print_status(rsp->status);
+       print_bdaddr(rsp->bdaddr);
+       print_lt_addr(rsp->lt_addr);
+}
+
+static void receive_sync_train_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_receive_sync_train *cmd = data;
+
+       print_bdaddr(cmd->bdaddr);
+       print_timeout(cmd->timeout);
+       print_window(cmd->window);
+       print_interval(cmd->interval);
+}
+
+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);
+}
+
 static void hold_mode_cmd(const void *data, uint8_t size)
 {
        const struct bt_hci_cmd_hold_mode *cmd = data;
@@ -4211,6 +4385,59 @@ static void write_le_host_supported_cmd(const void *data, uint8_t size)
        print_field("Simultaneous: 0x%2.2x", cmd->simultaneous);
 }
 
+static void set_reserved_lt_addr_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_set_reserved_lt_addr *cmd = data;
+
+       print_lt_addr(cmd->lt_addr);
+}
+
+static void set_reserved_lt_addr_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_set_reserved_lt_addr *rsp = data;
+
+       print_status(rsp->status);
+       print_lt_addr(rsp->lt_addr);
+}
+
+static void delete_reserved_lt_addr_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_delete_reserved_lt_addr *cmd = data;
+
+       print_lt_addr(cmd->lt_addr);
+}
+
+static void delete_reserved_lt_addr_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_delete_reserved_lt_addr *rsp = data;
+
+       print_status(rsp->status);
+       print_lt_addr(rsp->lt_addr);
+}
+
+static void set_slave_broadcast_data_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_set_slave_broadcast_data *cmd = data;
+
+       print_lt_addr(cmd->lt_addr);
+       print_broadcast_fragment(cmd->fragment);
+       print_field("Length: %d", cmd->length);
+
+       if (size - 3 != cmd->length)
+               print_text(COLOR_ERROR, "invalid data size (%d != %d)",
+                                               size - 3, cmd->length);
+
+       packet_hexdump(data + 3, size - 3);
+}
+
+static void set_slave_broadcast_data_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_set_slave_broadcast_data *rsp = data;
+
+       print_status(rsp->status);
+       print_lt_addr(rsp->lt_addr);
+}
+
 static void read_sync_train_params_rsp(const void *data, uint8_t size)
 {
        const struct bt_hci_rsp_read_sync_train_params *rsp = data;
@@ -4222,6 +4449,83 @@ static void read_sync_train_params_rsp(const void *data, uint8_t size)
        print_field("Service Data: 0x%2.2x", rsp->service_data);
 }
 
+static void write_sync_train_params_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_write_sync_train_params *cmd = data;
+
+       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));
+       print_field("Service Data: 0x%2.2x", cmd->service_data);
+}
+
+static void write_sync_train_params_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_write_sync_train_params *rsp = data;
+
+       print_status(rsp->status);
+       print_interval(rsp->interval);
+}
+
+static void read_secure_conn_support_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_read_secure_conn_support *rsp = data;
+
+       print_status(rsp->status);
+       print_secure_conn_support(rsp->support);
+}
+
+static void write_secure_conn_support_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_write_secure_conn_support *cmd = data;
+
+       print_secure_conn_support(cmd->support);
+}
+
+static void read_auth_payload_timeout_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_read_auth_payload_timeout *cmd = data;
+
+       print_handle(cmd->handle);
+}
+
+static void read_auth_payload_timeout_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_read_auth_payload_timeout *rsp = data;
+
+       print_status(rsp->status);
+       print_handle(rsp->handle);
+       print_auth_payload_timeout(rsp->timeout);
+}
+
+static void write_auth_payload_timeout_cmd(const void *data, uint8_t size)
+{
+       const struct bt_hci_cmd_write_auth_payload_timeout *cmd = data;
+
+       print_handle(cmd->handle);
+       print_auth_payload_timeout(cmd->timeout);
+}
+
+static void write_auth_payload_timeout_rsp(const void *data, uint8_t size)
+{
+       const struct bt_hci_rsp_write_auth_payload_timeout *rsp = data;
+
+       print_status(rsp->status);
+       print_handle(rsp->handle);
+}
+
+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);
+}
+
 static void read_local_version_rsp(const void *data, uint8_t size)
 {
        const struct bt_hci_rsp_read_local_version *rsp = data;
@@ -4575,7 +4879,7 @@ static void le_set_adv_parameters_cmd(const void *data, uint8_t size)
                str = "Connectable undirected - ADV_IND";
                break;
        case 0x01:
-               str = "Connectable directed - ADV_DIRECT_IND";
+               str = "Connectable directed - ADV_DIRECT_IND (high duty cycle)";
                break;
        case 0x02:
                str = "Scannable undirected - ADV_SCAN_IND";
@@ -4583,6 +4887,9 @@ static void le_set_adv_parameters_cmd(const void *data, uint8_t size)
        case 0x03:
                str = "Non connectable undirect - ADV_NONCONN_IND";
                break;
+       case 0x04:
+               str = "Connectable directed - ADV_DIRECT_IND (low duty cycle)";
+               break;
        default:
                str = "Reserved";
                break;
@@ -5047,10 +5354,10 @@ static const struct opcode_data opcode_table[] = {
                                read_lmp_handle_rsp, 8, true },
        { 0x0428, 131, "Setup Synchronous Connection",
                                setup_sync_conn_cmd, 17, true },
-       { 0x0429, 132, "Accept Synchronous Connection",
-                               accept_sync_conn_cmd, 21, true },
-       { 0x042a, 133, "Reject Synchronous Connection",
-                               reject_sync_conn_cmd, 7, true },
+       { 0x0429, 132, "Accept Synchronous Connection Request",
+                               accept_sync_conn_request_cmd, 21, true },
+       { 0x042a, 133, "Reject Synchronous Connection Request",
+                               reject_sync_conn_request_cmd, 7, true },
        { 0x042b, 151, "IO Capability Request Reply",
                                io_capability_request_reply_cmd, 9, true,
                                status_bdaddr_rsp, 7, true },
@@ -5093,13 +5400,25 @@ static const struct opcode_data opcode_table[] = {
        { 0x043c, 175, "Flow Specifcation Modify",
                                flow_spec_modify_cmd, 34, true },
        { 0x043d, 235, "Enhanced Setup Synchronous Connection" },
-       { 0x043e, 236, "Enhanced Accept Synchronous Connection" },
-       { 0x043f, 246, "Truncated Page" },
-       { 0x0440, 247, "Truncated Page Cancel" },
-       { 0x0441, 248, "Set Connectionless Slave Broadcast" },
-       { 0x0442, 249, "Set Connectionless Slave Broadcast Receive" },
-       { 0x0443, 250, "Start Synchronization Train" },
-       { 0x0444, 251, "Receive Synchronization Train" },
+       { 0x043e, 236, "Enhanced Accept Synchronous Connection Request" },
+       { 0x043f, 246, "Truncated Page",
+                               truncated_page_cmd, 9, true },
+       { 0x0440, 247, "Truncated Page Cancel",
+                               truncated_page_cancel_cmd, 6, true,
+                               status_bdaddr_rsp, 7, true },
+       { 0x0441, 248, "Set Connectionless Slave Broadcast",
+                               set_slave_broadcast_cmd, 11, true,
+                               set_slave_broadcast_rsp, 4, true },
+       { 0x0442, 249, "Set Connectionless Slave Broadcast Receive",
+                               set_slave_broadcast_receive_cmd, 34, true,
+                               set_slave_broadcast_receive_rsp, 8, true },
+       { 0x0443, 250, "Start Synchronization Train",
+                               null_cmd, 0, true },
+       { 0x0444, 251, "Receive Synchronization Train",
+                               receive_sync_train_cmd, 12, true },
+       { 0x0445, 257, "Remote OOB Extended Data Request Reply",
+                               remote_oob_ext_data_request_reply_cmd, 70, true,
+                               status_bdaddr_rsp, 7, true },
 
        /* OGF 2 - Link Policy */
        { 0x0801,  33, "Holde Mode",
@@ -5379,13 +5698,40 @@ static const struct opcode_data opcode_table[] = {
        { 0x0c71, 241, "Set MWS Transport Layer" },
        { 0x0c72, 242, "Set MWS Scan Frequency Table" },
        { 0x0c73, 244, "Set MWS Pattern Configuration" },
-       { 0x0c74, 252, "Set Reserved LT_ADDR" },
-       { 0x0c75, 253, "Delete Reserved LT_ADDR" },
-       { 0x0c76, 254, "Set Connectionless Slave Broadcast Data" },
+       { 0x0c74, 252, "Set Reserved LT_ADDR",
+                               set_reserved_lt_addr_cmd, 1, true,
+                               set_reserved_lt_addr_rsp, 2, true },
+       { 0x0c75, 253, "Delete Reserved LT_ADDR",
+                               delete_reserved_lt_addr_cmd, 1, true,
+                               delete_reserved_lt_addr_rsp, 2, true },
+       { 0x0c76, 254, "Set Connectionless Slave Broadcast Data",
+                               set_slave_broadcast_data_cmd, 3, false,
+                               set_slave_broadcast_data_rsp, 2, true },
        { 0x0c77, 255, "Read Synchronization Train Parameters",
                                null_cmd, 0, true,
                                read_sync_train_params_rsp, 8, true },
-       { 0x0c78, 256, "Write Synchronization Train Parameters" },
+       { 0x0c78, 256, "Write Synchronization Train Parameters",
+                               write_sync_train_params_cmd, 9, true,
+                               write_sync_train_params_rsp, 3, true },
+       { 0x0c79, 258, "Read Secure Connections Host Support",
+                               null_cmd, 0, true,
+                               read_secure_conn_support_rsp, 2, true },
+       { 0x0c7a, 259, "Write Secure Connections Host Support",
+                               write_secure_conn_support_cmd, 1, true,
+                               status_rsp, 1, true },
+       { 0x0c7b, 260, "Read Authenticated Payload Timeout",
+                               read_auth_payload_timeout_cmd, 2, true,
+                               read_auth_payload_timeout_rsp, 5, true },
+       { 0x0c7c, 261, "Write Authenticated Payload Timeout",
+                               write_auth_payload_timeout_cmd, 4, true,
+                               write_auth_payload_timeout_rsp, 3, true },
+       { 0x0c7d, 262, "Read Local OOB Extended Data",
+                               null_cmd, 0, true,
+                               read_local_oob_ext_data_rsp, 65, true },
+       { 0x0c7e, 264, "Read Extended Page Timeout" },
+       { 0x0c7f, 265, "Write Extended Page Timeout" },
+       { 0x0c80, 266, "Read Extended Inquiry Length" },
+       { 0x0c81, 267, "Write Extended Inquiry Length" },
 
        /* OGF 4 - Information Parameter */
        { 0x1001, 115, "Read Local Version Information",
@@ -5460,6 +5806,7 @@ static const struct opcode_data opcode_table[] = {
        { 0x1807, 189, "Enable AMP Receiver Reports" },
        { 0x1808, 190, "AMP Test End" },
        { 0x1809, 191, "AMP Test" },
+       { 0x180a, 263, "Write Secure Connections Test Mode" },
 
        /* OGF 8 - LE Control */
        { 0x2001, 200, "LE Set Event Mask",
@@ -5548,6 +5895,8 @@ static const struct opcode_data opcode_table[] = {
        { 0x201f, 230, "LE Test End",
                                null_cmd, 0, true,
                                le_test_end_rsp, 3, true },
+       { 0x2020, 268, "LE Remote Connection Parameter Request Reply" },
+       { 0x2021, 269, "LE Remote Connection Parameter Request Negative Reply" },
        { }
 };
 
@@ -5563,11 +5912,11 @@ static const char *get_supported_command(int bit)
        return NULL;
 }
 
-static void status_evt(const void *data, uint8_t size)
+static void inquiry_complete_evt(const void *data, uint8_t size)
 {
-       uint8_t status = *((uint8_t *) data);
+       const struct bt_hci_evt_inquiry_complete *evt = data;
 
-       print_status(status);
+       print_status(evt->status);
 }
 
 static void inquiry_result_evt(const void *data, uint8_t size)
@@ -6254,6 +6603,80 @@ static void amp_status_change_evt(const void *data, uint8_t size)
        print_amp_status(evt->amp_status);
 }
 
+static void sync_train_complete_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_sync_train_complete *evt = data;
+
+       print_status(evt->status);
+}
+
+static void sync_train_received_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_sync_train_received *evt = data;
+
+       print_status(evt->status);
+       print_bdaddr(evt->bdaddr);
+       print_field("Offset: 0x%8.8x", btohl(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_interval(evt->interval);
+       print_field("Service Data: 0x%2.2x", evt->service_data);
+}
+
+static void slave_broadcast_receive_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_slave_broadcast_receive *evt = data;
+
+       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("Receive status: 0x%2.2x", evt->status);
+       print_broadcast_fragment(evt->fragment);
+       print_field("Length: %d", evt->length);
+
+       if (size - 18 != evt->length)
+               print_text(COLOR_ERROR, "invalid data size (%d != %d)",
+                                               size - 18, evt->length);
+
+       packet_hexdump(data + 18, size - 18);
+}
+
+static void slave_broadcast_timeout_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_slave_broadcast_timeout *evt = data;
+
+       print_bdaddr(evt->bdaddr);
+       print_lt_addr(evt->lt_addr);
+}
+
+static void truncated_page_complete_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_truncated_page_complete *evt = data;
+
+       print_status(evt->status);
+       print_bdaddr(evt->bdaddr);
+}
+
+static void slave_page_response_timeout_evt(const void *data, uint8_t size)
+{
+}
+
+static void slave_broadcast_channel_map_change_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_slave_broadcast_channel_map_change *evt = data;
+
+       print_channel_map(evt->map);
+}
+
+static void auth_payload_timeout_expired_evt(const void *data, uint8_t size)
+{
+       const struct bt_hci_evt_auth_payload_timeout_expired *evt = data;
+
+       print_handle(evt->handle);
+}
+
 static void le_conn_complete_evt(const void *data, uint8_t size)
 {
        const struct bt_hci_evt_le_conn_complete *evt = data;
@@ -6368,6 +6791,7 @@ static const struct subevent_data subevent_table[] = {
                                le_remote_features_complete_evt, 11, true },
        { 0x05, "LE Long Term Key Request",
                                le_long_term_key_request_evt, 12, true },
+       { 0x06, "LE Remote Connection Parameter Request" },
        { }
 };
 
@@ -6436,7 +6860,7 @@ struct event_data {
 
 static const struct event_data event_table[] = {
        { 0x01, "Inquiry Complete",
-                               status_evt, 1, true },
+                               inquiry_complete_evt, 1, true },
        { 0x02, "Inquiry Result",
                                inquiry_result_evt, 1, false },
        { 0x03, "Connect Complete",
@@ -6564,6 +6988,24 @@ static const struct event_data event_table[] = {
                                short_range_mode_change_evt, 3, true },
        { 0x4d, "AMP Status Change",
                                amp_status_change_evt, 2, true },
+       { 0x4e, "Triggered Clock Capture" },
+       { 0x4f, "Synchronization Train Complete",
+                               sync_train_complete_evt, 1, true },
+       { 0x50, "Synchronization Train Received",
+                               sync_train_received_evt, 29, true },
+       { 0x51, "Connectionless Slave Broadcast Receive",
+                               slave_broadcast_receive_evt, 18, false },
+       { 0x52, "Connectionless Slave Broadcast Timeout",
+                               slave_broadcast_timeout_evt, 7, true },
+       { 0x53, "Truncated Page Complete",
+                               truncated_page_complete_evt, 7, true },
+       { 0x54, "Slave Page Response Timeout",
+                               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" },
+       { 0x57, "Authenticated Payload Timeout Expired",
+                               auth_payload_timeout_expired_evt, 2, true },
        { 0xfe, "Testing" },
        { 0xff, "Vendor", vendor_evt, 0, false },
        { }
index e6e5035..8c98c12 100644 (file)
@@ -121,6 +121,14 @@ static ssize_t autopair_pincb(struct btd_adapter *adapter,
                }
 
                break;
+       case 0x06:              /* Imaging */
+               if (class & 0x80) {     /* Printer */
+                       if (attempt > 1)
+                               return 0;
+                       memcpy(pinbuf, "0000", 4);
+                       return 4;
+               }
+               break;
        }
 
        return 0;
index ea91c4d..35fdaeb 100644 (file)
@@ -227,7 +227,7 @@ static DBusMessage *create_request_oob_reply(struct btd_adapter *adapter,
        uint8_t *peir = eir;
        int len;
 
-       len = eir_create_oob(adapter_get_address(adapter),
+       len = eir_create_oob(btd_adapter_get_address(adapter),
                                btd_adapter_get_name(adapter),
                                btd_adapter_get_class(adapter), hash,
                                randomizer, main_opts.did_vendor,
@@ -640,7 +640,7 @@ static void store_params(struct btd_adapter *adapter, struct btd_device *device,
 
        if (params->name) {
                device_store_cached_name(device, params->name);
-               device_set_name(device, params->name);
+               btd_device_device_set_name(device, params->name);
        }
 
        /* TODO handle UUIDs? */
@@ -701,7 +701,8 @@ static DBusMessage *push_oob(DBusConnection *conn, DBusMessage *msg, void *data)
                return error_reply(msg, EINVAL);
        }
 
-       device = adapter_get_device(adapter, &remote.address, BDADDR_BREDR);
+       device = btd_adapter_get_device(adapter, &remote.address,
+                                                               BDADDR_BREDR);
 
        err = check_device(device);
        if (err < 0) {
@@ -769,7 +770,8 @@ static DBusMessage *request_oob(DBusConnection *conn, DBusMessage *msg,
        if (bacmp(&remote.address, BDADDR_ANY) == 0)
                goto read_local;
 
-       device = adapter_get_device(adapter, &remote.address, BDADDR_BREDR);
+       device = btd_adapter_get_device(adapter, &remote.address,
+                                                               BDADDR_BREDR);
 
        err = check_device(device);
        if (err < 0) {
diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
new file mode 100644 (file)
index 0000000..45fa170
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2009  Bastien Nocera <hadess@hadess.net>
+ *  Copyright (C) 2011  Antonio Ospite <ospite@studenti.unina.it>
+ *  Copyright (C) 2013  Szymon Janc <szymon.janc@gmail.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <linux/hidraw.h>
+#include <linux/input.h>
+#include <glib.h>
+#include <libudev.h>
+
+#include "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/plugin.h"
+#include "src/log.h"
+
+static const struct {
+       const char *name;
+       uint16_t source;
+       uint16_t vid;
+       uint16_t pid;
+       uint16_t version;
+} devices[] = {
+       {
+               .name = "PLAYSTATION(R)3 Controller",
+               .source = 0x0002,
+               .vid = 0x054c,
+               .pid = 0x0268,
+               .version = 0x0000,
+       },
+};
+
+static struct udev *ctx = NULL;
+static struct udev_monitor *monitor = NULL;
+static guint watch_id = 0;
+
+static int get_device_bdaddr(int fd, bdaddr_t *bdaddr)
+{
+       uint8_t buf[18];
+       int ret;
+
+       memset(buf, 0, sizeof(buf));
+
+       buf[0] = 0xf2;
+
+       ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
+       if (ret < 0) {
+               error("sixaxis: failed to read device address (%s)",
+                                                       strerror(errno));
+               return ret;
+       }
+
+       baswap(bdaddr, (bdaddr_t *) (buf + 4));
+
+       return 0;
+}
+
+static int get_master_bdaddr(int fd, bdaddr_t *bdaddr)
+{
+       uint8_t buf[8];
+       int ret;
+
+       memset(buf, 0, sizeof(buf));
+
+       buf[0] = 0xf5;
+
+       ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf);
+       if (ret < 0) {
+               error("sixaxis: failed to read master address (%s)",
+                                                       strerror(errno));
+               return ret;
+       }
+
+       baswap(bdaddr, (bdaddr_t *) (buf + 2));
+
+       return 0;
+}
+
+static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr)
+{
+       uint8_t buf[8];
+       int ret;
+
+       buf[0] = 0xf5;
+       buf[1] = 0x01;
+
+       baswap((bdaddr_t *) (buf + 2), bdaddr);
+
+       ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf);
+       if (ret < 0)
+               error("sixaxis: failed to write master address (%s)",
+                                                       strerror(errno));
+
+       return ret;
+}
+
+static gboolean setup_leds(GIOChannel *channel, GIOCondition cond,
+                                                       gpointer user_data)
+{
+       /*
+        * the total time the led is active (0xff means forever)
+        * |     duty_length: cycle time in deciseconds (0 - "blink very fast")
+        * |     |     ??? (Maybe a phase shift or duty_length multiplier?)
+        * |     |     |     % of duty_length led is off (0xff means 100%)
+        * |     |     |     |     % of duty_length led is on (0xff means 100%)
+        * |     |     |     |     |
+        * 0xff, 0x27, 0x10, 0x00, 0x32,
+        */
+       uint8_t leds_report[] = {
+               0x01,
+               0x00, 0x00, 0x00, 0x00, 0x00, /* rumble values TBD */
+               0x00, 0x00, 0x00, 0x00, 0x00, /* LED_1=0x02, LED_2=0x04 ... */
+               0xff, 0x27, 0x10, 0x00, 0x32, /* LED_4 */
+               0xff, 0x27, 0x10, 0x00, 0x32, /* LED_3 */
+               0xff, 0x27, 0x10, 0x00, 0x32, /* LED_2 */
+               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);
+
+       ret = write(fd, leds_report, sizeof(leds_report));
+       if (ret == sizeof(leds_report))
+               return FALSE;
+
+       if (ret < 0)
+               error("sixaxis: failed to set LEDS (%s)", strerror(errno));
+       else
+               error("sixaxis: failed to set LEDS (%d bytes written)", ret);
+
+       return FALSE;
+}
+
+static bool setup_device(int fd, int index, struct btd_adapter *adapter)
+{
+       char device_addr[18], master_addr[18], adapter_addr[18];
+       bdaddr_t device_bdaddr, master_bdaddr;
+       const bdaddr_t *adapter_bdaddr;
+       struct btd_device *device;
+
+       if (get_device_bdaddr(fd, &device_bdaddr) < 0)
+               return false;
+
+       if (get_master_bdaddr(fd, &master_bdaddr) < 0)
+               return false;
+
+       /* 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);
+       if (device && btd_device_is_connected(device))
+               return false;
+
+       adapter_bdaddr = btd_adapter_get_address(adapter);
+
+       if (bacmp(adapter_bdaddr, &master_bdaddr)) {
+               if (set_master_bdaddr(fd, adapter_bdaddr) < 0)
+                       return false;
+       }
+
+       ba2str(&device_bdaddr, device_addr);
+       ba2str(&master_bdaddr, master_addr);
+       ba2str(adapter_bdaddr, adapter_addr);
+       DBG("remote %s old_master %s new_master %s",
+                               device_addr, master_addr, adapter_addr);
+
+       device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR);
+
+       if (g_slist_find_custom(btd_device_get_uuids(device), HID_UUID,
+                                               (GCompareFunc)strcasecmp)) {
+               DBG("device %s already known, skipping", device_addr);
+               return true;
+       }
+
+       info("sixaxis: setting up new device");
+
+       btd_device_device_set_name(device, devices[index].name);
+       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;
+}
+
+static int get_js_number(struct udev_device *udevice)
+{
+       struct udev_list_entry *devices, *dev_list_entry;
+       struct udev_enumerate *enumerate;
+       struct udev_device *hid_parent;
+       const char *hidraw_node;
+       const char *hid_phys;
+       int number = 0;
+
+       hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
+                                                               "hid", NULL);
+
+       hid_phys = udev_device_get_property_value(hid_parent, "HID_PHYS");
+       hidraw_node = udev_device_get_devnode(udevice);
+       if (!hid_phys || !hidraw_node)
+               return 0;
+
+       enumerate = udev_enumerate_new(udev_device_get_udev(udevice));
+       udev_enumerate_add_match_sysname(enumerate, "js*");
+       udev_enumerate_scan_devices(enumerate);
+       devices = udev_enumerate_get_list_entry(enumerate);
+
+       udev_list_entry_foreach(dev_list_entry, devices) {
+               struct udev_device *input_parent;
+               struct udev_device *js_dev;
+               const char *input_phys;
+               const char *devname;
+
+               devname = udev_list_entry_get_name(dev_list_entry);
+               js_dev = udev_device_new_from_syspath(
+                                               udev_device_get_udev(udevice),
+                                               devname);
+
+               input_parent = udev_device_get_parent_with_subsystem_devtype(
+                                                       js_dev, "input", NULL);
+               if (!input_parent)
+                       goto next;
+
+               /* check if this is the joystick relative to the hidraw device
+                * above */
+               input_phys = udev_device_get_sysattr_value(input_parent,
+                                                                       "phys");
+               if (!input_phys)
+                       goto next;
+
+               if (!strcmp(input_phys, hid_phys)) {
+                       number = atoi(udev_device_get_sysnum(js_dev));
+
+                       /* joystick numbers start from 0, leds from 1 */
+                       number++;
+
+                       udev_device_unref(js_dev);
+                       break;
+               }
+next:
+               udev_device_unref(js_dev);
+       }
+
+       udev_enumerate_unref(enumerate);
+
+       return number;
+}
+
+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;
+
+       hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice,
+                                                               "hid", NULL);
+       if (!hid_parent)
+               return -1;
+
+       hid_id = udev_device_get_property_value(hid_parent, "HID_ID");
+
+       if (sscanf(hid_id, "%hx:%hx:%hx", bus, &vid, &pid) != 3)
+               return -1;
+
+       for (i = 0; G_N_ELEMENTS(devices); i++) {
+               if (devices[i].vid == vid && devices[i].pid == pid)
+                       return i;
+       }
+
+       return -1;
+}
+
+static void device_added(struct udev_device *udevice)
+{
+       struct btd_adapter *adapter;
+       GIOChannel *io;
+       uint16_t bus;
+       int index;
+       int fd;
+
+       adapter = btd_adapter_get_default();
+       if (!adapter)
+               return;
+
+       index = get_supported_device(udevice, &bus);
+       if (index < 0)
+               return;
+
+       info("sixaxis: compatible device connected: %s (%04X:%04X)",
+                               devices[index].name, devices[index].vid,
+                               devices[index].pid);
+
+       fd = open(udev_device_get_devnode(udevice), O_RDWR);
+       if (fd < 0)
+               return;
+
+       io = g_io_channel_unix_new(fd);
+
+       switch (bus) {
+       case BUS_USB:
+               if (!setup_device(fd, index, adapter))
+                       break;
+
+               /* fall through */
+       case BUS_BLUETOOTH:
+               /* wait for events before setting leds */
+               g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                               setup_leds,
+                               GINT_TO_POINTER(get_js_number(udevice)));
+
+               break;
+       default:
+               DBG("uknown bus type (%u)", bus);
+               break;
+       }
+
+       g_io_channel_set_close_on_unref(io, TRUE);
+       g_io_channel_unref(io);
+}
+
+static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,
+                                                       gpointer data)
+{
+       struct udev_device *udevice;
+
+       udevice = udev_monitor_receive_device(monitor);
+       if (!udevice)
+               return TRUE;
+
+       if (!g_strcmp0(udev_device_get_action(udevice), "add"))
+               device_added(udevice);
+
+       udev_device_unref(udevice);
+
+       return TRUE;
+}
+
+static int sixaxis_init(void)
+{
+       GIOChannel *channel;
+
+       DBG("");
+
+       ctx = udev_new();
+       if (!ctx)
+               return -EIO;
+
+       monitor = udev_monitor_new_from_netlink(ctx, "udev");
+       if (!monitor) {
+               udev_unref(ctx);
+               ctx = NULL;
+
+               return -EIO;
+       }
+
+       /* Listen for newly connected hidraw interfaces */
+       udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw",
+                                                                       NULL);
+       udev_monitor_enable_receiving(monitor);
+
+       channel = g_io_channel_unix_new(udev_monitor_get_fd(monitor));
+       watch_id = g_io_add_watch(channel, G_IO_IN, monitor_watch, NULL);
+       g_io_channel_unref(channel);
+
+       return 0;
+}
+
+static void sixaxis_exit(void)
+{
+       DBG("");
+
+       g_source_remove(watch_id);
+       watch_id = 0;
+
+       udev_monitor_unref(monitor);
+       monitor = NULL;
+
+       udev_unref(ctx);
+       ctx = NULL;
+}
+
+BLUETOOTH_PLUGIN_DEFINE(sixaxis, VERSION, BLUETOOTH_PLUGIN_PRIORITY_LOW,
+                                               sixaxis_init, sixaxis_exit)
index 6cc21ee..96a6569 100644 (file)
@@ -106,7 +106,7 @@ static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device,
 
 found:
        DBG("Forcing fixed pin on detected wiimote %s", addr);
-       memcpy(pinbuf, adapter_get_address(adapter), 6);
+       memcpy(pinbuf, btd_adapter_get_address(adapter), 6);
        return 6;
 }
 
index 864cb18..29a1593 100644 (file)
@@ -1887,7 +1887,7 @@ static int a2dp_source_disconnect(struct btd_service *service)
 
        DBG("path %s", path);
 
-       return source_disconnect(service, FALSE);
+       return source_disconnect(service);
 }
 
 static int a2dp_sink_connect(struct btd_service *service)
@@ -1919,7 +1919,7 @@ static int a2dp_sink_disconnect(struct btd_service *service)
 
        DBG("path %s", path);
 
-       return sink_disconnect(service, FALSE);
+       return sink_disconnect(service);
 }
 
 static int a2dp_source_server_probe(struct btd_profile *p,
index dac7a66..6669ddc 100644 (file)
@@ -1307,11 +1307,11 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
        avctp_set_state(session, AVCTP_STATE_CONNECTING);
        session->control = avctp_channel_create(session, chan, NULL);
 
-       src = adapter_get_address(device_get_adapter(dev));
+       src = btd_adapter_get_address(device_get_adapter(dev));
        dst = device_get_address(dev);
 
        session->auth_id = btd_request_authorization(src, dst,
-                                                       AVRCP_TARGET_UUID,
+                                                       AVRCP_REMOTE_UUID,
                                                        auth_cb, session);
        if (session->auth_id == 0)
                goto drop;
@@ -1371,7 +1371,7 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
 
        DBG("AVCTP: incoming connect from %s", address);
 
-       device = adapter_find_device(adapter_find(&src), &dst);
+       device = btd_adapter_find_device(adapter_find(&src), &dst);
        if (!device)
                return;
 
@@ -1422,7 +1422,7 @@ static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master,
 int avctp_register(struct btd_adapter *adapter, gboolean master)
 {
        struct avctp_server *server;
-       const bdaddr_t *src = adapter_get_address(adapter);
+       const bdaddr_t *src = btd_adapter_get_address(adapter);
 
        server = g_new0(struct avctp_server, 1);
 
@@ -1488,7 +1488,7 @@ static struct avctp_pending_req *pending_create(struct avctp_channel *chan,
        tmp = g_slist_copy(chan->processed);
 
        /* Find first unused transaction id */
-       for (l = tmp; l; l = l->next) {
+       for (l = tmp; l; l = g_slist_next(l)) {
                struct avctp_pending_req *req = l->data;
 
                if (req->transaction == chan->transaction) {
@@ -1937,6 +1937,7 @@ struct avctp *avctp_connect(struct btd_device *device)
        struct avctp *session;
        GError *err = NULL;
        GIOChannel *io;
+       const bdaddr_t *src;
 
        session = avctp_get_internal(device);
        if (!session)
@@ -1947,9 +1948,10 @@ struct avctp *avctp_connect(struct btd_device *device)
 
        avctp_set_state(session, AVCTP_STATE_CONNECTING);
 
+       src = btd_adapter_get_address(session->server->adapter);
+
        io = bt_io_connect(avctp_connect_cb, session, NULL, &err,
-                               BT_IO_OPT_SOURCE_BDADDR,
-                               adapter_get_address(session->server->adapter),
+                               BT_IO_OPT_SOURCE_BDADDR, src,
                                BT_IO_OPT_DEST_BDADDR,
                                device_get_address(session->device),
                                BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
@@ -1971,6 +1973,7 @@ struct avctp *avctp_connect(struct btd_device *device)
 
 int avctp_connect_browsing(struct avctp *session)
 {
+       const bdaddr_t *src;
        GError *err = NULL;
        GIOChannel *io;
 
@@ -1982,9 +1985,10 @@ int avctp_connect_browsing(struct avctp *session)
 
        avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING);
 
+       src = btd_adapter_get_address(session->server->adapter);
+
        io = bt_io_connect(avctp_connect_browsing_cb, session, NULL, &err,
-                               BT_IO_OPT_SOURCE_BDADDR,
-                               adapter_get_address(session->server->adapter),
+                               BT_IO_OPT_SOURCE_BDADDR, src,
                                BT_IO_OPT_DEST_BDADDR,
                                device_get_address(session->device),
                                BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
index 9378350..f866b39 100644 (file)
@@ -394,9 +394,6 @@ struct avdtp {
 
        avdtp_session_state_t state;
 
-       /* True if the entire device is being disconnected */
-       gboolean device_disconnect;
-
        guint auth_id;
 
        GIOChannel *io;
@@ -1200,11 +1197,6 @@ static void set_disconnect_timer(struct avdtp *session)
        if (session->dc_timer)
                remove_disconnect_timer(session);
 
-       if (session->device_disconnect) {
-               session->dc_timer = g_idle_add(disconnect_timeout, session);
-               return;
-       }
-
        session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
                                                disconnect_timeout,
                                                session);
@@ -2475,7 +2467,7 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
 
        DBG("AVDTP: incoming connect from %s", address);
 
-       device = adapter_find_device(adapter_find(&src), &dst);
+       device = btd_adapter_find_device(adapter_find(&src), &dst);
        if (!device)
                goto drop;
 
@@ -2531,11 +2523,13 @@ static GIOChannel *l2cap_connect(struct avdtp *session)
 {
        GError *err = NULL;
        GIOChannel *io;
+       const bdaddr_t *src;
+
+       src = btd_adapter_get_address(session->server->adapter);
 
        io = bt_io_connect(avdtp_connect_cb, session,
                                NULL, &err,
-                               BT_IO_OPT_SOURCE_BDADDR,
-                               adapter_get_address(session->server->adapter),
+                               BT_IO_OPT_SOURCE_BDADDR, src,
                                BT_IO_OPT_DEST_BDADDR,
                                device_get_address(session->device),
                                BT_IO_OPT_PSM, AVDTP_PSM,
@@ -3183,8 +3177,8 @@ struct avdtp_service_capability *avdtp_stream_get_codec(
        return NULL;
 }
 
-gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
-                               struct avdtp_service_capability *cap)
+static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
+                                       struct avdtp_service_capability *cap)
 {
        GSList *l;
        struct avdtp_service_capability *stream_cap;
@@ -3219,7 +3213,16 @@ gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
 struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
                                                struct avdtp_stream *stream)
 {
-       return avdtp_get_remote_sep(stream->session, stream->rseid);
+       GSList *l;
+
+       for (l = stream->session->seps; l; l = l->next) {
+               struct avdtp_remote_sep *sep = l->data;
+
+               if (sep->seid == stream->rseid)
+                       return sep;
+       }
+
+       return NULL;
 }
 
 gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
@@ -3268,46 +3271,11 @@ static int process_queue(struct avdtp *session)
        return send_req(session, FALSE, req);
 }
 
-struct avdtp_remote_sep *avdtp_get_remote_sep(struct avdtp *session,
-                                               uint8_t seid)
-{
-       GSList *l;
-
-       for (l = session->seps; l; l = l->next) {
-               struct avdtp_remote_sep *sep = l->data;
-
-               if (sep->seid == seid)
-                       return sep;
-       }
-
-       return NULL;
-}
-
-uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep)
-{
-       return sep->seid;
-}
-
-uint8_t avdtp_get_type(struct avdtp_remote_sep *sep)
-{
-       return sep->type;
-}
-
 struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep)
 {
        return sep->codec;
 }
 
-gboolean avdtp_get_delay_reporting(struct avdtp_remote_sep *sep)
-{
-       return sep->delay_reporting;
-}
-
-struct avdtp_stream *avdtp_get_stream(struct avdtp_remote_sep *sep)
-{
-       return sep->stream;
-}
-
 struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
                                                        void *data, int length)
 {
@@ -3709,7 +3677,8 @@ static struct avdtp_server *avdtp_server_init(struct btd_adapter *adapter)
 
        server = g_new0(struct avdtp_server, 1);
 
-       server->io = avdtp_server_socket(adapter_get_address(adapter), TRUE);
+       server->io = avdtp_server_socket(btd_adapter_get_address(adapter),
+                                                                       TRUE);
        if (!server->io) {
                g_free(server);
                return NULL;
@@ -3867,11 +3836,6 @@ gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream)
        return g_slist_find(session->streams, stream) ? TRUE : FALSE;
 }
 
-void avdtp_set_device_disconnect(struct avdtp *session, gboolean dev_dc)
-{
-       session->device_disconnect = dev_dc;
-}
-
 unsigned int avdtp_add_state_cb(struct btd_device *dev,
                                avdtp_session_state_cb cb, void *user_data)
 {
index 5606506..390c154 100644 (file)
@@ -221,19 +221,8 @@ struct avdtp *avdtp_ref(struct avdtp *session);
 struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category,
                                                        void *data, int size);
 
-struct avdtp_remote_sep *avdtp_get_remote_sep(struct avdtp *session,
-                                               uint8_t seid);
-
-uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep);
-
-uint8_t avdtp_get_type(struct avdtp_remote_sep *sep);
-
 struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep);
 
-gboolean avdtp_get_delay_reporting(struct avdtp_remote_sep *sep);
-
-struct avdtp_stream *avdtp_get_stream(struct avdtp_remote_sep *sep);
-
 int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
                        void *user_data);
 
@@ -251,8 +240,6 @@ gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
                                        GSList **caps);
 struct avdtp_service_capability *avdtp_stream_get_codec(
                                                struct avdtp_stream *stream);
-gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
-                               struct avdtp_service_capability *cap);
 gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
                                        GSList *caps);
 struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
@@ -306,5 +293,3 @@ int avdtp_error_posix_errno(struct avdtp_error *err);
 
 struct btd_adapter *avdtp_get_adapter(struct avdtp *session);
 struct btd_device *avdtp_get_device(struct avdtp *session);
-
-void avdtp_set_device_disconnect(struct avdtp *session, gboolean dev_dc);
index 400af06..4f39622 100644 (file)
@@ -388,16 +388,13 @@ gboolean sink_new_stream(struct btd_service *service, struct avdtp *session,
        return TRUE;
 }
 
-int sink_disconnect(struct btd_service *service, gboolean shutdown)
+int sink_disconnect(struct btd_service *service)
 {
        struct sink *sink = btd_service_get_user_data(service);
 
        if (!sink->session)
                return -ENOTCONN;
 
-       if (shutdown)
-               avdtp_set_device_disconnect(sink->session, TRUE);
-
        /* cancel pending connect */
        if (sink->connect_id > 0) {
                a2dp_cancel(sink->connect_id);
index 904a33d..93c62a2 100644 (file)
@@ -47,4 +47,4 @@ int sink_connect(struct btd_service *service);
 gboolean sink_new_stream(struct btd_service *service, struct avdtp *session,
                                struct avdtp_stream *stream);
 gboolean sink_setup_stream(struct btd_service *service, struct avdtp *session);
-int sink_disconnect(struct btd_service *service, gboolean shutdown);
+int sink_disconnect(struct btd_service *service);
index 24a4353..7b129b7 100644 (file)
@@ -380,16 +380,13 @@ gboolean source_new_stream(struct btd_service *service, struct avdtp *session,
        return TRUE;
 }
 
-int source_disconnect(struct btd_service *service, gboolean shutdown)
+int source_disconnect(struct btd_service *service)
 {
        struct source *source = btd_service_get_user_data(service);
 
        if (!source->session)
                return -ENOTCONN;
 
-       if (shutdown)
-               avdtp_set_device_disconnect(source->session, TRUE);
-
        /* cancel pending connect */
        if (source->connect_id > 0) {
                a2dp_cancel(source->connect_id);
index c16fb4b..a014c68 100644 (file)
@@ -48,4 +48,4 @@ gboolean source_new_stream(struct btd_service *service, struct avdtp *session,
                                struct avdtp_stream *stream);
 gboolean source_setup_stream(struct btd_service *service,
                                                        struct avdtp *session);
-int source_disconnect(struct btd_service *service, gboolean shutdown);
+int source_disconnect(struct btd_service *service);
index 7b4e799..6203fa8 100644 (file)
@@ -1205,8 +1205,8 @@ static void mcl_connected(struct mcap_mcl *mcl, gpointer data)
                struct hdp_adapter *hdp_adapter = data;
                struct btd_device *device;
 
-               device = adapter_get_device(hdp_adapter->btd_adapter, &addr,
-                                                               BDADDR_BREDR);
+               device = btd_adapter_get_device(hdp_adapter->btd_adapter,
+                                                       &addr, BDADDR_BREDR);
                if (!device)
                        return;
                hdp_device = create_health_device(device);
@@ -1322,6 +1322,7 @@ static void release_adapter_instance(struct hdp_adapter *hdp_adapter)
 static gboolean update_adapter(struct hdp_adapter *hdp_adapter)
 {
        GError *err = NULL;
+       const bdaddr_t *src;
 
        if (applications == NULL) {
                release_adapter_instance(hdp_adapter);
@@ -1331,8 +1332,9 @@ static gboolean update_adapter(struct hdp_adapter *hdp_adapter)
        if (hdp_adapter->mi != NULL)
                goto update;
 
-       hdp_adapter->mi = mcap_create_instance(
-                               adapter_get_address(hdp_adapter->btd_adapter),
+       src = btd_adapter_get_address(hdp_adapter->btd_adapter);
+
+       hdp_adapter->mi = mcap_create_instance(src,
                                BT_IO_SEC_MEDIUM, 0, 0,
                                mcl_connected, mcl_reconnected,
                                mcl_disconnected, mcl_uncached,
index 34e4671..7de87a8 100644 (file)
@@ -853,7 +853,7 @@ gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app,
        const bdaddr_t *dst;
        uuid_t uuid;
 
-       src = adapter_get_address(device_get_adapter(device->dev));
+       src = btd_adapter_get_address(device_get_adapter(device->dev));
        dst = device_get_address(device->dev);
 
        mdep_data = g_new0(struct get_mdep_data, 1);
@@ -1080,7 +1080,7 @@ gboolean hdp_establish_mcl(struct hdp_device *device,
        const bdaddr_t *dst;
        uuid_t uuid;
 
-       src = adapter_get_address(device_get_adapter(device->dev));
+       src = btd_adapter_get_address(device_get_adapter(device->dev));
        dst = device_get_address(device->dev);
 
        conn_data = g_new0(struct conn_mcl_data, 1);
@@ -1151,7 +1151,7 @@ gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func,
        const bdaddr_t *dst;
        uuid_t uuid;
 
-       src = adapter_get_address(device_get_adapter(device->dev));
+       src = btd_adapter_get_address(device_get_adapter(device->dev));
        dst = device_get_address(device->dev);
 
        dcpsm_data = g_new0(struct get_dcpsm_data, 1);
index 6523161..62f6dbb 100644 (file)
@@ -667,7 +667,7 @@ static gboolean input_device_auto_reconnect(gpointer user_data)
        /* Stop the recurrent reconnection attempts if the device is reconnected
         * or is marked for removal. */
        if (device_is_temporary(idev->device) ||
-                                       device_is_connected(idev->device))
+                                       btd_device_is_connected(idev->device))
                return FALSE;
 
        /* Only attempt an auto-reconnect for at most 3 minutes (6 * 30s). */
@@ -713,7 +713,7 @@ static void input_device_enter_reconnect_mode(struct input_device *idev)
        /* 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) ||
-                                       device_is_connected(idev->device))
+                                       btd_device_is_connected(idev->device))
                return;
 
        if (idev->reconnect_timer > 0)
@@ -811,8 +811,11 @@ static struct input_device *input_device_new(struct btd_service *service)
        struct input_device *idev;
        char name[HCI_MAX_NAME_LENGTH + 1];
 
+       if (!rec)
+               return NULL;
+
        idev = g_new0(struct input_device, 1);
-       bacpy(&idev->src, adapter_get_address(adapter));
+       bacpy(&idev->src, btd_adapter_get_address(adapter));
        bacpy(&idev->dst, device_get_address(device));
        idev->service = btd_service_ref(service);
        idev->device = btd_device_ref(device);
@@ -880,7 +883,7 @@ static struct input_device *find_device(const bdaddr_t *src,
        struct btd_device *device;
        struct btd_service *service;
 
-       device = adapter_find_device(adapter_find(src), dst);
+       device = btd_adapter_find_device(adapter_find(src), dst);
        if (device == NULL)
                return NULL;
 
index 689ccdd..660043e 100644 (file)
 
 static int hid_server_probe(struct btd_profile *p, struct btd_adapter *adapter)
 {
-       return server_start(adapter_get_address(adapter));
+       return server_start(btd_adapter_get_address(adapter));
 }
 
 static void hid_server_remove(struct btd_profile *p,
                                                struct btd_adapter *adapter)
 {
-       server_stop(adapter_get_address(adapter));
+       server_stop(btd_adapter_get_address(adapter));
 }
 
 static struct btd_profile input_profile = {
index 21d4562..f6f85a0 100644 (file)
@@ -63,6 +63,100 @@ static int server_cmp(gconstpointer s, gconstpointer user_data)
        return bacmp(&server->src, src);
 }
 
+struct sixaxis_data {
+       GIOChannel *chan;
+       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));
+
+       if (err < 0)
+               goto fail;
+
+       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)
+               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);
+
+       return;
+
+fail:
+       g_io_channel_shutdown(data->chan, TRUE, NULL);
+       g_io_channel_unref(data->chan);
+       g_free(data);
+}
+
+static void sixaxis_browse_sdp(const bdaddr_t *src, const bdaddr_t *dst,
+                                               GIOChannel *chan, uint16_t psm)
+{
+       struct btd_device *device;
+       struct sixaxis_data *data;
+
+       if (psm != L2CAP_PSM_HIDP_CTRL)
+               return;
+
+       device = btd_adapter_find_device(adapter_find(src), dst);
+       if (!device)
+               return;
+
+       data = g_new0(struct sixaxis_data, 1);
+       data->chan = g_io_channel_ref(chan);
+       data->psm = psm;
+
+       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;
+
+       device = btd_adapter_find_device(adapter_find(src), dst);
+       if (!device)
+               return false;
+
+       if (btd_device_get_vendor(device) != 0x054c)
+               return false;
+
+       if (btd_device_get_product(device) != 0x0268)
+               return false;
+
+       return true;
+}
+
 static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data)
 {
        uint16_t psm;
@@ -95,6 +189,11 @@ static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data)
        if (ret == 0)
                return;
 
+       if (ret == -ENOENT && dev_is_sixaxis(&src, &dst)) {
+               sixaxis_browse_sdp(&src, &dst, chan, psm);
+               return;
+       }
+
        error("Refusing input device connect: %s (%d)", strerror(-ret), -ret);
 
        /* Send unplug virtual cable to unknown devices */
@@ -129,6 +228,9 @@ static void auth_callback(DBusError *derr, void *user_data)
                goto reject;
        }
 
+       if (!input_device_exists(&src, &dst) && dev_is_sixaxis(&src, &dst))
+               return;
+
        if (!bt_io_accept(server->confirm, connect_event_cb, server,
                                NULL, &err)) {
                error("bt_io_accept: %s", err->message);
@@ -175,7 +277,7 @@ static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
                goto drop;
        }
 
-       if (!input_device_exists(&src, &dst)) {
+       if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst)) {
                error("Refusing connection from %s: unknown device", addr);
                goto drop;
        }
similarity index 58%
rename from profiles/network/common.c
rename to profiles/network/bnep.c
index 0b291bd..0a719a2 100644 (file)
 #include <glib.h>
 
 #include "log.h"
-#include "common.h"
+#include "bnep.h"
 #include "lib/uuid.h"
 
+#define CON_SETUP_RETRIES      3
+#define CON_SETUP_TO           9
+
 static int ctl;
 
 static struct {
@@ -59,6 +62,35 @@ static struct {
        { NULL }
 };
 
+struct __service_16 {
+       uint16_t dst;
+       uint16_t src;
+} __attribute__ ((packed));
+
+struct bnep_conn {
+       GIOChannel      *io;
+       uint16_t        src;
+       uint16_t        dst;
+       guint   attempts;
+       guint   setup_to;
+       void    *data;
+       bnep_connect_cb conn_cb;
+};
+
+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;
@@ -149,9 +181,9 @@ int bnep_connadd(int sk, uint16_t role, char *dev)
 {
        struct bnep_connadd_req req;
 
+       memset(dev, 0, 16);
        memset(&req, 0, sizeof(req));
-       strncpy(req.device, dev, 16);
-       req.device[15] = '\0';
+       strcpy(req.device, "bnep%d");
        req.sock = sk;
        req.role = role;
        if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
@@ -215,6 +247,165 @@ int bnep_if_down(const char *devname)
        return 0;
 }
 
+static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
+                                                               gpointer data)
+{
+       struct bnep_conn *bc = 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;
+
+       if (bc->setup_to > 0) {
+               g_source_remove(bc->setup_to);
+               bc->setup_to = 0;
+       }
+
+       if (cond & (G_IO_HUP | G_IO_ERR)) {
+               error("Hangup or error on l2cap server socket");
+               goto failed;
+       }
+
+       sk = g_io_channel_unix_get_fd(chan);
+       memset(pkt, 0, BNEP_MTU);
+       r = read(sk, pkt, sizeof(pkt) - 1);
+       if (r < 0) {
+               error("IO Channel read error");
+               goto failed;
+       }
+
+       if (r == 0) {
+               error("No packet received on l2cap socket");
+               goto failed;
+       }
+
+       errno = EPROTO;
+
+       if ((size_t) r < sizeof(*rsp)) {
+               error("Packet received is not bnep type");
+               goto failed;
+       }
+
+       rsp = (void *) pkt;
+       if (rsp->type != BNEP_CONTROL) {
+               error("Packet received is not bnep type");
+               goto failed;
+       }
+
+       if (rsp->ctrl != BNEP_SETUP_CONN_RSP)
+               return TRUE;
+
+       r = ntohs(rsp->resp);
+       if (r != BNEP_SUCCESS) {
+               error("bnep failed");
+               goto failed;
+       }
+
+       memset(&timeo, 0, sizeof(timeo));
+       timeo.tv_sec = 0;
+       setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+
+       sk = g_io_channel_unix_get_fd(bc->io);
+       if (bnep_connadd(sk, bc->src, iface)) {
+               error("bnep conn could not be added");
+               goto failed;
+       }
+
+       if (bnep_if_up(iface)) {
+               error("could not up %s", iface);
+               goto failed;
+       }
+
+       bc->conn_cb(chan, iface, 0, bc->data);
+       free_bnep_connect(bc);
+
+       return FALSE;
+
+failed:
+       bc->conn_cb(NULL, NULL, -EIO, bc->data);
+       free_bnep_connect(bc);
+
+       return FALSE;
+}
+
+static int bnep_setup_conn_req(struct bnep_conn *bc)
+{
+       struct bnep_setup_conn_req *req;
+       struct __service_16 *s;
+       unsigned char pkt[BNEP_MTU];
+       int fd;
+
+       /* Send request */
+       req = (void *) pkt;
+       req->type = BNEP_CONTROL;
+       req->ctrl = BNEP_SETUP_CONN_REQ;
+       req->uuid_size = 2;     /* 16bit UUID */
+       s = (void *) req->service;
+       s->src = htons(bc->src);
+       s->dst = htons(bc->dst);
+
+       fd = g_io_channel_unix_get_fd(bc->io);
+       if (write(fd, pkt, sizeof(*req) + sizeof(*s)) < 0) {
+               error("bnep connection req send failed: %s", strerror(errno));
+               return -errno;
+       }
+
+       bc->attempts++;
+
+       return 0;
+}
+
+static gboolean bnep_conn_req_to(gpointer user_data)
+{
+       struct bnep_conn *bc = user_data;
+
+       if (bc->attempts == CON_SETUP_RETRIES) {
+               error("Too many bnep connection attempts");
+       } else {
+               error("bnep connection setup TO, retrying...");
+               if (bnep_setup_conn_req(bc) == 0)
+                       return TRUE;
+       }
+
+       bc->conn_cb(NULL, NULL, -ETIMEDOUT, bc->data);
+       free_bnep_connect(bc);
+
+       return FALSE;
+}
+
+int bnep_connect(int sk, uint16_t src, uint16_t dst, bnep_connect_cb conn_cb,
+                                                               void *data)
+{
+       struct bnep_conn *bc;
+       int err;
+
+       if (!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;
+
+       err = bnep_setup_conn_req(bc);
+       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);
+       return 0;
+}
+
 int bnep_add_to_bridge(const char *devname, const char *bridge)
 {
        int ifindex;
similarity index 87%
rename from profiles/network/common.h
rename to profiles/network/bnep.h
index 9a8caac..9043e46 100644 (file)
@@ -35,3 +35,8 @@ 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);
+
+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,
+                                                               void *data);
index 960a1fe..9aff319 100644 (file)
 #include "service.h"
 
 #include "error.h"
-#include "common.h"
+#include "bnep.h"
 #include "connection.h"
 
 #define NETWORK_PEER_INTERFACE "org.bluez.Network1"
-#define CON_SETUP_RETRIES      3
-#define CON_SETUP_TO           9
 
 typedef enum {
        CONNECTED,
@@ -73,16 +71,9 @@ struct network_conn {
        GIOChannel      *io;
        guint           dc_id;
        struct network_peer *peer;
-       guint           attempt_cnt;
-       guint           timeout_source;
        DBusMessage     *connect;
 };
 
-struct __service_16 {
-       uint16_t dst;
-       uint16_t src;
-} __attribute__ ((packed));
-
 static GSList *peers = NULL;
 
 static uint16_t get_service_id(struct btd_service *service)
@@ -163,11 +154,6 @@ static void local_connect_cb(struct network_conn *nc, int err)
 
 static void cancel_connection(struct network_conn *nc, int err)
 {
-       if (nc->timeout_source > 0) {
-               g_source_remove(nc->timeout_source);
-               nc->timeout_source = 0;
-       }
-
        btd_service_connecting_complete(nc->service, err);
        if (nc->connect)
                local_connect_cb(nc, err);
@@ -200,83 +186,24 @@ static void disconnect_cb(struct btd_device *device, gboolean removal,
        connection_destroy(NULL, user_data);
 }
 
-static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
-                                                       gpointer data)
+static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data)
 {
        struct network_conn *nc = data;
-       struct bnep_control_rsp *rsp;
-       struct timeval timeo;
-       char pkt[BNEP_MTU];
-       ssize_t r;
-       int sk;
        const char *path;
        DBusConnection *conn;
 
-       DBG("cond %u", cond);
-
-       if (cond & G_IO_NVAL)
-               return FALSE;
-
-       if (nc->timeout_source > 0) {
-               g_source_remove(nc->timeout_source);
-               nc->timeout_source = 0;
-       }
-
-       if (cond & (G_IO_HUP | G_IO_ERR)) {
-               error("Hangup or error on l2cap server socket");
-               goto failed;
-       }
-
-       sk = g_io_channel_unix_get_fd(chan);
-
-       memset(pkt, 0, BNEP_MTU);
-       r = read(sk, pkt, sizeof(pkt) -1);
-       if (r < 0) {
-               error("IO Channel read error");
-               goto failed;
-       }
-
-       if (r == 0) {
-               error("No packet received on l2cap socket");
-               goto failed;
-       }
-
-       errno = EPROTO;
-
-       if ((size_t) r < sizeof(*rsp)) {
-               error("Packet received is not bnep type");
-               goto failed;
-       }
-
-       rsp = (void *) pkt;
-       if (rsp->type != BNEP_CONTROL) {
-               error("Packet received is not bnep type");
-               goto failed;
-       }
-
-       if (rsp->ctrl != BNEP_SETUP_CONN_RSP)
-               return TRUE;
-
-       r = ntohs(rsp->resp);
-
-       if (r != BNEP_SUCCESS) {
-               error("bnep failed");
-               goto failed;
-       }
-
-       memset(&timeo, 0, sizeof(timeo));
-       timeo.tv_sec = 0;
-
-       setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+       DBG("");
 
-       if (bnep_connadd(sk, BNEP_SVC_PANU, nc->dev)) {
-               error("%s could not be added", nc->dev);
+       if (err < 0) {
+               error("connect failed %s", strerror(-err));
                goto failed;
        }
 
-       bnep_if_up(nc->dev);
+       info("%s connected", nc->dev);
 
+       memcpy(nc->dev, iface, sizeof(nc->dev));
        btd_service_connecting_complete(nc->service, 0);
+
        if (nc->connect)
                local_connect_cb(nc, 0);
 
@@ -292,101 +219,30 @@ static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond,
 
        nc->state = CONNECTED;
        nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb,
-                                               nc, NULL);
-
-       info("%s connected", nc->dev);
-       /* Start watchdog */
+                                                               nc, NULL);
        g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                       (GIOFunc) bnep_watchdog_cb, nc);
+                                                       bnep_watchdog_cb, nc);
        g_io_channel_unref(nc->io);
        nc->io = NULL;
 
-       return FALSE;
+       return;
 
 failed:
        cancel_connection(nc, -EIO);
-
-       return FALSE;
-}
-
-static int bnep_send_conn_req(struct network_conn *nc)
-{
-       struct bnep_setup_conn_req *req;
-       struct __service_16 *s;
-       unsigned char pkt[BNEP_MTU];
-       int fd;
-
-       DBG("");
-
-       /* Send request */
-       req = (void *) pkt;
-       req->type = BNEP_CONTROL;
-       req->ctrl = BNEP_SETUP_CONN_REQ;
-       req->uuid_size = 2;     /* 16bit UUID */
-       s = (void *) req->service;
-       s->dst = htons(nc->id);
-       s->src = htons(BNEP_SVC_PANU);
-
-       fd = g_io_channel_unix_get_fd(nc->io);
-       if (write(fd, pkt, sizeof(*req) + sizeof(*s)) < 0) {
-               int err = -errno;
-               error("bnep connection req send failed: %s", strerror(errno));
-               return err;
-       }
-
-       nc->attempt_cnt++;
-
-       return 0;
-}
-
-static gboolean bnep_conn_req_to(gpointer user_data)
-{
-       struct network_conn *nc;
-
-       nc = user_data;
-       if (nc->attempt_cnt == CON_SETUP_RETRIES) {
-               error("Too many bnep connection attempts");
-       } else {
-               error("bnep connection setup TO, retrying...");
-               if (bnep_send_conn_req(nc) == 0)
-                       return TRUE;
-       }
-
-       cancel_connection(nc, -ETIMEDOUT);
-
-       return FALSE;
-}
-
-static int bnep_connect(struct network_conn *nc)
-{
-       int err;
-
-       nc->attempt_cnt = 0;
-
-       err = bnep_send_conn_req(nc);
-       if (err < 0)
-               return err;
-
-       nc->timeout_source = g_timeout_add_seconds(CON_SETUP_TO,
-                                                       bnep_conn_req_to, nc);
-
-       g_io_add_watch(nc->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                                       bnep_setup_cb, nc);
-
-       return 0;
 }
 
 static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
 {
        struct network_conn *nc = data;
-       int perr;
+       int sk, perr;
 
        if (err) {
                error("%s", err->message);
                goto failed;
        }
 
-       perr = bnep_connect(nc);
+       sk = g_io_channel_unix_get_fd(nc->io);
+       perr = bnep_connect(sk, BNEP_SVC_PANU, nc->id, bnep_conn_cb, nc);
        if (perr < 0) {
                error("bnep connect(): %s (%d)", strerror(-perr), -perr);
                goto failed;
@@ -452,7 +308,7 @@ int connection_connect(struct btd_service *service)
        if (nc->state != DISCONNECTED)
                return -EALREADY;
 
-       src = adapter_get_address(device_get_adapter(peer->device));
+       src = btd_adapter_get_address(device_get_adapter(peer->device));
        dst = device_get_address(peer->device);
 
        nc->io = bt_io_connect(connect_cb, nc,
@@ -692,8 +548,6 @@ int connection_register(struct btd_service *service)
 
        nc = g_new0(struct network_conn, 1);
        nc->id = id;
-       memset(nc->dev, 0, sizeof(nc->dev));
-       strcpy(nc->dev, "bnep%d");
        nc->service = btd_service_ref(service);
        nc->state = DISCONNECTED;
        nc->peer = peer;
index ab4224d..8ac2dec 100644 (file)
@@ -43,7 +43,7 @@
 #include "device.h"
 #include "profile.h"
 #include "service.h"
-#include "common.h"
+#include "bnep.h"
 #include "connection.h"
 #include "server.h"
 
index 7b784e5..b3aab11 100644 (file)
@@ -48,7 +48,7 @@
 #include "error.h"
 #include "sdpd.h"
 
-#include "common.h"
+#include "bnep.h"
 #include "server.h"
 
 #define NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1"
@@ -269,9 +269,6 @@ static int server_connadd(struct network_server *ns,
        char devname[16];
        int err, nsk;
 
-       memset(devname, 0, sizeof(devname));
-       strcpy(devname, "bnep%d");
-
        nsk = g_io_channel_unix_get_fd(session->io);
        err = bnep_connadd(nsk, dst_role, devname);
        if (err < 0)
@@ -765,7 +762,7 @@ static struct network_adapter *create_adapter(struct btd_adapter *adapter)
        na->io = bt_io_listen(NULL, confirm_event, na,
                                NULL, &err,
                                BT_IO_OPT_SOURCE_BDADDR,
-                               adapter_get_address(adapter),
+                               btd_adapter_get_address(adapter),
                                BT_IO_OPT_PSM, BNEP_PSM,
                                BT_IO_OPT_OMTU, BNEP_MTU,
                                BT_IO_OPT_IMTU, BNEP_MTU,
@@ -823,7 +820,7 @@ int server_register(struct btd_adapter *adapter, uint16_t id)
                                                                        path);
 
 done:
-       bacpy(&ns->src, adapter_get_address(adapter));
+       bacpy(&ns->src, btd_adapter_get_address(adapter));
        ns->id = id;
        ns->na = na;
        ns->record_id = 0;
index 63314a7..119862d 100644 (file)
@@ -1372,7 +1372,8 @@ int sap_server_register(struct btd_adapter *adapter)
 
        io = bt_io_listen(NULL, connect_confirm_cb, server,
                        NULL, &gerr,
-                       BT_IO_OPT_SOURCE_BDADDR, adapter_get_address(adapter),
+                       BT_IO_OPT_SOURCE_BDADDR,
+                       btd_adapter_get_address(adapter),
                        BT_IO_OPT_CHANNEL, SAP_SERVER_CHANNEL,
                        BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
                        BT_IO_OPT_MASTER, TRUE,
index d904a56..9480103 100644 (file)
@@ -690,7 +690,7 @@ int adapter_set_name(struct btd_adapter *adapter, const char *name)
        return set_name(adapter, name);
 }
 
-struct btd_device *adapter_find_device(struct btd_adapter *adapter,
+struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
                                                        const bdaddr_t *dst)
 {
        struct btd_device *device;
@@ -1012,7 +1012,7 @@ static struct btd_device *adapter_create_device(struct btd_adapter *adapter,
        if (!device)
                return NULL;
 
-       device_set_temporary(device, TRUE);
+       btd_device_set_temporary(device, TRUE);
 
        adapter->devices = g_slist_append(adapter->devices, device);
 
@@ -1072,7 +1072,7 @@ static void adapter_remove_device(struct btd_adapter *adapter,
        device_remove(dev, TRUE);
 }
 
-struct btd_device *adapter_get_device(struct btd_adapter *adapter,
+struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter,
                                        const bdaddr_t *addr,
                                        uint8_t addr_type)
 {
@@ -1081,7 +1081,7 @@ struct btd_device *adapter_get_device(struct btd_adapter *adapter,
        if (!adapter)
                return NULL;
 
-       device = adapter_find_device(adapter, addr);
+       device = btd_adapter_find_device(adapter, addr);
        if (device)
                return device;
 
@@ -2156,9 +2156,9 @@ static DBusMessage *remove_device(DBusConnection *conn,
 
        device = list->data;
 
-       device_set_temporary(device, TRUE);
+       btd_device_set_temporary(device, TRUE);
 
-       if (!device_is_connected(device)) {
+       if (!btd_device_is_connected(device)) {
                adapter_remove_device(adapter, device);
                return dbus_message_new_method_return(msg);
        }
@@ -2196,11 +2196,6 @@ static const GDBusPropertyTable adapter_properties[] = {
        { }
 };
 
-struct adapter_keys {
-       struct btd_adapter *adapter;
-       GSList *keys;
-};
-
 static int str2buf(const char *str, uint8_t *buf, size_t blen)
 {
        int i, dlen;
@@ -2479,8 +2474,8 @@ static void load_devices(struct btd_adapter *adapter)
 {
        char filename[PATH_MAX + 1];
        char srcaddr[18];
-       struct adapter_keys keys = { adapter, NULL };
-       struct adapter_keys ltks = { adapter, NULL };
+       GSList *keys = NULL;
+       GSList *ltks = NULL;
        DIR *dir;
        struct dirent *entry;
 
@@ -2514,11 +2509,11 @@ static void load_devices(struct btd_adapter *adapter)
 
                key_info = get_key_info(key_file, entry->d_name);
                if (key_info)
-                       keys.keys = g_slist_append(keys.keys, key_info);
+                       keys = g_slist_append(keys, key_info);
 
                ltk_info = get_ltk_info(key_file, entry->d_name);
                if (ltk_info)
-                       ltks.keys = g_slist_append(ltks.keys, ltk_info);
+                       ltks = g_slist_append(ltks, ltk_info);
 
                list = g_slist_find_custom(adapter->devices, entry->d_name,
                                                        device_address_cmp);
@@ -2532,12 +2527,12 @@ static void load_devices(struct btd_adapter *adapter)
                if (!device)
                        goto free;
 
-               device_set_temporary(device, FALSE);
+               btd_device_set_temporary(device, FALSE);
                adapter->devices = g_slist_append(adapter->devices, device);
 
                /* TODO: register services from pre-loaded list of primaries */
 
-               list = device_get_uuids(device);
+               list = btd_device_get_uuids(device);
                if (list)
                        device_probe_profiles(device, list);
 
@@ -2553,11 +2548,11 @@ free:
 
        closedir(dir);
 
-       load_link_keys(adapter, keys.keys, main_opts.debug_keys);
-       g_slist_free_full(keys.keys, g_free);
+       load_link_keys(adapter, keys, main_opts.debug_keys);
+       g_slist_free_full(keys, g_free);
 
-       load_ltks(adapter, ltks.keys);
-       g_slist_free_full(ltks.keys, g_free);
+       load_ltks(adapter, ltks);
+       g_slist_free_full(ltks, g_free);
 }
 
 int btd_adapter_block_address(struct btd_adapter *adapter,
@@ -2726,7 +2721,8 @@ static void get_connections_complete(uint8_t status, uint16_t length,
                ba2str(&addr->bdaddr, address);
                DBG("Adding existing connection to %s", address);
 
-               device = adapter_get_device(adapter, &addr->bdaddr, addr->type);
+               device = btd_adapter_get_device(adapter, &addr->bdaddr,
+                                                               addr->type);
                if (device)
                        adapter_add_connection(adapter, device);
        }
@@ -3990,7 +3986,7 @@ const char *adapter_get_path(struct btd_adapter *adapter)
        return adapter->path;
 }
 
-const bdaddr_t *adapter_get_address(struct btd_adapter *adapter)
+const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter)
 {
        return &adapter->bdaddr;
 }
@@ -4087,16 +4083,11 @@ static void update_found_devices(struct btd_adapter *adapter,
        struct btd_device *dev;
        struct eir_data eir_data;
        char addr[18];
-       int err;
        GSList *list;
        bool name_known;
 
        memset(&eir_data, 0, sizeof(eir_data));
-       err = eir_parse(&eir_data, data, data_len);
-       if (err < 0) {
-               error("Error parsing EIR data: %s (%d)", strerror(-err), -err);
-               return;
-       }
+       eir_parse(&eir_data, data, data_len);
 
        /* Avoid creating LE device if it's not discoverable */
        if (bdaddr_type != BDADDR_BREDR &&
@@ -4151,7 +4142,7 @@ static void update_found_devices(struct btd_adapter *adapter,
        name_known = device_name_known(dev);
 
        if (eir_data.name && (eir_data.name_complete || !name_known))
-               device_set_name(dev, eir_data.name);
+               btd_device_device_set_name(dev, eir_data.name);
 
        if (eir_data.class != 0)
                device_set_class(dev, eir_data.class);
@@ -4194,7 +4185,7 @@ connect_le:
         * connect_list stop passive scanning so that a connection
         * attempt to it can be made
         */
-       if (device_is_le(dev) && !device_is_connected(dev) &&
+       if (device_is_le(dev) && !btd_device_is_connected(dev) &&
                                g_slist_find(adapter->connect_list, dev)) {
                adapter->connect_le = dev;
                stop_passive_scanning(adapter);
@@ -4350,9 +4341,12 @@ static void agent_auth_cb(struct agent *agent, DBusError *derr,
                                                        void *user_data)
 {
        struct btd_adapter *adapter = user_data;
-       struct service_auth *auth = adapter->auths->head->data;
+       struct service_auth *auth = g_queue_pop_head(adapter->auths);
 
-       g_queue_pop_head(adapter->auths);
+       if (!auth) {
+               DBG("No pending authorization");
+               return;
+       }
 
        auth->cb(derr, auth->user_data);
 
@@ -4423,7 +4417,7 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
        struct btd_device *device;
        static guint id = 0;
 
-       device = adapter_find_device(adapter, dst);
+       device = btd_adapter_find_device(adapter, dst);
        if (!device)
                return 0;
 
@@ -4629,7 +4623,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 = adapter_find_device(adapter, bdaddr);
+               device = btd_adapter_find_device(adapter, bdaddr);
                device_bonding_restart_timer(device);
 
                id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_REPLY,
@@ -4687,7 +4681,8 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length,
        ba2str(&ev->addr.bdaddr, addr);
        DBG("hci%u %s confirm_hint %u", adapter->dev_id, addr,
                                                        ev->confirm_hint);
-       device = adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type);
+       device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
        if (!device) {
                error("Unable to get device object for %s", addr);
                return;
@@ -4758,7 +4753,8 @@ static void user_passkey_request_callback(uint16_t index, uint16_t length,
        ba2str(&ev->addr.bdaddr, addr);
        DBG("hci%u %s", index, addr);
 
-       device = adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type);
+       device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
        if (!device) {
                error("Unable to get device object for %s", addr);
                return;
@@ -4790,7 +4786,8 @@ static void user_passkey_notify_callback(uint16_t index, uint16_t length,
        ba2str(&ev->addr.bdaddr, addr);
        DBG("hci%u %s", index, addr);
 
-       device = adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type);
+       device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
        if (!device) {
                error("Unable to get device object for %s", addr);
                return;
@@ -4872,7 +4869,8 @@ static void pin_code_request_callback(uint16_t index, uint16_t length,
 
        DBG("hci%u %s", adapter->dev_id, addr);
 
-       device = adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type);
+       device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
        if (!device) {
                error("Unable to get device object for %s", addr);
                return;
@@ -4957,9 +4955,9 @@ static void bonding_complete(struct btd_adapter *adapter,
        struct btd_device *device;
 
        if (status == 0)
-               device = adapter_get_device(adapter, bdaddr, addr_type);
+               device = btd_adapter_get_device(adapter, bdaddr, addr_type);
        else
-               device = adapter_find_device(adapter, bdaddr);
+               device = btd_adapter_find_device(adapter, bdaddr);
 
        if (device != NULL)
                device_bonding_complete(device, status);
@@ -4984,9 +4982,9 @@ static void bonding_attempt_complete(struct btd_adapter *adapter,
                                                        addr_type, status);
 
        if (status == 0)
-               device = adapter_get_device(adapter, bdaddr, addr_type);
+               device = btd_adapter_get_device(adapter, bdaddr, addr_type);
        else
-               device = adapter_find_device(adapter, bdaddr);
+               device = btd_adapter_find_device(adapter, bdaddr);
 
        if (status == MGMT_STATUS_AUTH_FAILED && adapter->pincode_requested) {
                /* On faliure, issue a bonding_retry if possible. */
@@ -5145,7 +5143,7 @@ static void dev_disconnected(struct btd_adapter *adapter,
 
        DBG("Device %s disconnected, reason %u", dst, reason);
 
-       device = adapter_find_device(adapter, &addr->bdaddr);
+       device = btd_adapter_find_device(adapter, &addr->bdaddr);
        if (device)
                adapter_remove_connection(adapter, device);
 
@@ -5220,7 +5218,7 @@ static void store_link_key(struct btd_adapter *adapter,
        char *str;
        int i;
 
-       ba2str(adapter_get_address(adapter), adapter_addr);
+       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,
@@ -5274,7 +5272,7 @@ static void new_link_key_callback(uint16_t index, uint16_t length,
                return;
        }
 
-       device = adapter_get_device(adapter, &addr->bdaddr, addr->type);
+       device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type);
        if (!device) {
                error("Unable to get device object for %s", dst);
                return;
@@ -5289,7 +5287,7 @@ static void new_link_key_callback(uint16_t index, uint16_t length,
                device_set_bonded(device, TRUE);
 
                if (device_is_temporary(device))
-                       device_set_temporary(device, FALSE);
+                       btd_device_set_temporary(device, FALSE);
        }
 
        bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
@@ -5369,7 +5367,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
        DBG("hci%u new LTK for %s authenticated %u enc_size %u",
                adapter->dev_id, dst, ev->key.authenticated, ev->key.enc_size);
 
-       device = adapter_get_device(adapter, &addr->bdaddr, addr->type);
+       device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type);
        if (!device) {
                error("Unable to get device object for %s", dst);
                return;
@@ -5377,7 +5375,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
 
        if (ev->store_hint) {
                const struct mgmt_ltk_info *key = &ev->key;
-               const bdaddr_t *bdaddr = adapter_get_address(adapter);
+               const bdaddr_t *bdaddr = btd_adapter_get_address(adapter);
 
                store_longtermkey(bdaddr, &key->addr.bdaddr,
                                        key->addr.type, key->val, key->master,
@@ -5387,7 +5385,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
                device_set_bonded(device, TRUE);
 
                if (device_is_temporary(device))
-                       device_set_temporary(device, FALSE);
+                       btd_device_set_temporary(device, FALSE);
        }
 
        if (ev->key.master)
@@ -5720,7 +5718,8 @@ static void connected_callback(uint16_t index, uint16_t length,
 
        DBG("hci%u device %s connected eir_len %u", index, addr, eir_len);
 
-       device = adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type);
+       device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+                                                               ev->addr.type);
        if (!device) {
                error("Unable to get device object for %s", addr);
                return;
@@ -5737,7 +5736,7 @@ static void connected_callback(uint16_t index, uint16_t length,
 
        if (eir_data.name != NULL) {
                device_store_cached_name(device, eir_data.name);
-               device_set_name(device, eir_data.name);
+               btd_device_device_set_name(device, eir_data.name);
        }
 
        eir_data_free(&eir_data);
@@ -5759,7 +5758,7 @@ static void device_blocked_callback(uint16_t index, uint16_t length,
        ba2str(&ev->addr.bdaddr, addr);
        DBG("hci%u %s blocked", index, addr);
 
-       device = adapter_find_device(adapter, &ev->addr.bdaddr);
+       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr);
        if (device)
                device_block(device, TRUE);
 }
@@ -5780,7 +5779,7 @@ static void device_unblocked_callback(uint16_t index, uint16_t length,
        ba2str(&ev->addr.bdaddr, addr);
        DBG("hci%u %s unblocked", index, addr);
 
-       device = adapter_find_device(adapter, &ev->addr.bdaddr);
+       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr);
        if (device)
                device_unblock(device, FALSE, TRUE);
 }
@@ -5802,7 +5801,7 @@ static void connect_failed_callback(uint16_t index, uint16_t length,
 
        DBG("hci%u %s status %u", index, addr, ev->status);
 
-       device = adapter_find_device(adapter, &ev->addr.bdaddr);
+       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr);
        if (device) {
                /* If the device is in a bonding process cancel any auth request
                 * sent to the agent before proceeding, but keep the bonding
@@ -5848,15 +5847,15 @@ static void unpaired_callback(uint16_t index, uint16_t length,
 
        DBG("hci%u addr %s", index, addr);
 
-       device = adapter_find_device(adapter, &ev->addr.bdaddr);
+       device = btd_adapter_find_device(adapter, &ev->addr.bdaddr);
        if (!device) {
                warn("No device object for unpaired device %s", addr);
                return;
        }
 
-       device_set_temporary(device, TRUE);
+       btd_device_set_temporary(device, TRUE);
 
-       if (device_is_connected(device))
+       if (btd_device_is_connected(device))
                device_request_disconnect(device, NULL);
        else
                adapter_remove_device(adapter, device);
index 80c5f77..de5b07d 100644 (file)
@@ -90,16 +90,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);
-struct btd_device *adapter_get_device(struct btd_adapter *adapter,
+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 *adapter_find_device(struct btd_adapter *adapter,
+struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
                                                        const bdaddr_t *dst);
 
 const char *adapter_get_path(struct btd_adapter *adapter);
-const bdaddr_t *adapter_get_address(struct btd_adapter *adapter);
+const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter);
 int adapter_set_name(struct btd_adapter *adapter, const char *name);
 
 int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec);
index 7880ba6..4c63cb9 100644 (file)
@@ -319,6 +319,9 @@ static void simple_agent_reply(DBusPendingCall *call, void *user_data)
         * is only called after a reply has been received */
        message = dbus_pending_call_steal_reply(call);
 
+       /* Protect from the callback freeing the agent */
+       agent_ref(agent);
+
        dbus_error_init(&err);
        if (dbus_set_error_from_message(&err, message)) {
                DBG("agent error reply: %s, %s", err.name, err.message);
@@ -330,6 +333,7 @@ static void simple_agent_reply(DBusPendingCall *call, void *user_data)
                        agent_cancel(agent);
                        dbus_message_unref(message);
                        dbus_error_free(&err);
+                       agent_unref(agent);
                        return;
                }
 
@@ -350,6 +354,7 @@ done:
 
        agent->request = NULL;
        agent_request_free(req, TRUE);
+       agent_unref(agent);
 }
 
 static int agent_call_authorize_service(struct agent_request *req,
@@ -421,6 +426,9 @@ static void pincode_reply(DBusPendingCall *call, void *user_data)
         * is only called after a reply has been received */
        message = dbus_pending_call_steal_reply(call);
 
+       /* Protect from the callback freeing the agent */
+       agent_ref(agent);
+
        dbus_error_init(&err);
        if (dbus_set_error_from_message(&err, message)) {
                error("Agent %s replied with an error: %s, %s",
@@ -460,6 +468,7 @@ done:
        dbus_pending_call_cancel(req->call);
        agent->request = NULL;
        agent_request_free(req, TRUE);
+       agent_unref(agent);
 }
 
 static int pincode_request_new(struct agent_request *req, const char *device_path,
index 2861a00..a6f1066 100644 (file)
@@ -156,7 +156,7 @@ static int adapter_cmp_addr(gconstpointer a, gconstpointer b)
        const struct gatt_server *server = a;
        const bdaddr_t *bdaddr = b;
 
-       return bacmp(adapter_get_address(server->adapter), bdaddr);
+       return bacmp(btd_adapter_get_address(server->adapter), bdaddr);
 }
 
 static int adapter_cmp(gconstpointer a, gconstpointer b)
@@ -1137,7 +1137,7 @@ guint attrib_channel_attach(GAttrib *attrib)
 
        channel->server = server;
 
-       device = adapter_find_device(server->adapter, &channel->dst);
+       device = btd_adapter_find_device(server->adapter, &channel->dst);
        if (device == NULL) {
                error("Device object not found for attrib server");
                g_free(channel);
@@ -1310,7 +1310,7 @@ int btd_adapter_gatt_server_start(struct btd_adapter *adapter)
        server = g_new0(struct gatt_server, 1);
        server->adapter = btd_adapter_ref(adapter);
 
-       addr = adapter_get_address(server->adapter);
+       addr = btd_adapter_get_address(server->adapter);
 
        /* BR/EDR socket */
        server->l2cap_io = bt_io_listen(connect_event, NULL, NULL, NULL, &gerr,
index b71c70d..214fa8a 100644 (file)
@@ -5,6 +5,8 @@
                info;
                error;
                debug;
+               baswap;
+               ba2str;
        local:
                *;
 };
index 82b66e6..18543ee 100644 (file)
@@ -263,7 +263,7 @@ static gboolean store_device_info_cb(gpointer user_data)
 
        device->store_id = 0;
 
-       ba2str(adapter_get_address(device->adapter), adapter_addr);
+       ba2str(btd_adapter_get_address(device->adapter), adapter_addr);
        ba2str(&device->bdaddr, device_addr);
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
                        device_addr);
@@ -409,7 +409,7 @@ void device_store_cached_name(struct btd_device *dev, const char *name)
                return;
        }
 
-       ba2str(adapter_get_address(dev->adapter), s_addr);
+       ba2str(btd_adapter_get_address(dev->adapter), s_addr);
        ba2str(&dev->bdaddr, d_addr);
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", s_addr, d_addr);
        filename[PATH_MAX] = '\0';
@@ -470,7 +470,7 @@ static void browse_request_cancel(struct browse_req *req)
        struct btd_device *device = req->device;
        struct btd_adapter *adapter = device->adapter;
 
-       bt_cancel_discovery(adapter_get_address(adapter), &device->bdaddr);
+       bt_cancel_discovery(btd_adapter_get_address(adapter), &device->bdaddr);
 
        attio_cleanup(device);
 
@@ -809,7 +809,7 @@ static void set_trust(GDBusPendingPropertySet id, gboolean value, void *data)
 {
        struct btd_device *device = data;
 
-       device_set_trusted(device, value);
+       btd_device_set_trusted(device, value);
 
        g_dbus_pending_property_success(id);
 }
@@ -997,7 +997,7 @@ int device_block(struct btd_device *device, gboolean update_only)
 
        store_device_info(device);
 
-       device_set_temporary(device, FALSE);
+       btd_device_set_temporary(device, FALSE);
 
        g_dbus_emit_property_changed(dbus_conn, device->path,
                                                DEVICE_INTERFACE, "Blocked");
@@ -1100,7 +1100,8 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg)
        }
 
        if (!device->connected) {
-               g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
+               if (msg)
+                       g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
                return;
        }
 
@@ -1151,7 +1152,7 @@ static void device_profile_connected(struct btd_device *dev,
        DBG("%s %s (%d)", profile->name, strerror(-err), -err);
 
        if (!err)
-               device_set_temporary(dev, FALSE);
+               btd_device_set_temporary(dev, FALSE);
 
        if (dev->pending == NULL)
                return;
@@ -1307,7 +1308,7 @@ static DBusMessage *connect_profiles(struct btd_device *dev, DBusMessage *msg,
        if (!btd_adapter_get_powered(dev->adapter))
                return btd_error_not_ready(msg);
 
-       device_set_temporary(dev, FALSE);
+       btd_device_set_temporary(dev, FALSE);
 
        if (!dev->svc_resolved)
                goto resolve_services;
@@ -1354,10 +1355,10 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
        if (device_is_le(dev)) {
                int err;
 
-               if (device_is_connected(dev))
+               if (btd_device_is_connected(dev))
                        return dbus_message_new_method_return(msg);
 
-               device_set_temporary(dev, FALSE);
+               btd_device_set_temporary(dev, FALSE);
 
                dev->disable_auto_connect = FALSE;
 
@@ -1433,11 +1434,17 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg,
        if (!service)
                return btd_error_invalid_args(msg);
 
+       if (dev->disconnect)
+               return btd_error_in_progress(msg);
+
+       dev->disconnect = dbus_message_ref(msg);
+
        err = btd_service_disconnect(service);
-       if (err == 0) {
-               dev->disconnect = dbus_message_ref(msg);
+       if (err == 0)
                return NULL;
-       }
+
+       dbus_message_unref(dev->disconnect);
+       dev->disconnect = NULL;
 
        if (err == -ENOTSUP)
                return btd_error_not_supported(msg);
@@ -1608,7 +1615,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
        uint8_t io_cap;
        int err;
 
-       device_set_temporary(device, FALSE);
+       btd_device_set_temporary(device, FALSE);
 
        if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID))
                return btd_error_invalid_args(msg);
@@ -1644,7 +1651,7 @@ 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) && !device_is_connected(device))
+       if (device_is_le(device) && !btd_device_is_connected(device))
                err = device_connect_le(device);
        else
                err = adapter_create_bonding(adapter, &device->bdaddr,
@@ -1790,7 +1797,7 @@ static const GDBusPropertyTable device_properties[] = {
        { }
 };
 
-gboolean device_is_connected(struct btd_device *device)
+gboolean btd_device_is_connected(struct btd_device *device)
 {
        return device->connected;
 }
@@ -2182,7 +2189,7 @@ struct btd_device *device_create_from_storage(struct btd_adapter *adapter,
        if (device == NULL)
                return NULL;
 
-       src = adapter_get_address(adapter);
+       src = btd_adapter_get_address(adapter);
        ba2str(src, srcaddr);
 
        load_info(device, srcaddr, address, key_file);
@@ -2207,7 +2214,7 @@ struct btd_device *device_create(struct btd_adapter *adapter,
                return NULL;
 
        device->bdaddr_type = bdaddr_type;
-       sba = adapter_get_address(adapter);
+       sba = btd_adapter_get_address(adapter);
        ba2str(sba, src);
 
        str = load_cached_name(device, src, dst);
@@ -2230,7 +2237,7 @@ char *btd_device_get_storage_path(struct btd_device *device,
                return NULL;
        }
 
-       ba2str(adapter_get_address(device->adapter), srcaddr);
+       ba2str(btd_adapter_get_address(device->adapter), srcaddr);
        ba2str(&device->bdaddr, dstaddr);
 
        if (!filename)
@@ -2240,7 +2247,7 @@ char *btd_device_get_storage_path(struct btd_device *device,
                                                        filename);
 }
 
-void device_set_name(struct btd_device *device, const char *name)
+void btd_device_device_set_name(struct btd_device *device, const char *name)
 {
        if (strncmp(name, device->name, MAX_NAME_LENGTH) == 0)
                return;
@@ -2284,6 +2291,8 @@ void device_set_class(struct btd_device *device, uint32_t class)
 
        g_dbus_emit_property_changed(dbus_conn, device->path,
                                                DEVICE_INTERFACE, "Class");
+       g_dbus_emit_property_changed(dbus_conn, device->path,
+                                               DEVICE_INTERFACE, "Icon");
 }
 
 uint32_t btd_device_get_class(struct btd_device *device)
@@ -2341,7 +2350,7 @@ static void delete_folder_tree(const char *dirname)
 
 static void device_remove_stored(struct btd_device *device)
 {
-       const bdaddr_t *src = adapter_get_address(device->adapter);
+       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];
@@ -2473,7 +2482,7 @@ static gboolean record_has_uuid(const sdp_record_t *rec,
        return FALSE;
 }
 
-GSList *device_get_uuids(struct btd_device *device)
+GSList *btd_device_get_uuids(struct btd_device *device)
 {
        return device->uuids;
 }
@@ -2716,7 +2725,7 @@ static void update_bredr_services(struct browse_req *req, sdp_list_t *recs)
        char *data;
        gsize length = 0;
 
-       ba2str(adapter_get_address(device->adapter), srcaddr);
+       ba2str(btd_adapter_get_address(device->adapter), srcaddr);
        ba2str(&device->bdaddr, dstaddr);
 
        if (!device->temporary) {
@@ -2960,7 +2969,7 @@ static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
        /* Search for mandatory uuids */
        if (uuid_list[req->search_uuid]) {
                sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
-               bt_search_service(adapter_get_address(adapter),
+               bt_search_service(btd_adapter_get_address(adapter),
                                                &device->bdaddr, &uuid,
                                                browse_cb, user_data, NULL);
                return;
@@ -2993,7 +3002,7 @@ static void store_services(struct btd_device *device)
        if (prim_uuid == NULL)
                return;
 
-       ba2str(adapter_get_address(adapter), src_addr);
+       ba2str(btd_adapter_get_address(adapter), src_addr);
        ba2str(&device->bdaddr, dst_addr);
 
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", src_addr,
@@ -3111,7 +3120,7 @@ static void register_all_services(struct browse_req *req, GSList *services)
 {
        struct btd_device *device = req->device;
 
-       device_set_temporary(device, FALSE);
+       btd_device_set_temporary(device, FALSE);
 
        update_gatt_services(req, device->primaries, services);
        g_slist_free_full(device->primaries, g_free);
@@ -3369,7 +3378,8 @@ int device_connect_le(struct btd_device *dev)
         * pairing finishes
         */
        io = bt_io_connect(att_connect_cb, attcb, NULL, &gerr,
-                       BT_IO_OPT_SOURCE_BDADDR, adapter_get_address(adapter),
+                       BT_IO_OPT_SOURCE_BDADDR,
+                       btd_adapter_get_address(adapter),
                        BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
                        BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
                        BT_IO_OPT_DEST_TYPE, dev->bdaddr_type,
@@ -3452,7 +3462,7 @@ static int device_browse_primary(struct btd_device *device, DBusMessage *msg)
        device->att_io = bt_io_connect(att_connect_cb,
                                attcb, NULL, NULL,
                                BT_IO_OPT_SOURCE_BDADDR,
-                               adapter_get_address(adapter),
+                               btd_adapter_get_address(adapter),
                                BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
                                BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
                                BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
@@ -3498,8 +3508,8 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
        req->device = device;
        sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
 
-       err = bt_search_service(adapter_get_address(adapter), &device->bdaddr,
-                                               &uuid, browse_cb, req, NULL);
+       err = bt_search_service(btd_adapter_get_address(adapter),
+                               &device->bdaddr, &uuid, browse_cb, req, NULL);
        if (err < 0) {
                browse_request_free(req);
                return err;
@@ -3522,6 +3532,23 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
        return err;
 }
 
+int device_discover_services(struct btd_device *device)
+{
+       int err;
+
+       if (device_is_bredr(device))
+               err = device_browse_sdp(device, NULL);
+       else
+               err = device_browse_primary(device, NULL);
+
+       if (err == 0 && device->discov_timer) {
+               g_source_remove(device->discov_timer);
+               device->discov_timer = 0;
+       }
+
+       return err;
+}
+
 struct btd_adapter *device_get_adapter(struct btd_device *device)
 {
        if (!device)
@@ -3548,7 +3575,7 @@ gboolean device_is_temporary(struct btd_device *device)
        return device->temporary;
 }
 
-void device_set_temporary(struct btd_device *device, gboolean temporary)
+void btd_device_set_temporary(struct btd_device *device, gboolean temporary)
 {
        if (!device)
                return;
@@ -3564,7 +3591,7 @@ void device_set_temporary(struct btd_device *device, gboolean temporary)
        device->temporary = temporary;
 }
 
-void device_set_trusted(struct btd_device *device, gboolean trusted)
+void btd_device_set_trusted(struct btd_device *device, gboolean trusted)
 {
        if (!device)
                return;
@@ -4279,7 +4306,7 @@ static sdp_list_t *read_device_records(struct btd_device *device)
        sdp_list_t *recs = NULL;
        sdp_record_t *rec;
 
-       ba2str(adapter_get_address(device->adapter), local);
+       ba2str(btd_adapter_get_address(device->adapter), local);
        ba2str(&device->bdaddr, peer);
 
        snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
index deec8d0..3a33cb2 100644 (file)
@@ -33,7 +33,7 @@ struct btd_device *device_create_from_storage(struct btd_adapter *adapter,
 char *btd_device_get_storage_path(struct btd_device *device,
                                const char *filename);
 
-void device_set_name(struct btd_device *device, const char *name);
+void btd_device_device_set_name(struct btd_device *device, const char *name);
 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);
@@ -46,7 +46,7 @@ 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);
-GSList *device_get_uuids(struct btd_device *device);
+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,
                                                const char *uuid);
@@ -69,12 +69,12 @@ gboolean device_is_paired(struct btd_device *device);
 gboolean device_is_bonded(struct btd_device *device);
 gboolean device_is_trusted(struct btd_device *device);
 void device_set_paired(struct btd_device *device, gboolean paired);
-void device_set_temporary(struct btd_device *device, gboolean temporary);
-void device_set_trusted(struct btd_device *device, gboolean trusted);
+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_legacy(struct btd_device *device, bool legacy);
 void device_set_rssi(struct btd_device *device, int8_t rssi);
-gboolean device_is_connected(struct btd_device *device);
+gboolean btd_device_is_connected(struct btd_device *device);
 bool device_is_retrying(struct btd_device *device);
 void device_bonding_complete(struct btd_device *device, uint8_t status);
 gboolean device_is_bonding(struct btd_device *device, const char *sender);
@@ -131,5 +131,7 @@ bool device_remove_svc_complete_callback(struct btd_device *dev,
 struct btd_service *btd_device_get_service(struct btd_device *dev,
                                                const char *remote_uuid);
 
+int device_discover_services(struct btd_device *device);
+
 void btd_device_init(void);
 void btd_device_cleanup(void);
index 084884e..7745ff3 100644 (file)
--- a/src/eir.c
+++ b/src/eir.c
@@ -129,7 +129,7 @@ static char *name2utf8(const uint8_t *name, uint8_t len)
        return g_strdup(utf8_name);
 }
 
-int eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
+void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
 {
        uint16_t len = 0;
 
@@ -138,7 +138,7 @@ int eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
 
        /* No EIR data to parse */
        if (eir_data == NULL)
-               return 0;
+               return;
 
        while (len < eir_len - 1) {
                uint8_t field_len = eir_data[0];
@@ -226,8 +226,6 @@ int eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
 
                eir_data += field_len + 1;
        }
-
-       return 0;
 }
 
 int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len)
@@ -248,7 +246,7 @@ int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len)
 
        /* optional OOB EIR data */
        if (eir_len > 0)
-               return eir_parse(eir, eir_data, eir_len);
+               eir_parse(eir, eir_data, eir_len);
 
        return 0;
 }
index 1b6242d..411986e 100644 (file)
--- a/src/eir.h
+++ b/src/eir.h
@@ -52,7 +52,7 @@ struct eir_data {
 };
 
 void eir_data_free(struct eir_data *eir);
-int eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len);
+void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len);
 int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len);
 int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod,
                        const uint8_t *hash, const uint8_t *randomizer,
index d8cbe6a..3c0d27c 100644 (file)
                                </sequence>                             \
                        </sequence>                                     \
                </attribute>                                            \
+               <attribute id=\"0x0005\">                               \
+                       <sequence>                                      \
+                               <uuid value=\"0x1002\" />               \
+                       </sequence>                                     \
+               </attribute>                                            \
                <attribute id=\"0x0009\">                               \
                        <sequence>                                      \
                                <sequence>                              \
                                </sequence>                             \
                        </sequence>                                     \
                </attribute>                                            \
+               <attribute id=\"0x0005\">                               \
+                       <sequence>                                      \
+                               <uuid value=\"0x1002\" />               \
+                       </sequence>                                     \
+               </attribute>                                            \
                <attribute id=\"0x0009\">                               \
                        <sequence>                                      \
                                <sequence>                              \
                                </sequence>                             \
                        </sequence>                                     \
                </attribute>                                            \
+               <attribute id=\"0x0005\">                               \
+                       <sequence>                                      \
+                               <uuid value=\"0x1002\" />               \
+                       </sequence>                                     \
+               </attribute>                                            \
                <attribute id=\"0x0009\">                               \
                        <sequence>                                      \
                                <sequence>                              \
@@ -703,7 +718,7 @@ static void ext_io_destroy(gpointer p)
        }
 
        if (ext_io->resolving)
-               bt_cancel_discovery(adapter_get_address(ext_io->adapter),
+               bt_cancel_discovery(btd_adapter_get_address(ext_io->adapter),
                                        device_get_address(ext_io->device));
 
        if (ext_io->adapter)
@@ -1032,7 +1047,7 @@ static struct ext_io *create_conn(struct ext_io *server, GIOChannel *io,
        GIOCondition cond;
        char addr[18];
 
-       device = adapter_find_device(server->adapter, dst);
+       device = btd_adapter_find_device(server->adapter, dst);
        if (device == NULL) {
                ba2str(dst, addr);
                error("%s device %s not found", server->ext->name, addr);
@@ -1242,7 +1257,7 @@ static uint32_t ext_start_servers(struct ext_profile *ext,
 
                io = bt_io_listen(connect, confirm, l2cap, NULL, &err,
                                        BT_IO_OPT_SOURCE_BDADDR,
-                                       adapter_get_address(adapter),
+                                       btd_adapter_get_address(adapter),
                                        BT_IO_OPT_MODE, ext->mode,
                                        BT_IO_OPT_PSM, psm,
                                        BT_IO_OPT_SEC_LEVEL, ext->sec_level,
@@ -1280,7 +1295,7 @@ static uint32_t ext_start_servers(struct ext_profile *ext,
 
                io = bt_io_listen(connect, confirm, rfcomm, NULL, &err,
                                        BT_IO_OPT_SOURCE_BDADDR,
-                                       adapter_get_address(adapter),
+                                       btd_adapter_get_address(adapter),
                                        BT_IO_OPT_CHANNEL, chan,
                                        BT_IO_OPT_SEC_LEVEL, ext->sec_level,
                                        BT_IO_OPT_INVALID);
@@ -1565,7 +1580,7 @@ static void record_cb(sdp_list_t *recs, int err, gpointer user_data)
                goto failed;
        }
 
-       err = connect_io(conn, adapter_get_address(conn->adapter),
+       err = connect_io(conn, btd_adapter_get_address(conn->adapter),
                                        device_get_address(conn->device));
        if (err < 0) {
                error("Connecting %s failed: %s", ext->name, strerror(-err));
@@ -1622,10 +1637,10 @@ static int ext_connect_dev(struct btd_service *service)
        if (ext->remote_psm || ext->remote_chan) {
                conn->psm = ext->remote_psm;
                conn->chan = ext->remote_chan;
-               err = connect_io(conn, adapter_get_address(adapter),
+               err = connect_io(conn, btd_adapter_get_address(adapter),
                                                device_get_address(dev));
        } else {
-               err = resolve_service(conn, adapter_get_address(adapter),
+               err = resolve_service(conn, btd_adapter_get_address(adapter),
                                                device_get_address(dev));
        }
 
index acfd7c3..505ac79 100644 (file)
@@ -315,6 +315,31 @@ static const struct l2cap_server_data l2cap_server_nval_cid_test2 = {
        .expect_rsp_len = sizeof(l2cap_nval_cfg_rsp),
 };
 
+static const struct l2cap_client_data le_client_connect_success_test = {
+       .client_psm = 0x0080,
+       .server_psm = 0x0080,
+};
+
+static const struct l2cap_client_data le_client_connect_nval_psm_test = {
+       .client_psm = 0x0080,
+       .expect_err = ECONNREFUSED,
+};
+
+static const uint8_t le_connect_req[] = {      0x80, 0x00, /* PSM */
+                                               0x41, 0x00, /* SCID */
+                                               0x20, 0x00, /* MTU */
+                                               0x20, 0x00, /* MPS */
+                                               0x05, 0x00, /* Credits */
+};
+
+static const struct l2cap_server_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,
+};
+
 static void client_connectable_complete(uint16_t opcode, uint8_t status,
                                        const void *param, uint8_t len,
                                        void *user_data)
@@ -737,5 +762,14 @@ int main(int argc, char *argv[])
                                &l2cap_server_nval_cid_test2,
                                setup_powered_server, test_server);
 
+       test_l2cap_le("L2CAP LE Client - Success",
+                               &le_client_connect_success_test,
+                               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);
+
        return tester_run();
 }
index c5c8763..9a7c809 100644 (file)
@@ -28,6 +28,7 @@
 #endif
 
 #include <stdio.h>
+#include <stdbool.h>
 #include <errno.h>
 #include <ctype.h>
 #include <fcntl.h>
@@ -48,6 +49,9 @@
 
 #define NIBBLE_TO_ASCII(c)  ((c) < 0x0a ? (c) + 0x30 : (c) + 0x57)
 
+#define BREDR_DEFAULT_PSM      0x1011
+#define LE_DEFAULT_PSM         0x0080
+
 /* Test modes */
 enum {
        SEND,
@@ -87,7 +91,7 @@ static long buffer_size = 2048;
 
 /* Default addr and psm and cid */
 static bdaddr_t bdaddr;
-static unsigned short psm = 0x1011;
+static unsigned short psm = 0;
 static unsigned short cid = 0;
 
 /* Default number of frames to send (-1 = infinite) */
@@ -258,6 +262,37 @@ static void hexdump(unsigned char *s, unsigned long l)
        }
 }
 
+static int getopts(int sk, struct l2cap_options *opts, bool connected)
+{
+       socklen_t optlen;
+       int err;
+
+       memset(opts, 0, sizeof(*opts));
+
+       if (bdaddr_type == BDADDR_BREDR || cid) {
+               optlen = sizeof(*opts);
+               return getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts, &optlen);
+       }
+
+       optlen = sizeof(opts->imtu);
+       err = getsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, &opts->imtu, &optlen);
+       if (err < 0 || !connected)
+               return err;
+
+       optlen = sizeof(opts->omtu);
+       return getsockopt(sk, SOL_BLUETOOTH, BT_SNDMTU, &opts->omtu, &optlen);
+}
+
+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));
+
+       return setsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, &opts->imtu,
+                                                       sizeof(opts->imtu));
+}
+
 static int do_connect(char *svr)
 {
        struct sockaddr_l2 addr;
@@ -290,12 +325,9 @@ static int do_connect(char *svr)
        }
 
        /* Get default options */
-       memset(&opts, 0, sizeof(opts));
-       optlen = sizeof(opts);
-
-       if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+       if (getopts(sk, &opts, false) < 0) {
                syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)",
-                                                       strerror(errno), errno);
+                                               strerror(errno), errno);
                goto error;
        }
 
@@ -308,7 +340,7 @@ static int do_connect(char *svr)
        opts.txwin_size = txwin_size;
        opts.max_tx = max_transmit;
 
-       if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+       if (setopts(sk, &opts) < 0) {
                syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)",
                                                        strerror(errno), errno);
                goto error;
@@ -400,10 +432,7 @@ static int do_connect(char *svr)
        }
 
        /* Get current options */
-       memset(&opts, 0, sizeof(opts));
-       optlen = sizeof(opts);
-
-       if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+       if (getopts(sk, &opts, true) < 0) {
                syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)",
                                                        strerror(errno), errno);
                goto error;
@@ -531,10 +560,7 @@ static void do_listen(void (*handler)(int sk))
        }
 
        /* Get default options */
-       memset(&opts, 0, sizeof(opts));
-       optlen = sizeof(opts);
-
-       if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+       if (getopts(sk, &opts, false) < 0) {
                syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)",
                                                        strerror(errno), errno);
                goto error;
@@ -550,7 +576,7 @@ static void do_listen(void (*handler)(int sk))
        opts.txwin_size = txwin_size;
        opts.max_tx = max_transmit;
 
-       if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+       if (setopts(sk, &opts) < 0) {
                syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)",
                                                        strerror(errno), errno);
                goto error;
@@ -629,10 +655,7 @@ static void do_listen(void (*handler)(int sk))
                }
 
                /* Get current options */
-               memset(&opts, 0, sizeof(opts));
-               optlen = sizeof(opts);
-
-               if (getsockopt(nsk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+               if (getopts(nsk, &opts, true) < 0) {
                        syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)",
                                                        strerror(errno), errno);
                        if (!defer_setup) {
@@ -1525,6 +1548,13 @@ int main(int argc, char *argv[])
                }
        }
 
+       if (!psm) {
+               if (bdaddr_type == BDADDR_BREDR)
+                       psm = BREDR_DEFAULT_PSM;
+               else
+                       psm = LE_DEFAULT_PSM;
+       }
+
        if (need_addr && !(argc - optind)) {
                usage();
                exit(1);
index e37c56f..86c2271 100644 (file)
@@ -427,7 +427,7 @@ int main(int argc, char *argv[])
                exit(EXIT_FAILURE);
        }
 
-       if (option_root && chdir(option_root) > 0) {
+       if (option_root && chdir(option_root) < 0) {
                perror("chdir:");
                exit(EXIT_FAILURE);
        }
index f985b1e..b4b65ec 100644 (file)
@@ -1174,18 +1174,15 @@ static int add_sp(sdp_session_t *session, svc_info_t *si)
        sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
        root = sdp_list_append(0, &root_uuid);
        sdp_set_browse_groups(&record, root);
-       sdp_list_free(root, 0);
 
        sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
        svclass_id = sdp_list_append(0, &sp_uuid);
        sdp_set_service_classes(&record, svclass_id);
-       sdp_list_free(svclass_id, 0);
 
        sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
        profile.version = 0x0100;
        profiles = sdp_list_append(0, &profile);
        sdp_set_profile_descs(&record, profiles);
-       sdp_list_free(profiles, 0);
 
        sdp_uuid16_create(&l2cap, L2CAP_UUID);
        proto[0] = sdp_list_append(0, &l2cap);
@@ -1226,6 +1223,9 @@ end:
        sdp_list_free(proto[1], 0);
        sdp_list_free(apseq, 0);
        sdp_list_free(aproto, 0);
+       sdp_list_free(root, 0);
+       sdp_list_free(svclass_id, 0);
+       sdp_list_free(profiles, 0);
 
        return ret;
 }
@@ -1813,7 +1813,10 @@ end:
        sdp_list_free(proto[1], 0);
        sdp_list_free(proto[2], 0);
        sdp_list_free(apseq, 0);
+       sdp_list_free(pfseq, 0);
        sdp_list_free(aproto, 0);
+       sdp_list_free(root, 0);
+       sdp_list_free(svclass_id, NULL);
 
        return ret;
 }
@@ -1885,7 +1888,10 @@ end:
        sdp_list_free(proto[1], 0);
        sdp_list_free(proto[2], 0);
        sdp_list_free(apseq, 0);
+       sdp_list_free(pfseq, 0);
        sdp_list_free(aproto, 0);
+       sdp_list_free(root, 0);
+       sdp_list_free(svclass_id, 0);
 
        return ret;
 }
index 46dc65a..05d620a 100644 (file)
 #include <unistd.h>
 #include <errno.h>
 #include <stdbool.h>
+#include <sys/socket.h>
 
 #include <glib.h>
 
 #include "lib/bluetooth.h"
+#include "lib/hci.h"
 #include "lib/mgmt.h"
 
 #include "monitor/bt.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 {
@@ -51,23 +87,203 @@ struct test_data {
        struct hciemu *hciemu;
        enum hciemu_type hciemu_type;
        unsigned int io_id;
+       uint8_t ia[6];
+       uint8_t ia_type;
+       uint8_t ra[6];
+       uint8_t ra_type;
+       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 smp_server_data {
-       const void *send_req;
-       uint16_t send_req_len;
-       const void *expect_rsp;
-       uint16_t expect_rsp_len;
+struct smp_req_rsp {
+       const void *send;
+       uint16_t send_len;
+       const void *expect;
+       uint16_t expect_len;
 };
 
-struct smp_client_data {
-       const void *expect_req;
-       uint16_t expect_req_len;
-       const void *send_rsp;
-       uint16_t send_rsp_len;
+struct smp_data {
+       const struct smp_req_rsp *req;
+       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;
@@ -180,6 +396,13 @@ 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");
+               tester_pre_setup_failed();
+               return;
+       }
+
        data->mgmt = mgmt_new_default();
        if (!data->mgmt) {
                tester_warn("Failed to setup management interface");
@@ -203,6 +426,11 @@ 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;
+       }
+
        hciemu_unref(data->hciemu);
        data->hciemu = NULL;
 }
@@ -217,11 +445,11 @@ static void test_data_free(void *test_data)
 #define test_smp(name, data, setup, func) \
        do { \
                struct test_data *user; \
-               user = malloc(sizeof(struct test_data)); \
+               user = calloc(1, sizeof(struct test_data)); \
                if (!user) \
                        break; \
                user->hciemu_type = HCIEMU_TYPE_LE; \
-               user->io_id = 0; \
+               user->alg_sk = -1; \
                user->test_data = data; \
                tester_add_full(name, data, \
                                test_pre_setup, setup, func, NULL, \
@@ -231,21 +459,27 @@ static void test_data_free(void *test_data)
 static const uint8_t smp_nval_req_1[] = { 0x0b, 0x00 };
 static const uint8_t smp_nval_req_1_rsp[] = { 0x05, 0x07 };
 
-static const struct smp_server_data smp_server_nval_req_1_test = {
-       .send_req = smp_nval_req_1,
-       .send_req_len = sizeof(smp_nval_req_1),
-       .expect_rsp = smp_nval_req_1_rsp,
-       .expect_rsp_len = sizeof(smp_nval_req_1_rsp),
+static const struct smp_req_rsp nval_req_1[] = {
+       { smp_nval_req_1, sizeof(smp_nval_req_1),
+                       smp_nval_req_1_rsp, sizeof(smp_nval_req_1_rsp) },
+};
+
+static const struct smp_data smp_server_nval_req_1_test = {
+       .req = nval_req_1,
+       .req_count = G_N_ELEMENTS(nval_req_1),
 };
 
 static const uint8_t smp_nval_req_2[7] = { 0x01 };
 static const uint8_t smp_nval_req_2_rsp[] = { 0x05, 0x06 };
 
-static const struct smp_server_data smp_server_nval_req_2_test = {
-       .send_req = smp_nval_req_2,
-       .send_req_len = sizeof(smp_nval_req_2),
-       .expect_rsp = smp_nval_req_2_rsp,
-       .expect_rsp_len = sizeof(smp_nval_req_2_rsp),
+static const struct smp_req_rsp srv_nval_req_1[] = {
+       { smp_nval_req_2, sizeof(smp_nval_req_2),
+                       smp_nval_req_2_rsp, sizeof(smp_nval_req_2_rsp) },
+};
+
+static const struct smp_data smp_server_nval_req_2_test = {
+       .req = srv_nval_req_1,
+       .req_count = G_N_ELEMENTS(srv_nval_req_1),
 };
 
 static const uint8_t smp_basic_req_1[] = {     0x01,   /* Pairing Request */
@@ -255,28 +489,45 @@ static const uint8_t smp_basic_req_1[] = {        0x01,   /* Pairing Request */
                                                0x10,   /* Max key size */
                                                0x00,   /* Init. key dist. */
                                                0x01,   /* Rsp. key dist. */
-
 };
 static const uint8_t smp_basic_req_1_rsp[] = { 0x02,   /* Pairing Response */
                                                0x03,   /* NoInputNoOutput */
                                                0x00,   /* OOB Flag */
-                                               0x00,   /* SMP auth none */
+                                               0x01,   /* Bonding - no MITM */
                                                0x10,   /* Max key size */
                                                0x00,   /* Init. key dist. */
-                                               0x00,   /* Rsp. key dist. */
+                                               0x01,   /* Rsp. key dist. */
+};
+
+static const uint8_t smp_confirm_req_1[17] = { 0x03 };
+static const uint8_t smp_random_req_1[17] = { 0x04 };
+
+static const struct smp_req_rsp srv_basic_req_1[] = {
+       { smp_basic_req_1, sizeof(smp_basic_req_1),
+                       smp_basic_req_1_rsp, sizeof(smp_basic_req_1_rsp) },
+       { smp_confirm_req_1, sizeof(smp_confirm_req_1),
+                       smp_confirm_req_1, sizeof(smp_confirm_req_1) },
+       { smp_random_req_1, sizeof(smp_random_req_1),
+                       smp_random_req_1, sizeof(smp_random_req_1) },
+};
 
+static const struct smp_data smp_server_basic_req_1_test = {
+       .req = srv_basic_req_1,
+       .req_count = G_N_ELEMENTS(srv_basic_req_1),
 };
 
-static const struct smp_server_data smp_server_basic_req_1_test = {
-       .send_req = smp_basic_req_1,
-       .send_req_len = sizeof(smp_basic_req_1),
-       .expect_rsp = smp_basic_req_1_rsp,
-       .expect_rsp_len = sizeof(smp_basic_req_1_rsp),
+static const struct smp_req_rsp cli_basic_req_1[] = {
+       { NULL, 0, smp_basic_req_1, sizeof(smp_basic_req_1) },
+       { smp_basic_req_1_rsp, sizeof(smp_basic_req_1_rsp),
+                       smp_confirm_req_1, sizeof(smp_confirm_req_1) },
+       { smp_confirm_req_1, sizeof(smp_confirm_req_1),
+                       smp_random_req_1, sizeof(smp_random_req_1) },
+       { smp_random_req_1, sizeof(smp_random_req_1), NULL, 0 },
 };
 
-static const struct smp_client_data smp_client_basic_req_1_test = {
-       .expect_req = smp_basic_req_1,
-       .expect_req_len = sizeof(smp_basic_req_1),
+static const struct smp_data smp_client_basic_req_1_test = {
+       .req = cli_basic_req_1,
+       .req_count = G_N_ELEMENTS(cli_basic_req_1),
 };
 
 static void client_connectable_complete(uint16_t opcode, uint8_t status,
@@ -341,63 +592,182 @@ static void pair_device_complete(uint8_t status, uint16_t length,
        tester_test_passed();
 }
 
-static void smp_server(const void *data, uint16_t len, void *user_data)
+static const void *get_pdu(const uint8_t *data)
 {
        struct test_data *test_data = tester_get_data();
-       const struct smp_client_data *cli = test_data->test_data;
+       uint8_t opcode = data[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));
+               break;
+       case 0x02: /* Pairing Response */
+               memcpy(test_data->smp_prsp, data, sizeof(test_data->smp_prsp));
+               break;
+       case 0x03: /* Pairing Confirm */
+               buf[0] = data[0];
+               smp_c1(test_data->smp_prnd, res);
+               swap128(res, &buf[1]);
+               return buf;
+       case 0x04: /* Pairing Random */
+               buf[0] = data[0];
+               swap128(test_data->smp_prnd, &buf[1]);
+               return buf;
+       default:
+               break;
+       }
+
+       return data;
+}
+
+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;
+
+       err = smp_c1(data->smp_rrnd, res);
+       if (err < 0)
+               return false;
 
-       tester_print("Received SMP request");
+       swap128(res, confirm);
 
-       if (!cli->expect_req) {
+       if (memcmp(data->smp_pcnf, confirm, sizeof(data->smp_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);
+       } else {
+               smp_s1(data->smp_prnd, data->smp_rrnd, key);
+               swap128(key, data->smp_ltk);
+       }
+
+       return true;
+}
+
+static void smp_server(const void *data, uint16_t len, void *user_data)
+{
+       struct test_data *test_data = user_data;
+       struct bthost *bthost = hciemu_client_get_host(test_data->hciemu);
+       const struct smp_data *smp = test_data->test_data;
+       const struct smp_req_rsp *req;
+       const void *pdu;
+       uint8_t opcode;
+
+       if (len < 1) {
+               tester_warn("Received too small SMP PDU");
+               goto failed;
+       }
+
+       opcode = *((const uint8_t *) data);
+
+       tester_print("Received SMP opcode 0x%02x", opcode);
+
+       if (test_data->counter >= smp->req_count) {
                tester_test_passed();
                return;
        }
 
-       if (cli->expect_req_len != len) {
-               tester_warn("Unexpected SMP request length (%u != %u)",
-                                               len, cli->expect_req_len);
+       req = &smp->req[test_data->counter++];
+       if (!req->expect)
+               goto next;
+
+       if (req->expect_len != len) {
+               tester_warn("Unexpected SMP PDU length (%u != %u)",
+                                                       len, req->expect_len);
                goto failed;
        }
 
-       if (memcmp(cli->expect_req, data, len) != 0) {
-               tester_warn("Unexpected SMP request");
-               goto failed;
+       switch (opcode) {
+       case 0x01: /* Pairing Request */
+               memcpy(test_data->smp_preq, data, sizeof(test_data->smp_preq));
+               break;
+       case 0x02: /* Pairing Response */
+               memcpy(test_data->smp_prsp, data, sizeof(test_data->smp_prsp));
+               break;
+       case 0x03: /* Pairing Confirm */
+               memcpy(test_data->smp_pcnf, data + 1, 16);
+               goto next;
+       case 0x04: /* Pairing Random */
+               swap128(data + 1, test_data->smp_rrnd);
+               if (!verify_random(data + 1))
+                       goto failed;
+               goto next;
+       default:
+               break;
        }
 
-       if (cli->send_rsp) {
-               struct bthost *bthost;
+       if (memcmp(req->expect, data, len) != 0) {
+               tester_warn("Unexpected SMP PDU");
+               goto failed;
+       }
 
-               bthost = hciemu_client_get_host(test_data->hciemu);
-               bthost_send_cid(bthost, test_data->handle, SMP_CID,
-                                       cli->send_rsp, cli->send_rsp_len);
+next:
+       if (smp->req_count == test_data->counter) {
+               tester_test_passed();
                return;
        }
 
-       tester_test_passed();
+       req = &smp->req[test_data->counter];
+
+       pdu = get_pdu(req->send);
+       bthost_send_cid(bthost, test_data->handle, SMP_CID, pdu,
+                                                       req->send_len);
+
+       if (!req->expect)
+               tester_test_passed();
+
        return;
 
 failed:
        tester_test_failed();
 }
 
-static void smp_server_new_conn(uint16_t handle, void *user_data)
+static void smp_new_conn(uint16_t handle, void *user_data)
 {
        struct test_data *data = user_data;
+       const struct smp_data *smp = data->test_data;
        struct bthost *bthost = hciemu_client_get_host(data->hciemu);
+       const struct smp_req_rsp *req;
+       const void *pdu;
 
-       tester_print("New server connection with handle 0x%04x", handle);
+       tester_print("New SMP client connection with handle 0x%04x", handle);
 
        data->handle = handle;
 
-       bthost_add_cid_hook(bthost, handle, SMP_CID, smp_server, NULL);
+       bthost_add_cid_hook(bthost, handle, SMP_CID, smp_server, data);
+
+       if (smp->req_count == data->counter)
+               return;
+
+       req = &smp->req[data->counter];
+
+       if (!req->send)
+               return;
+
+       tester_print("Sending SMP PDU");
+
+       pdu = get_pdu(req->send);
+       bthost_send_cid(bthost, handle, SMP_CID, pdu, req->send_len);
 }
 
-static void test_client(const void *test_data)
+static void init_bdaddr(struct test_data *data)
 {
-       struct test_data *data = tester_get_data();
-       const uint8_t *client_bdaddr;
-       struct mgmt_cp_pair_device cp;
-       struct bthost *bthost;
+       const uint8_t *master_bdaddr, *client_bdaddr;
+
+       master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
+       if (!master_bdaddr) {
+               tester_warn("No master bdaddr");
+               tester_test_failed();
+               return;
+       }
 
        client_bdaddr = hciemu_get_client_bdaddr(data->hciemu);
        if (!client_bdaddr) {
@@ -406,10 +776,30 @@ static void test_client(const void *test_data)
                return;
        }
 
+       data->ia_type = LE_PUBLIC_ADDRESS;
+       data->ra_type = LE_PUBLIC_ADDRESS;
+
+       if (data->out) {
+               memcpy(data->ia, client_bdaddr, sizeof(data->ia));
+               memcpy(data->ra, master_bdaddr, sizeof(data->ra));
+       } else {
+               memcpy(data->ia, master_bdaddr, sizeof(data->ia));
+               memcpy(data->ra, client_bdaddr, sizeof(data->ra));
+       }
+}
+
+static void test_client(const void *test_data)
+{
+       struct test_data *data = tester_get_data();
+       struct mgmt_cp_pair_device cp;
+       struct bthost *bthost;
+
+       init_bdaddr(data);
+
        bthost = hciemu_client_get_host(data->hciemu);
-       bthost_set_connect_cb(bthost, smp_server_new_conn, data);
+       bthost_set_connect_cb(bthost, smp_new_conn, data);
 
-       memcpy(&cp.addr.bdaddr, client_bdaddr, sizeof(bdaddr_t));
+       memcpy(&cp.addr.bdaddr, data->ra, sizeof(data->ra));
        cp.addr.type = BDADDR_LE_PUBLIC;
        cp.io_cap = 0x03; /* NoInputNoOutput */
 
@@ -441,6 +831,8 @@ static void setup_powered_server(const void *test_data)
 
        mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
                                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_ADVERTISING, data->mgmt_index,
                                sizeof(param), param, NULL, NULL, NULL);
        mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
@@ -448,72 +840,19 @@ static void setup_powered_server(const void *test_data)
                        NULL, NULL);
 }
 
-static void smp_client(const void *data, uint16_t len, void *user_data)
-{
-       struct test_data *test_data = user_data;
-       const struct smp_server_data *srv = test_data->test_data;
-
-       tester_print("SMP client received response");
-
-       if (!srv->expect_rsp) {
-               tester_test_passed();
-               return;
-       }
-
-       if (srv->expect_rsp_len != len) {
-               tester_warn("Unexpected SMP response length (%u != %u)",
-                                               len, srv->expect_rsp_len);
-               goto failed;
-       }
-
-       if (memcmp(srv->expect_rsp, data, len) != 0) {
-               tester_warn("Unexpected SMP response");
-               goto failed;
-       }
-
-       tester_test_passed();
-       return;
-
-failed:
-       tester_test_failed();
-}
-
-static void smp_client_new_conn(uint16_t handle, void *user_data)
-{
-       struct test_data *data = user_data;
-       const struct smp_server_data *srv = data->test_data;
-       struct bthost *bthost = hciemu_client_get_host(data->hciemu);
-
-       tester_print("New SMP client connection with handle 0x%04x", handle);
-
-       bthost_add_cid_hook(bthost, handle, SMP_CID, smp_client, data);
-
-       if (!srv->send_req)
-               return;
-
-       tester_print("Sending SMP Request from client");
-
-       bthost_send_cid(bthost, handle, SMP_CID, srv->send_req,
-                                                       srv->send_req_len);
-}
-
 static void test_server(const void *test_data)
 {
        struct test_data *data = tester_get_data();
-       const uint8_t *master_bdaddr;
        struct bthost *bthost;
 
-       master_bdaddr = hciemu_get_master_bdaddr(data->hciemu);
-       if (!master_bdaddr) {
-               tester_warn("No master bdaddr");
-               tester_test_failed();
-               return;
-       }
+       data->out = true;
+
+       init_bdaddr(data);
 
        bthost = hciemu_client_get_host(data->hciemu);
-       bthost_set_connect_cb(bthost, smp_client_new_conn, data);
+       bthost_set_connect_cb(bthost, smp_new_conn, data);
 
-       bthost_hci_connect(bthost, master_bdaddr, BDADDR_LE_PUBLIC);
+       bthost_hci_connect(bthost, data->ra, BDADDR_LE_PUBLIC);
 }
 
 int main(int argc, char *argv[])
diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
new file mode 100644 (file)
index 0000000..fb555b8
--- /dev/null
@@ -0,0 +1,1090 @@
+/*
+ *
+ *  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 <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/avdtp.h"
+
+struct test_pdu {
+       bool valid;
+       const uint8_t *data;
+       size_t size;
+};
+
+struct test_data {
+       char *test_name;
+       struct test_pdu *pdu_list;
+};
+
+#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)
+
+struct context {
+       GMainLoop *main_loop;
+       struct avdtp *session;
+       struct avdtp_local_sep *sep;
+       struct avdtp_stream *stream;
+       guint source;
+       int fd;
+       int mtu;
+       gboolean pending_open;
+       gboolean pending_suspend;
+       unsigned int pdu_offset;
+       const struct test_data *data;
+};
+
+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;
+
+       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, "AVDTP: ");
+
+       g_assert(len == (ssize_t) pdu->size);
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-02-C"))
+               g_timeout_add_seconds(1, context_quit, context);
+
+       return FALSE;
+}
+
+static void context_process(struct context *context)
+{
+       if (!context->data->pdu_list[context->pdu_offset].valid) {
+               context_quit(context);
+               return;
+       }
+
+       g_idle_add(send_pdu, context);
+}
+
+static gboolean transport_open(struct avdtp_stream *stream)
+{
+       int fd;
+
+       fd = open("/dev/null", O_RDWR, 0);
+       if (fd < 0)
+               g_assert_not_reached();
+
+       return avdtp_stream_set_transport(stream, fd, 672, 672);
+}
+
+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))
+               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, "AVDTP: ");
+
+       g_assert((size_t) len == pdu->size);
+
+       g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+       if (context->pending_open) {
+               context->pending_open = FALSE;
+               g_assert(transport_open(context->stream));
+       }
+
+       if (context->pending_suspend) {
+               int ret;
+
+               context->pending_suspend = FALSE;
+               ret = avdtp_suspend(context->session, context->stream);
+               g_assert_cmpint(ret, ==, 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 = avdtp_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 execute_context(struct context *context)
+{
+       g_main_loop_run(context->main_loop);
+
+       g_source_remove(context->source);
+       avdtp_unref(context->session);
+
+       g_main_loop_unref(context->main_loop);
+
+       test_free(context->data);
+       g_free(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)
+{
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct avdtp_media_codec_capability *codec_caps;
+       uint8_t cap[4] = { 0xff, 0xff, 2, 64 };
+
+       *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);
+
+       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 context *context = user_data;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-18-C")) {
+               *err = 0xc0;
+               return FALSE;
+       }
+
+       context->pending_open = TRUE;
+       context->stream = stream;
+
+       return TRUE;
+}
+
+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 context *context = user_data;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-09-C"))
+               return FALSE;
+
+       cb(session, stream, NULL);
+
+       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 context *context = user_data;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-21-C")) {
+               *err = 0xc0;
+               return FALSE;
+       }
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-25-C"))
+               context->pending_suspend = TRUE;
+
+       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 context *context = user_data;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-24-C")) {
+               *err = 0xc0;
+               return FALSE;
+       }
+
+       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 context *context = user_data;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-27-C")) {
+               *err = 0xc0;
+               return FALSE;
+       }
+
+       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 context *context = user_data;
+       int ret;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-07-C")) {
+               g_assert(err != NULL);
+               g_assert_cmpint(avdtp_error_error_code(err), ==, 0x13);
+               context_quit(context);
+               return;
+       }
+
+       g_assert(err == NULL);
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-11-C") ||
+               g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-10-C"))
+               ret = avdtp_get_configuration(session, stream);
+       else if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-23-C"))
+               ret = avdtp_abort(session, stream);
+       else
+               ret = avdtp_open(session, stream);
+
+       g_assert_cmpint(ret, ==, 0);
+}
+
+static void sep_getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                               struct avdtp_stream *stream,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct context *context = user_data;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-10-C")) {
+               g_assert(err != NULL);
+               g_assert_cmpint(avdtp_error_error_code(err), ==, 0x12);
+       } else
+               g_assert(err == NULL);
+
+       context_quit(context);
+}
+
+static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
+                       struct avdtp_stream *stream, struct avdtp_error *err,
+                       void *user_data)
+{
+       int ret;
+
+       g_assert(err == NULL);
+
+       g_assert(transport_open(stream));
+
+       ret = avdtp_start(session, stream);
+       g_assert_cmpint(ret, ==, 0);
+}
+
+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 context *context = user_data;
+       int ret;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-19-C") ||
+               g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-22-C")) {
+               g_assert(err != NULL);
+               g_assert_cmpint(avdtp_error_error_code(err), ==, 0x31);
+               context_quit(context);
+               return;
+       }
+
+       g_assert(err == NULL);
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-19-C"))
+               ret = avdtp_close(session, stream, FALSE);
+       else
+               ret = avdtp_suspend(session, stream);
+
+       g_assert_cmpint(ret, ==, 0);
+}
+
+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 context *context = user_data;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-25-C")) {
+               g_assert(err != NULL);
+               g_assert_cmpint(avdtp_error_error_code(err), ==, 0x31);
+               context_quit(context);
+       }
+}
+
+static struct avdtp_sep_cfm sep_cfm = {
+       .set_configuration      = sep_setconf_cfm,
+       .get_configuration      = sep_getconf_cfm,
+       .open                   = sep_open_cfm,
+       .start                  = sep_start_cfm,
+       .suspend                = sep_suspend_cfm,
+};
+
+static void test_server(gconstpointer data)
+{
+       struct context *context = create_context(0x0100, data);
+       struct avdtp_local_sep *sep;
+
+       sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
+                                       0x00, FALSE, &sep_ind, &sep_cfm,
+                                       context);
+
+       g_idle_add(send_pdu, context);
+
+       execute_context(context);
+
+       avdtp_unregister_sep(sep);
+}
+
+static void test_server_1_3(gconstpointer data)
+{
+       struct context *context = create_context(0x0103, data);
+       struct avdtp_local_sep *sep;
+
+       sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, 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);
+
+       g_idle_add(send_pdu, context);
+
+       execute_context(context);
+}
+
+static void discover_cb(struct avdtp *session, GSList *seps,
+                               struct avdtp_error *err, void *user_data)
+{
+       struct context *context = user_data;
+       struct avdtp_stream *stream;
+       struct avdtp_remote_sep *rsep;
+       struct avdtp_service_capability *media_transport, *media_codec;
+       struct avdtp_media_codec_capability *cap;
+       GSList *caps;
+       uint8_t data[4] = { 0x21, 0x02, 2, 32 };
+       int ret;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-05-C") ||
+               g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-07-C") ||
+               g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-25-C"))
+               return;
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-01-C")) {
+               g_assert(err != NULL);
+               g_assert_cmpint(avdtp_error_error_code(err), ==, 0x01);
+               context_quit(context);
+               return;
+       }
+
+       if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-04-C") ||
+               g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-32-C")) {
+               g_assert(err != NULL);
+               g_assert_cmpint(avdtp_error_error_code(err), ==, 0x11);
+               context_quit(context);
+               return;
+       }
+
+       g_assert(err == NULL);
+       g_assert_cmpint(g_slist_length(seps), !=, 0);
+
+       rsep = avdtp_find_remote_sep(session, context->sep);
+       g_assert(rsep != NULL);
+
+       media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT,
+                                               NULL, 0);
+
+       caps = g_slist_append(NULL, media_transport);
+
+       cap = g_malloc0(sizeof(*cap) + sizeof(data));
+       cap->media_type = AVDTP_MEDIA_TYPE_AUDIO;
+       cap->media_codec_type = 0x00;
+       memcpy(cap->data, data, sizeof(data));
+
+       media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap,
+                                               sizeof(*cap) + sizeof(data));
+
+       caps = g_slist_append(caps, media_codec);
+       g_free(cap);
+
+       ret = avdtp_set_configuration(session, rsep, context->sep, caps,
+                                                               &stream);
+       g_assert_cmpint(ret, ==, 0);
+
+       g_slist_free_full(caps, g_free);
+}
+
+static void test_client(gconstpointer data)
+{
+       struct context *context = create_context(0x0100, data);
+       struct avdtp_local_sep *sep;
+
+       sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+                                       0x00, FALSE, NULL, &sep_cfm,
+                                       context);
+       context->sep = sep;
+
+       avdtp_discover(context->session, discover_cb, context);
+
+       execute_context(context);
+
+       avdtp_unregister_sep(sep);
+}
+
+static void test_client_1_3(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, 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);
+
+       if (g_test_verbose())
+               __btd_log_init("*", 0);
+
+       /*
+        * Stream Management Service
+        *
+        * To verify that the following procedures are implemented according to
+        * their specification in AVDTP.
+        */
+       define_test("/TP/SIG/SMG/BV-05-C", test_client,
+                       raw_pdu(0x00, 0x01));
+       define_test("/TP/SIG/SMG/BV-06-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00));
+       define_test("/TP/SIG/SMG/BV-07-C", test_client,
+                       raw_pdu(0x10, 0x01),
+                       raw_pdu(0x12, 0x01, 0x04, 0x00),
+                       raw_pdu(0x20, 0x02, 0x04));
+       define_test("/TP/SIG/SMG/BV-08-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40));
+       define_test("/TP/SIG/SMG/BV-09-C", test_client,
+                       raw_pdu(0x30, 0x01),
+                       raw_pdu(0x32, 0x01, 0x04, 0x00),
+                       raw_pdu(0x40, 0x02, 0x04),
+                       raw_pdu(0x42, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
+       define_test("/TP/SIG/SMG/BV-10-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03));
+       define_test("/TP/SIG/SMG/BV-11-C", test_client,
+                       raw_pdu(0x60, 0x01),
+                       raw_pdu(0x62, 0x01, 0x04, 0x00),
+                       raw_pdu(0x70, 0x02, 0x04),
+                       raw_pdu(0x72, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x80, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x82, 0x03),
+                       raw_pdu(0x90, 0x04, 0x04));
+       define_test("/TP/SIG/SMG/BV-12-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x04, 0x04),
+                       raw_pdu(0x32, 0x04, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0x21, 0x02, 0x02, 0x20));
+       define_test("/TP/SIG/SMG/BV-15-C", test_client,
+                       raw_pdu(0xa0, 0x01),
+                       raw_pdu(0xa2, 0x01, 0x04, 0x00),
+                       raw_pdu(0xb0, 0x02, 0x04),
+                       raw_pdu(0xb2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0xc0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0xc2, 0x03),
+                       raw_pdu(0xd0, 0x06, 0x04));
+       define_test("/TP/SIG/SMG/BV-16-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x32, 0x06));
+       define_test("/TP/SIG/SMG/BV-17-C", test_client,
+                       raw_pdu(0xe0, 0x01),
+                       raw_pdu(0xe2, 0x01, 0x04, 0x00),
+                       raw_pdu(0xf0, 0x02, 0x04),
+                       raw_pdu(0xf2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x00, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x02, 0x03),
+                       raw_pdu(0x10, 0x06, 0x04),
+                       raw_pdu(0x12, 0x06),
+                       raw_pdu(0x20, 0x07, 0x04));
+       define_test("/TP/SIG/SMG/BV-18-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x32, 0x06),
+                       raw_pdu(0x40, 0x07, 0x04),
+                       raw_pdu(0x42, 0x07));
+       define_test("/TP/SIG/SMG/BV-19-C", test_client,
+                       raw_pdu(0x30, 0x01),
+                       raw_pdu(0x32, 0x01, 0x04, 0x00),
+                       raw_pdu(0x40, 0x02, 0x04),
+                       raw_pdu(0x42, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x52, 0x03),
+                       raw_pdu(0x60, 0x06, 0x04),
+                       raw_pdu(0x62, 0x06),
+                       raw_pdu(0x70, 0x07, 0x04),
+                       raw_pdu(0x72, 0x07),
+                       raw_pdu(0x80, 0x08, 0x04));
+       define_test("/TP/SIG/SMG/BV-20-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       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, 0x08, 0x04),
+                       raw_pdu(0x52, 0x08));
+       define_test("/TP/SIG/SMG/BV-21-C", test_client,
+                       raw_pdu(0x90, 0x01),
+                       raw_pdu(0x92, 0x01, 0x04, 0x00),
+                       raw_pdu(0xa0, 0x02, 0x04),
+                       raw_pdu(0xa2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0xb0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0xb2, 0x03),
+                       raw_pdu(0xc0, 0x06, 0x04),
+                       raw_pdu(0xc2, 0x06),
+                       raw_pdu(0xd0, 0x07, 0x04),
+                       raw_pdu(0xd2, 0x07),
+                       raw_pdu(0xe0, 0x09, 0x04));
+       define_test("/TP/SIG/SMG/BV-22-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       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, 0x09, 0x04),
+                       raw_pdu(0x52, 0x09));
+       define_test("/TP/SIG/SMG/BV-23-C", test_client,
+                       raw_pdu(0xf0, 0x01),
+                       raw_pdu(0xf2, 0x01, 0x04, 0x00),
+                       raw_pdu(0x00, 0x02, 0x04),
+                       raw_pdu(0x02, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x10, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x12, 0x03),
+                       raw_pdu(0x20, 0x0a, 0x04));
+       define_test("/TP/SIG/SMG/BV-24-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x0a, 0x04),
+                       raw_pdu(0x32, 0x0a));
+       define_test("/TP/SIG/SMG/BV-25-C", test_client_1_3,
+                       raw_pdu(0x30, 0x01),
+                       raw_pdu(0x32, 0x01, 0x04, 0x00),
+                       raw_pdu(0x40, 0x0c, 0x04));
+       define_test("/TP/SIG/SMG/BV-26-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));
+       define_test("/TP/SIG/SMG/BV-27-C", test_server_1_3,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40));
+       define_test("/TP/SIG/SMG/BV-28-C", test_client_1_3,
+                       raw_pdu(0x50, 0x01),
+                       raw_pdu(0x52, 0x01, 0x04, 0x00),
+                       raw_pdu(0x60, 0x0c, 0x04),
+                       raw_pdu(0x62, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40, 0x0f, 0x00),
+                       raw_pdu(0x70, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
+       define_test("/TP/SIG/SMG/BV-31-C", test_client_1_3,
+                       raw_pdu(0x80, 0x01),
+                       raw_pdu(0x82, 0x01, 0x04, 0x00),
+                       raw_pdu(0x90, 0x0c, 0x04),
+                       raw_pdu(0x92, 0x0c, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
+                               0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0xff, 0xff, 0x02, 0x40, 0x08, 0x00),
+                       raw_pdu(0xa0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+                               0x00));
+       define_test("/TP/SIG/SMG/BI-01-C", test_client,
+                       raw_pdu(0xb0, 0x01),
+                       raw_pdu(0xb3, 0x01, 0x01));
+       define_test("/TP/SIG/SMG/BI-02-C", test_server,
+                       raw_pdu(0x01, 0x01));
+       define_test("/TP/SIG/SMG/BI-03-C", test_server_0_sep,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x03, 0x01, 0x19));
+       define_test("/TP/SIG/SMG/BI-04-C", test_client,
+                       raw_pdu(0xc0, 0x01),
+                       raw_pdu(0xc2, 0x01, 0x04, 0x00),
+                       raw_pdu(0xd0, 0x02, 0x04),
+                       raw_pdu(0xd3, 0x02, 0x11));
+       define_test("/TP/SIG/SMG/BI-05-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02),
+                       raw_pdu(0x13, 0x02, 0x11));
+       define_test("/TP/SIG/SMG/BI-06-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x00),
+                       raw_pdu(0x13, 0x02, 0x12));
+       define_test("/TP/SIG/SMG/BI-07-C", test_client,
+                       raw_pdu(0xe0, 0x01),
+                       raw_pdu(0xe2, 0x01, 0x04, 0x00),
+                       raw_pdu(0xf0, 0x02, 0x04),
+                       raw_pdu(0xf2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x00, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x03, 0x03, 0x00, 0x13));
+       define_test("/TP/SIG/SMG/BI-08-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x33, 0x03, 0x00, 0x13));
+       define_test("/TP/SIG/SMG/BI-09-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x23, 0x03, 0x00, 0x29));
+       define_test("/TP/SIG/SMG/BI-10-C", test_client,
+                       raw_pdu(0x10, 0x01),
+                       raw_pdu(0x12, 0x01, 0x04, 0x00),
+                       raw_pdu(0x20, 0x02, 0x04),
+                       raw_pdu(0x22, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x30, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x32, 0x03),
+                       raw_pdu(0x40, 0x04, 0x04),
+                       raw_pdu(0x43, 0x04, 0x12));
+       define_test("/TP/SIG/SMG/BI-11-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x04, 0x00),
+                       raw_pdu(0x33, 0x04, 0x12));
+       define_test("/TP/SIG/SMG/BI-17-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x33, 0x06, 0x31));
+       define_test("/TP/SIG/SMG/BI-18-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x33, 0x06, 0xc0));
+       define_test("/TP/SIG/SMG/BI-19-C", test_client,
+                       raw_pdu(0x50, 0x01),
+                       raw_pdu(0x52, 0x01, 0x04, 0x00),
+                       raw_pdu(0x60, 0x02, 0x04),
+                       raw_pdu(0x62, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x70, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x72, 0x03),
+                       raw_pdu(0x80, 0x06, 0x04),
+                       raw_pdu(0x82, 0x06),
+                       raw_pdu(0x90, 0x07, 0x04),
+                       raw_pdu(0x93, 0x07, 0x04, 0x31));
+       define_test("/TP/SIG/SMG/BI-20-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x07, 0x04),
+                       raw_pdu(0x33, 0x07, 0x04, 0x31));
+       define_test("/TP/SIG/SMG/BI-21-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x32, 0x06),
+                       raw_pdu(0x40, 0x07, 0x04),
+                       raw_pdu(0x43, 0x07, 0x04, 0xc0));
+       define_test("/TP/SIG/SMG/BI-22-C", test_client,
+                       raw_pdu(0xa0, 0x01),
+                       raw_pdu(0xa2, 0x01, 0x04, 0x00),
+                       raw_pdu(0xb0, 0x02, 0x04),
+                       raw_pdu(0xb2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0xc0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0xc2, 0x03),
+                       raw_pdu(0xd0, 0x06, 0x04),
+                       raw_pdu(0xd2, 0x06),
+                       raw_pdu(0xe0, 0x07, 0x04),
+                       raw_pdu(0xe3, 0x07, 0x04, 0x31));
+       define_test("/TP/SIG/SMG/BI-23-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x32, 0x06),
+                       raw_pdu(0x40, 0x08, 0x00),
+                       raw_pdu(0x43, 0x08, 0x12));
+       define_test("/TP/SIG/SMG/BI-24-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x32, 0x06),
+                       raw_pdu(0x40, 0x08, 0x04),
+                       raw_pdu(0x43, 0x08, 0xc0));
+       define_test("/TP/SIG/SMG/BI-25-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x32, 0x06),
+                       raw_pdu(0x40, 0x07, 0x04),
+                       raw_pdu(0x42, 0x07),
+                       raw_pdu(0xf0, 0x09, 0x04),
+                       raw_pdu(0xf3, 0x09, 0x04, 0x31));
+       define_test("/TP/SIG/SMG/BI-26-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       raw_pdu(0x22, 0x03),
+                       raw_pdu(0x30, 0x06, 0x04),
+                       raw_pdu(0x32, 0x06),
+                       raw_pdu(0x40, 0x09, 0x04),
+                       raw_pdu(0x43, 0x09, 0x04, 0x31));
+       define_test("/TP/SIG/SMG/BI-27-C", test_server,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
+                               0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+                       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, 0x09, 0x04),
+                       raw_pdu(0x53, 0x09, 0x04, 0xc0));
+       define_test("/TP/SIG/SMG/BI-28-C", test_server,
+                       raw_pdu(0x00, 0xff),
+                       raw_pdu(0x01, 0x3f));
+       define_test("/TP/SIG/SMG/BI-30-C", test_client,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x02, 0x04),
+                       raw_pdu(0x12, 0x02, 0xee, 0x01, 0x00, 0x01, 0x00, 0x07,
+                               0x06, 0x00, 0x00, 0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
+       define_test("/TP/SIG/SMG/ESR04/BI-28-C", test_server,
+                       raw_pdu(0x00, 0x3f),
+                       raw_pdu(0x01, 0x3f));
+       define_test("/TP/SIG/SMG/BI-32-C", test_client_1_3,
+                       raw_pdu(0x30, 0x01),
+                       raw_pdu(0x32, 0x01, 0x04, 0x00),
+                       raw_pdu(0x40, 0x0c, 0x04),
+                       raw_pdu(0x43, 0x0c, 0x11));
+       define_test("/TP/SIG/SMG/BI-33-C", test_server_1_3,
+                       raw_pdu(0x00, 0x01),
+                       raw_pdu(0x02, 0x01, 0x04, 0x00),
+                       raw_pdu(0x10, 0x0c),
+                       raw_pdu(0x13, 0x0c, 0x11));
+       define_test("/TP/SIG/SMG/BI-35-C", test_client_1_3,
+                       raw_pdu(0x50, 0x01),
+                       raw_pdu(0x52, 0x01, 0x04, 0x00),
+                       raw_pdu(0x60, 0x0c, 0x04),
+                       raw_pdu(0x62, 0x0c, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
+                               0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0xff, 0xff, 0x02, 0x40, 0x08, 0x00),
+                       raw_pdu(0x70, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08,
+                               0x00));
+       define_test("/TP/SIG/SMG/BI-36-C", test_client_1_3,
+                       raw_pdu(0x80, 0x01),
+                       raw_pdu(0x82, 0x01, 0x04, 0x00),
+                       raw_pdu(0x90, 0x0c, 0x04),
+                       raw_pdu(0x92, 0x0c, 0xee, 0x01, 0x00, 0x01, 0x00, 0x07,
+                               0x06, 0x00, 0x00, 0xff, 0xff, 0x02, 0x40),
+                       raw_pdu(0xa0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
+                               0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
+
+       return g_test_run();
+}
index 1a6e1c9..6d9d554 100644 (file)
@@ -537,13 +537,11 @@ static void test_basic(void)
 {
        struct eir_data data;
        unsigned char buf[HCI_MAX_EIR_LENGTH];
-       int err;
 
        memset(buf, 0, sizeof(buf));
        memset(&data, 0, sizeof(data));
 
-       err = eir_parse(&data, buf, HCI_MAX_EIR_LENGTH);
-       g_assert(err == 0);
+       eir_parse(&data, buf, HCI_MAX_EIR_LENGTH);
        g_assert(data.services == NULL);
        g_assert(data.name == NULL);
 
@@ -554,12 +552,10 @@ static void test_parsing(gconstpointer data)
 {
        const struct test_data *test = data;
        struct eir_data eir;
-       int err;
 
        memset(&eir, 0, sizeof(eir));
 
-       err = eir_parse(&eir, test->eir_data, test->eir_size);
-       g_assert(err == 0);
+       eir_parse(&eir, test->eir_data, test->eir_size);
 
        if (g_test_verbose() == TRUE) {
                GSList *list;