+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.
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
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
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
@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 \
@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
$(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) \
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) \
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) \
@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
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 \
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) \
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)
$(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 \
$(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) \
$(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) \
$(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) \
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)
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 \
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)
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 \
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
@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
@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 \
-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
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@
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=; \
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) \
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)
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): \
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)
-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)
-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)
-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)
-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)
@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@
@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@
@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@
@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@
@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
@AMDEP_TRUE@@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
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
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
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
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)
])
hidhost.c \
socket.c \
ipc.c ipc.h \
+ avdtp.c \
a2dp.c \
pan.c \
../src/log.c \
../lib/hci.c \
../btio/btio.c \
../src/sdp-client.c \
+ ../profiles/network/bnep.c \
LOCAL_C_INCLUDES := \
$(call include-path-for, glib) \
sdp.h \
rfcomm.h \
sco.h \
+ bnep.h \
$(shell mkdir -p $(LOCAL_PATH)/../lib/bluetooth)
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@
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
============
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.
#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;
uint8_t state;
GIOChannel *io;
guint watch;
+ struct avdtp *session;
};
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);
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)
bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED);
- a2dp_device_free(dev);
-
return FALSE;
}
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;
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,
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);
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;
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;
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;
}
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;
*
*/
-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);
--- /dev/null
+/*
+ *
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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);
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 */
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)
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)
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)
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)
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)
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);
}
memcpy(&uuid128, tmp, sizeof(uuid_t));
break;
default:
+ sdp_list_free(svcclass, free);
continue;
}
/* 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);
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);
}
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,
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,
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)
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];
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;
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,
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)
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;
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,
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)
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)
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;
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 */
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;
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)];
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)
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;
return HAL_STATUS_FAILED;
}
- if (cur_disc != disc) {
+ if (cur_disc != disc && conn) {
if (!set_discoverable(disc ? 0x01 : 0x00, 0))
return HAL_STATUS_FAILED;
}
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,
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;
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,
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];
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;
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;
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)
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? */
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);
}
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);
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
memset(&msg, 0, sizeof(msg));
memset(&iv, 0, sizeof(iv));
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
iv.iov_base = &cs;
iv.iov_len = sizeof(cs);
if (argc == 3) {
*user = TYPE_ENUM(btsock_type_t);
*enum_func = enum_defines;
+ } else if (argc == 5) {
+ *user = (void *) uuids;
+ *enum_func = enum_strings;
}
}
} 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;
}
}
/* 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
};
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;
(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;
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)
{
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()
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 = {
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;
}
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)
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;
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 */
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 */
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;
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)
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;
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)
{
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;
fail:
hal_ipc_cleanup();
bt_hal_cbacks = NULL;
+
+ hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
+
return status;
}
hal_ipc_cleanup();
bt_hal_cbacks = NULL;
+
+ hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH);
}
static int get_adapter_properties(void)
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;
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)
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;
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 = {
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;
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;
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;
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;
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)
{
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)
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 = {
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)
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;
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;
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 */
}
}
- notification_dispatch(ev, fd);
+ handle_msg(buf, ret);
}
close(notif_sk);
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));
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;
}
*
*/
+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);
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];
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;
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;
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)
{
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()
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 = {
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);
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:
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,
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++) {
{
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]);
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);
static bdaddr_t adapter_addr;
-static int notification_sk = -1;
static GIOChannel *ctrl_io = NULL;
static GIOChannel *intr_io = NULL;
static GSList *devices = NULL;
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,
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,
}
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);
}
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);
}
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));
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);
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,
return;
failed:
+ bt_hid_notify_state(dev, state);
hid_device_free(dev);
}
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;
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);
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;
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;
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 */
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
* 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);
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);
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;
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
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
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;
}
}
-bool bt_hid_register(int sk, const bdaddr_t *addr)
+bool bt_hid_register(const bdaddr_t *addr)
{
GError *err = NULL;
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);
g_io_channel_unref(intr_io);
intr_io = NULL;
}
+
+ ipc_unregister(HAL_SERVICE_ID_HIDHOST);
}
*
*/
-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);
#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;
memset(&msg, 0, sizeof(msg));
memset(&m, 0, sizeof(m));
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
m.service_id = service_id;
m.opcode = opcode;
}
}
-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;
+}
*
*/
-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);
#include <unistd.h>
#include <sys/signalfd.h>
-#include <sys/socket.h>
-#include <sys/un.h>
#include <glib.h>
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:
/* 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)
{
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) {
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,
{ 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;
}
}
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;
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);
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();
#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;
}
{
DBG("");
- notification_sk = -1;
+ bnep_cleanup();
+
+ ipc_unregister(HAL_SERVICE_ID_PAN);
}
*
*/
-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);
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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.
#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);
}
*
*/
+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);
}
}
+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;
{ "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" },
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;
}
#! /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.
# 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=''
ANDROID_FALSE
ANDROID_TRUE
CONFIGDIR
+SIXAXIS_FALSE
+SIXAXIS_TRUE
EXPERIMENTAL_FALSE
EXPERIMENTAL_TRUE
DATAFILES_FALSE
with_systemduserunitdir
enable_datafiles
enable_experimental
+enable_sixaxis
enable_android
'
ac_precious_vars='build_alias
# 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]...
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
--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:
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.
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 $@
# Define the identity of the package.
PACKAGE='bluez'
- VERSION='5.11'
+ VERSION='5.12'
cat >>confdefs.h <<_ACEOF
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
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'
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
# 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
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\\"
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])
[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
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
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;
{
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;
dc.handle = cpu_to_le16(handle);
dc.reason = reason;
- remote = btdev->conn;
-
btdev->conn = NULL;
remote->conn = NULL;
}
}
+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)
{
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;
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;
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)
{
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);
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;
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;
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);
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:
#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)
uint16_t setting;
};
+#define BT_SNDMTU 12
+#define BT_RCVMTU 13
+
#define BT_VOICE_TRANSPARENT 0x0003
#define BT_VOICE_CVSD_16BIT 0x0060
{ "2.1", 0x04 },
{ "3.0", 0x05 },
{ "4.0", 0x06 },
+ { "4.1", 0x07 },
{ NULL }
};
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;
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));
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;
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;
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;
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;
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));
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;
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;
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 },
{ },
};
{ 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 },
{ }
};
#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
{ 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" },
{ }
};
{
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 {
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));
case 0x01:
str = "Enabled";
break;
- break;
default:
str = "Reserved";
break;
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;
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;
break;
}
break;
+ case 0x02:
+ str = "Enabled with AES-CCM";
+ break;
default:
str = "Reserved";
break;
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;
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";
{ 0, "Secure Simple Pairing (Host Support)" },
{ 1, "LE Supported (Host)" },
{ 2, "Simultaneous LE and BR/EDR (Host)" },
+ { 3, "Secure Connections (Host Support)" },
{ }
};
{ 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" },
{ }
};
{ 20, "Slave Page Response Timeout" },
{ 21, "Connectionless Slave Broadcast Channel Map Change" },
{ 22, "Inquiry Response Notification" },
+ { 23, "Authenticated Payload Timeout Expired" },
{ }
};
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" },
{ }
};
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));
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);
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;
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;
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;
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";
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;
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 },
{ 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",
{ 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",
{ 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",
{ 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" },
{ }
};
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)
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;
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" },
{ }
};
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",
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 },
{ }
}
break;
+ case 0x06: /* Imaging */
+ if (class & 0x80) { /* Printer */
+ if (attempt > 1)
+ return 0;
+ memcpy(pinbuf, "0000", 4);
+ return 4;
+ }
+ break;
}
return 0;
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,
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? */
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) {
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) {
--- /dev/null
+/*
+ *
+ * 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)
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;
}
DBG("path %s", path);
- return source_disconnect(service, FALSE);
+ return source_disconnect(service);
}
static int a2dp_sink_connect(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,
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;
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;
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);
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) {
struct avctp *session;
GError *err = NULL;
GIOChannel *io;
+ const bdaddr_t *src;
session = avctp_get_internal(device);
if (!session)
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,
int avctp_connect_browsing(struct avctp *session)
{
+ const bdaddr_t *src;
GError *err = NULL;
GIOChannel *io;
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,
avdtp_session_state_t state;
- /* True if the entire device is being disconnected */
- gboolean device_disconnect;
-
guint auth_id;
GIOChannel *io;
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);
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;
{
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,
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;
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,
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)
{
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;
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)
{
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);
GSList **caps);
struct avdtp_service_capability *avdtp_stream_get_codec(
struct avdtp_stream *stream);
-gboolean avdtp_stream_has_capability(struct avdtp_stream *stream,
- struct avdtp_service_capability *cap);
gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream,
GSList *caps);
struct avdtp_remote_sep *avdtp_stream_get_remote_sep(
struct 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);
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);
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);
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);
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);
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);
static gboolean update_adapter(struct hdp_adapter *hdp_adapter)
{
GError *err = NULL;
+ const bdaddr_t *src;
if (applications == NULL) {
release_adapter_instance(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,
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);
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);
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);
/* 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). */
/* 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)
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);
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;
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 = {
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;
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 */
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);
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;
}
#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 {
{ 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;
{
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) {
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;
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);
#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,
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)
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);
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);
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;
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,
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;
#include "device.h"
#include "profile.h"
#include "service.h"
-#include "common.h"
+#include "bnep.h"
#include "connection.h"
#include "server.h"
#include "error.h"
#include "sdpd.h"
-#include "common.h"
+#include "bnep.h"
#include "server.h"
#define NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1"
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)
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,
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;
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,
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;
if (!device)
return NULL;
- device_set_temporary(device, TRUE);
+ btd_device_set_temporary(device, TRUE);
adapter->devices = g_slist_append(adapter->devices, device);
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)
{
if (!adapter)
return NULL;
- device = adapter_find_device(adapter, addr);
+ device = btd_adapter_find_device(adapter, addr);
if (device)
return device;
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);
}
{ }
};
-struct adapter_keys {
- struct btd_adapter *adapter;
- GSList *keys;
-};
-
static int str2buf(const char *str, uint8_t *buf, size_t blen)
{
int i, dlen;
{
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;
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);
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);
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,
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);
}
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;
}
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 &&
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);
* 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);
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);
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;
/* 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,
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;
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;
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;
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;
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);
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. */
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);
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,
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;
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);
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;
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,
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)
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;
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);
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);
}
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);
}
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
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);
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);
* 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);
agent_cancel(agent);
dbus_message_unref(message);
dbus_error_free(&err);
+ agent_unref(agent);
return;
}
agent->request = NULL;
agent_request_free(req, TRUE);
+ agent_unref(agent);
}
static int agent_call_authorize_service(struct agent_request *req,
* 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",
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,
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)
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);
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,
info;
error;
debug;
+ baswap;
+ ba2str;
local:
*;
};
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);
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';
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);
{
struct btd_device *device = data;
- device_set_trusted(device, value);
+ btd_device_set_trusted(device, value);
g_dbus_pending_property_success(id);
}
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");
}
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;
}
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;
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;
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;
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);
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);
* 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,
{ }
};
-gboolean device_is_connected(struct btd_device *device)
+gboolean btd_device_is_connected(struct btd_device *device)
{
return device->connected;
}
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);
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);
return NULL;
}
- ba2str(adapter_get_address(device->adapter), srcaddr);
+ ba2str(btd_adapter_get_address(device->adapter), srcaddr);
ba2str(&device->bdaddr, dstaddr);
if (!filename)
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;
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)
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];
return FALSE;
}
-GSList *device_get_uuids(struct btd_device *device)
+GSList *btd_device_get_uuids(struct btd_device *device)
{
return device->uuids;
}
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) {
/* 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;
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,
{
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);
* 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,
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,
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;
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)
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;
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;
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);
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);
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);
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);
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);
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;
/* No EIR data to parse */
if (eir_data == NULL)
- return 0;
+ return;
while (len < eir_len - 1) {
uint8_t field_len = eir_data[0];
eir_data += field_len + 1;
}
-
- return 0;
}
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;
}
};
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,
</sequence> \
</sequence> \
</attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
<attribute id=\"0x0009\"> \
<sequence> \
<sequence> \
</sequence> \
</sequence> \
</attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
<attribute id=\"0x0009\"> \
<sequence> \
<sequence> \
</sequence> \
</sequence> \
</attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
<attribute id=\"0x0009\"> \
<sequence> \
<sequence> \
}
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)
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);
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,
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);
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));
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));
}
.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)
&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();
}
#endif
#include <stdio.h>
+#include <stdbool.h>
#include <errno.h>
#include <ctype.h>
#include <fcntl.h>
#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,
/* 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) */
}
}
+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;
}
/* 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;
}
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;
}
/* 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;
}
/* 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;
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;
}
/* 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) {
}
}
+ if (!psm) {
+ if (bdaddr_type == BDADDR_BREDR)
+ psm = BREDR_DEFAULT_PSM;
+ else
+ psm = LE_DEFAULT_PSM;
+ }
+
if (need_addr && !(argc - optind)) {
usage();
exit(1);
exit(EXIT_FAILURE);
}
- if (option_root && chdir(option_root) > 0) {
+ if (option_root && chdir(option_root) < 0) {
perror("chdir:");
exit(EXIT_FAILURE);
}
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);
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;
}
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;
}
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;
}
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
+#include <sys/socket.h>
#include <glib.h>
#include "lib/bluetooth.h"
+#include "lib/hci.h"
#include "lib/mgmt.h"
#include "monitor/bt.h"
#include "src/shared/mgmt.h"
#include "src/shared/hciemu.h"
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
+#ifndef AF_ALG
+#define AF_ALG 38
+#define PF_ALG AF_ALG
+
+#include <linux/types.h>
+
+struct sockaddr_alg {
+ __u16 salg_family;
+ __u8 salg_type[14];
+ __u32 salg_feat;
+ __u32 salg_mask;
+ __u8 salg_name[64];
+};
+
+struct af_alg_iv {
+ __u32 ivlen;
+ __u8 iv[0];
+};
+
+#define ALG_SET_KEY 1
+#define ALG_SET_IV 2
+#define ALG_SET_OP 3
+
+#define ALG_OP_DECRYPT 0
+#define ALG_OP_ENCRYPT 1
+
+#else
+#include <linux/if_alg.h>
+#endif
+
#define SMP_CID 0x0006
struct test_data {
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;
{
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");
data->io_id = 0;
}
+ if (data->alg_sk >= 0) {
+ close(data->alg_sk);
+ data->alg_sk = -1;
+ }
+
hciemu_unref(data->hciemu);
data->hciemu = NULL;
}
#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, \
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 */
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,
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) {
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 */
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,
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[])
--- /dev/null
+/*
+ *
+ * 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();
+}
{
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);
{
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;