summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--Makefile.am12
-rw-r--r--Makefile.in225
-rw-r--r--Makefile.plugins10
-rw-r--r--acinclude.m42
-rw-r--r--android/Android.mk3
-rw-r--r--android/Makefile.am9
-rw-r--r--android/README15
-rw-r--r--android/a2dp.c169
-rw-r--r--android/a2dp.h4
-rw-r--r--android/avdtp.c3269
-rw-r--r--android/avdtp.h275
-rw-r--r--android/bluetooth.c709
-rw-r--r--android/bluetooth.h2
-rw-r--r--android/client/if-sock.c15
-rw-r--r--android/hal-a2dp.c54
-rw-r--r--android/hal-bluetooth.c364
-rw-r--r--android/hal-hidhost.c89
-rw-r--r--android/hal-ipc.c134
-rw-r--r--android/hal-ipc.h10
-rw-r--r--android/hal-msg.h2
-rw-r--r--android/hal-pan.c53
-rw-r--r--android/hal-sock.c30
-rw-r--r--android/hal-utils.c6
-rw-r--r--android/hal.h4
-rw-r--r--android/hidhost.c367
-rw-r--r--android/hidhost.h4
-rw-r--r--android/ipc.c255
-rw-r--r--android/ipc.h17
-rw-r--r--android/main.c322
-rw-r--r--android/pan.c323
-rw-r--r--android/pan.h4
-rw-r--r--android/pics-did.txt42
-rw-r--r--android/pics-gap.txt714
-rw-r--r--android/pics-hid.txt290
-rw-r--r--android/pics-opp.txt189
-rw-r--r--android/pics-pan.txt151
-rw-r--r--android/pics-pbap.txt446
-rw-r--r--android/socket.c903
-rw-r--r--android/socket.h9
-rw-r--r--client/main.c28
-rwxr-xr-xconfigure45
-rw-r--r--configure.ac7
-rw-r--r--doc/device-api.txt1
-rw-r--r--emulator/btdev.c94
-rw-r--r--emulator/bthost.c71
-rw-r--r--emulator/bthost.h3
-rw-r--r--lib/bluetooth.c24
-rw-r--r--lib/bluetooth.h10
-rw-r--r--lib/hci.c1
-rw-r--r--monitor/bt.h232
-rw-r--r--monitor/l2cap.c50
-rw-r--r--monitor/ll.c2
-rw-r--r--monitor/packet.c514
-rw-r--r--plugins/autopair.c8
-rw-r--r--plugins/neard.c10
-rw-r--r--plugins/sixaxis.c434
-rw-r--r--plugins/wiimote.c2
-rw-r--r--profiles/audio/a2dp.c4
-rw-r--r--profiles/audio/avctp.c22
-rw-r--r--profiles/audio/avdtp.c74
-rw-r--r--profiles/audio/avdtp.h15
-rw-r--r--profiles/audio/sink.c5
-rw-r--r--profiles/audio/sink.h2
-rw-r--r--profiles/audio/source.c5
-rw-r--r--profiles/audio/source.h2
-rw-r--r--profiles/health/hdp.c10
-rw-r--r--profiles/health/hdp_util.c6
-rw-r--r--profiles/input/device.c11
-rw-r--r--profiles/input/manager.c4
-rw-r--r--profiles/input/server.c104
-rw-r--r--profiles/network/bnep.c (renamed from profiles/network/common.c)197
-rw-r--r--profiles/network/bnep.h (renamed from profiles/network/common.h)5
-rw-r--r--profiles/network/connection.c176
-rw-r--r--profiles/network/manager.c2
-rw-r--r--profiles/network/server.c9
-rw-r--r--profiles/sap/server.c3
-rw-r--r--src/adapter.c115
-rw-r--r--src/adapter.h6
-rw-r--r--src/agent.c9
-rw-r--r--src/attrib-server.c6
-rw-r--r--src/bluetooth.ver2
-rw-r--r--src/device.c93
-rw-r--r--src/device.h12
-rw-r--r--src/eir.c8
-rw-r--r--src/eir.h2
-rw-r--r--src/profile.c29
-rw-r--r--tools/l2cap-tester.c34
-rw-r--r--tools/l2test.c70
-rw-r--r--tools/obex-server-tool.c2
-rw-r--r--tools/sdptool.c12
-rw-r--r--tools/smp-tester.c573
-rw-r--r--unit/test-avdtp.c1090
-rw-r--r--unit/test-eir.c8
94 files changed, 11992 insertions, 1783 deletions
diff --git a/ChangeLog b/ChangeLog
index ca48336b..1db42c9d 100644
--- 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.
diff --git a/Makefile.am b/Makefile.am
index 328ccdbd..81a458b9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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
diff --git a/Makefile.in b/Makefile.in
index 9556969b..9dfabfc5 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -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
diff --git a/Makefile.plugins b/Makefile.plugins
index 7c5f71d3..6a1ddbf4 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -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
diff --git a/acinclude.m4 b/acinclude.m4
index 5bfa29d4..20658529 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -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)
])
diff --git a/android/Android.mk b/android/Android.mk
index 616a338b..549613c0 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -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)
diff --git a/android/Makefile.am b/android/Makefile.am
index 4e0c9ed0..df047629 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -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
diff --git a/android/README b/android/README
index 6c2c53f2..68c3e9fd 100644
--- a/android/README
+++ b/android/README
@@ -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.
diff --git a/android/a2dp.c b/android/a2dp.c
index 936c28eb..107cbb8b 100644
--- a/android/a2dp.c
+++ b/android/a2dp.c
@@ -43,11 +43,11 @@
#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;
diff --git a/android/a2dp.h b/android/a2dp.h
index 35316180..7e9b2f62 100644
--- a/android/a2dp.h
+++ b/android/a2dp.h
@@ -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
index 00000000..5ae3afc4
--- /dev/null
+++ b/android/avdtp.c
@@ -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
index 00000000..97608758
--- /dev/null
+++ b/android/avdtp.h
@@ -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);
diff --git a/android/bluetooth.c b/android/bluetooth.c
index 83f20e2e..a3145cb7 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -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);
}
diff --git a/android/bluetooth.h b/android/bluetooth.h
index 44b8e9e8..86872ee6 100644
--- a/android/bluetooth.h
+++ b/android/bluetooth.h
@@ -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);
diff --git a/android/client/if-sock.c b/android/client/if-sock.c
index 2cd06e87..5394a5da 100644
--- a/android/client/if-sock.c
+++ b/android/client/if-sock.c
@@ -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
};
diff --git a/android/hal-a2dp.c b/android/hal-a2dp.c
index e9fadb7a..c8989957 100644
--- a/android/hal-a2dp.c
+++ b/android/hal-a2dp.c
@@ -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 = {
diff --git a/android/hal-bluetooth.c b/android/hal-bluetooth.c
index 078d5376..7cac15c7 100644
--- a/android/hal-bluetooth.c
+++ b/android/hal-bluetooth.c
@@ -28,14 +28,26 @@
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 = {
diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c
index 2ce17a39..6a6b6825 100644
--- a/android/hal-hidhost.c
+++ b/android/hal-hidhost.c
@@ -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 = {
diff --git a/android/hal-ipc.c b/android/hal-ipc.c
index 026e2459..b19704aa 100644
--- a/android/hal-ipc.c
+++ b/android/hal-ipc.c
@@ -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;
}
diff --git a/android/hal-ipc.h b/android/hal-ipc.h
index ea53e1c3..2fbf30f9 100644
--- a/android/hal-ipc.h
+++ b/android/hal-ipc.h
@@ -15,8 +15,18 @@
*
*/
+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);
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 44fd5c84..80c4a255 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -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];
diff --git a/android/hal-pan.c b/android/hal-pan.c
index 2bc560e2..8c0f8d82 100644
--- a/android/hal-pan.c
+++ b/android/hal-pan.c
@@ -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 = {
diff --git a/android/hal-sock.c b/android/hal-sock.c
index b7bc88e9..f45be30e 100644
--- a/android/hal-sock.c
+++ b/android/hal-sock.c
@@ -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,
diff --git a/android/hal-utils.c b/android/hal-utils.c
index 4f44d989..e3c0c607 100644
--- a/android/hal-utils.c
+++ b/android/hal-utils.c
@@ -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]);
diff --git a/android/hal.h b/android/hal.h
index 72090fe5..b4754114 100644
--- a/android/hal.h
+++ b/android/hal.h
@@ -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);
diff --git a/android/hidhost.c b/android/hidhost.c
index f5a607c2..8bfdfeda 100644
--- a/android/hidhost.c
+++ b/android/hidhost.c
@@ -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);
}
diff --git a/android/hidhost.h b/android/hidhost.h
index 688086ae..ea144464 100644
--- a/android/hidhost.h
+++ b/android/hidhost.h
@@ -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);
diff --git a/android/ipc.c b/android/ipc.c
index 729f1577..6f940cdb 100644
--- a/android/ipc.c
+++ b/android/ipc.c
@@ -30,13 +30,225 @@
#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;
+}
diff --git a/android/ipc.h b/android/ipc.h
index cf0f3d67..6cd102b0 100644
--- a/android/ipc.h
+++ b/android/ipc.h
@@ -21,6 +21,19 @@
*
*/
-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);
diff --git a/android/main.c b/android/main.c
index a4f5e847..5210b4bc 100644
--- a/android/main.c
+++ b/android/main.c
@@ -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();
diff --git a/android/pan.c b/android/pan.c
index 46b3700d..6b098b23 100644
--- a/android/pan.c
+++ b/android/pan.c
@@ -29,72 +29,333 @@
#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);
}
diff --git a/android/pan.h b/android/pan.h
index 24303786..3178d88a 100644
--- a/android/pan.h
+++ b/android/pan.h
@@ -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
index 00000000..e8c914a5
--- /dev/null
+++ b/android/pics-did.txt
@@ -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
index 00000000..cd274e81
--- /dev/null
+++ b/android/pics-gap.txt
@@ -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
index 00000000..ba506266
--- /dev/null
+++ b/android/pics-hid.txt
@@ -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
index 00000000..11e61ff8
--- /dev/null
+++ b/android/pics-opp.txt
@@ -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
index 00000000..406b6a9b
--- /dev/null
+++ b/android/pics-pan.txt
@@ -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
index 00000000..2af5fd8d
--- /dev/null
+++ b/android/pics-pbap.txt
@@ -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.
diff --git a/android/socket.c b/android/socket.c
index c283c5f0..90208740 100644
--- a/android/socket.c
+++ b/android/socket.c
@@ -27,64 +27,919 @@
#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);
}
diff --git a/android/socket.h b/android/socket.h
index 7aa55746..5150b890 100644
--- a/android/socket.h
+++ b/android/socket.h
@@ -21,7 +21,14 @@
*
*/
+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);
diff --git a/client/main.c b/client/main.c
index 0dd15107..ebc85c68 100644
--- a/client/main.c
+++ b/client/main.c
@@ -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;
}
diff --git a/configure b/configure
index 76c63e75..7e58a43d 100755
--- 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\\"
diff --git a/configure.ac b/configure.ac
index 949846ef..18d0b554 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/doc/device-api.txt b/doc/device-api.txt
index 62017802..2d9fa3c9 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -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
diff --git a/emulator/btdev.c b/emulator/btdev.c
index 5f04bd17..d0dff74e 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -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;
diff --git a/emulator/bthost.c b/emulator/bthost.c
index 0c5836f6..10e7a057 100644
--- a/emulator/bthost.c
+++ b/emulator/bthost.c
@@ -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;
diff --git a/emulator/bthost.h b/emulator/bthost.h
index 32b10f9c..474ada91 100644
--- a/emulator/bthost.h
+++ b/emulator/bthost.h
@@ -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);
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 5598ccfd..17ee2c1d 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -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:
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index 012fde40..61c1f9ac 100644
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -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
diff --git a/lib/hci.c b/lib/hci.c
index 6e378367..005578a3 100644
--- 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 }
};
diff --git a/monitor/bt.h b/monitor/bt.h
index 2aec9504..849ed86f 100644
--- a/monitor/bt.h
+++ b/monitor/bt.h
@@ -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));
diff --git a/monitor/l2cap.c b/monitor/l2cap.c
index ad6e64b0..c2158829 100644
--- a/monitor/l2cap.c
+++ b/monitor/l2cap.c
@@ -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 },
{ },
};
diff --git a/monitor/ll.c b/monitor/ll.c
index e8b7a7a6..1a66572b 100644
--- a/monitor/ll.c
+++ b/monitor/ll.c
@@ -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 },
{ }
};
diff --git a/monitor/packet.c b/monitor/packet.c
index 0efa60bc..6ec7d9be 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -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 },
{ }
diff --git a/plugins/autopair.c b/plugins/autopair.c
index e6e5035e..8c98c12b 100644
--- a/plugins/autopair.c
+++ b/plugins/autopair.c
@@ -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;
diff --git a/plugins/neard.c b/plugins/neard.c
index ea91c4dd..35fdaeb5 100644
--- a/plugins/neard.c
+++ b/plugins/neard.c
@@ -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
index 00000000..45fa1705
--- /dev/null
+++ b/plugins/sixaxis.c
@@ -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)
diff --git a/plugins/wiimote.c b/plugins/wiimote.c
index 6cc21ee5..96a65695 100644
--- a/plugins/wiimote.c
+++ b/plugins/wiimote.c
@@ -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;
}
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 864cb188..29a15939 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -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,
diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index dac7a66c..6669ddc0 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -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,
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 93783509..f866b392 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -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)
{
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index 56065062..390c154e 100644
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -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);
diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c
index 400af061..4f39622e 100644
--- a/profiles/audio/sink.c
+++ b/profiles/audio/sink.c
@@ -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);
diff --git a/profiles/audio/sink.h b/profiles/audio/sink.h
index 904a33d4..93c62a20 100644
--- a/profiles/audio/sink.h
+++ b/profiles/audio/sink.h
@@ -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);
diff --git a/profiles/audio/source.c b/profiles/audio/source.c
index 24a43536..7b129b76 100644
--- a/profiles/audio/source.c
+++ b/profiles/audio/source.c
@@ -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);
diff --git a/profiles/audio/source.h b/profiles/audio/source.h
index c16fb4bc..a014c68d 100644
--- a/profiles/audio/source.h
+++ b/profiles/audio/source.h
@@ -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);
diff --git a/profiles/health/hdp.c b/profiles/health/hdp.c
index 7b4e7995..6203fa8a 100644
--- a/profiles/health/hdp.c
+++ b/profiles/health/hdp.c
@@ -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,
diff --git a/profiles/health/hdp_util.c b/profiles/health/hdp_util.c
index 34e4671d..7de87a85 100644
--- a/profiles/health/hdp_util.c
+++ b/profiles/health/hdp_util.c
@@ -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);
diff --git a/profiles/input/device.c b/profiles/input/device.c
index 65231612..62f6dbb2 100644
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -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;
diff --git a/profiles/input/manager.c b/profiles/input/manager.c
index 689ccddd..660043ee 100644
--- a/profiles/input/manager.c
+++ b/profiles/input/manager.c
@@ -46,13 +46,13 @@
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 = {
diff --git a/profiles/input/server.c b/profiles/input/server.c
index 21d4562b..f6f85a0d 100644
--- a/profiles/input/server.c
+++ b/profiles/input/server.c
@@ -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;
}
diff --git a/profiles/network/common.c b/profiles/network/bnep.c
index 0b291bdb..0a719a2e 100644
--- a/profiles/network/common.c
+++ b/profiles/network/bnep.c
@@ -43,9 +43,12 @@
#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;
diff --git a/profiles/network/common.h b/profiles/network/bnep.h
index 9a8caac0..9043e46f 100644
--- a/profiles/network/common.h
+++ b/profiles/network/bnep.h
@@ -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);
diff --git a/profiles/network/connection.c b/profiles/network/connection.c
index 960a1fe2..9aff3191 100644
--- a/profiles/network/connection.c
+++ b/profiles/network/connection.c
@@ -47,12 +47,10 @@
#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;
diff --git a/profiles/network/manager.c b/profiles/network/manager.c
index ab4224de..8ac2decb 100644
--- a/profiles/network/manager.c
+++ b/profiles/network/manager.c
@@ -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"
diff --git a/profiles/network/server.c b/profiles/network/server.c
index 7b784e54..b3aab118 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -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;
diff --git a/profiles/sap/server.c b/profiles/sap/server.c
index 63314a77..119862df 100644
--- a/profiles/sap/server.c
+++ b/profiles/sap/server.c
@@ -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,
diff --git a/src/adapter.c b/src/adapter.c
index d904a563..94801038 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -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);
diff --git a/src/adapter.h b/src/adapter.h
index 80c5f772..de5b07dc 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -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);
diff --git a/src/agent.c b/src/agent.c
index 7880ba6a..4c63cb97 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -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,
diff --git a/src/attrib-server.c b/src/attrib-server.c
index 2861a005..a6f1066e 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -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,
diff --git a/src/bluetooth.ver b/src/bluetooth.ver
index b71c70df..214fa8a6 100644
--- a/src/bluetooth.ver
+++ b/src/bluetooth.ver
@@ -5,6 +5,8 @@
info;
error;
debug;
+ baswap;
+ ba2str;
local:
*;
};
diff --git a/src/device.c b/src/device.c
index 82b66e65..18543ee2 100644
--- a/src/device.c
+++ b/src/device.c
@@ -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);
diff --git a/src/device.h b/src/device.h
index deec8d0e..3a33cb2b 100644
--- a/src/device.h
+++ b/src/device.h
@@ -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);
diff --git a/src/eir.c b/src/eir.c
index 084884eb..7745ff39 100644
--- 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;
}
diff --git a/src/eir.h b/src/eir.h
index 1b6242d7..411986e2 100644
--- 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,
diff --git a/src/profile.c b/src/profile.c
index d8cbe6a8..3c0d27c5 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -417,6 +417,11 @@
</sequence> \
</sequence> \
</attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
<attribute id=\"0x0009\"> \
<sequence> \
<sequence> \
@@ -458,6 +463,11 @@
</sequence> \
</sequence> \
</attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
<attribute id=\"0x0009\"> \
<sequence> \
<sequence> \
@@ -496,6 +506,11 @@
</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));
}
diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c
index acfd7c34..505ac794 100644
--- a/tools/l2cap-tester.c
+++ b/tools/l2cap-tester.c
@@ -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();
}
diff --git a/tools/l2test.c b/tools/l2test.c
index c5c87631..9a7c809a 100644
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -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);
diff --git a/tools/obex-server-tool.c b/tools/obex-server-tool.c
index e37c56fd..86c22713 100644
--- a/tools/obex-server-tool.c
+++ b/tools/obex-server-tool.c
@@ -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);
}
diff --git a/tools/sdptool.c b/tools/sdptool.c
index f985b1e8..b4b65ec3 100644
--- a/tools/sdptool.c
+++ b/tools/sdptool.c
@@ -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;
}
diff --git a/tools/smp-tester.c b/tools/smp-tester.c
index 46dc65a7..05d620a6 100644
--- a/tools/smp-tester.c
+++ b/tools/smp-tester.c
@@ -29,10 +29,12 @@
#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"
@@ -42,6 +44,40 @@
#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
index 00000000..fb555b80
--- /dev/null
+++ b/unit/test-avdtp.c
@@ -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();
+}
diff --git a/unit/test-eir.c b/unit/test-eir.c
index 1a6e1c99..6d9d554f 100644
--- a/unit/test-eir.c
+++ b/unit/test-eir.c
@@ -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;