diff options
94 files changed, 11992 insertions, 1783 deletions
@@ -1,3 +1,14 @@ +ver 5.12: + Fix issue with missing reply to DisconnectProfile. + Fix issue with icon property and class of device changes. + Fix issue with HID devices when SDP record is not available. + Fix issue with handling auto-pairing of printers. + Fix issue with agent authorization handling. + Add support for PS3 controller setup and pairing. + Add support for LE L2CAP CoC test capabilities. + Add support for AVDTP qualification test cases. + Add support for SMP cryptographic test cases. +s ver 5.11: Fix issue with connection attempt when not powered. Fix issue with assigning player to AVRCP target role. diff --git a/Makefile.am b/Makefile.am index 328ccdbd..81a458b9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -80,7 +80,7 @@ include_HEADERS += $(lib_headers) lib_LTLIBRARIES += lib/libbluetooth.la lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources) -lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:2:17 +lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:3:17 lib_libbluetooth_la_DEPENDENCIES = $(local_headers) endif @@ -249,6 +249,14 @@ unit_test_sdp_SOURCES = unit/test-sdp.c \ src/sdpd-service.c src/sdpd-request.c unit_test_sdp_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ +unit_tests += unit/test-avdtp + +unit_test_avdtp_SOURCES = unit/test-avdtp.c \ + src/shared/util.h src/shared/util.c \ + src/log.h src/log.c \ + android/avdtp.c android/avdtp.h +unit_test_avdtp_LDADD = @GLIB_LIBS@ + unit_tests += unit/test-gdbus-client unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c @@ -320,7 +328,7 @@ $(lib_libbluetooth_la_OBJECTS): $(local_headers) lib/bluetooth/%.h: lib/%.h $(AM_V_at)$(MKDIR_P) lib/bluetooth - $(AM_V_GEN)$(LN_S) -f $(abs_top_builddir)/$< $@ + $(AM_V_GEN)$(LN_S) -f "$(abs_top_builddir)"/$< $@ clean-local: $(RM) -r lib/bluetooth diff --git a/Makefile.in b/Makefile.in index 9556969b..9dfabfc5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -108,10 +108,11 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \ @EXPERIMENTAL_TRUE@ profiles/thermometer/thermometer.c \ @EXPERIMENTAL_TRUE@ profiles/heartrate/heartrate.c \ @EXPERIMENTAL_TRUE@ profiles/cyclingspeed/cyclingspeed.c -@MAINTAINER_MODE_TRUE@am__append_12 = plugins/external-dummy.la -@CLIENT_TRUE@am__append_13 = client/bluetoothctl -@MONITOR_TRUE@am__append_14 = monitor/btmon -@EXPERIMENTAL_TRUE@am__append_15 = emulator/btvirt emulator/b1ee \ +@SIXAXIS_TRUE@am__append_12 = plugins/sixaxis.la +@MAINTAINER_MODE_TRUE@am__append_13 = plugins/external-dummy.la +@CLIENT_TRUE@am__append_14 = client/bluetoothctl +@MONITOR_TRUE@am__append_15 = monitor/btmon +@EXPERIMENTAL_TRUE@am__append_16 = emulator/btvirt emulator/b1ee \ @EXPERIMENTAL_TRUE@ tools/mgmt-tester tools/gap-tester \ @EXPERIMENTAL_TRUE@ tools/l2cap-tester tools/sco-tester \ @EXPERIMENTAL_TRUE@ tools/smp-tester tools/bdaddr tools/avinfo \ @@ -121,41 +122,41 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \ @EXPERIMENTAL_TRUE@ tools/btinfo tools/btattach tools/btsnoop \ @EXPERIMENTAL_TRUE@ tools/btiotest tools/cltest \ @EXPERIMENTAL_TRUE@ tools/mpris-player -@TOOLS_TRUE@am__append_16 = tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \ +@TOOLS_TRUE@am__append_17 = tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \ @TOOLS_TRUE@ tools/rfcomm tools/rctest tools/l2test tools/l2ping \ @TOOLS_TRUE@ tools/sdptool tools/ciptool tools/bccmd -@TOOLS_TRUE@am__append_17 = tools/hciattach.1 tools/hciconfig.1 \ +@TOOLS_TRUE@am__append_18 = tools/hciattach.1 tools/hciconfig.1 \ @TOOLS_TRUE@ tools/hcitool.1 tools/hcidump.1 \ @TOOLS_TRUE@ tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \ @TOOLS_TRUE@ tools/sdptool.1 tools/ciptool.1 tools/bccmd.1 -@TOOLS_FALSE@am__append_18 = tools/hciattach.1 tools/hciconfig.1 \ +@TOOLS_FALSE@am__append_19 = tools/hciattach.1 tools/hciconfig.1 \ @TOOLS_FALSE@ tools/hcitool.1 tools/hcidump.1 \ @TOOLS_FALSE@ tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \ @TOOLS_FALSE@ tools/sdptool.1 tools/ciptool.1 tools/bccmd.1 @HID2HCI_TRUE@udev_PROGRAMS = tools/hid2hci$(EXEEXT) -@HID2HCI_TRUE@am__append_19 = tools/hid2hci.1 -@HID2HCI_FALSE@am__append_20 = tools/hid2hci.1 -@EXPERIMENTAL_TRUE@am__append_21 = tools/bdaddr.1 -@READLINE_TRUE@am__append_22 = attrib/gatttool \ +@HID2HCI_TRUE@am__append_20 = tools/hid2hci.1 +@HID2HCI_FALSE@am__append_21 = tools/hid2hci.1 +@EXPERIMENTAL_TRUE@am__append_22 = tools/bdaddr.1 +@READLINE_TRUE@am__append_23 = attrib/gatttool \ @READLINE_TRUE@ tools/obex-client-tool tools/obex-server-tool \ @READLINE_TRUE@ tools/bluetooth-player tools/obexctl -@EXPERIMENTAL_TRUE@am__append_23 = profiles/iap/iapd +@EXPERIMENTAL_TRUE@am__append_24 = profiles/iap/iapd @CUPS_TRUE@cups_PROGRAMS = profiles/cups/bluetooth$(EXEEXT) -@EXPERIMENTAL_TRUE@am__append_24 = pcsuite -@EXPERIMENTAL_TRUE@am__append_25 = obexd/plugins/pcsuite.c -@OBEX_TRUE@am__append_26 = irmc pbap -@OBEX_TRUE@am__append_27 = obexd/plugins/irmc.c obexd/plugins/pbap.c \ +@EXPERIMENTAL_TRUE@am__append_25 = pcsuite +@EXPERIMENTAL_TRUE@am__append_26 = obexd/plugins/pcsuite.c +@OBEX_TRUE@am__append_27 = irmc pbap +@OBEX_TRUE@am__append_28 = obexd/plugins/irmc.c obexd/plugins/pbap.c \ @OBEX_TRUE@ obexd/plugins/vcard.h obexd/plugins/vcard.c \ @OBEX_TRUE@ obexd/plugins/phonebook.h \ @OBEX_TRUE@ obexd/plugins/phonebook-dummy.c -@ANDROID_TRUE@am__append_28 = android/system-emulator \ +@ANDROID_TRUE@am__append_29 = android/system-emulator \ @ANDROID_TRUE@ android/bluetoothd android/haltest -@ANDROID_TRUE@am__append_29 = android/libhal-internal.la -@HID2HCI_TRUE@am__append_30 = $(rules_DATA) +@ANDROID_TRUE@am__append_30 = android/libhal-internal.la +@HID2HCI_TRUE@am__append_31 = $(rules_DATA) TESTS = $(am__EXEEXT_8) subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -286,6 +287,16 @@ plugins_external_dummy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(plugins_external_dummy_la_LDFLAGS) $(LDFLAGS) -o $@ @MAINTAINER_MODE_TRUE@am_plugins_external_dummy_la_rpath = -rpath \ @MAINTAINER_MODE_TRUE@ $(plugindir) +plugins_sixaxis_la_LIBADD = +am__plugins_sixaxis_la_SOURCES_DIST = plugins/sixaxis.c +@SIXAXIS_TRUE@am_plugins_sixaxis_la_OBJECTS = \ +@SIXAXIS_TRUE@ plugins/plugins_sixaxis_la-sixaxis.lo +plugins_sixaxis_la_OBJECTS = $(am_plugins_sixaxis_la_OBJECTS) +plugins_sixaxis_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(plugins_sixaxis_la_CFLAGS) $(CFLAGS) \ + $(plugins_sixaxis_la_LDFLAGS) $(LDFLAGS) -o $@ +@SIXAXIS_TRUE@am_plugins_sixaxis_la_rpath = -rpath $(plugindir) @CLIENT_TRUE@am__EXEEXT_1 = client/bluetoothctl$(EXEEXT) @MONITOR_TRUE@am__EXEEXT_2 = monitor/btmon$(EXEEXT) @TOOLS_TRUE@am__EXEEXT_3 = tools/hciattach$(EXEEXT) \ @@ -325,7 +336,7 @@ plugins_external_dummy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ am__EXEEXT_8 = unit/test-eir$(EXEEXT) unit/test-uuid$(EXEEXT) \ unit/test-textfile$(EXEEXT) unit/test-crc$(EXEEXT) \ unit/test-mgmt$(EXEEXT) unit/test-sdp$(EXEEXT) \ - unit/test-gdbus-client$(EXEEXT) \ + unit/test-avdtp$(EXEEXT) unit/test-gdbus-client$(EXEEXT) \ unit/test-gobex-header$(EXEEXT) \ unit/test-gobex-packet$(EXEEXT) unit/test-gobex$(EXEEXT) \ unit/test-gobex-transfer$(EXEEXT) \ @@ -339,9 +350,11 @@ am__android_bluetoothd_SOURCES_DIST = android/main.c src/log.c \ src/shared/util.h src/shared/util.c src/shared/mgmt.h \ src/shared/mgmt.c android/bluetooth.h android/bluetooth.c \ android/hidhost.h android/hidhost.c android/ipc.h \ - android/ipc.c android/a2dp.h android/a2dp.c android/socket.h \ - android/socket.c android/pan.h android/pan.c btio/btio.h \ - btio/btio.c src/sdp-client.h src/sdp-client.c + android/ipc.c android/avdtp.h android/avdtp.c android/a2dp.h \ + android/a2dp.c android/socket.h android/socket.c android/pan.h \ + android/pan.c btio/btio.h btio/btio.c src/sdp-client.h \ + src/sdp-client.c profiles/network/bnep.h \ + profiles/network/bnep.c @ANDROID_TRUE@am_android_bluetoothd_OBJECTS = android/main.$(OBJEXT) \ @ANDROID_TRUE@ src/log.$(OBJEXT) src/sdpd-database.$(OBJEXT) \ @ANDROID_TRUE@ src/sdpd-server.$(OBJEXT) \ @@ -352,9 +365,10 @@ am__android_bluetoothd_SOURCES_DIST = android/main.c src/log.c \ @ANDROID_TRUE@ src/shared/mgmt.$(OBJEXT) \ @ANDROID_TRUE@ android/bluetooth.$(OBJEXT) \ @ANDROID_TRUE@ android/hidhost.$(OBJEXT) android/ipc.$(OBJEXT) \ -@ANDROID_TRUE@ android/a2dp.$(OBJEXT) android/socket.$(OBJEXT) \ -@ANDROID_TRUE@ android/pan.$(OBJEXT) btio/btio.$(OBJEXT) \ -@ANDROID_TRUE@ src/sdp-client.$(OBJEXT) +@ANDROID_TRUE@ android/avdtp.$(OBJEXT) android/a2dp.$(OBJEXT) \ +@ANDROID_TRUE@ android/socket.$(OBJEXT) android/pan.$(OBJEXT) \ +@ANDROID_TRUE@ btio/btio.$(OBJEXT) src/sdp-client.$(OBJEXT) \ +@ANDROID_TRUE@ profiles/network/bnep.$(OBJEXT) android_bluetoothd_OBJECTS = $(am_android_bluetoothd_OBJECTS) @ANDROID_TRUE@android_bluetoothd_DEPENDENCIES = \ @ANDROID_TRUE@ lib/libbluetooth-internal.la @@ -583,8 +597,8 @@ am__src_bluetoothd_SOURCES_DIST = plugins/hostname.c plugins/wiimote.c \ profiles/audio/avctp.h profiles/audio/avctp.c \ profiles/audio/avrcp.h profiles/audio/avrcp.c \ profiles/audio/player.h profiles/audio/player.c \ - profiles/network/manager.c profiles/network/common.h \ - profiles/network/common.c profiles/network/server.h \ + profiles/network/manager.c profiles/network/bnep.h \ + profiles/network/bnep.c profiles/network/server.h \ profiles/network/server.c profiles/network/connection.h \ profiles/network/connection.c profiles/input/manager.c \ profiles/input/server.h profiles/input/server.c \ @@ -666,7 +680,7 @@ am__objects_14 = plugins/bluetoothd-hostname.$(OBJEXT) \ profiles/audio/bluetoothd-avrcp.$(OBJEXT) \ profiles/audio/bluetoothd-player.$(OBJEXT) \ profiles/network/bluetoothd-manager.$(OBJEXT) \ - profiles/network/bluetoothd-common.$(OBJEXT) \ + profiles/network/bluetoothd-bnep.$(OBJEXT) \ profiles/network/bluetoothd-server.$(OBJEXT) \ profiles/network/bluetoothd-connection.$(OBJEXT) \ profiles/input/bluetoothd-manager.$(OBJEXT) \ @@ -1010,6 +1024,11 @@ am__tools_smp_tester_SOURCES_DIST = tools/smp-tester.c monitor/bt.h \ tools_smp_tester_OBJECTS = $(am_tools_smp_tester_OBJECTS) @EXPERIMENTAL_TRUE@tools_smp_tester_DEPENDENCIES = \ @EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la +am_unit_test_avdtp_OBJECTS = unit/test-avdtp.$(OBJEXT) \ + src/shared/util.$(OBJEXT) src/log.$(OBJEXT) \ + android/avdtp.$(OBJEXT) +unit_test_avdtp_OBJECTS = $(am_unit_test_avdtp_OBJECTS) +unit_test_avdtp_DEPENDENCIES = am_unit_test_crc_OBJECTS = unit/test-crc.$(OBJEXT) \ monitor/crc.$(OBJEXT) unit_test_crc_OBJECTS = $(am_unit_test_crc_OBJECTS) @@ -1092,11 +1111,12 @@ SOURCES = $(profiles_sap_libsap_a_SOURCES) \ $(lib_libbluetooth_internal_la_SOURCES) \ $(lib_libbluetooth_la_SOURCES) \ $(plugins_external_dummy_la_SOURCES) \ - $(android_bluetoothd_SOURCES) $(android_haltest_SOURCES) \ - $(android_system_emulator_SOURCES) $(attrib_gatttool_SOURCES) \ - $(client_bluetoothctl_SOURCES) $(emulator_b1ee_SOURCES) \ - $(emulator_btvirt_SOURCES) $(monitor_btmon_SOURCES) \ - $(obexd_src_obexd_SOURCES) $(nodist_obexd_src_obexd_SOURCES) \ + $(plugins_sixaxis_la_SOURCES) $(android_bluetoothd_SOURCES) \ + $(android_haltest_SOURCES) $(android_system_emulator_SOURCES) \ + $(attrib_gatttool_SOURCES) $(client_bluetoothctl_SOURCES) \ + $(emulator_b1ee_SOURCES) $(emulator_btvirt_SOURCES) \ + $(monitor_btmon_SOURCES) $(obexd_src_obexd_SOURCES) \ + $(nodist_obexd_src_obexd_SOURCES) \ $(profiles_cups_bluetooth_SOURCES) \ $(profiles_iap_iapd_SOURCES) $(src_bluetoothd_SOURCES) \ $(nodist_src_bluetoothd_SOURCES) tools/amptest.c \ @@ -1116,9 +1136,10 @@ SOURCES = $(profiles_sap_libsap_a_SOURCES) \ $(tools_obex_server_tool_SOURCES) $(tools_obexctl_SOURCES) \ tools/rctest.c tools/rfcomm.c $(tools_sco_tester_SOURCES) \ tools/scotest.c $(tools_sdptool_SOURCES) \ - $(tools_smp_tester_SOURCES) $(unit_test_crc_SOURCES) \ - $(unit_test_eir_SOURCES) $(unit_test_gdbus_client_SOURCES) \ - $(unit_test_gobex_SOURCES) $(unit_test_gobex_apparam_SOURCES) \ + $(tools_smp_tester_SOURCES) $(unit_test_avdtp_SOURCES) \ + $(unit_test_crc_SOURCES) $(unit_test_eir_SOURCES) \ + $(unit_test_gdbus_client_SOURCES) $(unit_test_gobex_SOURCES) \ + $(unit_test_gobex_apparam_SOURCES) \ $(unit_test_gobex_header_SOURCES) \ $(unit_test_gobex_packet_SOURCES) \ $(unit_test_gobex_transfer_SOURCES) $(unit_test_lib_SOURCES) \ @@ -1130,6 +1151,7 @@ DIST_SOURCES = $(am__profiles_sap_libsap_a_SOURCES_DIST) \ $(lib_libbluetooth_internal_la_SOURCES) \ $(am__lib_libbluetooth_la_SOURCES_DIST) \ $(am__plugins_external_dummy_la_SOURCES_DIST) \ + $(am__plugins_sixaxis_la_SOURCES_DIST) \ $(am__android_bluetoothd_SOURCES_DIST) \ $(am__android_haltest_SOURCES_DIST) \ $(am__android_system_emulator_SOURCES_DIST) \ @@ -1164,7 +1186,8 @@ DIST_SOURCES = $(am__profiles_sap_libsap_a_SOURCES_DIST) \ $(am__tools_obexctl_SOURCES_DIST) tools/rctest.c \ tools/rfcomm.c $(am__tools_sco_tester_SOURCES_DIST) \ tools/scotest.c $(am__tools_sdptool_SOURCES_DIST) \ - $(am__tools_smp_tester_SOURCES_DIST) $(unit_test_crc_SOURCES) \ + $(am__tools_smp_tester_SOURCES_DIST) \ + $(unit_test_avdtp_SOURCES) $(unit_test_crc_SOURCES) \ $(unit_test_eir_SOURCES) $(unit_test_gdbus_client_SOURCES) \ $(unit_test_gobex_SOURCES) $(unit_test_gobex_apparam_SOURCES) \ $(unit_test_gobex_header_SOURCES) \ @@ -1363,25 +1386,27 @@ AM_MAKEFLAGS = --no-print-directory lib_LTLIBRARIES = $(am__append_2) noinst_LIBRARIES = $(am__append_7) noinst_LTLIBRARIES = lib/libbluetooth-internal.la \ - gdbus/libgdbus-internal.la $(am__append_29) -dist_man_MANS = $(am__append_17) $(am__append_19) + gdbus/libgdbus-internal.la $(am__append_30) +dist_man_MANS = $(am__append_18) $(am__append_20) dist_noinst_MANS = CLEANFILES = $(builtin_files) src/bluetooth.service \ obexd/src/builtin.h $(builtin_files) obexd/src/obex.service \ - $(am__append_30) + $(am__append_31) EXTRA_DIST = src/bluetooth.service.in src/org.bluez.service \ src/genbuiltin src/bluetooth.conf src/main.conf \ profiles/network/network.conf profiles/input/input.conf \ - profiles/proximity/proximity.conf $(am__append_18) \ - $(am__append_20) $(am__append_21) obexd/src/obex.service.in \ + profiles/proximity/proximity.conf $(am__append_19) \ + $(am__append_21) $(am__append_22) obexd/src/obex.service.in \ obexd/src/org.bluez.obex.service obexd/src/genbuiltin \ android/Android.mk android/hal-ipc-api.txt android/README \ - tools/hid2hci.rules $(test_scripts) doc/assigned-numbers.txt \ - doc/supported-features.txt doc/mgmt-api.txt \ - doc/adapter-api.txt doc/device-api.txt doc/agent-api.txt \ - doc/profile-api.txt doc/network-api.txt doc/media-api.txt \ - doc/health-api.txt doc/sap-api.txt doc/alert-api.txt \ - doc/proximity-api.txt doc/heartrate-api.txt \ + android/pics-gap.txt android/pics-hid.txt android/pics-pan.txt \ + android/pics-did.txt android/pics-opp.txt \ + android/pics-pbap.txt tools/hid2hci.rules $(test_scripts) \ + doc/assigned-numbers.txt doc/supported-features.txt \ + doc/mgmt-api.txt doc/adapter-api.txt doc/device-api.txt \ + doc/agent-api.txt doc/profile-api.txt doc/network-api.txt \ + doc/media-api.txt doc/health-api.txt doc/sap-api.txt \ + doc/alert-api.txt doc/proximity-api.txt doc/heartrate-api.txt \ doc/thermometer-api.txt doc/cyclingspeed-api.txt \ doc/obex-api.txt doc/obex-agent-api.txt tools/magic.btsnoop include_HEADERS = $(am__append_1) @@ -1401,7 +1426,7 @@ AM_LDFLAGS = $(MISC_LDFLAGS) plugindir = $(libdir)/bluetooth/plugins @MAINTAINER_MODE_FALSE@build_plugindir = $(plugindir) @MAINTAINER_MODE_TRUE@build_plugindir = $(abs_top_srcdir)/plugins/.libs -plugin_LTLIBRARIES = $(am__append_12) +plugin_LTLIBRARIES = $(am__append_12) $(am__append_13) lib_sources = lib/bluetooth.c lib/hci.c lib/sdp.c lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h \ lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h \ @@ -1412,7 +1437,7 @@ extra_sources = lib/uuid.c local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file))) BUILT_SOURCES = $(local_headers) src/builtin.h obexd/src/builtin.h @LIBRARY_TRUE@lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources) -@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:2:17 +@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:3:17 @LIBRARY_TRUE@lib_libbluetooth_la_DEPENDENCIES = $(local_headers) lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \ $(extra_headers) $(extra_sources) @@ -1450,8 +1475,8 @@ builtin_sources = plugins/hostname.c plugins/wiimote.c \ profiles/audio/avctp.h profiles/audio/avctp.c \ profiles/audio/avrcp.h profiles/audio/avrcp.c \ profiles/audio/player.h profiles/audio/player.c \ - profiles/network/manager.c profiles/network/common.h \ - profiles/network/common.c profiles/network/server.h \ + profiles/network/manager.c profiles/network/bnep.h \ + profiles/network/bnep.c profiles/network/server.h \ profiles/network/server.c profiles/network/connection.h \ profiles/network/connection.c profiles/input/manager.c \ profiles/input/server.h profiles/input/server.c \ @@ -1462,6 +1487,11 @@ builtin_sources = plugins/hostname.c plugins/wiimote.c \ profiles/deviceinfo/deviceinfo.c $(am__append_11) builtin_nodist = @EXPERIMENTAL_TRUE@profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c +@SIXAXIS_TRUE@plugins_sixaxis_la_SOURCES = plugins/sixaxis.c +@SIXAXIS_TRUE@plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ +@SIXAXIS_TRUE@ -no-undefined @UDEV_LIBS@ + +@SIXAXIS_TRUE@plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@ @MAINTAINER_MODE_TRUE@plugins_external_dummy_la_SOURCES = plugins/external-dummy.c @MAINTAINER_MODE_TRUE@plugins_external_dummy_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ @MAINTAINER_MODE_TRUE@ -no-undefined @@ -1723,12 +1753,12 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \ @SYSTEMD_TRUE@dbussessionbusdir = @DBUS_SESSIONBUSDIR@ @SYSTEMD_TRUE@dbussessionbus_DATA = obexd/src/org.bluez.obex.service obex_plugindir = $(libdir)/obex/plugins -obexd_builtin_modules = filesystem bluetooth $(am__append_24) opp ftp \ - $(am__append_26) mas mns +obexd_builtin_modules = filesystem bluetooth $(am__append_25) opp ftp \ + $(am__append_27) mas mns obexd_builtin_sources = obexd/plugins/filesystem.c \ obexd/plugins/filesystem.h obexd/plugins/bluetooth.c \ - $(am__append_25) obexd/plugins/opp.c obexd/plugins/ftp.c \ - obexd/plugins/ftp.h $(am__append_27) obexd/plugins/mas.c \ + $(am__append_26) obexd/plugins/opp.c obexd/plugins/ftp.c \ + obexd/plugins/ftp.h $(am__append_28) obexd/plugins/mas.c \ obexd/src/map_ap.h obexd/plugins/messages.h \ obexd/plugins/messages-dummy.c obexd/client/mns.c \ obexd/src/map_ap.h obexd/client/map-event.h @@ -1792,11 +1822,13 @@ nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files) @ANDROID_TRUE@ android/bluetooth.h android/bluetooth.c \ @ANDROID_TRUE@ android/hidhost.h android/hidhost.c \ @ANDROID_TRUE@ android/ipc.h android/ipc.c \ +@ANDROID_TRUE@ android/avdtp.h android/avdtp.c \ @ANDROID_TRUE@ android/a2dp.h android/a2dp.c \ @ANDROID_TRUE@ android/socket.h android/socket.c \ @ANDROID_TRUE@ android/pan.h android/pan.c \ @ANDROID_TRUE@ btio/btio.h btio/btio.c \ -@ANDROID_TRUE@ src/sdp-client.h src/sdp-client.c +@ANDROID_TRUE@ src/sdp-client.h src/sdp-client.c \ +@ANDROID_TRUE@ profiles/network/bnep.h profiles/network/bnep.c @ANDROID_TRUE@android_bluetoothd_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @ANDROID_TRUE@android_libhal_internal_la_SOURCES = android/hal.h android/hal-bluetooth.c \ @@ -1854,7 +1886,7 @@ AM_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \ -I$(srcdir)/gdbus -I$(srcdir)/btio unit_tests = unit/test-eir unit/test-uuid unit/test-textfile \ - unit/test-crc unit/test-mgmt unit/test-sdp \ + unit/test-crc unit/test-mgmt unit/test-sdp unit/test-avdtp \ unit/test-gdbus-client unit/test-gobex-header \ unit/test-gobex-packet unit/test-gobex \ unit/test-gobex-transfer unit/test-gobex-apparam unit/test-lib @@ -1877,6 +1909,12 @@ unit_test_sdp_SOURCES = unit/test-sdp.c \ src/sdpd-service.c src/sdpd-request.c unit_test_sdp_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ +unit_test_avdtp_SOURCES = unit/test-avdtp.c \ + src/shared/util.h src/shared/util.c \ + src/log.h src/log.c \ + android/avdtp.c android/avdtp.h + +unit_test_avdtp_LDADD = @GLIB_LIBS@ unit_test_gdbus_client_SOURCES = unit/test-gdbus-client.c unit_test_gdbus_client_LDADD = gdbus/libgdbus-internal.la \ @GLIB_LIBS@ @DBUS_LIBS@ @@ -2123,6 +2161,10 @@ plugins/plugins_external_dummy_la-external-dummy.lo: \ plugins/$(am__dirstamp) plugins/$(DEPDIR)/$(am__dirstamp) plugins/external-dummy.la: $(plugins_external_dummy_la_OBJECTS) $(plugins_external_dummy_la_DEPENDENCIES) $(EXTRA_plugins_external_dummy_la_DEPENDENCIES) plugins/$(am__dirstamp) $(AM_V_CCLD)$(plugins_external_dummy_la_LINK) $(am_plugins_external_dummy_la_rpath) $(plugins_external_dummy_la_OBJECTS) $(plugins_external_dummy_la_LIBADD) $(LIBS) +plugins/plugins_sixaxis_la-sixaxis.lo: plugins/$(am__dirstamp) \ + plugins/$(DEPDIR)/$(am__dirstamp) +plugins/sixaxis.la: $(plugins_sixaxis_la_OBJECTS) $(plugins_sixaxis_la_DEPENDENCIES) $(EXTRA_plugins_sixaxis_la_DEPENDENCIES) plugins/$(am__dirstamp) + $(AM_V_CCLD)$(plugins_sixaxis_la_LINK) $(am_plugins_sixaxis_la_rpath) $(plugins_sixaxis_la_OBJECTS) $(plugins_sixaxis_la_LIBADD) $(LIBS) install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ @@ -2352,6 +2394,8 @@ android/hidhost.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/ipc.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) +android/avdtp.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) android/a2dp.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) android/socket.$(OBJEXT): android/$(am__dirstamp) \ @@ -2368,6 +2412,14 @@ btio/btio.$(OBJEXT): btio/$(am__dirstamp) \ btio/$(DEPDIR)/$(am__dirstamp) src/sdp-client.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) +profiles/network/$(am__dirstamp): + @$(MKDIR_P) profiles/network + @: > profiles/network/$(am__dirstamp) +profiles/network/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) profiles/network/$(DEPDIR) + @: > profiles/network/$(DEPDIR)/$(am__dirstamp) +profiles/network/bnep.$(OBJEXT): profiles/network/$(am__dirstamp) \ + profiles/network/$(DEPDIR)/$(am__dirstamp) android/bluetoothd$(EXEEXT): $(android_bluetoothd_OBJECTS) $(android_bluetoothd_DEPENDENCIES) $(EXTRA_android_bluetoothd_DEPENDENCIES) android/$(am__dirstamp) @rm -f android/bluetoothd$(EXEEXT) $(AM_V_CCLD)$(LINK) $(android_bluetoothd_OBJECTS) $(android_bluetoothd_LDADD) $(LIBS) @@ -2725,16 +2777,10 @@ profiles/audio/bluetoothd-avrcp.$(OBJEXT): \ profiles/audio/bluetoothd-player.$(OBJEXT): \ profiles/audio/$(am__dirstamp) \ profiles/audio/$(DEPDIR)/$(am__dirstamp) -profiles/network/$(am__dirstamp): - @$(MKDIR_P) profiles/network - @: > profiles/network/$(am__dirstamp) -profiles/network/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) profiles/network/$(DEPDIR) - @: > profiles/network/$(DEPDIR)/$(am__dirstamp) profiles/network/bluetoothd-manager.$(OBJEXT): \ profiles/network/$(am__dirstamp) \ profiles/network/$(DEPDIR)/$(am__dirstamp) -profiles/network/bluetoothd-common.$(OBJEXT): \ +profiles/network/bluetoothd-bnep.$(OBJEXT): \ profiles/network/$(am__dirstamp) \ profiles/network/$(DEPDIR)/$(am__dirstamp) profiles/network/bluetoothd-server.$(OBJEXT): \ @@ -3241,6 +3287,11 @@ unit/$(am__dirstamp): unit/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) unit/$(DEPDIR) @: > unit/$(DEPDIR)/$(am__dirstamp) +unit/test-avdtp.$(OBJEXT): unit/$(am__dirstamp) \ + unit/$(DEPDIR)/$(am__dirstamp) +unit/test-avdtp$(EXEEXT): $(unit_test_avdtp_OBJECTS) $(unit_test_avdtp_DEPENDENCIES) $(EXTRA_unit_test_avdtp_DEPENDENCIES) unit/$(am__dirstamp) + @rm -f unit/test-avdtp$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(unit_test_avdtp_OBJECTS) $(unit_test_avdtp_LDADD) $(LIBS) unit/test-crc.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) unit/test-crc$(EXEEXT): $(unit_test_crc_OBJECTS) $(unit_test_crc_DEPENDENCIES) $(EXTRA_unit_test_crc_DEPENDENCIES) unit/$(am__dirstamp) @@ -3362,6 +3413,7 @@ mostlyclean-compile: -rm -f android/android_libhal_internal_la-hal-pan.lo -rm -f android/android_libhal_internal_la-hal-sock.$(OBJEXT) -rm -f android/android_libhal_internal_la-hal-sock.lo + -rm -f android/avdtp.$(OBJEXT) -rm -f android/bluetooth.$(OBJEXT) -rm -f android/client/android_haltest-haltest.$(OBJEXT) -rm -f android/client/android_haltest-history.$(OBJEXT) @@ -3491,6 +3543,8 @@ mostlyclean-compile: -rm -f plugins/bluetoothd-wiimote.$(OBJEXT) -rm -f plugins/plugins_external_dummy_la-external-dummy.$(OBJEXT) -rm -f plugins/plugins_external_dummy_la-external-dummy.lo + -rm -f plugins/plugins_sixaxis_la-sixaxis.$(OBJEXT) + -rm -f plugins/plugins_sixaxis_la-sixaxis.lo -rm -f profiles/alert/bluetoothd-server.$(OBJEXT) -rm -f profiles/audio/bluetoothd-a2dp.$(OBJEXT) -rm -f profiles/audio/bluetoothd-avctp.$(OBJEXT) @@ -3522,10 +3576,11 @@ mostlyclean-compile: -rm -f profiles/input/bluetoothd-manager.$(OBJEXT) -rm -f profiles/input/bluetoothd-server.$(OBJEXT) -rm -f profiles/input/bluetoothd-suspend-dummy.$(OBJEXT) - -rm -f profiles/network/bluetoothd-common.$(OBJEXT) + -rm -f profiles/network/bluetoothd-bnep.$(OBJEXT) -rm -f profiles/network/bluetoothd-connection.$(OBJEXT) -rm -f profiles/network/bluetoothd-manager.$(OBJEXT) -rm -f profiles/network/bluetoothd-server.$(OBJEXT) + -rm -f profiles/network/bnep.$(OBJEXT) -rm -f profiles/proximity/bluetoothd-immalert.$(OBJEXT) -rm -f profiles/proximity/bluetoothd-linkloss.$(OBJEXT) -rm -f profiles/proximity/bluetoothd-main.$(OBJEXT) @@ -3655,6 +3710,7 @@ mostlyclean-compile: -rm -f tools/sdptool.$(OBJEXT) -rm -f tools/smp-tester.$(OBJEXT) -rm -f tools/ubcsp.$(OBJEXT) + -rm -f unit/test-avdtp.$(OBJEXT) -rm -f unit/test-crc.$(OBJEXT) -rm -f unit/test-eir.$(OBJEXT) -rm -f unit/test-gdbus-client.$(OBJEXT) @@ -3681,6 +3737,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/avdtp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/hidhost.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/ipc.Po@am__quote@ @@ -3800,6 +3857,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-policy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/bluetoothd-wiimote.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/alert/$(DEPDIR)/bluetoothd-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-a2dp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/audio/$(DEPDIR)/bluetoothd-avctp.Po@am__quote@ @@ -3831,10 +3889,11 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-manager.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/input/$(DEPDIR)/bluetoothd-suspend-dummy.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-bnep.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-connection.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-manager.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bluetoothd-server.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@profiles/network/$(DEPDIR)/bnep.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-immalert.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-linkloss.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@profiles/proximity/$(DEPDIR)/bluetoothd-main.Po@am__quote@ @@ -3964,6 +4023,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/sdp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/smp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/tcpip.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-avdtp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-crc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-eir.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@unit/$(DEPDIR)/test-gdbus-client.Po@am__quote@ @@ -4052,6 +4112,13 @@ plugins/plugins_external_dummy_la-external-dummy.lo: plugins/external-dummy.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_external_dummy_la_CFLAGS) $(CFLAGS) -c -o plugins/plugins_external_dummy_la-external-dummy.lo `test -f 'plugins/external-dummy.c' || echo '$(srcdir)/'`plugins/external-dummy.c +plugins/plugins_sixaxis_la-sixaxis.lo: plugins/sixaxis.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_sixaxis_la_CFLAGS) $(CFLAGS) -MT plugins/plugins_sixaxis_la-sixaxis.lo -MD -MP -MF plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Tpo -c -o plugins/plugins_sixaxis_la-sixaxis.lo `test -f 'plugins/sixaxis.c' || echo '$(srcdir)/'`plugins/sixaxis.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Tpo plugins/$(DEPDIR)/plugins_sixaxis_la-sixaxis.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugins/sixaxis.c' object='plugins/plugins_sixaxis_la-sixaxis.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_sixaxis_la_CFLAGS) $(CFLAGS) -c -o plugins/plugins_sixaxis_la-sixaxis.lo `test -f 'plugins/sixaxis.c' || echo '$(srcdir)/'`plugins/sixaxis.c + android/client/android_haltest-haltest.o: android/client/haltest.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-haltest.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-haltest.Tpo -c -o android/client/android_haltest-haltest.o `test -f 'android/client/haltest.c' || echo '$(srcdir)/'`android/client/haltest.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-haltest.Tpo android/client/$(DEPDIR)/android_haltest-haltest.Po @@ -5116,19 +5183,19 @@ profiles/network/bluetoothd-manager.obj: profiles/network/manager.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-manager.obj `if test -f 'profiles/network/manager.c'; then $(CYGPATH_W) 'profiles/network/manager.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/manager.c'; fi` -profiles/network/bluetoothd-common.o: profiles/network/common.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-common.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-common.Tpo -c -o profiles/network/bluetoothd-common.o `test -f 'profiles/network/common.c' || echo '$(srcdir)/'`profiles/network/common.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-common.Tpo profiles/network/$(DEPDIR)/bluetoothd-common.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/common.c' object='profiles/network/bluetoothd-common.o' libtool=no @AMDEPBACKSLASH@ +profiles/network/bluetoothd-bnep.o: profiles/network/bnep.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-bnep.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo -c -o profiles/network/bluetoothd-bnep.o `test -f 'profiles/network/bnep.c' || echo '$(srcdir)/'`profiles/network/bnep.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo profiles/network/$(DEPDIR)/bluetoothd-bnep.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/bnep.c' object='profiles/network/bluetoothd-bnep.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-common.o `test -f 'profiles/network/common.c' || echo '$(srcdir)/'`profiles/network/common.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-bnep.o `test -f 'profiles/network/bnep.c' || echo '$(srcdir)/'`profiles/network/bnep.c -profiles/network/bluetoothd-common.obj: profiles/network/common.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-common.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-common.Tpo -c -o profiles/network/bluetoothd-common.obj `if test -f 'profiles/network/common.c'; then $(CYGPATH_W) 'profiles/network/common.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/common.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-common.Tpo profiles/network/$(DEPDIR)/bluetoothd-common.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/common.c' object='profiles/network/bluetoothd-common.obj' libtool=no @AMDEPBACKSLASH@ +profiles/network/bluetoothd-bnep.obj: profiles/network/bnep.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-bnep.obj -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo -c -o profiles/network/bluetoothd-bnep.obj `if test -f 'profiles/network/bnep.c'; then $(CYGPATH_W) 'profiles/network/bnep.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/bnep.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) profiles/network/$(DEPDIR)/bluetoothd-bnep.Tpo profiles/network/$(DEPDIR)/bluetoothd-bnep.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='profiles/network/bnep.c' object='profiles/network/bluetoothd-bnep.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-common.obj `if test -f 'profiles/network/common.c'; then $(CYGPATH_W) 'profiles/network/common.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/common.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -c -o profiles/network/bluetoothd-bnep.obj `if test -f 'profiles/network/bnep.c'; then $(CYGPATH_W) 'profiles/network/bnep.c'; else $(CYGPATH_W) '$(srcdir)/profiles/network/bnep.c'; fi` profiles/network/bluetoothd-server.o: profiles/network/server.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_bluetoothd_CFLAGS) $(CFLAGS) -MT profiles/network/bluetoothd-server.o -MD -MP -MF profiles/network/$(DEPDIR)/bluetoothd-server.Tpo -c -o profiles/network/bluetoothd-server.o `test -f 'profiles/network/server.c' || echo '$(srcdir)/'`profiles/network/server.c @@ -6835,7 +6902,7 @@ $(lib_libbluetooth_la_OBJECTS): $(local_headers) lib/bluetooth/%.h: lib/%.h $(AM_V_at)$(MKDIR_P) lib/bluetooth - $(AM_V_GEN)$(LN_S) -f $(abs_top_builddir)/$< $@ + $(AM_V_GEN)$(LN_S) -f "$(abs_top_builddir)"/$< $@ clean-local: $(RM) -r lib/bluetooth diff --git a/Makefile.plugins b/Makefile.plugins index 7c5f71d3..6a1ddbf4 100644 --- a/Makefile.plugins +++ b/Makefile.plugins @@ -47,7 +47,7 @@ builtin_sources += profiles/audio/control.h profiles/audio/control.c \ builtin_modules += network builtin_sources += profiles/network/manager.c \ - profiles/network/common.h profiles/network/common.c \ + profiles/network/bnep.h profiles/network/bnep.c \ profiles/network/server.h profiles/network/server.c \ profiles/network/connection.h \ profiles/network/connection.c @@ -110,3 +110,11 @@ builtin_sources += profiles/heartrate/heartrate.c builtin_modules += cyclingspeed builtin_sources += profiles/cyclingspeed/cyclingspeed.c endif + +if SIXAXIS +plugin_LTLIBRARIES += plugins/sixaxis.la +plugins_sixaxis_la_SOURCES = plugins/sixaxis.c +plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ + -no-undefined @UDEV_LIBS@ +plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@ +endif diff --git a/acinclude.m4 b/acinclude.m4 index 5bfa29d4..20658529 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -21,6 +21,8 @@ AC_DEFUN([COMPILER_FLAGS], [ with_cflags="$with_cflags -Wredundant-decls" with_cflags="$with_cflags -Wcast-align" with_cflags="$with_cflags -DG_DISABLE_DEPRECATED" + with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28" + with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28" fi AC_SUBST([WARNING_CFLAGS], $with_cflags) ]) diff --git a/android/Android.mk b/android/Android.mk index 616a338b..549613c0 100644 --- a/android/Android.mk +++ b/android/Android.mk @@ -25,6 +25,7 @@ LOCAL_SRC_FILES := \ hidhost.c \ socket.c \ ipc.c ipc.h \ + avdtp.c \ a2dp.c \ pan.c \ ../src/log.c \ @@ -41,6 +42,7 @@ LOCAL_SRC_FILES := \ ../lib/hci.c \ ../btio/btio.c \ ../src/sdp-client.c \ + ../profiles/network/bnep.c \ LOCAL_C_INCLUDES := \ $(call include-path-for, glib) \ @@ -65,6 +67,7 @@ lib_headers := \ sdp.h \ rfcomm.h \ sco.h \ + bnep.h \ $(shell mkdir -p $(LOCAL_PATH)/../lib/bluetooth) diff --git a/android/Makefile.am b/android/Makefile.am index 4e0c9ed0..df047629 100644 --- a/android/Makefile.am +++ b/android/Makefile.am @@ -19,11 +19,13 @@ android_bluetoothd_SOURCES = android/main.c \ android/bluetooth.h android/bluetooth.c \ android/hidhost.h android/hidhost.c \ android/ipc.h android/ipc.c \ + android/avdtp.h android/avdtp.c \ android/a2dp.h android/a2dp.c \ android/socket.h android/socket.c \ android/pan.h android/pan.c \ btio/btio.h btio/btio.c \ - src/sdp-client.h src/sdp-client.c + src/sdp-client.h src/sdp-client.c \ + profiles/network/bnep.h profiles/network/bnep.c android_bluetoothd_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @@ -83,4 +85,7 @@ android_haltest_LDFLAGS = -pthread endif -EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README +EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README \ + android/pics-gap.txt android/pics-hid.txt \ + android/pics-pan.txt android/pics-did.txt \ + android/pics-opp.txt android/pics-pbap.txt diff --git a/android/README b/android/README index 6c2c53f2..68c3e9fd 100644 --- a/android/README +++ b/android/README @@ -82,9 +82,12 @@ Testing tool ============ BT HAL test tools located in android/haltest is provided for HAL level testing -of both Android daemon and HAL library. Start it and type 'adapter init' in -prompt to initialize HAL library. On Android required bluetoothd service will -be started automatically. On Linux it is required to start android/bluetoothd -manually before init command timeout. To deinitialize HAL library and stop -daemon type 'adapter cleanup'. Type 'help' for more information. Tab completion -is also supported. +of both Android daemon and HAL library. Start it with '-n' parameter and type +'bluetooth init' in prompt to initialize HAL library. Running without parameter +will make haltest try to initialize all services after start. On Android +required bluetoothd service will be started automatically. On Linux it is +required to start android/bluetoothd manually before init command timeout or +use provided android/system-emulator, which takes care of launching daemon +automatically on HAL library initialization. To deinitialize HAL library and +stop daemon type 'bluetooth cleanup'. Type 'help' for more information. Tab +completion is also supported. diff --git a/android/a2dp.c b/android/a2dp.c index 936c28eb..107cbb8b 100644 --- a/android/a2dp.c +++ b/android/a2dp.c @@ -43,11 +43,11 @@ #include "ipc.h" #include "utils.h" #include "bluetooth.h" +#include "avdtp.h" #define L2CAP_PSM_AVDTP 0x19 #define SVC_HINT_CAPTURING 0x08 -static int notification_sk = -1; static GIOChannel *server = NULL; static GSList *devices = NULL; static bdaddr_t adapter_addr; @@ -58,6 +58,7 @@ struct a2dp_device { uint8_t state; GIOChannel *io; guint watch; + struct avdtp *session; }; static int device_cmp(gconstpointer s, gconstpointer user_data) @@ -70,6 +71,9 @@ static int device_cmp(gconstpointer s, gconstpointer user_data) static void a2dp_device_free(struct a2dp_device *dev) { + if (dev->session) + avdtp_unref(dev->session); + if (dev->watch > 0) g_source_remove(dev->watch); @@ -107,8 +111,13 @@ static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state) bdaddr2android(&dev->dst, ev.bdaddr); ev.state = state; - ipc_send(notification_sk, HAL_SERVICE_ID_A2DP, - HAL_EV_A2DP_CONN_STATE, sizeof(ev), &ev, -1); + ipc_send_notif(HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE, sizeof(ev), + &ev); + + if (state != HAL_A2DP_STATE_DISCONNECTED) + return; + + a2dp_device_free(dev); } static gboolean watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) @@ -117,8 +126,6 @@ static gboolean watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); - a2dp_device_free(dev); - return FALSE; } @@ -126,23 +133,42 @@ static void signaling_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct a2dp_device *dev = user_data; + uint16_t imtu, omtu; + GError *gerr = NULL; + int fd; if (err) { bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); error("%s", err->message); - a2dp_device_free(dev); return; } + bt_io_get(chan, &gerr, + BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_OMTU, &omtu, + BT_IO_OPT_INVALID); + if (gerr) { + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); + error("%s", gerr->message); + g_error_free(gerr); + return; + } + + /* FIXME: Add proper version */ + fd = g_io_channel_unix_get_fd(chan); + dev->session = avdtp_new(fd, imtu, omtu, 0x0100); + dev->watch = g_io_add_watch(dev->io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, watch_cb, dev); bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED); } -static uint8_t bt_a2dp_connect(struct hal_cmd_a2dp_connect *cmd, uint16_t len) +static void bt_a2dp_connect(const void *buf, uint16_t len) { + const struct hal_cmd_a2dp_connect *cmd = buf; struct a2dp_device *dev; + uint8_t status; char addr[18]; bdaddr_t dst; GSList *l; @@ -150,14 +176,13 @@ static uint8_t bt_a2dp_connect(struct hal_cmd_a2dp_connect *cmd, uint16_t len) DBG(""); - if (len < sizeof(*cmd)) - return HAL_STATUS_INVALID; - android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); - if (l) - return HAL_STATUS_FAILED; + if (l) { + status = HAL_STATUS_FAILED; + goto failed; + } dev = a2dp_device_new(&dst); dev->io = bt_io_connect(signaling_connect_cb, dev, NULL, &err, @@ -170,7 +195,8 @@ static uint8_t bt_a2dp_connect(struct hal_cmd_a2dp_connect *cmd, uint16_t len) error("%s", err->message); g_error_free(err); a2dp_device_free(dev); - return HAL_STATUS_FAILED; + status = HAL_STATUS_FAILED; + goto failed; } ba2str(&dev->dst, addr); @@ -178,26 +204,29 @@ static uint8_t bt_a2dp_connect(struct hal_cmd_a2dp_connect *cmd, uint16_t len) bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING); - return HAL_STATUS_SUCCESS; + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status); } -static uint8_t bt_a2dp_disconnect(struct hal_cmd_a2dp_connect *cmd, - uint16_t len) +static void bt_a2dp_disconnect(const void *buf, uint16_t len) { + const struct hal_cmd_a2dp_connect *cmd = buf; + uint8_t status; struct a2dp_device *dev; GSList *l; bdaddr_t dst; DBG(""); - if (len < sizeof(*cmd)) - return HAL_STATUS_INVALID; - android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); - if (!l) - return HAL_STATUS_FAILED; + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } dev = l->data; @@ -207,28 +236,19 @@ static uint8_t bt_a2dp_disconnect(struct hal_cmd_a2dp_connect *cmd, bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTING); - return HAL_STATUS_SUCCESS; -} - -void bt_a2dp_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len) -{ - uint8_t status = HAL_STATUS_FAILED; - - switch (opcode) { - case HAL_OP_A2DP_CONNECT: - status = bt_a2dp_connect(buf, len); - break; - case HAL_OP_A2DP_DISCONNECT: - status = bt_a2dp_disconnect(buf, len); - break; - default: - DBG("Unhandled command, opcode 0x%x", opcode); - break; - } + status = HAL_STATUS_SUCCESS; - ipc_send_rsp(sk, HAL_SERVICE_ID_A2DP, status); +failed: + ipc_send_rsp(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, status); } +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_A2DP_CONNECT */ + { bt_a2dp_connect, false, sizeof(struct hal_cmd_a2dp_connect) }, + /* HAL_OP_A2DP_DISCONNECT */ + { bt_a2dp_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) }, +}; + static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct a2dp_device *dev; @@ -280,52 +300,52 @@ static sdp_record_t *a2dp_record(void) return NULL; sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); + root = sdp_list_append(NULL, &root_uuid); sdp_set_browse_groups(record, root); sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID); - svclass_id = sdp_list_append(0, &a2dp_uuid); + svclass_id = sdp_list_append(NULL, &a2dp_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID); profile[0].version = a2dp_ver; - pfseq = sdp_list_append(0, &profile[0]); + pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); + proto[0] = sdp_list_append(NULL, &l2cap_uuid); psm = sdp_data_alloc(SDP_UINT16, &lp); proto[0] = sdp_list_append(proto[0], psm); - apseq = sdp_list_append(0, proto[0]); + apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID); - proto[1] = sdp_list_append(0, &avdtp_uuid); + proto[1] = sdp_list_append(NULL, &avdtp_uuid); version = sdp_data_alloc(SDP_UINT16, &avdtp_ver); proto[1] = sdp_list_append(proto[1], version); apseq = sdp_list_append(apseq, proto[1]); - aproto = sdp_list_append(0, apseq); + aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); features = sdp_data_alloc(SDP_UINT16, &feat); sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); - sdp_set_info_attr(record, "Audio Source", 0, 0); + sdp_set_info_attr(record, "Audio Source", NULL, NULL); - free(psm); - free(version); - sdp_list_free(proto[0], 0); - sdp_list_free(proto[1], 0); - sdp_list_free(apseq, 0); - sdp_list_free(pfseq, 0); - sdp_list_free(aproto, 0); - sdp_list_free(root, 0); - sdp_list_free(svclass_id, 0); + sdp_data_free(psm); + sdp_data_free(version); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); return record; } -bool bt_a2dp_register(int sk, const bdaddr_t *addr) +bool bt_a2dp_register(const bdaddr_t *addr) { GError *err = NULL; sdp_record_t *rec; @@ -346,27 +366,46 @@ bool bt_a2dp_register(int sk, const bdaddr_t *addr) } rec = a2dp_record(); + if (!rec) { + error("Failed to allocate A2DP record"); + goto fail; + } + if (bt_adapter_add_record(rec, SVC_HINT_CAPTURING) < 0) { - error("Failed to register on A2DP record"); + error("Failed to register A2DP record"); sdp_record_free(rec); - g_io_channel_shutdown(server, TRUE, NULL); - g_io_channel_unref(server); - server = NULL; - return false; + goto fail; } record_id = rec->handle; - notification_sk = sk; + ipc_register(HAL_SERVICE_ID_A2DP, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); return true; + +fail: + g_io_channel_shutdown(server, TRUE, NULL); + g_io_channel_unref(server); + server = NULL; + return false; +} + +static void a2dp_device_disconnected(gpointer data, gpointer user_data) +{ + struct a2dp_device *dev = data; + + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); } void bt_a2dp_unregister(void) { DBG(""); - notification_sk = -1; + g_slist_foreach(devices, a2dp_device_disconnected, NULL); + devices = NULL; + + ipc_unregister(HAL_SERVICE_ID_A2DP); bt_adapter_remove_record(record_id); record_id = 0; diff --git a/android/a2dp.h b/android/a2dp.h index 35316180..7e9b2f62 100644 --- a/android/a2dp.h +++ b/android/a2dp.h @@ -21,7 +21,5 @@ * */ -void bt_a2dp_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len); - -bool bt_a2dp_register(int sk, const bdaddr_t *addr); +bool bt_a2dp_register(const bdaddr_t *addr); void bt_a2dp_unregister(void); diff --git a/android/avdtp.c b/android/avdtp.c new file mode 100644 index 00000000..5ae3afc4 --- /dev/null +++ b/android/avdtp.c @@ -0,0 +1,3269 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <errno.h> +#include <unistd.h> +#include <assert.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include <glib.h> + +#include "log.h" +#include "avdtp.h" + +#define AVDTP_PSM 25 + +#define MAX_SEID 0x3E + +#ifndef MAX +# define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +#define AVDTP_DISCOVER 0x01 +#define AVDTP_GET_CAPABILITIES 0x02 +#define AVDTP_SET_CONFIGURATION 0x03 +#define AVDTP_GET_CONFIGURATION 0x04 +#define AVDTP_RECONFIGURE 0x05 +#define AVDTP_OPEN 0x06 +#define AVDTP_START 0x07 +#define AVDTP_CLOSE 0x08 +#define AVDTP_SUSPEND 0x09 +#define AVDTP_ABORT 0x0A +#define AVDTP_SECURITY_CONTROL 0x0B +#define AVDTP_GET_ALL_CAPABILITIES 0x0C +#define AVDTP_DELAY_REPORT 0x0D + +#define AVDTP_PKT_TYPE_SINGLE 0x00 +#define AVDTP_PKT_TYPE_START 0x01 +#define AVDTP_PKT_TYPE_CONTINUE 0x02 +#define AVDTP_PKT_TYPE_END 0x03 + +#define AVDTP_MSG_TYPE_COMMAND 0x00 +#define AVDTP_MSG_TYPE_GEN_REJECT 0x01 +#define AVDTP_MSG_TYPE_ACCEPT 0x02 +#define AVDTP_MSG_TYPE_REJECT 0x03 + +#define REQ_TIMEOUT 6 +#define ABORT_TIMEOUT 2 +#define DISCONNECT_TIMEOUT 1 +#define START_TIMEOUT 1 + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct avdtp_common_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; +} __attribute__ ((packed)); + +struct avdtp_single_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; + uint8_t signal_id:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +struct avdtp_start_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; + uint8_t no_of_packets; + uint8_t signal_id:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +struct avdtp_continue_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; +} __attribute__ ((packed)); + +struct seid_info { + uint8_t rfa0:1; + uint8_t inuse:1; + uint8_t seid:6; + uint8_t rfa2:3; + uint8_t type:1; + uint8_t media_type:4; +} __attribute__ ((packed)); + +struct seid { + uint8_t rfa0:2; + uint8_t seid:6; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avdtp_common_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; +} __attribute__ ((packed)); + +struct avdtp_single_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; + uint8_t rfa0:2; + uint8_t signal_id:6; +} __attribute__ ((packed)); + +struct avdtp_start_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; + uint8_t no_of_packets; + uint8_t rfa0:2; + uint8_t signal_id:6; +} __attribute__ ((packed)); + +struct avdtp_continue_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; +} __attribute__ ((packed)); + +struct seid_info { + uint8_t seid:6; + uint8_t inuse:1; + uint8_t rfa0:1; + uint8_t media_type:4; + uint8_t type:1; + uint8_t rfa2:3; +} __attribute__ ((packed)); + +struct seid { + uint8_t seid:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +/* packets */ + +struct discover_resp { + struct seid_info seps[0]; +} __attribute__ ((packed)); + +struct getcap_resp { + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct start_req { + struct seid first_seid; + struct seid other_seids[0]; +} __attribute__ ((packed)); + +struct suspend_req { + struct seid first_seid; + struct seid other_seids[0]; +} __attribute__ ((packed)); + +struct seid_rej { + uint8_t error; +} __attribute__ ((packed)); + +struct conf_rej { + uint8_t category; + uint8_t error; +} __attribute__ ((packed)); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct seid_req { + uint8_t rfa0:2; + uint8_t acp_seid:6; +} __attribute__ ((packed)); + +struct setconf_req { + uint8_t rfa0:2; + uint8_t acp_seid:6; + uint8_t rfa1:2; + uint8_t int_seid:6; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct stream_rej { + uint8_t rfa0:2; + uint8_t acp_seid:6; + uint8_t error; +} __attribute__ ((packed)); + +struct reconf_req { + uint8_t rfa0:2; + uint8_t acp_seid:6; + + uint8_t serv_cap; + uint8_t serv_cap_len; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct delay_req { + uint8_t rfa0:2; + uint8_t acp_seid:6; + uint16_t delay; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct seid_req { + uint8_t acp_seid:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +struct setconf_req { + uint8_t acp_seid:6; + uint8_t rfa0:2; + uint8_t int_seid:6; + uint8_t rfa1:2; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct stream_rej { + uint8_t acp_seid:6; + uint8_t rfa0:2; + uint8_t error; +} __attribute__ ((packed)); + +struct reconf_req { + uint8_t acp_seid:6; + uint8_t rfa0:2; + + uint8_t serv_cap; + uint8_t serv_cap_len; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct delay_req { + uint8_t acp_seid:6; + uint8_t rfa0:2; + uint16_t delay; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +struct in_buf { + gboolean active; + int no_of_packets; + uint8_t transaction; + uint8_t message_type; + uint8_t signal_id; + uint8_t buf[1024]; + uint8_t data_size; +}; + +struct pending_req { + uint8_t transaction; + uint8_t signal_id; + void *data; + size_t data_size; + struct avdtp_stream *stream; /* Set if the request targeted a stream */ + guint timeout; + gboolean collided; +}; + +struct avdtp_remote_sep { + uint8_t seid; + uint8_t type; + uint8_t media_type; + struct avdtp_service_capability *codec; + gboolean delay_reporting; + GSList *caps; /* of type struct avdtp_service_capability */ + struct avdtp_stream *stream; +}; + +struct avdtp_local_sep { + avdtp_state_t state; + struct avdtp_stream *stream; + struct seid_info info; + uint8_t codec; + gboolean delay_reporting; + GSList *caps; + struct avdtp_sep_ind *ind; + struct avdtp_sep_cfm *cfm; + void *user_data; +}; + +struct stream_callback { + avdtp_stream_state_cb cb; + void *user_data; + unsigned int id; +}; + +struct discover_callback { + unsigned int id; + avdtp_discover_cb_t cb; + void *user_data; +}; + +struct avdtp_stream { + GIOChannel *io; + uint16_t imtu; + uint16_t omtu; + struct avdtp *session; + struct avdtp_local_sep *lsep; + uint8_t rseid; + GSList *caps; + GSList *callbacks; + struct avdtp_service_capability *codec; + guint io_id; /* Transport GSource ID */ + guint timer; /* Waiting for other side to close or open + * the transport channel */ + gboolean open_acp; /* If we are in ACT role for Open */ + gboolean close_int; /* If we are in INT role for Close */ + gboolean abort_int; /* If we are in INT role for Abort */ + guint start_timer; /* Wait START command timer */ + gboolean delay_reporting; + uint16_t delay; /* AVDTP 1.3 Delay Reporting feature */ + gboolean starting; /* only valid while sep state == OPEN */ +}; + +/* Structure describing an AVDTP connection between two devices */ + +struct avdtp { + unsigned int ref; + + uint16_t version; + + struct avdtp_server *server; + + guint auth_id; + + GIOChannel *io; + guint io_id; + + GSList *seps; /* Elements of type struct avdtp_remote_sep * */ + + GSList *streams; /* Elements of type struct avdtp_stream * */ + + GSList *req_queue; /* Elements of type struct pending_req * */ + GSList *prio_queue; /* Same as req_queue but is processed before it */ + + struct avdtp_stream *pending_open; + + uint16_t imtu; + uint16_t omtu; + + struct in_buf in; + + char *buf; + + struct discover_callback *discover; + struct pending_req *req; +}; + +static GSList *lseps = NULL; + +static int send_request(struct avdtp *session, gboolean priority, + struct avdtp_stream *stream, uint8_t signal_id, + void *buffer, size_t size); +static gboolean avdtp_parse_resp(struct avdtp *session, + struct avdtp_stream *stream, + uint8_t transaction, uint8_t signal_id, + void *buf, int size); +static gboolean avdtp_parse_rej(struct avdtp *session, + struct avdtp_stream *stream, + uint8_t transaction, uint8_t signal_id, + void *buf, int size); +static int process_queue(struct avdtp *session); +static void avdtp_sep_set_state(struct avdtp *session, + struct avdtp_local_sep *sep, + avdtp_state_t state); + +static const char *avdtp_statestr(avdtp_state_t state) +{ + switch (state) { + case AVDTP_STATE_IDLE: + return "IDLE"; + case AVDTP_STATE_CONFIGURED: + return "CONFIGURED"; + case AVDTP_STATE_OPEN: + return "OPEN"; + case AVDTP_STATE_STREAMING: + return "STREAMING"; + case AVDTP_STATE_CLOSING: + return "CLOSING"; + case AVDTP_STATE_ABORTING: + return "ABORTING"; + default: + return "<unknown state>"; + } +} + +static gboolean try_send(int sk, void *data, size_t len) +{ + int err; + + do { + err = send(sk, data, len, 0); + } while (err < 0 && errno == EINTR); + + if (err < 0) { + error("send: %s (%d)", strerror(errno), errno); + return FALSE; + } else if ((size_t) err != len) { + error("try_send: complete buffer not sent (%d/%zu bytes)", + err, len); + return FALSE; + } + + return TRUE; +} + +static gboolean avdtp_send(struct avdtp *session, uint8_t transaction, + uint8_t message_type, uint8_t signal_id, + void *data, size_t len) +{ + unsigned int cont_fragments, sent; + struct avdtp_start_header start; + struct avdtp_continue_header cont; + int sock; + + if (session->io == NULL) { + error("avdtp_send: session is closed"); + return FALSE; + } + + sock = g_io_channel_unix_get_fd(session->io); + + /* Single packet - no fragmentation */ + if (sizeof(struct avdtp_single_header) + len <= session->omtu) { + struct avdtp_single_header single; + + memset(&single, 0, sizeof(single)); + + single.transaction = transaction; + single.packet_type = AVDTP_PKT_TYPE_SINGLE; + single.message_type = message_type; + single.signal_id = signal_id; + + memcpy(session->buf, &single, sizeof(single)); + memcpy(session->buf + sizeof(single), data, len); + + return try_send(sock, session->buf, sizeof(single) + len); + } + + /* Check if there is enough space to start packet */ + if (session->omtu < sizeof(start)) { + error("No enough space to fragment packet"); + return FALSE; + } + + /* Count the number of needed fragments */ + cont_fragments = (len - (session->omtu - sizeof(start))) / + (session->omtu - sizeof(cont)) + 1; + + DBG("%zu bytes split into %d fragments", len, cont_fragments + 1); + + /* Send the start packet */ + memset(&start, 0, sizeof(start)); + start.transaction = transaction; + start.packet_type = AVDTP_PKT_TYPE_START; + start.message_type = message_type; + start.no_of_packets = cont_fragments + 1; + start.signal_id = signal_id; + + memcpy(session->buf, &start, sizeof(start)); + memcpy(session->buf + sizeof(start), data, + session->omtu - sizeof(start)); + + if (!try_send(sock, session->buf, session->omtu)) + return FALSE; + + DBG("first packet with %zu bytes sent", session->omtu - sizeof(start)); + + sent = session->omtu - sizeof(start); + + /* Send the continue fragments and the end packet */ + while (sent < len) { + int left, to_copy; + + left = len - sent; + if (left + sizeof(cont) > session->omtu) { + cont.packet_type = AVDTP_PKT_TYPE_CONTINUE; + to_copy = session->omtu - sizeof(cont); + DBG("sending continue with %d bytes", to_copy); + } else { + cont.packet_type = AVDTP_PKT_TYPE_END; + to_copy = left; + DBG("sending end with %d bytes", to_copy); + } + + cont.transaction = transaction; + cont.message_type = message_type; + + memcpy(session->buf, &cont, sizeof(cont)); + memcpy(session->buf + sizeof(cont), data + sent, to_copy); + + if (!try_send(sock, session->buf, to_copy + sizeof(cont))) + return FALSE; + + sent += to_copy; + } + + return TRUE; +} + +static void pending_req_free(void *data) +{ + struct pending_req *req = data; + + if (req->timeout) + g_source_remove(req->timeout); + g_free(req->data); + g_free(req); +} + +static void close_stream(struct avdtp_stream *stream) +{ + int sock; + + if (stream->io == NULL) + return; + + sock = g_io_channel_unix_get_fd(stream->io); + + shutdown(sock, SHUT_RDWR); + + g_io_channel_shutdown(stream->io, FALSE, NULL); + + g_io_channel_unref(stream->io); + stream->io = NULL; +} + +static gboolean stream_close_timeout(gpointer user_data) +{ + struct avdtp_stream *stream = user_data; + + DBG("Timed out waiting for peer to close the transport channel"); + + stream->timer = 0; + + close_stream(stream); + + return FALSE; +} + +static gboolean stream_open_timeout(gpointer user_data) +{ + struct avdtp_stream *stream = user_data; + + DBG("Timed out waiting for peer to open the transport channel"); + + stream->timer = 0; + + stream->session->pending_open = NULL; + + avdtp_abort(stream->session, stream); + + return FALSE; +} + +void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id) +{ + err->category = category; + + if (category == AVDTP_ERRNO) + err->err.posix_errno = id; + else + err->err.error_code = id; +} + +uint8_t avdtp_error_category(struct avdtp_error *err) +{ + return err->category; +} + +int avdtp_error_error_code(struct avdtp_error *err) +{ + assert(err->category != AVDTP_ERRNO); + return err->err.error_code; +} + +int avdtp_error_posix_errno(struct avdtp_error *err) +{ + assert(err->category == AVDTP_ERRNO); + return err->err.posix_errno; +} + +static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session, + uint8_t rseid) +{ + GSList *l; + + for (l = session->streams; l != NULL; l = g_slist_next(l)) { + struct avdtp_stream *stream = l->data; + + if (stream->rseid == rseid) + return stream; + } + + return NULL; +} + +static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid) +{ + GSList *l; + + for (l = seps; l != NULL; l = g_slist_next(l)) { + struct avdtp_remote_sep *sep = l->data; + + if (sep->seid == seid) + return sep; + } + + return NULL; +} + +static void stream_free(void *data) +{ + struct avdtp_stream *stream = data; + struct avdtp_remote_sep *rsep; + + stream->lsep->info.inuse = 0; + stream->lsep->stream = NULL; + + rsep = find_remote_sep(stream->session->seps, stream->rseid); + if (rsep) + rsep->stream = NULL; + + if (stream->timer) + g_source_remove(stream->timer); + + if (stream->io) + close_stream(stream); + + if (stream->io_id) + g_source_remove(stream->io_id); + + g_slist_free_full(stream->callbacks, g_free); + g_slist_free_full(stream->caps, g_free); + + g_free(stream); +} + +static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avdtp_stream *stream = data; + struct avdtp_local_sep *sep = stream->lsep; + + if (stream->close_int && sep->cfm && sep->cfm->close) + sep->cfm->close(stream->session, sep, stream, NULL, + sep->user_data); + + if (!(cond & G_IO_NVAL)) + close_stream(stream); + + stream->io_id = 0; + + if (!stream->abort_int) + avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); + + return FALSE; +} + +static void handle_transport_connect(struct avdtp *session, GIOChannel *io, + uint16_t imtu, uint16_t omtu) +{ + struct avdtp_stream *stream = session->pending_open; + struct avdtp_local_sep *sep = stream->lsep; + + session->pending_open = NULL; + + if (stream->timer) { + g_source_remove(stream->timer); + stream->timer = 0; + } + + if (io == NULL) + return; + + if (stream->io == NULL) + stream->io = g_io_channel_ref(io); + + stream->omtu = omtu; + stream->imtu = imtu; + + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + + stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) transport_cb, stream); +} + +static int pending_req_cmp(gconstpointer a, gconstpointer b) +{ + const struct pending_req *req = a; + const struct avdtp_stream *stream = b; + + if (req->stream == stream) + return 0; + + return -1; +} + +static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream) +{ + GSList *l; + struct pending_req *req; + + while ((l = g_slist_find_custom(session->prio_queue, stream, + pending_req_cmp))) { + req = l->data; + pending_req_free(req); + session->prio_queue = g_slist_remove(session->prio_queue, req); + } + + while ((l = g_slist_find_custom(session->req_queue, stream, + pending_req_cmp))) { + req = l->data; + pending_req_free(req); + session->req_queue = g_slist_remove(session->req_queue, req); + } +} + +static void handle_unanswered_req(struct avdtp *session, + struct avdtp_stream *stream) +{ + struct pending_req *req; + struct avdtp_local_sep *lsep; + struct avdtp_error err; + + if (session->req->signal_id == AVDTP_ABORT) { + /* Avoid freeing the Abort request here */ + DBG("handle_unanswered_req: Abort req, returning"); + session->req->stream = NULL; + return; + } + + req = session->req; + session->req = NULL; + + avdtp_error_init(&err, AVDTP_ERRNO, EIO); + + lsep = stream->lsep; + + switch (req->signal_id) { + case AVDTP_RECONFIGURE: + error("No reply to Reconfigure request"); + if (lsep && lsep->cfm && lsep->cfm->reconfigure) + lsep->cfm->reconfigure(session, lsep, stream, &err, + lsep->user_data); + break; + case AVDTP_OPEN: + error("No reply to Open request"); + if (lsep && lsep->cfm && lsep->cfm->open) + lsep->cfm->open(session, lsep, stream, &err, + lsep->user_data); + break; + case AVDTP_START: + error("No reply to Start request"); + if (lsep && lsep->cfm && lsep->cfm->start) + lsep->cfm->start(session, lsep, stream, &err, + lsep->user_data); + break; + case AVDTP_SUSPEND: + error("No reply to Suspend request"); + if (lsep && lsep->cfm && lsep->cfm->suspend) + lsep->cfm->suspend(session, lsep, stream, &err, + lsep->user_data); + break; + case AVDTP_CLOSE: + error("No reply to Close request"); + if (lsep && lsep->cfm && lsep->cfm->close) + lsep->cfm->close(session, lsep, stream, &err, + lsep->user_data); + break; + case AVDTP_SET_CONFIGURATION: + error("No reply to SetConfiguration request"); + if (lsep && lsep->cfm && lsep->cfm->set_configuration) + lsep->cfm->set_configuration(session, lsep, stream, + &err, lsep->user_data); + } + + pending_req_free(req); +} + +static void avdtp_sep_set_state(struct avdtp *session, + struct avdtp_local_sep *sep, + avdtp_state_t state) +{ + struct avdtp_stream *stream = sep->stream; + avdtp_state_t old_state; + struct avdtp_error err, *err_ptr = NULL; + GSList *l; + + if (!stream) { + error("Error changing sep state: stream not available"); + return; + } + + if (sep->state == state) { + avdtp_error_init(&err, AVDTP_ERRNO, EIO); + DBG("stream state change failed: %s", avdtp_strerror(&err)); + err_ptr = &err; + } else { + err_ptr = NULL; + DBG("stream state changed: %s -> %s", + avdtp_statestr(sep->state), + avdtp_statestr(state)); + } + + old_state = sep->state; + sep->state = state; + + switch (state) { + case AVDTP_STATE_CONFIGURED: + if (sep->info.type == AVDTP_SEP_TYPE_SINK) + avdtp_delay_report(session, stream, stream->delay); + break; + case AVDTP_STATE_OPEN: + stream->starting = FALSE; + break; + case AVDTP_STATE_STREAMING: + if (stream->start_timer) { + g_source_remove(stream->start_timer); + stream->start_timer = 0; + } + stream->open_acp = FALSE; + break; + case AVDTP_STATE_CLOSING: + case AVDTP_STATE_ABORTING: + if (stream->start_timer) { + g_source_remove(stream->start_timer); + stream->start_timer = 0; + } + break; + case AVDTP_STATE_IDLE: + if (stream->start_timer) { + g_source_remove(stream->start_timer); + stream->start_timer = 0; + } + if (session->pending_open == stream) + handle_transport_connect(session, NULL, 0, 0); + if (session->req && session->req->stream == stream) + handle_unanswered_req(session, stream); + /* Remove pending commands for this stream from the queue */ + cleanup_queue(session, stream); + break; + default: + break; + } + + l = stream->callbacks; + while (l != NULL) { + struct stream_callback *cb = l->data; + l = g_slist_next(l); + cb->cb(stream, old_state, state, err_ptr, cb->user_data); + } + + if (state == AVDTP_STATE_IDLE && + g_slist_find(session->streams, stream)) { + session->streams = g_slist_remove(session->streams, stream); + stream_free(stream); + } +} + +static void finalize_discovery(struct avdtp *session, int err) +{ + struct discover_callback *discover = session->discover; + struct avdtp_error avdtp_err; + + if (!discover) + return; + + avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err); + + if (discover->id > 0) + g_source_remove(discover->id); + + discover->cb(session, session->seps, err ? &avdtp_err : NULL, + discover->user_data); + g_free(discover); + session->discover = NULL; +} + +static void release_stream(struct avdtp_stream *stream, struct avdtp *session) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (sep->cfm && sep->cfm->abort && + (sep->state != AVDTP_STATE_ABORTING || + stream->abort_int)) + sep->cfm->abort(session, sep, stream, NULL, sep->user_data); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); +} + +static void sep_free(gpointer data) +{ + struct avdtp_remote_sep *sep = data; + + g_slist_free_full(sep->caps, g_free); + g_free(sep); +} + +static void avdtp_free(void *data) +{ + struct avdtp *session = data; + + DBG("%p", session); + + g_slist_free_full(session->streams, stream_free); + + if (session->io) { + g_io_channel_shutdown(session->io, FALSE, NULL); + g_io_channel_unref(session->io); + } + + if (session->io_id) { + g_source_remove(session->io_id); + session->io_id = 0; + } + + if (session->req) + pending_req_free(session->req); + + g_slist_free_full(session->req_queue, pending_req_free); + g_slist_free_full(session->prio_queue, pending_req_free); + g_slist_free_full(session->seps, sep_free); + + g_free(session->buf); + + g_free(session); +} + +static void connection_lost(struct avdtp *session, int err) +{ + DBG("Disconnected: %s (%d)", strerror(err), err); + + g_slist_foreach(session->streams, (GFunc) release_stream, session); + session->streams = NULL; + + finalize_discovery(session, err); + + if (session->ref > 0) + return; + + avdtp_free(session); +} + +void avdtp_unref(struct avdtp *session) +{ + if (!session) + return; + + session->ref--; + + DBG("%p: ref=%d", session, session->ref); + + if (session->ref > 0) + return; + + finalize_discovery(session, ECONNABORTED); + + avdtp_free(session); +} + +struct avdtp *avdtp_ref(struct avdtp *session) +{ + session->ref++; + + DBG("%p: ref=%d", session, session->ref); + + return session; +} + +static struct avdtp_local_sep *find_local_sep_by_seid(uint8_t seid) +{ + GSList *l; + + for (l = lseps; l != NULL; l = g_slist_next(l)) { + struct avdtp_local_sep *sep = l->data; + + if (sep->info.seid == seid) + return sep; + } + + return NULL; +} + +struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session, + struct avdtp_local_sep *lsep) +{ + GSList *l; + + if (lsep->info.inuse) + return NULL; + + for (l = session->seps; l != NULL; l = g_slist_next(l)) { + struct avdtp_remote_sep *sep = l->data; + struct avdtp_service_capability *cap; + struct avdtp_media_codec_capability *codec_data; + + /* Type must be different: source <-> sink */ + if (sep->type == lsep->info.type) + continue; + + if (sep->media_type != lsep->info.media_type) + continue; + + if (!sep->codec) + continue; + + cap = sep->codec; + codec_data = (void *) cap->data; + + if (codec_data->media_codec_type != lsep->codec) + continue; + + if (sep->stream == NULL) + return sep; + } + + return NULL; +} + +static GSList *caps_to_list(uint8_t *data, int size, + struct avdtp_service_capability **codec, + gboolean *delay_reporting) +{ + GSList *caps; + int processed; + + if (delay_reporting) + *delay_reporting = FALSE; + + for (processed = 0, caps = NULL; processed + 2 <= size;) { + struct avdtp_service_capability *cap; + uint8_t length, category; + + category = data[0]; + length = data[1]; + + if (processed + 2 + length > size) { + error("Invalid capability data in getcap resp"); + break; + } + + cap = g_malloc(sizeof(struct avdtp_service_capability) + + length); + memcpy(cap, data, 2 + length); + + processed += 2 + length; + data += 2 + length; + + caps = g_slist_append(caps, cap); + + if (category == AVDTP_MEDIA_CODEC && + length >= + sizeof(struct avdtp_media_codec_capability)) + *codec = cap; + else if (category == AVDTP_DELAY_REPORTING && delay_reporting) + *delay_reporting = TRUE; + } + + return caps; +} + +static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction, + uint8_t signal_id) +{ + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT, + signal_id, NULL, 0); +} + +static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction, + void *buf, int size) +{ + GSList *l; + unsigned int rsp_size, sep_count, i; + struct seid_info *seps; + gboolean ret; + + sep_count = g_slist_length(lseps); + + if (sep_count == 0) { + uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND; + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_DISCOVER, &err, sizeof(err)); + } + + rsp_size = sep_count * sizeof(struct seid_info); + + seps = g_new0(struct seid_info, sep_count); + + for (l = lseps, i = 0; l != NULL; l = l->next, i++) { + struct avdtp_local_sep *sep = l->data; + + memcpy(&seps[i], &sep->info, sizeof(struct seid_info)); + } + + ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_DISCOVER, seps, rsp_size); + g_free(seps); + + return ret; +} + +static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, unsigned int size, + gboolean get_all) +{ + GSList *l, *caps; + struct avdtp_local_sep *sep = NULL; + unsigned int rsp_size; + uint8_t err, buf[1024], *ptr = buf; + uint8_t cmd; + + cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES; + + if (size < sizeof(struct seid_req)) { + err = AVDTP_BAD_LENGTH; + goto failed; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (!sep->ind->get_capability(session, sep, get_all, &caps, + &err, sep->user_data)) + goto failed; + + for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { + struct avdtp_service_capability *cap = l->data; + + if (rsp_size + cap->length + 2 > sizeof(buf)) + break; + + memcpy(ptr, cap, cap->length + 2); + rsp_size += cap->length + 2; + ptr += cap->length + 2; + + g_free(cap); + } + + g_slist_free(caps); + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd, + buf, rsp_size); + +failed: + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd, + &err, sizeof(err)); +} + +static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream, + struct avdtp_error *err) +{ + struct conf_rej rej; + struct avdtp_local_sep *sep; + + if (err != NULL) { + rej.error = AVDTP_UNSUPPORTED_CONFIGURATION; + rej.category = err->err.error_code; + avdtp_send(session, session->in.transaction, + AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, + &rej, sizeof(rej)); + return; + } + + if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_SET_CONFIGURATION, NULL, 0)) { + stream_free(stream); + return; + } + + sep = stream->lsep; + sep->stream = stream; + sep->info.inuse = 1; + session->streams = g_slist_append(session->streams, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); +} + +static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction, + struct setconf_req *req, unsigned int size) +{ + struct conf_rej rej; + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + uint8_t err, category = 0x00; + GSList *l; + + if (size < sizeof(struct setconf_req)) { + error("Too short getcap request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (sep->stream) { + err = AVDTP_SEP_IN_USE; + goto failed; + } + + stream = g_new0(struct avdtp_stream, 1); + stream->session = session; + stream->lsep = sep; + stream->rseid = req->int_seid; + stream->caps = caps_to_list(req->caps, + size - sizeof(struct setconf_req), + &stream->codec, + &stream->delay_reporting); + + /* Verify that the Media Transport capability's length = 0. Reject otherwise */ + for (l = stream->caps; l != NULL; l = g_slist_next(l)) { + struct avdtp_service_capability *cap = l->data; + + if (cap->category == AVDTP_MEDIA_TRANSPORT && cap->length != 0) { + err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT; + goto failed_stream; + } + } + + if (stream->delay_reporting && session->version < 0x0103) + session->version = 0x0103; + + if (sep->ind && sep->ind->set_configuration) { + if (!sep->ind->set_configuration(session, sep, stream, + stream->caps, + setconf_cb, + sep->user_data)) { + err = AVDTP_UNSUPPORTED_CONFIGURATION; + category = 0x00; + goto failed_stream; + } + } else { + if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_SET_CONFIGURATION, NULL, 0)) { + stream_free(stream); + return FALSE; + } + + sep->stream = stream; + sep->info.inuse = 1; + session->streams = g_slist_append(session->streams, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); + } + + return TRUE; + +failed_stream: + stream_free(stream); +failed: + rej.error = err; + rej.category = category; + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_SET_CONFIGURATION, &rej, sizeof(rej)); +} + +static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, int size) +{ + GSList *l; + struct avdtp_local_sep *sep = NULL; + int rsp_size; + uint8_t err; + uint8_t buf[1024]; + uint8_t *ptr = buf; + + if (size < (int) sizeof(struct seid_req)) { + error("Too short getconf request"); + return FALSE; + } + + memset(buf, 0, sizeof(buf)); + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + if (!sep->stream || !sep->stream->caps) { + err = AVDTP_UNSUPPORTED_CONFIGURATION; + goto failed; + } + + for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { + struct avdtp_service_capability *cap = l->data; + + if (rsp_size + cap->length + 2 > (int) sizeof(buf)) + break; + + memcpy(ptr, cap, cap->length + 2); + rsp_size += cap->length + 2; + ptr += cap->length + 2; + } + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_GET_CONFIGURATION, buf, rsp_size); + +failed: + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_GET_CONFIGURATION, &err, sizeof(err)); +} + +static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, int size) +{ + struct conf_rej rej; + + rej.error = AVDTP_NOT_SUPPORTED_COMMAND; + rej.category = 0x00; + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_RECONFIGURE, &rej, sizeof(rej)); +} + +static void check_seid_collision(struct pending_req *req, uint8_t id) +{ + struct seid_req *seid = req->data; + + if (seid->acp_seid == id) + req->collided = TRUE; +} + +static void check_start_collision(struct pending_req *req, uint8_t id) +{ + struct start_req *start = req->data; + struct seid *seid = &start->first_seid; + int count = 1 + req->data_size - sizeof(struct start_req); + int i; + + for (i = 0; i < count; i++, seid++) { + if (seid->seid == id) { + req->collided = TRUE; + return; + } + } +} + +static void check_suspend_collision(struct pending_req *req, uint8_t id) +{ + struct suspend_req *suspend = req->data; + struct seid *seid = &suspend->first_seid; + int count = 1 + req->data_size - sizeof(struct suspend_req); + int i; + + for (i = 0; i < count; i++, seid++) { + if (seid->seid == id) { + req->collided = TRUE; + return; + } + } +} + +static void avdtp_check_collision(struct avdtp *session, uint8_t cmd, + struct avdtp_stream *stream) +{ + struct pending_req *req = session->req; + + if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT)) + return; + + if (cmd == AVDTP_ABORT) + cmd = req->signal_id; + + switch (cmd) { + case AVDTP_OPEN: + case AVDTP_CLOSE: + check_seid_collision(req, stream->rseid); + break; + case AVDTP_START: + check_start_collision(req, stream->rseid); + break; + case AVDTP_SUSPEND: + check_suspend_collision(req, stream->rseid); + break; + } +} + +static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, unsigned int size) +{ + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + uint8_t err; + + if (size < sizeof(struct seid_req)) { + error("Too short abort request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (sep->state != AVDTP_STATE_CONFIGURED) { + err = AVDTP_BAD_STATE; + goto failed; + } + + stream = sep->stream; + + if (sep->ind && sep->ind->open) { + if (!sep->ind->open(session, sep, stream, &err, + sep->user_data)) + goto failed; + } + + avdtp_check_collision(session, AVDTP_OPEN, stream); + + if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_OPEN, NULL, 0)) + return FALSE; + + stream->open_acp = TRUE; + session->pending_open = stream; + stream->timer = g_timeout_add_seconds(REQ_TIMEOUT, + stream_open_timeout, + stream); + + return TRUE; + +failed: + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_OPEN, &err, sizeof(err)); +} + +static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction, + struct start_req *req, unsigned int size) +{ + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + struct stream_rej rej; + struct seid *seid; + uint8_t err, failed_seid; + int seid_count, i; + + if (size < sizeof(struct start_req)) { + error("Too short start request"); + return FALSE; + } + + seid_count = 1 + size - sizeof(struct start_req); + + seid = &req->first_seid; + + for (i = 0; i < seid_count; i++, seid++) { + failed_seid = seid->seid; + + sep = find_local_sep_by_seid(req->first_seid.seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + stream = sep->stream; + + /* Also reject start cmd if state is not open */ + if (sep->state != AVDTP_STATE_OPEN) { + err = AVDTP_BAD_STATE; + goto failed; + } + stream->starting = TRUE; + + if (sep->ind && sep->ind->start) { + if (!sep->ind->start(session, sep, stream, &err, + sep->user_data)) + goto failed; + } + + avdtp_check_collision(session, AVDTP_START, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); + } + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_START, NULL, 0); + +failed: + DBG("Rejecting (%d)", err); + memset(&rej, 0, sizeof(rej)); + rej.acp_seid = failed_seid; + rej.error = err; + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_START, &rej, sizeof(rej)); +} + +static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, unsigned int size) +{ + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + uint8_t err; + + if (size < sizeof(struct seid_req)) { + error("Too short close request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (sep->state != AVDTP_STATE_OPEN && + sep->state != AVDTP_STATE_STREAMING) { + err = AVDTP_BAD_STATE; + goto failed; + } + + stream = sep->stream; + + if (sep->ind && sep->ind->close) { + if (!sep->ind->close(session, sep, stream, &err, + sep->user_data)) + goto failed; + } + + avdtp_check_collision(session, AVDTP_CLOSE, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); + + if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_CLOSE, NULL, 0)) + return FALSE; + + stream->timer = g_timeout_add_seconds(REQ_TIMEOUT, + stream_close_timeout, + stream); + + return TRUE; + +failed: + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_CLOSE, &err, sizeof(err)); +} + +static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction, + struct suspend_req *req, unsigned int size) +{ + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + struct stream_rej rej; + struct seid *seid; + uint8_t err, failed_seid; + int seid_count, i; + + if (size < sizeof(struct suspend_req)) { + error("Too short suspend request"); + return FALSE; + } + + seid_count = 1 + size - sizeof(struct suspend_req); + + seid = &req->first_seid; + + for (i = 0; i < seid_count; i++, seid++) { + failed_seid = seid->seid; + + sep = find_local_sep_by_seid(req->first_seid.seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + stream = sep->stream; + + if (sep->state != AVDTP_STATE_STREAMING) { + err = AVDTP_BAD_STATE; + goto failed; + } + + if (sep->ind && sep->ind->suspend) { + if (!sep->ind->suspend(session, sep, stream, &err, + sep->user_data)) + goto failed; + } + + avdtp_check_collision(session, AVDTP_SUSPEND, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + } + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_SUSPEND, NULL, 0); + +failed: + memset(&rej, 0, sizeof(rej)); + rej.acp_seid = failed_seid; + rej.error = err; + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_SUSPEND, &rej, sizeof(rej)); +} + +static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, unsigned int size) +{ + struct avdtp_local_sep *sep; + uint8_t err; + gboolean ret; + + if (size < sizeof(struct seid_req)) { + error("Too short abort request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep || !sep->stream) + return TRUE; + + if (sep->ind && sep->ind->abort) + sep->ind->abort(session, sep, sep->stream, &err, + sep->user_data); + + avdtp_check_collision(session, AVDTP_ABORT, sep->stream); + + ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_ABORT, NULL, 0); + if (ret) + avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); + + return ret; +} + +static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, int size) +{ + return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL); +} + +static gboolean avdtp_delayreport_cmd(struct avdtp *session, + uint8_t transaction, + struct delay_req *req, + unsigned int size) +{ + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + uint8_t err; + + if (size < sizeof(struct delay_req)) { + error("Too short delay report request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + stream = sep->stream; + + if (sep->state != AVDTP_STATE_CONFIGURED && + sep->state != AVDTP_STATE_STREAMING) { + err = AVDTP_BAD_STATE; + goto failed; + } + + stream->delay = ntohs(req->delay); + + if (sep->ind && sep->ind->delayreport) { + if (!sep->ind->delayreport(session, sep, stream->rseid, + stream->delay, &err, + sep->user_data)) + goto failed; + } + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_DELAY_REPORT, NULL, 0); + +failed: + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_DELAY_REPORT, &err, sizeof(err)); +} + +static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction, + uint8_t signal_id, void *buf, int size) +{ + switch (signal_id) { + case AVDTP_DISCOVER: + DBG("Received DISCOVER_CMD"); + return avdtp_discover_cmd(session, transaction, buf, size); + case AVDTP_GET_CAPABILITIES: + DBG("Received GET_CAPABILITIES_CMD"); + return avdtp_getcap_cmd(session, transaction, buf, size, + FALSE); + case AVDTP_GET_ALL_CAPABILITIES: + DBG("Received GET_ALL_CAPABILITIES_CMD"); + return avdtp_getcap_cmd(session, transaction, buf, size, TRUE); + case AVDTP_SET_CONFIGURATION: + DBG("Received SET_CONFIGURATION_CMD"); + return avdtp_setconf_cmd(session, transaction, buf, size); + case AVDTP_GET_CONFIGURATION: + DBG("Received GET_CONFIGURATION_CMD"); + return avdtp_getconf_cmd(session, transaction, buf, size); + case AVDTP_RECONFIGURE: + DBG("Received RECONFIGURE_CMD"); + return avdtp_reconf_cmd(session, transaction, buf, size); + case AVDTP_OPEN: + DBG("Received OPEN_CMD"); + return avdtp_open_cmd(session, transaction, buf, size); + case AVDTP_START: + DBG("Received START_CMD"); + return avdtp_start_cmd(session, transaction, buf, size); + case AVDTP_CLOSE: + DBG("Received CLOSE_CMD"); + return avdtp_close_cmd(session, transaction, buf, size); + case AVDTP_SUSPEND: + DBG("Received SUSPEND_CMD"); + return avdtp_suspend_cmd(session, transaction, buf, size); + case AVDTP_ABORT: + DBG("Received ABORT_CMD"); + return avdtp_abort_cmd(session, transaction, buf, size); + case AVDTP_SECURITY_CONTROL: + DBG("Received SECURITY_CONTROL_CMD"); + return avdtp_secctl_cmd(session, transaction, buf, size); + case AVDTP_DELAY_REPORT: + DBG("Received DELAY_REPORT_CMD"); + return avdtp_delayreport_cmd(session, transaction, buf, size); + default: + DBG("Received unknown request id %u", signal_id); + return avdtp_unknown_cmd(session, transaction, signal_id); + } +} + +enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS }; + +static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session, + void *buf, size_t size) +{ + struct avdtp_common_header *header = buf; + struct avdtp_single_header *single = (void *) session->buf; + struct avdtp_start_header *start = (void *) session->buf; + void *payload; + gsize payload_size; + + switch (header->packet_type) { + case AVDTP_PKT_TYPE_SINGLE: + if (size < sizeof(*single)) { + error("Received too small single packet (%zu bytes)", size); + return PARSE_ERROR; + } + if (session->in.active) { + error("SINGLE: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + + payload = session->buf + sizeof(*single); + payload_size = size - sizeof(*single); + + session->in.active = TRUE; + session->in.data_size = 0; + session->in.no_of_packets = 1; + session->in.transaction = header->transaction; + session->in.message_type = header->message_type; + session->in.signal_id = single->signal_id; + + break; + case AVDTP_PKT_TYPE_START: + if (size < sizeof(*start)) { + error("Received too small start packet (%zu bytes)", size); + return PARSE_ERROR; + } + if (session->in.active) { + error("START: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + + session->in.active = TRUE; + session->in.data_size = 0; + session->in.transaction = header->transaction; + session->in.message_type = header->message_type; + session->in.no_of_packets = start->no_of_packets; + session->in.signal_id = start->signal_id; + + payload = session->buf + sizeof(*start); + payload_size = size - sizeof(*start); + + break; + case AVDTP_PKT_TYPE_CONTINUE: + if (size < sizeof(struct avdtp_continue_header)) { + error("Received too small continue packet (%zu bytes)", + size); + return PARSE_ERROR; + } + if (!session->in.active) { + error("CONTINUE: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + if (session->in.transaction != header->transaction) { + error("Continue transaction id doesn't match"); + return PARSE_ERROR; + } + if (session->in.no_of_packets <= 1) { + error("Too few continue packets"); + return PARSE_ERROR; + } + + payload = session->buf + sizeof(struct avdtp_continue_header); + payload_size = size - sizeof(struct avdtp_continue_header); + + break; + case AVDTP_PKT_TYPE_END: + if (size < sizeof(struct avdtp_continue_header)) { + error("Received too small end packet (%zu bytes)", size); + return PARSE_ERROR; + } + if (!session->in.active) { + error("END: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + if (session->in.transaction != header->transaction) { + error("End transaction id doesn't match"); + return PARSE_ERROR; + } + if (session->in.no_of_packets > 1) { + error("Got an end packet too early"); + return PARSE_ERROR; + } + + payload = session->buf + sizeof(struct avdtp_continue_header); + payload_size = size - sizeof(struct avdtp_continue_header); + + break; + default: + error("Invalid AVDTP packet type 0x%02X", header->packet_type); + return PARSE_ERROR; + } + + if (session->in.data_size + payload_size > + sizeof(session->in.buf)) { + error("Not enough incoming buffer space!"); + return PARSE_ERROR; + } + + memcpy(session->in.buf + session->in.data_size, payload, payload_size); + session->in.data_size += payload_size; + + if (session->in.no_of_packets > 1) { + session->in.no_of_packets--; + DBG("Received AVDTP fragment. %d to go", + session->in.no_of_packets); + return PARSE_FRAGMENT; + } + + session->in.active = FALSE; + + return PARSE_SUCCESS; +} + +static gboolean session_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avdtp *session = data; + struct avdtp_common_header *header; + ssize_t size; + int fd; + + DBG(""); + + if (cond & G_IO_NVAL) + return FALSE; + + header = (void *) session->buf; + + if (cond & (G_IO_HUP | G_IO_ERR)) + goto failed; + + fd = g_io_channel_unix_get_fd(chan); + size = read(fd, session->buf, session->imtu); + if (size < 0) { + error("IO Channel read error"); + goto failed; + } + + if ((size_t) size < sizeof(struct avdtp_common_header)) { + error("Received too small packet (%zu bytes)", size); + goto failed; + } + + switch (avdtp_parse_data(session, session->buf, size)) { + case PARSE_ERROR: + goto failed; + case PARSE_FRAGMENT: + return TRUE; + case PARSE_SUCCESS: + break; + } + + if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) { + if (!avdtp_parse_cmd(session, session->in.transaction, + session->in.signal_id, + session->in.buf, + session->in.data_size)) { + error("Unable to handle command. Disconnecting"); + goto failed; + } + + if (session->req && session->req->collided) { + DBG("Collision detected"); + goto next; + } + + return TRUE; + } + + if (session->req == NULL) { + error("No pending request, ignoring message"); + return TRUE; + } + + if (header->transaction != session->req->transaction) { + error("Transaction label doesn't match"); + return TRUE; + } + + if (session->in.signal_id != session->req->signal_id) { + error("Response signal doesn't match"); + return TRUE; + } + + g_source_remove(session->req->timeout); + session->req->timeout = 0; + + switch (header->message_type) { + case AVDTP_MSG_TYPE_ACCEPT: + if (!avdtp_parse_resp(session, session->req->stream, + session->in.transaction, + session->in.signal_id, + session->in.buf, + session->in.data_size)) { + error("Unable to parse accept response"); + goto failed; + } + break; + case AVDTP_MSG_TYPE_REJECT: + if (!avdtp_parse_rej(session, session->req->stream, + session->in.transaction, + session->in.signal_id, + session->in.buf, + session->in.data_size)) { + error("Unable to parse reject response"); + goto failed; + } + break; + case AVDTP_MSG_TYPE_GEN_REJECT: + error("Received a General Reject message"); + break; + default: + error("Unknown message type 0x%02X", header->message_type); + break; + } + +next: + pending_req_free(session->req); + session->req = NULL; + + process_queue(session); + + return TRUE; + +failed: + connection_lost(session, EIO); + + return FALSE; +} + +struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version) +{ + struct avdtp *session; + GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + session = g_new0(struct avdtp, 1); + session->io = g_io_channel_unix_new(fd); + session->version = version; + session->imtu = imtu; + session->omtu = omtu; + session->buf = g_malloc0(MAX(session->imtu, session->omtu)); + + /* This watch should be low priority since otherwise the + * connect callback might be dispatched before the session + * callback if the kernel wakes us up at the same time for + * them. This could happen if a headset is very quick in + * sending the Start command after connecting the stream + * transport channel. + */ + session->io_id = g_io_add_watch_full(session->io, G_PRIORITY_LOW, cond, + (GIOFunc) session_cb, session, + NULL); + + return avdtp_ref(session); +} + +static void queue_request(struct avdtp *session, struct pending_req *req, + gboolean priority) +{ + if (priority) + session->prio_queue = g_slist_append(session->prio_queue, req); + else + session->req_queue = g_slist_append(session->req_queue, req); +} + +static uint8_t req_get_seid(struct pending_req *req) +{ + if (req->signal_id == AVDTP_DISCOVER) + return 0; + + return ((struct seid_req *) (req->data))->acp_seid; +} + +static int cancel_request(struct avdtp *session, int err) +{ + struct pending_req *req; + struct seid_req sreq; + struct avdtp_local_sep *lsep; + struct avdtp_stream *stream; + uint8_t seid; + struct avdtp_error averr; + + req = session->req; + session->req = NULL; + + avdtp_error_init(&averr, AVDTP_ERRNO, err); + + seid = req_get_seid(req); + if (seid) + stream = find_stream_by_rseid(session, seid); + else + stream = NULL; + + if (stream) { + stream->abort_int = TRUE; + lsep = stream->lsep; + } else + lsep = NULL; + + switch (req->signal_id) { + case AVDTP_RECONFIGURE: + error("Reconfigure: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->reconfigure) + lsep->cfm->reconfigure(session, lsep, stream, &averr, + lsep->user_data); + break; + case AVDTP_OPEN: + error("Open: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->open) + lsep->cfm->open(session, lsep, stream, &averr, + lsep->user_data); + break; + case AVDTP_START: + error("Start: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->start) { + lsep->cfm->start(session, lsep, stream, &averr, + lsep->user_data); + if (stream) + stream->starting = FALSE; + } + break; + case AVDTP_SUSPEND: + error("Suspend: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->suspend) + lsep->cfm->suspend(session, lsep, stream, &averr, + lsep->user_data); + break; + case AVDTP_CLOSE: + error("Close: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->close) { + lsep->cfm->close(session, lsep, stream, &averr, + lsep->user_data); + if (stream) + stream->close_int = FALSE; + } + break; + case AVDTP_SET_CONFIGURATION: + error("SetConfiguration: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->set_configuration) + lsep->cfm->set_configuration(session, lsep, stream, + &averr, lsep->user_data); + goto failed; + case AVDTP_DISCOVER: + error("Discover: %s (%d)", strerror(err), err); + goto failed; + case AVDTP_GET_CAPABILITIES: + error("GetCapabilities: %s (%d)", strerror(err), err); + goto failed; + case AVDTP_ABORT: + error("Abort: %s (%d)", strerror(err), err); + goto failed; + } + + if (!stream) + goto failed; + + memset(&sreq, 0, sizeof(sreq)); + sreq.acp_seid = seid; + + err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq, + sizeof(sreq)); + if (err < 0) { + error("Unable to send abort request"); + goto failed; + } + + goto done; + +failed: + connection_lost(session, err); +done: + pending_req_free(req); + return err; +} + +static gboolean request_timeout(gpointer user_data) +{ + struct avdtp *session = user_data; + + cancel_request(session, ETIMEDOUT); + + return FALSE; +} + +static int send_req(struct avdtp *session, gboolean priority, + struct pending_req *req) +{ + static int transaction = 0; + int err; + + if (session->req != NULL) { + queue_request(session, req, priority); + return 0; + } + + req->transaction = transaction++; + transaction %= 16; + + /* FIXME: Should we retry to send if the buffer + was not totally sent or in case of EINTR? */ + if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND, + req->signal_id, req->data, req->data_size)) { + err = -EIO; + goto failed; + } + + session->req = req; + + req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ? + ABORT_TIMEOUT : REQ_TIMEOUT, + request_timeout, + session); + return 0; + +failed: + g_free(req->data); + g_free(req); + return err; +} + +static int send_request(struct avdtp *session, gboolean priority, + struct avdtp_stream *stream, uint8_t signal_id, + void *buffer, size_t size) +{ + struct pending_req *req; + + if (stream && stream->abort_int && signal_id != AVDTP_ABORT) { + DBG("Unable to send requests while aborting"); + return -EINVAL; + } + + req = g_new0(struct pending_req, 1); + req->signal_id = signal_id; + req->data = g_malloc(size); + memcpy(req->data, buffer, size); + req->data_size = size; + req->stream = stream; + + return send_req(session, priority, req); +} + +static gboolean avdtp_discover_resp(struct avdtp *session, + struct discover_resp *resp, int size) +{ + int sep_count, i; + uint8_t getcap_cmd; + int ret = 0; + gboolean getcap_pending = FALSE; + + if (session->version >= 0x0103) + getcap_cmd = AVDTP_GET_ALL_CAPABILITIES; + else + getcap_cmd = AVDTP_GET_CAPABILITIES; + + sep_count = size / sizeof(struct seid_info); + + for (i = 0; i < sep_count; i++) { + struct avdtp_remote_sep *sep; + struct avdtp_stream *stream; + struct seid_req req; + + DBG("seid %d type %d media %d in use %d", + resp->seps[i].seid, resp->seps[i].type, + resp->seps[i].media_type, resp->seps[i].inuse); + + stream = find_stream_by_rseid(session, resp->seps[i].seid); + + sep = find_remote_sep(session->seps, resp->seps[i].seid); + if (!sep) { + if (resp->seps[i].inuse && !stream) + continue; + sep = g_new0(struct avdtp_remote_sep, 1); + session->seps = g_slist_append(session->seps, sep); + } + + sep->stream = stream; + sep->seid = resp->seps[i].seid; + sep->type = resp->seps[i].type; + sep->media_type = resp->seps[i].media_type; + + memset(&req, 0, sizeof(req)); + req.acp_seid = sep->seid; + + ret = send_request(session, TRUE, NULL, getcap_cmd, + &req, sizeof(req)); + if (ret < 0) + break; + getcap_pending = TRUE; + } + + if (!getcap_pending) + finalize_discovery(session, -ret); + + return TRUE; +} + +static gboolean avdtp_get_capabilities_resp(struct avdtp *session, + struct getcap_resp *resp, + unsigned int size) +{ + struct avdtp_remote_sep *sep; + uint8_t seid; + + /* Check for minimum required packet size includes: + * 1. getcap resp header + * 2. media transport capability (2 bytes) + * 3. media codec capability type + length (2 bytes) + * 4. the actual media codec elements + * */ + if (size < (sizeof(struct getcap_resp) + 4 + + sizeof(struct avdtp_media_codec_capability))) { + error("Too short getcap resp packet"); + return FALSE; + } + + seid = ((struct seid_req *) session->req->data)->acp_seid; + + sep = find_remote_sep(session->seps, seid); + + DBG("seid %d type %d media %d", sep->seid, + sep->type, sep->media_type); + + if (sep->caps) { + g_slist_free_full(sep->caps, g_free); + sep->caps = NULL; + sep->codec = NULL; + sep->delay_reporting = FALSE; + } + + sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp), + &sep->codec, &sep->delay_reporting); + + return TRUE; +} + +static gboolean avdtp_set_configuration_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_single_header *resp, + int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (sep->cfm && sep->cfm->set_configuration) + sep->cfm->set_configuration(session, sep, stream, NULL, + sep->user_data); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); + + return TRUE; +} + +static gboolean avdtp_reconfigure_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_single_header *resp, int size) +{ + return TRUE; +} + +static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + session->pending_open = stream; + + if (!stream->open_acp && sep->cfm && sep->cfm->open) + sep->cfm->open(session, sep, stream, NULL, sep->user_data); + + return TRUE; +} + +static gboolean avdtp_start_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + /* We might be in STREAMING already if both sides send START_CMD at the + * same time and the one in SNK role doesn't reject it as it should */ + if (sep->state != AVDTP_STATE_STREAMING) + avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); + + if (sep->cfm && sep->cfm->start) + sep->cfm->start(session, sep, stream, NULL, sep->user_data); + + return TRUE; +} + +static gboolean avdtp_close_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); + + close_stream(stream); + + return TRUE; +} + +static gboolean avdtp_suspend_resp(struct avdtp *session, + struct avdtp_stream *stream, + void *data, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + + if (sep->cfm && sep->cfm->suspend) + sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); + + return TRUE; +} + +static gboolean avdtp_abort_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); + + if (sep->cfm && sep->cfm->abort) + sep->cfm->abort(session, sep, stream, NULL, sep->user_data); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); + + return TRUE; +} + +static gboolean avdtp_delay_report_resp(struct avdtp *session, + struct avdtp_stream *stream, + void *data, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (sep->cfm && sep->cfm->delay_report) + sep->cfm->delay_report(session, sep, stream, NULL, sep->user_data); + + return TRUE; +} + +static gboolean avdtp_parse_resp(struct avdtp *session, + struct avdtp_stream *stream, + uint8_t transaction, uint8_t signal_id, + void *buf, int size) +{ + struct pending_req *next; + const char *get_all = ""; + + if (session->prio_queue) + next = session->prio_queue->data; + else if (session->req_queue) + next = session->req_queue->data; + else + next = NULL; + + switch (signal_id) { + case AVDTP_DISCOVER: + DBG("DISCOVER request succeeded"); + return avdtp_discover_resp(session, buf, size); + case AVDTP_GET_ALL_CAPABILITIES: + get_all = "ALL_"; + case AVDTP_GET_CAPABILITIES: + DBG("GET_%sCAPABILITIES request succeeded", get_all); + if (!avdtp_get_capabilities_resp(session, buf, size)) + return FALSE; + if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES || + next->signal_id == AVDTP_GET_ALL_CAPABILITIES))) + finalize_discovery(session, 0); + return TRUE; + } + + /* The remaining commands require an existing stream so bail out + * here if the stream got unexpectedly disconnected */ + if (!stream) { + DBG("AVDTP: stream was closed while waiting for reply"); + return TRUE; + } + + switch (signal_id) { + case AVDTP_SET_CONFIGURATION: + DBG("SET_CONFIGURATION request succeeded"); + return avdtp_set_configuration_resp(session, stream, + buf, size); + case AVDTP_RECONFIGURE: + DBG("RECONFIGURE request succeeded"); + return avdtp_reconfigure_resp(session, stream, buf, size); + case AVDTP_OPEN: + DBG("OPEN request succeeded"); + return avdtp_open_resp(session, stream, buf, size); + case AVDTP_SUSPEND: + DBG("SUSPEND request succeeded"); + return avdtp_suspend_resp(session, stream, buf, size); + case AVDTP_START: + DBG("START request succeeded"); + return avdtp_start_resp(session, stream, buf, size); + case AVDTP_CLOSE: + DBG("CLOSE request succeeded"); + return avdtp_close_resp(session, stream, buf, size); + case AVDTP_ABORT: + DBG("ABORT request succeeded"); + return avdtp_abort_resp(session, stream, buf, size); + case AVDTP_DELAY_REPORT: + DBG("DELAY_REPORT request succeeded"); + return avdtp_delay_report_resp(session, stream, buf, size); + } + + error("Unknown signal id in accept response: %u", signal_id); + return TRUE; +} + +static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size, + struct avdtp_error *err) +{ + if (size < sizeof(struct seid_rej)) { + error("Too small packet for seid_rej"); + return FALSE; + } + + avdtp_error_init(err, 0x00, rej->error); + + return TRUE; +} + +static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size, + struct avdtp_error *err) +{ + if (size < sizeof(struct conf_rej)) { + error("Too small packet for conf_rej"); + return FALSE; + } + + avdtp_error_init(err, rej->category, rej->error); + + return TRUE; +} + +static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size, + struct avdtp_error *err, + uint8_t *acp_seid) +{ + if (size < sizeof(struct stream_rej)) { + error("Too small packet for stream_rej"); + return FALSE; + } + + avdtp_error_init(err, 0x00, rej->error); + + if (acp_seid) + *acp_seid = rej->acp_seid; + + return TRUE; +} + +static gboolean avdtp_parse_rej(struct avdtp *session, + struct avdtp_stream *stream, + uint8_t transaction, uint8_t signal_id, + void *buf, int size) +{ + struct avdtp_error err; + uint8_t acp_seid; + struct avdtp_local_sep *sep = stream ? stream->lsep : NULL; + + switch (signal_id) { + case AVDTP_DISCOVER: + case AVDTP_GET_CAPABILITIES: + case AVDTP_GET_ALL_CAPABILITIES: + if (!seid_rej_to_err(buf, size, &err)) + return FALSE; + error("%s request rejected: %s (%d)", + signal_id == AVDTP_DISCOVER ? "DISCOVER" : + signal_id == AVDTP_GET_CAPABILITIES ? + "GET_CAPABILITIES" : "GET_ALL_CAPABILITIES", + avdtp_strerror(&err), err.err.error_code); + if (session->discover) { + session->discover->cb(session, session->seps, &err, + session->discover->user_data); + g_free(session->discover); + session->discover = NULL; + } + return TRUE; + case AVDTP_OPEN: + if (!seid_rej_to_err(buf, size, &err)) + return FALSE; + error("OPEN request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->open) + sep->cfm->open(session, sep, stream, &err, + sep->user_data); + return TRUE; + case AVDTP_SET_CONFIGURATION: + if (!conf_rej_to_err(buf, size, &err)) + return FALSE; + error("SET_CONFIGURATION request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->set_configuration) + sep->cfm->set_configuration(session, sep, stream, + &err, sep->user_data); + return TRUE; + case AVDTP_GET_CONFIGURATION: + if (!seid_rej_to_err(buf, size, &err)) + return FALSE; + error("GET_CONFIGURATION request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->get_configuration) + sep->cfm->get_configuration(session, sep, stream, &err, + sep->user_data); + return TRUE; + case AVDTP_RECONFIGURE: + if (!conf_rej_to_err(buf, size, &err)) + return FALSE; + error("RECONFIGURE request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->reconfigure) + sep->cfm->reconfigure(session, sep, stream, &err, + sep->user_data); + return TRUE; + case AVDTP_START: + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) + return FALSE; + error("START request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->start) { + sep->cfm->start(session, sep, stream, &err, + sep->user_data); + stream->starting = FALSE; + } + return TRUE; + case AVDTP_SUSPEND: + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) + return FALSE; + error("SUSPEND request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->suspend) + sep->cfm->suspend(session, sep, stream, &err, + sep->user_data); + return TRUE; + case AVDTP_CLOSE: + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) + return FALSE; + error("CLOSE request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->close) { + sep->cfm->close(session, sep, stream, &err, + sep->user_data); + stream->close_int = FALSE; + } + return TRUE; + case AVDTP_ABORT: + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) + return FALSE; + error("ABORT request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->abort) + sep->cfm->abort(session, sep, stream, &err, + sep->user_data); + return FALSE; + case AVDTP_DELAY_REPORT: + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) + return FALSE; + error("DELAY_REPORT request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->delay_report) + sep->cfm->delay_report(session, sep, stream, &err, + sep->user_data); + return TRUE; + default: + error("Unknown reject response signal id: %u", signal_id); + return TRUE; + } +} + +struct avdtp_service_capability *avdtp_stream_get_codec( + struct avdtp_stream *stream) +{ + GSList *l; + + for (l = stream->caps; l; l = l->next) { + struct avdtp_service_capability *cap = l->data; + + if (cap->category == AVDTP_MEDIA_CODEC) + return cap; + } + + return NULL; +} + +static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, + struct avdtp_service_capability *cap) +{ + GSList *l; + struct avdtp_service_capability *stream_cap; + + for (l = stream->caps; l; l = g_slist_next(l)) { + stream_cap = l->data; + + if (stream_cap->category != cap->category || + stream_cap->length != cap->length) + continue; + + if (memcmp(stream_cap->data, cap->data, cap->length) == 0) + return TRUE; + } + + return FALSE; +} + +gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, + GSList *caps) +{ + for (; caps; caps = g_slist_next(caps)) { + struct avdtp_service_capability *cap = caps->data; + + if (!avdtp_stream_has_capability(stream, cap)) + return FALSE; + } + + return TRUE; +} + +struct avdtp_remote_sep *avdtp_stream_get_remote_sep( + struct avdtp_stream *stream) +{ + GSList *l; + + for (l = stream->session->seps; l; l = l->next) { + struct avdtp_remote_sep *sep = l->data; + + if (sep->seid == stream->rseid) + return sep; + } + + return NULL; +} + +gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd, + size_t imtu, size_t omtu) +{ + GIOChannel *io; + + if (stream != stream->session->pending_open) + return FALSE; + + io = g_io_channel_unix_new(fd); + + handle_transport_connect(stream->session, io, imtu, omtu); + + g_io_channel_unref(io); + + return TRUE; + +} + +gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, + uint16_t *imtu, uint16_t *omtu, + GSList **caps) +{ + if (stream->io == NULL) + return FALSE; + + if (sock) + *sock = g_io_channel_unix_get_fd(stream->io); + + if (omtu) + *omtu = stream->omtu; + + if (imtu) + *imtu = stream->imtu; + + if (caps) + *caps = stream->caps; + + return TRUE; +} + +static int process_queue(struct avdtp *session) +{ + GSList **queue, *l; + struct pending_req *req; + + if (session->req) + return 0; + + if (session->prio_queue) + queue = &session->prio_queue; + else + queue = &session->req_queue; + + if (!*queue) + return 0; + + l = *queue; + req = l->data; + + *queue = g_slist_remove(*queue, req); + + return send_req(session, FALSE, req); +} + +struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep) +{ + return sep->codec; +} + +struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, + void *data, int length) +{ + struct avdtp_service_capability *cap; + + if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING) + return NULL; + + cap = g_malloc(sizeof(struct avdtp_service_capability) + length); + cap->category = category; + cap->length = length; + memcpy(cap->data, data, length); + + return cap; +} + +static gboolean process_discover(gpointer data) +{ + struct avdtp *session = data; + + session->discover->id = 0; + + finalize_discovery(session, 0); + + return FALSE; +} + +int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, + void *user_data) +{ + int err; + + if (session->discover) + return -EBUSY; + + session->discover = g_new0(struct discover_callback, 1); + + if (session->seps) { + session->discover->cb = cb; + session->discover->user_data = user_data; + session->discover->id = g_idle_add(process_discover, session); + return 0; + } + + err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0); + if (err == 0) { + session->discover->cb = cb; + session->discover->user_data = user_data; + } + + return err; +} + +gboolean avdtp_stream_remove_cb(struct avdtp *session, + struct avdtp_stream *stream, + unsigned int id) +{ + GSList *l; + struct stream_callback *cb; + + if (!stream) + return FALSE; + + for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) { + struct stream_callback *tmp = l->data; + if (tmp && tmp->id == id) { + cb = tmp; + break; + } + } + + if (!cb) + return FALSE; + + stream->callbacks = g_slist_remove(stream->callbacks, cb); + g_free(cb); + + return TRUE; +} + +unsigned int avdtp_stream_add_cb(struct avdtp *session, + struct avdtp_stream *stream, + avdtp_stream_state_cb cb, void *data) +{ + struct stream_callback *stream_cb; + static unsigned int id = 0; + + stream_cb = g_new(struct stream_callback, 1); + stream_cb->cb = cb; + stream_cb->user_data = data; + stream_cb->id = ++id; + + stream->callbacks = g_slist_append(stream->callbacks, stream_cb); + + return stream_cb->id; +} + +int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + + return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION, + &req, sizeof(req)); +} + +static void copy_capabilities(gpointer data, gpointer user_data) +{ + struct avdtp_service_capability *src_cap = data; + struct avdtp_service_capability *dst_cap; + GSList **l = user_data; + + dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data, + src_cap->length); + + *l = g_slist_append(*l, dst_cap); +} + +int avdtp_set_configuration(struct avdtp *session, + struct avdtp_remote_sep *rsep, + struct avdtp_local_sep *lsep, + GSList *caps, + struct avdtp_stream **stream) +{ + struct setconf_req *req; + struct avdtp_stream *new_stream; + unsigned char *ptr; + int err, caps_len; + struct avdtp_service_capability *cap; + GSList *l; + + if (!(lsep && rsep)) + return -EINVAL; + + DBG("%p: int_seid=%u, acp_seid=%u", session, + lsep->info.seid, rsep->seid); + + new_stream = g_new0(struct avdtp_stream, 1); + new_stream->session = session; + new_stream->lsep = lsep; + new_stream->rseid = rsep->seid; + + if (rsep->delay_reporting && lsep->delay_reporting) { + struct avdtp_service_capability *delay_reporting; + + delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING, + NULL, 0); + caps = g_slist_append(caps, delay_reporting); + new_stream->delay_reporting = TRUE; + } + + g_slist_foreach(caps, copy_capabilities, &new_stream->caps); + + /* Calculate total size of request */ + for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { + cap = l->data; + caps_len += cap->length + 2; + } + + req = g_malloc0(sizeof(struct setconf_req) + caps_len); + + req->int_seid = lsep->info.seid; + req->acp_seid = rsep->seid; + + /* Copy the capabilities into the request */ + for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { + cap = l->data; + memcpy(ptr, cap, cap->length + 2); + ptr += cap->length + 2; + } + + err = send_request(session, FALSE, new_stream, + AVDTP_SET_CONFIGURATION, req, + sizeof(struct setconf_req) + caps_len); + if (err < 0) + stream_free(new_stream); + else { + lsep->info.inuse = 1; + lsep->stream = new_stream; + rsep->stream = new_stream; + session->streams = g_slist_append(session->streams, new_stream); + if (stream) + *stream = new_stream; + } + + g_free(req); + + return err; +} + +int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state > AVDTP_STATE_CONFIGURED) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + + return send_request(session, FALSE, stream, AVDTP_OPEN, + &req, sizeof(req)); +} + +static gboolean start_timeout(gpointer user_data) +{ + struct avdtp_stream *stream = user_data; + struct avdtp *session = stream->session; + + stream->open_acp = FALSE; + + if (avdtp_start(session, stream) < 0) + error("wait_timeout: avdtp_start failed"); + + stream->start_timer = 0; + + return FALSE; +} + +int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) +{ + struct start_req req; + int ret; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state != AVDTP_STATE_OPEN) + return -EINVAL; + + /* Recommendation 12: + * If the RD has configured and opened a stream it is also responsible + * to start the streaming via GAVDP_START. + */ + if (stream->open_acp) { + /* If timer already active wait it */ + if (stream->start_timer) + return 0; + + stream->start_timer = g_timeout_add_seconds(START_TIMEOUT, + start_timeout, + stream); + return 0; + } + + if (stream->close_int == TRUE) { + error("avdtp_start: rejecting start since close is initiated"); + return -EINVAL; + } + + if (stream->starting == TRUE) { + DBG("stream already started"); + return -EINPROGRESS; + } + + memset(&req, 0, sizeof(req)); + req.first_seid.seid = stream->rseid; + + ret = send_request(session, FALSE, stream, AVDTP_START, + &req, sizeof(req)); + if (ret == 0) + stream->starting = TRUE; + + return ret; +} + +int avdtp_close(struct avdtp *session, struct avdtp_stream *stream, + gboolean immediate) +{ + struct seid_req req; + int ret; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state < AVDTP_STATE_OPEN) + return -EINVAL; + + if (stream->close_int == TRUE) { + error("avdtp_close: rejecting since close is already initiated"); + return -EINVAL; + } + + if (immediate && session->req && stream == session->req->stream) + return avdtp_abort(session, stream); + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + + ret = send_request(session, FALSE, stream, AVDTP_CLOSE, + &req, sizeof(req)); + if (ret == 0) + stream->close_int = TRUE; + + return ret; +} + +int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + + return send_request(session, FALSE, stream, AVDTP_SUSPEND, + &req, sizeof(req)); +} + +int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + int ret; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state == AVDTP_STATE_ABORTING) + return -EINVAL; + + if (session->req && session->req->timeout > 0 && + stream == session->req->stream) + return cancel_request(session, ECANCELED); + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + + ret = send_request(session, TRUE, stream, AVDTP_ABORT, + &req, sizeof(req)); + if (ret == 0) + stream->abort_int = TRUE; + + return ret; +} + +int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, + uint16_t delay) +{ + struct delay_req req; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state != AVDTP_STATE_CONFIGURED && + stream->lsep->state != AVDTP_STATE_STREAMING) + return -EINVAL; + + if (!stream->delay_reporting || session->version < 0x0103) + return -EINVAL; + + stream->delay = delay; + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + req.delay = htons(delay); + + return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT, + &req, sizeof(req)); +} + +struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, + uint8_t codec_type, + gboolean delay_reporting, + struct avdtp_sep_ind *ind, + struct avdtp_sep_cfm *cfm, + void *user_data) +{ + struct avdtp_local_sep *sep; + + if (g_slist_length(lseps) > MAX_SEID) + return NULL; + + sep = g_new0(struct avdtp_local_sep, 1); + + sep->state = AVDTP_STATE_IDLE; + sep->info.seid = g_slist_length(lseps) + 1; + sep->info.type = type; + sep->info.media_type = media_type; + sep->codec = codec_type; + sep->ind = ind; + sep->cfm = cfm; + sep->user_data = user_data; + sep->delay_reporting = TRUE; + + DBG("SEP %p registered: type:%d codec:%d seid:%d", sep, + sep->info.type, sep->codec, sep->info.seid); + lseps = g_slist_append(lseps, sep); + + return sep; +} + +int avdtp_unregister_sep(struct avdtp_local_sep *sep) +{ + if (!sep) + return -EINVAL; + + lseps = g_slist_remove(lseps, sep); + + if (sep->stream) + release_stream(sep->stream, sep->stream->session); + + DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep, + sep->info.type, sep->codec, sep->info.seid); + + g_free(sep); + + return 0; +} + +const char *avdtp_strerror(struct avdtp_error *err) +{ + if (err->category == AVDTP_ERRNO) + return strerror(err->err.posix_errno); + + switch(err->err.error_code) { + case AVDTP_BAD_HEADER_FORMAT: + return "Bad Header Format"; + case AVDTP_BAD_LENGTH: + return "Bad Packet Length"; + case AVDTP_BAD_ACP_SEID: + return "Bad Acceptor SEID"; + case AVDTP_SEP_IN_USE: + return "Stream End Point in Use"; + case AVDTP_SEP_NOT_IN_USE: + return "Stream End Point Not in Use"; + case AVDTP_BAD_SERV_CATEGORY: + return "Bad Service Category"; + case AVDTP_BAD_PAYLOAD_FORMAT: + return "Bad Payload format"; + case AVDTP_NOT_SUPPORTED_COMMAND: + return "Command Not Supported"; + case AVDTP_INVALID_CAPABILITIES: + return "Invalid Capabilities"; + case AVDTP_BAD_RECOVERY_TYPE: + return "Bad Recovery Type"; + case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT: + return "Bad Media Transport Format"; + case AVDTP_BAD_RECOVERY_FORMAT: + return "Bad Recovery Format"; + case AVDTP_BAD_ROHC_FORMAT: + return "Bad Header Compression Format"; + case AVDTP_BAD_CP_FORMAT: + return "Bad Content Protection Format"; + case AVDTP_BAD_MULTIPLEXING_FORMAT: + return "Bad Multiplexing Format"; + case AVDTP_UNSUPPORTED_CONFIGURATION: + return "Configuration not supported"; + case AVDTP_BAD_STATE: + return "Bad State"; + default: + return "Unknown error"; + } +} + +avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep) +{ + return sep->state; +} + +gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream) +{ + return g_slist_find(session->streams, stream) ? TRUE : FALSE; +} diff --git a/android/avdtp.h b/android/avdtp.h new file mode 100644 index 00000000..97608758 --- /dev/null +++ b/android/avdtp.h @@ -0,0 +1,275 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct avdtp; +struct avdtp_stream; +struct avdtp_local_sep; +struct avdtp_remote_sep; +struct avdtp_error { + uint8_t category; + union { + uint8_t error_code; + int posix_errno; + } err; +}; + +/* SEP capability categories */ +#define AVDTP_MEDIA_TRANSPORT 0x01 +#define AVDTP_REPORTING 0x02 +#define AVDTP_RECOVERY 0x03 +#define AVDTP_CONTENT_PROTECTION 0x04 +#define AVDTP_HEADER_COMPRESSION 0x05 +#define AVDTP_MULTIPLEXING 0x06 +#define AVDTP_MEDIA_CODEC 0x07 +#define AVDTP_DELAY_REPORTING 0x08 +#define AVDTP_ERRNO 0xff + +/* AVDTP error definitions */ +#define AVDTP_BAD_HEADER_FORMAT 0x01 +#define AVDTP_BAD_LENGTH 0x11 +#define AVDTP_BAD_ACP_SEID 0x12 +#define AVDTP_SEP_IN_USE 0x13 +#define AVDTP_SEP_NOT_IN_USE 0x14 +#define AVDTP_BAD_SERV_CATEGORY 0x17 +#define AVDTP_BAD_PAYLOAD_FORMAT 0x18 +#define AVDTP_NOT_SUPPORTED_COMMAND 0x19 +#define AVDTP_INVALID_CAPABILITIES 0x1A +#define AVDTP_BAD_RECOVERY_TYPE 0x22 +#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT 0x23 +#define AVDTP_BAD_RECOVERY_FORMAT 0x25 +#define AVDTP_BAD_ROHC_FORMAT 0x26 +#define AVDTP_BAD_CP_FORMAT 0x27 +#define AVDTP_BAD_MULTIPLEXING_FORMAT 0x28 +#define AVDTP_UNSUPPORTED_CONFIGURATION 0x29 +#define AVDTP_BAD_STATE 0x31 + +/* SEP types definitions */ +#define AVDTP_SEP_TYPE_SOURCE 0x00 +#define AVDTP_SEP_TYPE_SINK 0x01 + +/* Media types definitions */ +#define AVDTP_MEDIA_TYPE_AUDIO 0x00 +#define AVDTP_MEDIA_TYPE_VIDEO 0x01 +#define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02 + +typedef enum { + AVDTP_STATE_IDLE, + AVDTP_STATE_CONFIGURED, + AVDTP_STATE_OPEN, + AVDTP_STATE_STREAMING, + AVDTP_STATE_CLOSING, + AVDTP_STATE_ABORTING, +} avdtp_state_t; + +struct avdtp_service_capability { + uint8_t category; + uint8_t length; + uint8_t data[0]; +} __attribute__ ((packed)); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct avdtp_media_codec_capability { + uint8_t rfa0:4; + uint8_t media_type:4; + uint8_t media_codec_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avdtp_media_codec_capability { + uint8_t media_type:4; + uint8_t rfa0:4; + uint8_t media_codec_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream, + avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, + void *user_data); + +typedef void (*avdtp_set_configuration_cb) (struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_error *err); + +/* Callbacks for when a reply is received to a command that we sent */ +struct avdtp_sep_cfm { + void (*set_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, + void *user_data); + void (*get_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, + void *user_data); + void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data); + void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data); + void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data); + void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data); + void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data); + void (*reconfigure) (struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data); + void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data); +}; + +/* Callbacks for indicating when we received a new command. The return value + * indicates whether the command should be rejected or accepted */ +struct avdtp_sep_ind { + gboolean (*get_capability) (struct avdtp *session, + struct avdtp_local_sep *sep, + gboolean get_all, + GSList **caps, uint8_t *err, + void *user_data); + gboolean (*set_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + GSList *caps, + avdtp_set_configuration_cb cb, + void *user_data); + gboolean (*get_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, + uint8_t *err, void *user_data); + gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data); + gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data); + gboolean (*suspend) (struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data); + gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data); + void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data); + gboolean (*reconfigure) (struct avdtp *session, + struct avdtp_local_sep *lsep, + uint8_t *err, void *user_data); + gboolean (*delayreport) (struct avdtp *session, + struct avdtp_local_sep *lsep, + uint8_t rseid, uint16_t delay, + uint8_t *err, void *user_data); +}; + +typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, + struct avdtp_error *err, void *user_data); + +struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version); + +void avdtp_unref(struct avdtp *session); +struct avdtp *avdtp_ref(struct avdtp *session); + +struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, + void *data, int size); + +struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); + +int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, + void *user_data); + +gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream); + +unsigned int avdtp_stream_add_cb(struct avdtp *session, + struct avdtp_stream *stream, + avdtp_stream_state_cb cb, void *data); +gboolean avdtp_stream_remove_cb(struct avdtp *session, + struct avdtp_stream *stream, + unsigned int id); + +gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd, + size_t imtu, size_t omtu); +gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, + uint16_t *imtu, uint16_t *omtu, + GSList **caps); +struct avdtp_service_capability *avdtp_stream_get_codec( + struct avdtp_stream *stream); +gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, + GSList *caps); +struct avdtp_remote_sep *avdtp_stream_get_remote_sep( + struct avdtp_stream *stream); + +int avdtp_set_configuration(struct avdtp *session, + struct avdtp_remote_sep *rsep, + struct avdtp_local_sep *lsep, + GSList *caps, + struct avdtp_stream **stream); + +int avdtp_get_configuration(struct avdtp *session, + struct avdtp_stream *stream); + +int avdtp_open(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_start(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_close(struct avdtp *session, struct avdtp_stream *stream, + gboolean immediate); +int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, + uint16_t delay); + +struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, + uint8_t codec_type, + gboolean delay_reporting, + struct avdtp_sep_ind *ind, + struct avdtp_sep_cfm *cfm, + void *user_data); + +/* Find a matching pair of local and remote SEP ID's */ +struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session, + struct avdtp_local_sep *lsep); + +int avdtp_unregister_sep(struct avdtp_local_sep *sep); + +avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep); + +void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id); +const char *avdtp_strerror(struct avdtp_error *err); +uint8_t avdtp_error_category(struct avdtp_error *err); +int avdtp_error_error_code(struct avdtp_error *err); +int avdtp_error_posix_errno(struct avdtp_error *err); diff --git a/android/bluetooth.c b/android/bluetooth.c index 83f20e2e..a3145cb7 100644 --- a/android/bluetooth.c +++ b/android/bluetooth.c @@ -60,8 +60,6 @@ static uint16_t option_index = MGMT_INDEX_NONE; -static int notification_sk = -1; - #define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \ + sizeof(struct hal_property)) /* This list contains addresses which are asked for records */ @@ -132,8 +130,8 @@ static void adapter_name_changed(const uint8_t *name) ev->props[0].len = len; memcpy(ev->props->val, name, len); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, + sizeof(buf), ev); } static void adapter_set_name(const uint8_t *name) @@ -173,8 +171,8 @@ static void powered_changed(void) DBG("%u", ev.state); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_STATE_CHANGED, + sizeof(ev), &ev); } static uint8_t settings2scan_mode(void) @@ -210,8 +208,8 @@ static void scan_mode_changed(void) DBG("mode %u", *mode); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, + sizeof(buf), buf); } static void adapter_class_changed(void) @@ -226,8 +224,8 @@ static void adapter_class_changed(void) ev->props[0].len = sizeof(uint32_t); memcpy(ev->props->val, &adapter.dev_class, sizeof(uint32_t)); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, + sizeof(buf), buf); } static void settings_changed(uint32_t settings) @@ -327,8 +325,8 @@ static void send_bond_state_change(const bdaddr_t *addr, uint8_t status, ev.state = state; bdaddr2android(addr, ev.bdaddr); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_BOND_STATE_CHANGED, + sizeof(ev), &ev); } static void cache_device_name(const bdaddr_t *addr, const char *name) @@ -408,8 +406,8 @@ static void remote_uuids_callback(struct browse_req *req) ev->props[0].len = sizeof(uint128_t) * g_slist_length(req->uuids); fill_uuids(req->uuids, ev->props[0].val); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_REMOTE_DEVICE_PROPS, len, ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS, + len, ev); g_free(ev); } @@ -450,6 +448,7 @@ static void update_records(struct browse_req *req, sdp_list_t *recs) memcpy(&uuid128, tmp, sizeof(uuid_t)); break; default: + sdp_list_free(svcclass, free); continue; } @@ -589,8 +588,10 @@ static void send_remote_device_name_prop(const bdaddr_t *bdaddr) /* Use cached name or bdaddr string */ name = get_device_name(bdaddr); - if (!name) + if (!name) { + ba2str(bdaddr, dst); name = dst; + } ev_len = BASELEN_REMOTE_DEV_PROP + strlen(name); ev = g_malloc0(ev_len); @@ -602,8 +603,8 @@ static void send_remote_device_name_prop(const bdaddr_t *bdaddr) ev->props[0].len = strlen(name); memcpy(&ev->props[0].val, name, strlen(name)); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_REMOTE_DEVICE_PROPS, sizeof(ev), ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_REMOTE_DEVICE_PROPS, + ev_len, ev); g_free(ev); } @@ -636,8 +637,8 @@ static void pin_code_request_callback(uint16_t index, uint16_t length, memset(&hal_ev, 0, sizeof(hal_ev)); bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST, - sizeof(hal_ev), &hal_ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST, + sizeof(hal_ev), &hal_ev); } static void send_ssp_request(const bdaddr_t *addr, uint8_t variant, @@ -654,8 +655,8 @@ static void send_ssp_request(const bdaddr_t *addr, uint8_t variant, ev.pairing_variant = variant; ev.passkey = passkey; - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST, - sizeof(ev), &ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST, + sizeof(ev), &ev); } static void user_confirm_request_callback(uint16_t index, uint16_t length, @@ -757,8 +758,8 @@ static void mgmt_discovering_event(uint16_t index, uint16_t length, cp.state = HAL_DISCOVERY_STATE_STOPPED; } - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp), &cp, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp), &cp); } static void confirm_device_name(const bdaddr_t *addr, uint8_t addr_type) @@ -774,131 +775,98 @@ static void confirm_device_name(const bdaddr_t *addr, uint8_t addr_type) error("Failed to send confirm name request"); } -static int fill_device_props(struct hal_property *prop, bdaddr_t *addr, - uint32_t cod, int8_t rssi, char *name) -{ - uint8_t num_props = 0; - /* fill Class of Device */ - if (cod) { - prop->type = HAL_PROP_DEVICE_CLASS; - prop->len = sizeof(cod); - memcpy(prop->val, &cod, prop->len); - prop = ((void *) prop) + sizeof(*prop) + sizeof(cod); - num_props++; - } - - /* fill RSSI */ - if (rssi) { - prop->type = HAL_PROP_DEVICE_RSSI; - prop->len = sizeof(rssi); - memcpy(prop->val, &rssi, prop->len); - prop = ((void *) prop) + sizeof(*prop) + sizeof(rssi); - num_props++; - } +static int fill_hal_prop(void *buf, uint8_t type, uint16_t len, + const void *val) +{ + struct hal_property *prop = buf; - /* fill name */ - if (name) { - prop->type = HAL_PROP_DEVICE_NAME; - prop->len = strlen(name); - memcpy(prop->val, name, prop->len); - prop = ((void *) prop) + sizeof(*prop) + prop->len; - num_props++; - } + prop->type = type; + prop->len = len; + memcpy(prop->val, val, len); - return num_props; + return sizeof(*prop) + len; } static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type, int8_t rssi, bool confirm, const uint8_t *data, uint8_t data_len) { - bool is_new_dev = false; - size_t props_size = 0; - size_t buff_size = 0; - void *buf; + uint8_t buf[BLUEZ_HAL_MTU]; + bool new_dev = false; struct eir_data eir; - GSList *l; - bdaddr_t *remote = NULL; - int err; + uint8_t *num_prop; + uint8_t opcode; + int size = 0; + memset(buf, 0, sizeof(buf)); memset(&eir, 0, sizeof(eir)); - err = eir_parse(&eir, data, data_len); - if (err < 0) { - error("Error parsing EIR data: %s (%d)", strerror(-err), -err); - return; - } - - l = g_slist_find_custom(found_devices, bdaddr, bdaddr_cmp); - if (l) - remote = l->data; + eir_parse(&eir, data, data_len); - if (!remote) { + if (!g_slist_find_custom(found_devices, bdaddr, bdaddr_cmp)) { + bdaddr_t *new_bdaddr; char addr[18]; - remote = g_new0(bdaddr_t, 1); - bacpy(remote, bdaddr); + new_bdaddr = g_new0(bdaddr_t, 1); + bacpy(new_bdaddr, bdaddr); - found_devices = g_slist_prepend(found_devices, remote); - is_new_dev = true; + found_devices = g_slist_prepend(found_devices, new_bdaddr); - ba2str(remote, addr); + ba2str(new_bdaddr, addr); DBG("New device found: %s", addr); - } - - props_size += sizeof(struct hal_property) + sizeof(eir.class); - props_size += sizeof(struct hal_property) + sizeof(rssi); - if (eir.name) { - props_size += sizeof(struct hal_property) + strlen(eir.name); - cache_device_name(remote, eir.name); + new_dev = true; } - if (is_new_dev) { - struct hal_ev_device_found *ev = NULL; - struct hal_property *prop = NULL; + if (new_dev) { + struct hal_ev_device_found *ev = (void *) buf; + bdaddr_t android_bdaddr; - /* with new device we also send bdaddr prop */ - props_size += sizeof(struct hal_property) + sizeof(eir.addr); + size += sizeof(*ev); - buff_size = sizeof(struct hal_ev_device_found) + props_size; - buf = g_new0(char, buff_size); - ev = buf; - prop = ev->props; + num_prop = &ev->num_props; + opcode = HAL_EV_DEVICE_FOUND; - /* fill first prop with bdaddr */ - prop->type = HAL_PROP_DEVICE_ADDR; - prop->len = sizeof(bdaddr_t); - bdaddr2android(bdaddr, prop->val); - prop = ((void *) prop) + sizeof(*prop) + sizeof(bdaddr_t); - ev->num_props += 1; + bdaddr2android(bdaddr, &android_bdaddr); - /* fill eir, name, and cod props */ - ev->num_props += fill_device_props(prop, remote, eir.class, - rssi, eir.name); - - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_DEVICE_FOUND, buff_size, ev, -1); - g_free(buf); + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR, + sizeof(android_bdaddr), &android_bdaddr); + (*num_prop)++; } else { - struct hal_ev_remote_device_props *ev = NULL; + struct hal_ev_remote_device_props *ev = (void *) buf; - buff_size = sizeof(*ev) + props_size; - buf = g_new0(char, buff_size); - ev = buf; + size += sizeof(*ev); - ev->num_props = fill_device_props(ev->props, remote, eir.class, - rssi, eir.name); + num_prop = &ev->num_props; + opcode = HAL_EV_REMOTE_DEVICE_PROPS; ev->status = HAL_STATUS_SUCCESS; bdaddr2android(bdaddr, ev->bdaddr); + } + + if (eir.class) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS, + sizeof(eir.class), &eir.class); + (*num_prop)++; + } + + if (rssi) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI, + sizeof(rssi), &rssi); + (*num_prop)++; + } + + if (eir.name) { + cache_device_name(bdaddr, eir.name); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_REMOTE_DEVICE_PROPS, buff_size, ev, -1); - g_free(buf); + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME, + strlen(eir.name), eir.name); + (*num_prop)++; } + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, opcode, size, buf); + if (confirm) { char addr[18]; @@ -940,8 +908,8 @@ static void mgmt_device_found_event(uint16_t index, uint16_t length, flags = btohl(ev->flags); ba2str(&ev->addr.bdaddr, addr); - DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u eir %u", - index, addr, ev->rssi, flags, eir_len, *eir); + DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u", + index, addr, ev->rssi, flags, eir_len); confirm_name = flags & MGMT_DEV_FOUND_CONFIRM_NAME; @@ -967,8 +935,8 @@ static void mgmt_device_connected_event(uint16_t index, uint16_t length, hal_ev.state = HAL_ACL_STATE_CONNECTED; bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ACL_STATE_CHANGED, + sizeof(hal_ev), &hal_ev); } static void mgmt_device_disconnected_event(uint16_t index, uint16_t length, @@ -986,8 +954,8 @@ static void mgmt_device_disconnected_event(uint16_t index, uint16_t length, hal_ev.state = HAL_ACL_STATE_DISCONNECTED; bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ACL_STATE_CHANGED, + sizeof(hal_ev), &hal_ev); } static uint8_t status_mgmt2hal(uint8_t mgmt) @@ -1160,7 +1128,7 @@ static void uuid16_to_uint128(uint16_t uuid, uint128_t *u128) ntoh128(&uuid128.value.uuid128, u128); } -static bool get_uuids(void) +static uint8_t get_uuids(void) { struct hal_ev_adapter_props_changed *ev; GSList *list = adapter.uuids; @@ -1193,10 +1161,10 @@ static bool get_uuids(void) p += sizeof(uint128_t); } - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, + sizeof(buf), ev); - return true; + return HAL_STATUS_SUCCESS; } static void remove_uuid_complete(uint8_t status, uint16_t length, @@ -1210,9 +1178,7 @@ static void remove_uuid_complete(uint8_t status, uint16_t length, mgmt_dev_class_changed_event(adapter.index, length, param, NULL); - /* send notification only if bluetooth service is registered */ - if (notification_sk >= 0) - get_uuids(); + get_uuids(); } static void remove_uuid(uint16_t uuid) @@ -1238,9 +1204,7 @@ static void add_uuid_complete(uint8_t status, uint16_t length, mgmt_dev_class_changed_event(adapter.index, length, param, NULL); - /* send notification only if bluetooth service is registered */ - if (notification_sk >= 0) - get_uuids(); + get_uuids(); } static void add_uuid(uint8_t svc_hint, uint16_t uuid) @@ -1392,7 +1356,7 @@ static void set_adapter_name_complete(uint8_t status, uint16_t length, adapter_set_name(rp->name); } -static uint8_t set_adapter_name(uint8_t *name, uint16_t len) +static uint8_t set_adapter_name(const uint8_t *name, uint16_t len) { struct mgmt_cp_set_local_name cp; @@ -1409,8 +1373,17 @@ static uint8_t set_adapter_name(uint8_t *name, uint16_t len) return HAL_STATUS_FAILED; } -static uint8_t set_discoverable_timeout(uint8_t *timeout) +static uint8_t set_discoverable_timeout(const void *buf, uint16_t len) { + const uint32_t *timeout = buf; + + if (len != sizeof(*timeout)) { + error("Invalid set disc timeout size (%u bytes), terminating", + len); + raise(SIGTERM); + return HAL_STATUS_FAILED; + } + /* Android handles discoverable timeout in Settings app. * There is no need to use kernel feature for that. * Just need to store this value here */ @@ -1713,7 +1686,7 @@ static bool set_discoverable(uint8_t mode, uint16_t timeout) return false; } -static void get_address(void) +static uint8_t get_address(void) { uint8_t buf[BASELEN_PROP_CHANGED + sizeof(bdaddr_t)]; struct hal_ev_adapter_props_changed *ev = (void *) buf; @@ -1725,67 +1698,69 @@ static void get_address(void) ev->props[0].len = sizeof(bdaddr_t); bdaddr2android(&adapter.bdaddr, ev->props[0].val); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, + sizeof(buf), buf); + + return HAL_STATUS_SUCCESS; } -static bool get_name(void) +static uint8_t get_name(void) { if (!adapter.name) - return false; + return HAL_STATUS_FAILED; adapter_name_changed((uint8_t *) adapter.name); - return true; + return HAL_STATUS_SUCCESS; } -static bool get_class(void) +static uint8_t get_class(void) { DBG(""); adapter_class_changed(); - return true; + return HAL_STATUS_SUCCESS; } -static bool get_type(void) +static uint8_t get_type(void) { DBG("Not implemented"); /* TODO: Add implementation */ - return false; + return HAL_STATUS_FAILED; } -static bool get_service(void) +static uint8_t get_service(void) { DBG("Not implemented"); /* TODO: Add implementation */ - return false; + return HAL_STATUS_FAILED; } -static bool get_scan_mode(void) +static uint8_t get_scan_mode(void) { DBG(""); scan_mode_changed(); - return true; + return HAL_STATUS_SUCCESS; } -static bool get_devices(void) +static uint8_t get_devices(void) { DBG("Not implemented"); /* TODO: Add implementation */ - return false; + return HAL_STATUS_FAILED; } -static bool get_discoverable_timeout(void) +static uint8_t get_discoverable_timeout(void) { struct hal_ev_adapter_props_changed *ev; uint8_t buf[BASELEN_PROP_CHANGED + sizeof(uint32_t)]; @@ -1801,39 +1776,51 @@ static bool get_discoverable_timeout(void) memcpy(&ev->props[0].val, &adapter.discoverable_timeout, sizeof(uint32_t)); - ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, - HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev, -1); + ipc_send_notif(HAL_SERVICE_ID_BLUETOOTH, HAL_EV_ADAPTER_PROPS_CHANGED, + sizeof(buf), ev); - return true; + return HAL_STATUS_SUCCESS; } -static bool get_property(void *buf, uint16_t len) +static void handle_get_adapter_prop_cmd(const void *buf, uint16_t len) { - struct hal_cmd_get_adapter_prop *cmd = buf; + const struct hal_cmd_get_adapter_prop *cmd = buf; + uint8_t status; switch (cmd->type) { case HAL_PROP_ADAPTER_ADDR: - get_address(); - return true; + status = get_address(); + break; case HAL_PROP_ADAPTER_NAME: - return get_name(); + status = get_name(); + break; case HAL_PROP_ADAPTER_UUIDS: - return get_uuids(); + status = get_uuids(); + break; case HAL_PROP_ADAPTER_CLASS: - return get_class(); + status = get_class(); + break; case HAL_PROP_ADAPTER_TYPE: - return get_type(); + status = get_type(); + break; case HAL_PROP_ADAPTER_SERVICE_REC: - return get_service(); + status = get_service(); + break; case HAL_PROP_ADAPTER_SCAN_MODE: - return get_scan_mode(); + status = get_scan_mode(); + break; case HAL_PROP_ADAPTER_BONDED_DEVICES: - return get_devices(); + status = get_devices(); + break; case HAL_PROP_ADAPTER_DISC_TIMEOUT: - return get_discoverable_timeout(); + status = get_discoverable_timeout(); + break; default: - return false; + status = HAL_STATUS_FAILED; + break; } + + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, status); } static void get_properties(void) @@ -1889,11 +1876,18 @@ static bool stop_discovery(void) return false; } -static uint8_t set_scan_mode(void *buf, uint16_t len) +static uint8_t set_scan_mode(const void *buf, uint16_t len) { - uint8_t *mode = buf; + const uint8_t *mode = buf; bool conn, disc, cur_conn, cur_disc; + if (len != sizeof(*mode)) { + error("Invalid set scan mode size (%u bytes), terminating", + len); + raise(SIGTERM); + return HAL_STATUS_FAILED; + } + cur_conn = adapter.current_settings & MGMT_SETTING_CONNECTABLE; cur_disc = adapter.current_settings & MGMT_SETTING_DISCOVERABLE; @@ -1931,7 +1925,7 @@ static uint8_t set_scan_mode(void *buf, uint16_t len) return HAL_STATUS_FAILED; } - if (cur_disc != disc) { + if (cur_disc != disc && conn) { if (!set_discoverable(disc ? 0x01 : 0x00, 0)) return HAL_STATUS_FAILED; } @@ -1945,21 +1939,35 @@ done: return HAL_STATUS_DONE; } -static uint8_t set_property(void *buf, uint16_t len) +static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len) { - struct hal_cmd_set_adapter_prop *cmd = buf; + const struct hal_cmd_set_adapter_prop *cmd = buf; + uint8_t status; + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid set adapter prop cmd (0x%x), terminating", + cmd->type); + raise(SIGTERM); + return; + } switch (cmd->type) { case HAL_PROP_ADAPTER_SCAN_MODE: - return set_scan_mode(cmd->val, cmd->len); + status = set_scan_mode(cmd->val, cmd->len); + break; case HAL_PROP_ADAPTER_NAME: - return set_adapter_name(cmd->val, cmd->len); + status = set_adapter_name(cmd->val, cmd->len); + break; case HAL_PROP_ADAPTER_DISC_TIMEOUT: - return set_discoverable_timeout(cmd->val); + status = set_discoverable_timeout(cmd->val, cmd->len); + break; default: DBG("Unhandled property type 0x%x", cmd->type); - return HAL_STATUS_FAILED; + status = HAL_STATUS_FAILED; + break; } + + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, status); } static void pair_device_complete(uint8_t status, uint16_t length, @@ -1978,9 +1986,10 @@ static void pair_device_complete(uint8_t status, uint16_t length, HAL_BOND_STATE_NONE); } -static bool create_bond(void *buf, uint16_t len) +static void handle_create_bond_cmd(const void *buf, uint16_t len) { - struct hal_cmd_create_bond *cmd = buf; + const struct hal_cmd_create_bond *cmd = buf; + uint8_t status; struct mgmt_cp_pair_device cp; cp.io_cap = DEFAULT_IO_CAPABILITY; @@ -1988,25 +1997,36 @@ static bool create_bond(void *buf, uint16_t len) android2bdaddr(cmd->bdaddr, &cp.addr.bdaddr); if (mgmt_send(mgmt_if, MGMT_OP_PAIR_DEVICE, adapter.index, sizeof(cp), - &cp, pair_device_complete, NULL, NULL) == 0) - return false; + &cp, pair_device_complete, NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto fail; + } + + status = HAL_STATUS_SUCCESS; set_device_bond_state(&cp.addr.bdaddr, HAL_STATUS_SUCCESS, HAL_BOND_STATE_BONDING); - return true; +fail: + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, status); } -static bool cancel_bond(void *buf, uint16_t len) +static void handle_cancel_bond_cmd(const void *buf, uint16_t len) { - struct hal_cmd_cancel_bond *cmd = buf; + const struct hal_cmd_cancel_bond *cmd = buf; struct mgmt_addr_info cp; + uint8_t status; cp.type = BDADDR_BREDR; android2bdaddr(cmd->bdaddr, &cp.bdaddr); - return mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index, - sizeof(cp), &cp, NULL, NULL, NULL) > 0; + if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) > 0) + status = HAL_STATUS_SUCCESS; + else + status = HAL_STATUS_FAILED; + + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, status); } static void unpair_device_complete(uint8_t status, uint16_t length, @@ -2023,23 +2043,30 @@ static void unpair_device_complete(uint8_t status, uint16_t length, HAL_BOND_STATE_NONE); } -static bool remove_bond(void *buf, uint16_t len) +static void handle_remove_bond_cmd(const void *buf, uint16_t len) { - struct hal_cmd_remove_bond *cmd = buf; + const struct hal_cmd_remove_bond *cmd = buf; struct mgmt_cp_unpair_device cp; + uint8_t status; cp.disconnect = 1; cp.addr.type = BDADDR_BREDR; android2bdaddr(cmd->bdaddr, &cp.addr.bdaddr); - return mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index, + if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index, sizeof(cp), &cp, unpair_device_complete, - NULL, NULL) > 0; + NULL, NULL) > 0) + status = HAL_STATUS_SUCCESS; + else + status = HAL_STATUS_FAILED; + + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, status); } -static uint8_t pin_reply(void *buf, uint16_t len) +static void handle_pin_reply_cmd(const void *buf, uint16_t len) { - struct hal_cmd_pin_reply *cmd = buf; + const struct hal_cmd_pin_reply *cmd = buf; + uint8_t status; bdaddr_t bdaddr; char addr[18]; @@ -2048,8 +2075,10 @@ static uint8_t pin_reply(void *buf, uint16_t len) DBG("%s accept %u pin_len %u", addr, cmd->accept, cmd->pin_len); - if (!cmd->accept && cmd->pin_len) - return HAL_STATUS_INVALID; + if (!cmd->accept && cmd->pin_len) { + status = HAL_STATUS_INVALID; + goto failed; + } if (cmd->accept) { struct mgmt_cp_pin_code_reply rp; @@ -2062,8 +2091,10 @@ static uint8_t pin_reply(void *buf, uint16_t len) memcpy(rp.pin_code, cmd->pin_code, rp.pin_len); if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_REPLY, adapter.index, - sizeof(rp), &rp, NULL, NULL, NULL) == 0) - return HAL_STATUS_FAILED; + sizeof(rp), &rp, NULL, NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto failed; + } } else { struct mgmt_cp_pin_code_neg_reply rp; @@ -2072,11 +2103,15 @@ static uint8_t pin_reply(void *buf, uint16_t len) if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_NEG_REPLY, adapter.index, sizeof(rp), &rp, - NULL, NULL, NULL) == 0) - return HAL_STATUS_FAILED; + NULL, NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto failed; + } } - return HAL_STATUS_SUCCESS; + status = HAL_STATUS_SUCCESS; +failed: + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, status); } static uint8_t user_confirm_reply(const bdaddr_t *bdaddr, bool accept) @@ -2133,11 +2168,11 @@ static uint8_t user_passkey_reply(const bdaddr_t *bdaddr, bool accept, return HAL_STATUS_SUCCESS; } -static uint8_t ssp_reply(void *buf, uint16_t len) +static void handle_ssp_reply_cmd(const void *buf, uint16_t len) { - struct hal_cmd_ssp_reply *cmd = buf; - uint8_t status; + const struct hal_cmd_ssp_reply *cmd = buf; bdaddr_t bdaddr; + uint8_t status; char addr[18]; /* TODO should parameters sanity be verified here? */ @@ -2164,150 +2199,226 @@ static uint8_t ssp_reply(void *buf, uint16_t len) break; } - return status; + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, status); } -static uint8_t get_remote_services(void *buf, uint16_t len) +static void handle_get_remote_services_cmd(const void *buf, uint16_t len) { - struct hal_cmd_get_remote_services *cmd = buf; + const struct hal_cmd_get_remote_services *cmd = buf; + uint8_t status; bdaddr_t addr; android2bdaddr(&cmd->bdaddr, &addr); - return browse_remote_sdp(&addr); + status = browse_remote_sdp(&addr); + + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES, + status); } -void bt_bluetooth_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len) +static void handle_enable_cmd(const void *buf, uint16_t len) { - uint8_t status = HAL_STATUS_FAILED; + uint8_t status; - switch (opcode) { - case HAL_OP_ENABLE: - /* Framework expects all properties to be emitted while - * enabling adapter */ - get_properties(); + /* Framework expects all properties to be emitted while + * enabling adapter */ + get_properties(); - if (adapter.current_settings & MGMT_SETTING_POWERED) { - status = HAL_STATUS_DONE; - goto error; - } + if (adapter.current_settings & MGMT_SETTING_POWERED) { + status = HAL_STATUS_DONE; + goto failed; + } - if (!set_mode(MGMT_OP_SET_POWERED, 0x01)) - goto error; + if (!set_mode(MGMT_OP_SET_POWERED, 0x01)) { + status = HAL_STATUS_FAILED; + goto failed; + } - break; - case HAL_OP_DISABLE: - if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { - status = HAL_STATUS_DONE; - goto error; - } + status = HAL_STATUS_SUCCESS; +failed: + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status); +} - if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) - goto error; +static void handle_disable_cmd(const void *buf, uint16_t len) +{ + uint8_t status; - break; - case HAL_OP_GET_ADAPTER_PROPS: - get_properties(); + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { + status = HAL_STATUS_DONE; + goto failed; + } - break; - case HAL_OP_GET_ADAPTER_PROP: - if (!get_property(buf, len)) - goto error; + if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) { + status = HAL_STATUS_FAILED; + goto failed; + } - break; - case HAL_OP_SET_ADAPTER_PROP: - status = set_property(buf, len); - if (status != HAL_STATUS_SUCCESS && status != HAL_STATUS_DONE) - goto error; + status = HAL_STATUS_SUCCESS; +failed: + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status); +} - break; - case HAL_OP_CREATE_BOND: - if (!create_bond(buf, len)) - goto error; +static void handle_get_adapter_props_cmd(const void *buf, uint16_t len) +{ + get_properties(); - break; - case HAL_OP_CANCEL_BOND: - if (!cancel_bond(buf, len)) - goto error; + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS, + HAL_STATUS_SUCCESS); +} - break; - case HAL_OP_REMOVE_BOND: - if (!remove_bond(buf, len)) - goto error; +static void handle_get_remote_device_props_cmd(const void *buf, uint16_t len) +{ + /* TODO */ - break; - case HAL_OP_PIN_REPLY: - status = pin_reply(buf, len); - if (status != HAL_STATUS_SUCCESS) - goto error; + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROPS, + HAL_STATUS_FAILED); +} - break; - case HAL_OP_SSP_REPLY: - status = ssp_reply(buf, len); - if (status != HAL_STATUS_SUCCESS) - goto error; - break; - case HAL_OP_START_DISCOVERY: - if (adapter.discovering) { - status = HAL_STATUS_DONE; - goto error; - } +static void handle_get_remote_device_prop_cmd(const void *buf, uint16_t len) +{ + /* TODO */ - if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { - status = HAL_STATUS_NOT_READY; - goto error; - } + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_DEVICE_PROP, + HAL_STATUS_FAILED); +} + +static void handle_set_remote_device_prop_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_set_remote_device_prop *cmd = buf; + uint8_t status; + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid set remote device prop cmd (0x%x), terminating", + cmd->type); + raise(SIGTERM); + return; + } - if (!start_discovery()) - goto error; + /* TODO */ + switch (cmd->type) { + default: + DBG("Unhandled property type 0x%x", cmd->type); + status = HAL_STATUS_FAILED; break; - case HAL_OP_CANCEL_DISCOVERY: - if (!adapter.discovering) { - status = HAL_STATUS_DONE; - goto error; - } + } - if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { - status = HAL_STATUS_NOT_READY; - goto error; - } + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_REMOTE_DEVICE_PROP, + status); +} - if (!stop_discovery()) - goto error; +static void handle_get_remote_service_rec_cmd(const void *buf, uint16_t len) +{ + /* TODO */ - break; - case HAL_OP_GET_REMOTE_SERVICES: - status = get_remote_services(buf, len); - if (status != HAL_STATUS_SUCCESS) - goto error; - break; - default: - DBG("Unhandled command, opcode 0x%x", opcode); - goto error; + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICE_REC, + HAL_STATUS_FAILED); +} + +static void handle_start_discovery_cmd(const void *buf, uint16_t len) +{ + uint8_t status; + + if (adapter.discovering) { + status = HAL_STATUS_DONE; + goto failed; } - ipc_send(sk, HAL_SERVICE_ID_BLUETOOTH, opcode, 0, NULL, -1); - return; + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { + status = HAL_STATUS_NOT_READY; + goto failed; + } -error: - error("Error handling command 0x%02x status %u", opcode, status); + if (!start_discovery()) { + status = HAL_STATUS_FAILED; + goto failed; + } - ipc_send_rsp(sk, HAL_SERVICE_ID_BLUETOOTH, status); + status = HAL_STATUS_SUCCESS; +failed: + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, status); } -bool bt_bluetooth_register(int sk) +static void handle_cancel_discovery_cmd(const void *buf, uint16_t len) { - DBG(""); + uint8_t status; - notification_sk = sk; + if (!adapter.discovering) { + status = HAL_STATUS_DONE; + goto failed; + } - return true; + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { + status = HAL_STATUS_NOT_READY; + goto failed; + } + + if (!stop_discovery()) { + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, status); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_ENABLE */ + { handle_enable_cmd, false, 0 }, + /* HAL_OP_DISABLE */ + { handle_disable_cmd, false, 0 }, + /* HAL_OP_GET_ADAPTER_PROPS */ + { handle_get_adapter_props_cmd, false, 0 }, + /* HAL_OP_GET_ADAPTER_PROP */ + { handle_get_adapter_prop_cmd, false, + sizeof(struct hal_cmd_get_adapter_prop) }, + /* HAL_OP_SET_ADAPTER_PROP */ + { handle_set_adapter_prop_cmd, true, + sizeof(struct hal_cmd_set_adapter_prop) }, + /* HAL_OP_GET_REMOTE_DEVICE_PROPS */ + { handle_get_remote_device_props_cmd, false, + sizeof(struct hal_cmd_get_remote_device_props) }, + /* HAL_OP_GET_REMOTE_DEVICE_PROP */ + { handle_get_remote_device_prop_cmd, false, + sizeof(struct hal_cmd_get_remote_device_prop) }, + /* HAL_OP_SET_REMOTE_DEVICE_PROP */ + { handle_set_remote_device_prop_cmd, true, + sizeof(struct hal_cmd_set_remote_device_prop) }, + /* HAL_OP_GET_REMOTE_SERVICE_REC */ + { handle_get_remote_service_rec_cmd, false, + sizeof(struct hal_cmd_get_remote_service_rec) }, + /* HAL_OP_GET_REMOTE_SERVICES */ + { handle_get_remote_services_cmd, false, + sizeof(struct hal_cmd_get_remote_services) }, + /* HAL_OP_START_DISCOVERY */ + { handle_start_discovery_cmd, false, 0 }, + /* HAL_OP_CANCEL_DISCOVERY */ + { handle_cancel_discovery_cmd, false, 0 }, + /* HAL_OP_CREATE_BOND */ + { handle_create_bond_cmd, false, sizeof(struct hal_cmd_create_bond) }, + /* HAL_OP_REMOVE_BOND */ + { handle_remove_bond_cmd, false, sizeof(struct hal_cmd_remove_bond) }, + /* HAL_OP_CANCEL_BOND */ + {handle_cancel_bond_cmd, false, sizeof(struct hal_cmd_cancel_bond) }, + /* HAL_OP_PIN_REPLY */ + { handle_pin_reply_cmd, false, sizeof(struct hal_cmd_pin_reply) }, + /* HAL_OP_SSP_REPLY */ + { handle_ssp_reply_cmd, false, sizeof(struct hal_cmd_ssp_reply) }, +}; + +void bt_bluetooth_register(void) +{ + DBG(""); + + ipc_register(HAL_SERVICE_ID_BLUETOOTH, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); } void bt_bluetooth_unregister(void) { DBG(""); - notification_sk = -1; + ipc_unregister(HAL_SERVICE_ID_CORE); } diff --git a/android/bluetooth.h b/android/bluetooth.h index 44b8e9e8..86872ee6 100644 --- a/android/bluetooth.h +++ b/android/bluetooth.h @@ -31,7 +31,7 @@ void bt_bluetooth_cleanup(void); void bt_bluetooth_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len); -bool bt_bluetooth_register(int sk); +void bt_bluetooth_register(void); void bt_bluetooth_unregister(void); int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint); diff --git a/android/client/if-sock.c b/android/client/if-sock.c index 2cd06e87..5394a5da 100644 --- a/android/client/if-sock.c +++ b/android/client/if-sock.c @@ -35,6 +35,10 @@ ENDMAP static int listen_fd[MAX_LISTEN_FD]; static int listen_fd_count; +static const char * const uuids[] = { + "00001101", "00001105", "0000112f", NULL +}; + /* * This function reads data from file descriptor and * prints it to the user @@ -130,6 +134,7 @@ static void read_accepted(int fd) memset(&msg, 0, sizeof(msg)); memset(&iv, 0, sizeof(iv)); + memset(cmsgbuf, 0, sizeof(cmsgbuf)); iv.iov_base = &cs; iv.iov_len = sizeof(cs); @@ -192,6 +197,9 @@ static void listen_c(int argc, const char **argv, enum_func *enum_func, if (argc == 3) { *user = TYPE_ENUM(btsock_type_t); *enum_func = enum_defines; + } else if (argc == 5) { + *user = (void *) uuids; + *enum_func = enum_strings; } } @@ -263,6 +271,9 @@ static void connect_c(int argc, const char **argv, enum_func *enum_func, } else if (argc == 4) { *user = TYPE_ENUM(btsock_type_t); *enum_func = enum_defines; + } else if (argc == 5) { + *user = (void *) uuids; + *enum_func = enum_strings; } } @@ -327,9 +338,9 @@ static void connect_p(int argc, const char **argv) /* Methods available in btsock_interface_t */ static struct method methods[] = { STD_METHODCH(listen, - "<sock_type> <srvc_name> <uuid> [<channle>] [<flags>]"), + "<sock_type> <srvc_name> <uuid> [<channel>] [<flags>]"), STD_METHODCH(connect, - "<addr> <sock_type> <uuid> <channle> [<flags>]"), + "<addr> <sock_type> <uuid> <channel> [<flags>]"), END_METHOD }; diff --git a/android/hal-a2dp.c b/android/hal-a2dp.c index e9fadb7a..c8989957 100644 --- a/android/hal-a2dp.c +++ b/android/hal-a2dp.c @@ -31,7 +31,7 @@ static bool interface_ready(void) return cbs != NULL; } -static void handle_conn_state(void *buf) +static void handle_conn_state(void *buf, uint16_t len) { struct hal_ev_a2dp_conn_state *ev = buf; @@ -40,7 +40,7 @@ static void handle_conn_state(void *buf) (bt_bdaddr_t *) (ev->bdaddr)); } -static void handle_audio_state(void *buf) +static void handle_audio_state(void *buf, uint16_t len) { struct hal_ev_a2dp_audio_state *ev = buf; @@ -48,24 +48,20 @@ static void handle_audio_state(void *buf) cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr)); } -/* will be called from notification thread context */ -void bt_notify_a2dp(uint8_t opcode, void *buf, uint16_t len) -{ - if (!interface_ready()) - return; - - switch (opcode) { - case HAL_EV_A2DP_CONN_STATE: - handle_conn_state(buf); - break; - case HAL_EV_A2DP_AUDIO_STATE: - handle_audio_state(buf); - break; - default: - DBG("Unhandled callback opcode=0x%x", opcode); - break; - } -} +/* handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ +static const struct hal_ipc_handler ev_handlers[] = { + { /* HAL_EV_A2DP_CONN_STATE */ + .handler = handle_conn_state, + .var_len = false, + .data_len = sizeof(struct hal_ev_a2dp_conn_state), + }, + { /* HAL_EV_A2DP_AUDIO_STATE */ + .handler = handle_audio_state, + .var_len = false, + .data_len = sizeof(struct hal_ev_a2dp_audio_state), + }, +}; static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr) { @@ -100,15 +96,29 @@ static bt_status_t disconnect(bt_bdaddr_t *bd_addr) static bt_status_t init(btav_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; + int ret; DBG(""); + if (interface_ready()) + return BT_STATUS_DONE; + cbs = callbacks; + hal_ipc_register(HAL_SERVICE_ID_A2DP, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + cmd.service_id = HAL_SERVICE_ID_A2DP; - return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, 0, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbs = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_A2DP); + } + + return ret; } static void cleanup() @@ -126,6 +136,8 @@ static void cleanup() hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, 0, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_A2DP); } static btav_interface_t iface = { diff --git a/android/hal-bluetooth.c b/android/hal-bluetooth.c index 078d5376..7cac15c7 100644 --- a/android/hal-bluetooth.c +++ b/android/hal-bluetooth.c @@ -28,14 +28,26 @@ static const bt_callbacks_t *bt_hal_cbacks = NULL; -#define create_enum_prop(prop, hal_prop, type) do { \ - type *pe = malloc(sizeof(type)); \ - prop.val = pe; \ - prop.len = sizeof(*pe); \ - *pe = *((uint8_t *) (hal_prop->val)); \ +#define enum_prop_to_hal(prop, hal_prop, type) do { \ + static type e; \ + prop.val = &e; \ + prop.len = sizeof(e); \ + e = *((uint8_t *) (hal_prop->val)); \ } while (0) -static void handle_adapter_state_changed(void *buf) +#define enum_prop_from_hal(prop, hal_len, hal_val, enum_type) do { \ + enum_type e; \ + if (prop->len != sizeof(e)) { \ + error("invalid HAL property %u (%u vs %zu), aborting ", \ + prop->type, prop->len, sizeof(e)); \ + exit(EXIT_FAILURE); \ + } \ + memcpy(&e, prop->val, sizeof(e)); \ + *((uint8_t *) hal_val) = e; /* enums are mapped to 1 byte */ \ + *hal_len = 1; \ +} while (0) + +static void handle_adapter_state_changed(void *buf, uint16_t len) { struct hal_ev_adapter_state_changed *ev = buf; @@ -46,105 +58,109 @@ static void handle_adapter_state_changed(void *buf) } static void adapter_props_to_hal(bt_property_t *send_props, - struct hal_property *hal_prop, - uint8_t num_props, void *buff_end) + struct hal_property *prop, + uint8_t num_props, uint16_t len) { - void *p = hal_prop; + void *buf = prop; uint8_t i; for (i = 0; i < num_props; i++) { - if (p + sizeof(*hal_prop) + hal_prop->len > buff_end) { - error("invalid adapter properties event, aborting"); + if (sizeof(*prop) + prop->len > len) { + error("invalid adapter properties(%zu > %u), aborting", + sizeof(*prop) + prop->len, len); exit(EXIT_FAILURE); } - send_props[i].type = hal_prop->type; + send_props[i].type = prop->type; - switch (hal_prop->type) { + switch (prop->type) { case HAL_PROP_ADAPTER_TYPE: - create_enum_prop(send_props[i], hal_prop, + enum_prop_to_hal(send_props[i], prop, bt_device_type_t); break; case HAL_PROP_ADAPTER_SCAN_MODE: - create_enum_prop(send_props[i], hal_prop, + enum_prop_to_hal(send_props[i], prop, bt_scan_mode_t); break; case HAL_PROP_ADAPTER_SERVICE_REC: default: - send_props[i].len = hal_prop->len; - send_props[i].val = hal_prop->val; + send_props[i].len = prop->len; + send_props[i].val = prop->val; break; } DBG("prop[%d]: %s", i, btproperty2str(&send_props[i])); + + len -= sizeof(*prop) + prop->len; + buf += sizeof(*prop) + prop->len; + prop = buf; } + + if (!len) + return; + + error("invalid adapter properties (%u bytes left), aborting", len); + exit(EXIT_FAILURE); } -static void adapter_hal_props_cleanup(bt_property_t *props, uint8_t num) +static void adapter_prop_from_hal(const bt_property_t *property, uint8_t *type, + uint16_t *len, void *val) { - uint8_t i; + /* type match IPC type */ + *type = property->type; - for (i = 0; i < num; i++) { - switch (props[i].type) { - case HAL_PROP_ADAPTER_TYPE: - case HAL_PROP_ADAPTER_SCAN_MODE: - free(props[i].val); - break; - default: - break; - } + switch(property->type) { + case HAL_PROP_ADAPTER_SCAN_MODE: + enum_prop_from_hal(property, len, val, bt_scan_mode_t); + break; + default: + *len = property->len; + memcpy(val, property->val, property->len); + break; } } static void device_props_to_hal(bt_property_t *send_props, - struct hal_property *hal_prop, - uint8_t num_props, void *buff_end) + struct hal_property *prop, uint8_t num_props, + uint16_t len) { - void *p = hal_prop; + void *buf = prop; uint8_t i; for (i = 0; i < num_props; i++) { - if (p + sizeof(*hal_prop) + hal_prop->len > buff_end) { - error("invalid adapter properties event, aborting"); + if (sizeof(*prop) + prop->len > len) { + error("invalid device properties (%zu > %u), aborting", + sizeof(*prop) + prop->len, len); exit(EXIT_FAILURE); } - send_props[i].type = hal_prop->type; + send_props[i].type = prop->type; - switch (hal_prop->type) { + switch (prop->type) { case HAL_PROP_DEVICE_TYPE: - create_enum_prop(send_props[i], hal_prop, + enum_prop_to_hal(send_props[i], prop, bt_device_type_t); break; case HAL_PROP_DEVICE_SERVICE_REC: case HAL_PROP_DEVICE_VERSION_INFO: default: - send_props[i].len = hal_prop->len; - send_props[i].val = hal_prop->val; + send_props[i].len = prop->len; + send_props[i].val = prop->val; break; } - p += sizeof(*hal_prop) + hal_prop->len; - hal_prop = p; + len -= sizeof(*prop) + prop->len; + buf += sizeof(*prop) + prop->len; + prop = buf; DBG("prop[%d]: %s", i, btproperty2str(&send_props[i])); } -} - -static void device_hal_props_cleanup(bt_property_t *props, uint8_t num) -{ - uint8_t i; + if (!len) + return; - for (i = 0; i < num; i++) { - switch (props[i].type) { - case HAL_PROP_DEVICE_TYPE: - free(props[i].val); - break; - default: - break; - } - } + error("invalid device properties (%u bytes left), aborting", len); + exit(EXIT_FAILURE); } static void handle_adapter_props_changed(void *buf, uint16_t len) @@ -157,14 +173,13 @@ static void handle_adapter_props_changed(void *buf, uint16_t len) if (!bt_hal_cbacks->adapter_properties_cb) return; - adapter_props_to_hal(props, ev->props, ev->num_props, buf + len); + len -= sizeof(*ev); + adapter_props_to_hal(props, ev->props, ev->num_props, len); bt_hal_cbacks->adapter_properties_cb(ev->status, ev->num_props, props); - - adapter_hal_props_cleanup(props, ev->num_props); } -static void handle_bond_state_change(void *buf) +static void handle_bond_state_change(void *buf, uint16_t len) { struct hal_ev_bond_state_changed *ev = buf; bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; @@ -176,7 +191,7 @@ static void handle_bond_state_change(void *buf) ev->state); } -static void handle_pin_request(void *buf) +static void handle_pin_request(void *buf, uint16_t len) { struct hal_ev_pin_request *ev = buf; /* Those are declared as packed, so it's safe to assign pointers */ @@ -189,7 +204,7 @@ static void handle_pin_request(void *buf) bt_hal_cbacks->pin_request_cb(addr, name, ev->class_of_dev); } -static void handle_ssp_request(void *buf) +static void handle_ssp_request(void *buf, uint16_t len) { struct hal_ev_ssp_request *ev = buf; /* Those are declared as packed, so it's safe to assign pointers */ @@ -221,7 +236,7 @@ static bool interface_ready(void) return bt_hal_cbacks != NULL; } -static void handle_discovery_state_changed(void *buf) +static void handle_discovery_state_changed(void *buf, uint16_t len) { struct hal_ev_discovery_state_changed *ev = buf; @@ -241,11 +256,10 @@ static void handle_device_found(void *buf, uint16_t len) if (!bt_hal_cbacks->device_found_cb) return; - device_props_to_hal(props, ev->props, ev->num_props, buf + len); + len -= sizeof(*ev); + device_props_to_hal(props, ev->props, ev->num_props, len); bt_hal_cbacks->device_found_cb(ev->num_props, props); - - device_hal_props_cleanup(props, ev->num_props); } static void handle_device_state_changed(void *buf, uint16_t len) @@ -258,16 +272,15 @@ static void handle_device_state_changed(void *buf, uint16_t len) if (!bt_hal_cbacks->remote_device_properties_cb) return; - device_props_to_hal(props, ev->props, ev->num_props, buf + len); + len -= sizeof(*ev); + device_props_to_hal(props, ev->props, ev->num_props, len); bt_hal_cbacks->remote_device_properties_cb(ev->status, (bt_bdaddr_t *)ev->bdaddr, ev->num_props, props); - - device_hal_props_cleanup(props, ev->num_props); } -static void handle_acl_state_changed(void *buf) +static void handle_acl_state_changed(void *buf, uint16_t len) { struct hal_ev_acl_state_changed *ev = buf; bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; @@ -279,47 +292,78 @@ static void handle_acl_state_changed(void *buf) ev->state); } -/* will be called from notification thread context */ -void bt_notify_adapter(uint8_t opcode, void *buf, uint16_t len) +static void handle_dut_mode_receive(void *buf, uint16_t len) { - if (!interface_ready()) - return; + struct hal_ev_dut_mode_receive *ev = buf; - DBG("opcode 0x%x", opcode); + DBG(""); - switch (opcode) { - case HAL_EV_ADAPTER_STATE_CHANGED: - handle_adapter_state_changed(buf); - break; - case HAL_EV_ADAPTER_PROPS_CHANGED: - handle_adapter_props_changed(buf, len); - break; - case HAL_EV_DISCOVERY_STATE_CHANGED: - handle_discovery_state_changed(buf); - break; - case HAL_EV_DEVICE_FOUND: - handle_device_found(buf, len); - break; - case HAL_EV_REMOTE_DEVICE_PROPS: - handle_device_state_changed(buf, len); - break; - case HAL_EV_BOND_STATE_CHANGED: - handle_bond_state_change(buf); - break; - case HAL_EV_PIN_REQUEST: - handle_pin_request(buf); - break; - case HAL_EV_SSP_REQUEST: - handle_ssp_request(buf); - break; - case HAL_EV_ACL_STATE_CHANGED: - handle_acl_state_changed(buf); - break; - default: - DBG("Unhandled callback opcode=0x%x", opcode); - break; + if (len != sizeof(*ev) + ev->len) { + error("invalid dut mode receive event (%u), aborting", len); + exit(EXIT_FAILURE); } -} + + if (bt_hal_cbacks->dut_mode_recv_cb) + bt_hal_cbacks->dut_mode_recv_cb(ev->opcode, ev->data, ev->len); +} + +/* handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ +static const struct hal_ipc_handler ev_handlers[] = { + { /* HAL_EV_ADAPTER_STATE_CHANGED */ + .handler = handle_adapter_state_changed, + .var_len = false, + .data_len = sizeof(struct hal_ev_adapter_state_changed) + }, + { /* HAL_EV_ADAPTER_PROPS_CHANGED */ + .handler = handle_adapter_props_changed, + .var_len = true, + .data_len = sizeof(struct hal_ev_adapter_props_changed) + + sizeof(struct hal_property), + }, + { /* HAL_EV_REMOTE_DEVICE_PROPS */ + .handler = handle_device_state_changed, + .var_len = true, + .data_len = sizeof(struct hal_ev_remote_device_props) + + sizeof(struct hal_property), + }, + { /* HAL_EV_DEVICE_FOUND */ + .handler = handle_device_found, + .var_len = true, + .data_len = sizeof(struct hal_ev_device_found) + + sizeof(struct hal_property), + }, + { /* HAL_EV_DISCOVERY_STATE_CHANGED */ + .handler = handle_discovery_state_changed, + .var_len = false, + .data_len = sizeof(struct hal_ev_discovery_state_changed), + }, + { /* HAL_EV_PIN_REQUEST */ + .handler = handle_pin_request, + .var_len = false, + .data_len = sizeof(struct hal_ev_pin_request), + }, + { /* HAL_EV_SSP_REQUEST */ + .handler = handle_ssp_request, + .var_len = false, + .data_len = sizeof(struct hal_ev_ssp_request), + }, + { /* HAL_EV_BOND_STATE_CHANGED */ + .handler = handle_bond_state_change, + .var_len = false, + .data_len = sizeof(struct hal_ev_bond_state_changed), + }, + { /* HAL_EV_ACL_STATE_CHANGED */ + .handler = handle_acl_state_changed, + .var_len = false, + .data_len = sizeof(struct hal_ev_acl_state_changed), + }, + { /* HAL_EV_DUT_MODE_RECEIVE */ + .handler = handle_dut_mode_receive, + .var_len = true, + .data_len = sizeof(struct hal_ev_dut_mode_receive), + }, +}; static int init(bt_callbacks_t *callbacks) { @@ -329,10 +373,13 @@ static int init(bt_callbacks_t *callbacks) DBG(""); if (interface_ready()) - return BT_STATUS_SUCCESS; + return BT_STATUS_DONE; bt_hal_cbacks = callbacks; + hal_ipc_register(HAL_SERVICE_ID_BLUETOOTH, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + if (!hal_ipc_init()) { bt_hal_cbacks = NULL; return BT_STATUS_FAIL; @@ -361,6 +408,9 @@ static int init(bt_callbacks_t *callbacks) fail: hal_ipc_cleanup(); bt_hal_cbacks = NULL; + + hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH); + return status; } @@ -396,6 +446,8 @@ static void cleanup(void) hal_ipc_cleanup(); bt_hal_cbacks = NULL; + + hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH); } static int get_adapter_properties(void) @@ -418,21 +470,6 @@ static int get_adapter_property(bt_property_type_t type) if (!interface_ready()) return BT_STATUS_NOT_READY; - switch (type) { - case BT_PROPERTY_BDNAME: - case BT_PROPERTY_BDADDR: - case BT_PROPERTY_UUIDS: - case BT_PROPERTY_CLASS_OF_DEVICE: - case BT_PROPERTY_TYPE_OF_DEVICE: - case BT_PROPERTY_SERVICE_RECORD: - case BT_PROPERTY_ADAPTER_SCAN_MODE: - case BT_PROPERTY_ADAPTER_BONDED_DEVICES: - case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT: - break; - default: - return BT_STATUS_PARM_INVALID; - } - /* type match IPC type */ cmd.type = type; @@ -450,66 +487,90 @@ static int set_adapter_property(const bt_property_t *property) if (!interface_ready()) return BT_STATUS_NOT_READY; - switch (property->type) { - case BT_PROPERTY_BDNAME: - case BT_PROPERTY_ADAPTER_SCAN_MODE: - case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT: - break; - default: - return BT_STATUS_PARM_INVALID; - } - - /* type match IPC type */ - cmd->type = property->type; - cmd->len = property->len; - memcpy(cmd->val, property->val, property->len); + adapter_prop_from_hal(property, &cmd->type, &cmd->len, cmd->val); return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, - sizeof(buf), cmd, 0, NULL, NULL); + sizeof(*cmd) + cmd->len, cmd, 0, NULL, NULL); } static int get_remote_device_properties(bt_bdaddr_t *remote_addr) { + struct hal_cmd_get_remote_device_props cmd; + DBG("bdaddr: %s", bdaddr2str(remote_addr)); if (!interface_ready()) return BT_STATUS_NOT_READY; - return BT_STATUS_UNSUPPORTED; + memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROPS, + sizeof(cmd), &cmd, 0, NULL, NULL); } static int get_remote_device_property(bt_bdaddr_t *remote_addr, bt_property_type_t type) { + struct hal_cmd_get_remote_device_prop cmd; + DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr), bt_property_type_t2str(type)); if (!interface_ready()) return BT_STATUS_NOT_READY; - return BT_STATUS_UNSUPPORTED; + memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); + + /* type match IPC type */ + cmd.type = type; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROP, + sizeof(cmd), &cmd, 0, NULL, NULL); } static int set_remote_device_property(bt_bdaddr_t *remote_addr, const bt_property_t *property) { + struct hal_cmd_set_remote_device_prop *cmd; + uint8_t buf[sizeof(*cmd) + property->len]; + DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr), - btproperty2str(property)); + bt_property_type_t2str(property->type)); if (!interface_ready()) return BT_STATUS_NOT_READY; - return BT_STATUS_UNSUPPORTED; + cmd = (void *) buf; + + memcpy(cmd->bdaddr, remote_addr, sizeof(cmd->bdaddr)); + + /* type match IPC type */ + cmd->type = property->type; + cmd->len = property->len; + memcpy(cmd->val, property->val, property->len); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_REMOTE_DEVICE_PROP, + sizeof(buf), cmd, 0, NULL, NULL); } static int get_remote_service_record(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid) { + struct hal_cmd_get_remote_service_rec cmd; + DBG("bdaddr: %s", bdaddr2str(remote_addr)); if (!interface_ready()) return BT_STATUS_NOT_READY; - return BT_STATUS_UNSUPPORTED; + memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); + memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICE_REC, + sizeof(cmd), &cmd, 0, NULL, NULL); } static int get_remote_services(bt_bdaddr_t *remote_addr) @@ -638,7 +699,7 @@ static int ssp_reply(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant, static const void *get_profile_interface(const char *profile_id) { - DBG("%s: %s", __func__, profile_id); + DBG("%s", profile_id); if (!interface_ready()) return NULL; @@ -660,22 +721,35 @@ static const void *get_profile_interface(const char *profile_id) static int dut_mode_configure(uint8_t enable) { - DBG(""); + struct hal_cmd_dut_mode_conf cmd; + + DBG("enable %u", enable); if (!interface_ready()) return BT_STATUS_NOT_READY; - return BT_STATUS_UNSUPPORTED; + cmd.enable = enable; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF, + sizeof(cmd), &cmd, 0, NULL, NULL); } static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t len) { - DBG(""); + uint8_t cmd_buf[sizeof(struct hal_cmd_dut_mode_send) + len]; + struct hal_cmd_dut_mode_send *cmd = (void *) cmd_buf; + + DBG("opcode %u len %u", opcode, len); if (!interface_ready()) return BT_STATUS_NOT_READY; - return BT_STATUS_UNSUPPORTED; + cmd->opcode = opcode; + cmd->len = len; + memcpy(cmd->data, buf, cmd->len); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND, + sizeof(cmd_buf), cmd, 0, NULL, NULL); } static const bt_interface_t bluetooth_if = { diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c index 2ce17a39..6a6b6825 100644 --- a/android/hal-hidhost.c +++ b/android/hal-hidhost.c @@ -32,7 +32,7 @@ static bool interface_ready(void) return cbacks != NULL; } -static void handle_conn_state(void *buf) +static void handle_conn_state(void *buf, uint16_t len) { struct hal_ev_hidhost_conn_state *ev = buf; @@ -41,7 +41,7 @@ static void handle_conn_state(void *buf) ev->state); } -static void handle_info(void *buf) +static void handle_info(void *buf, uint16_t len) { struct hal_ev_hidhost_info *ev = buf; bthh_hid_info_t info; @@ -60,7 +60,7 @@ static void handle_info(void *buf) cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info); } -static void handle_proto_mode(void *buf) +static void handle_proto_mode(void *buf, uint16_t len) { struct hal_ev_hidhost_proto_mode *ev = buf; @@ -69,16 +69,21 @@ static void handle_proto_mode(void *buf) ev->status, ev->mode); } -static void handle_get_report(void *buf) +static void handle_get_report(void *buf, uint16_t len) { struct hal_ev_hidhost_get_report *ev = buf; + if (len != sizeof(*ev) + ev->len) { + error("invalid get report event, aborting"); + exit(EXIT_FAILURE); + } + if (cbacks->get_report_cb) cbacks->get_report_cb((bt_bdaddr_t *) ev->bdaddr, ev->status, ev->data, ev->len); } -static void handle_virtual_unplug(void *buf) +static void handle_virtual_unplug(void *buf, uint16_t len) { struct hal_ev_hidhost_virtual_unplug *ev = buf; @@ -87,33 +92,35 @@ static void handle_virtual_unplug(void *buf) ev->status); } -/* will be called from notification thread context */ -void bt_notify_hidhost(uint8_t opcode, void *buf, uint16_t len) -{ - if (!interface_ready()) - return; - - switch (opcode) { - case HAL_EV_HIDHOST_CONN_STATE: - handle_conn_state(buf); - break; - case HAL_EV_HIDHOST_INFO: - handle_info(buf); - break; - case HAL_EV_HIDHOST_PROTO_MODE: - handle_proto_mode(buf); - break; - case HAL_EV_HIDHOST_GET_REPORT: - handle_get_report(buf); - break; - case HAL_EV_HIDHOST_VIRTUAL_UNPLUG: - handle_virtual_unplug(buf); - break; - default: - DBG("Unhandled callback opcode=0x%x", opcode); - break; - } -} +/* handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ +static const struct hal_ipc_handler ev_handlers[] = { + { /* HAL_EV_HIDHOST_CONN_STATE */ + .handler = handle_conn_state, + .var_len = false, + .data_len = sizeof(struct hal_ev_hidhost_conn_state) + }, + { /* HAL_EV_HIDHOST_INFO */ + .handler = handle_info, + .var_len = false, + .data_len = sizeof(struct hal_ev_hidhost_info), + }, + { /* HAL_EV_HIDHOST_PROTO_MODE */ + .handler = handle_proto_mode, + .var_len = false, + .data_len = sizeof(struct hal_ev_hidhost_proto_mode), + }, + { /* HAL_EV_HIDHOST_GET_REPORT */ + .handler = handle_get_report, + .var_len = true, + .data_len = sizeof(struct hal_ev_hidhost_get_report), + }, + { /* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */ + .handler = handle_virtual_unplug, + .var_len = false, + .data_len = sizeof(struct hal_ev_hidhost_virtual_unplug), + }, +}; static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr) { @@ -356,16 +363,30 @@ static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data) static bt_status_t init(bthh_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; + int ret; DBG(""); + if (interface_ready()) + return BT_STATUS_DONE; + /* store reference to user callbacks */ cbacks = callbacks; + hal_ipc_register(HAL_SERVICE_ID_HIDHOST, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + cmd.service_id = HAL_SERVICE_ID_HIDHOST; - return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, 0, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbacks = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST); + } + + return ret; } static void cleanup(void) @@ -383,6 +404,8 @@ static void cleanup(void) hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, 0, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST); } static bthh_interface_t hidhost_if = { diff --git a/android/hal-ipc.c b/android/hal-ipc.c index 026e2459..b19704aa 100644 --- a/android/hal-ipc.c +++ b/android/hal-ipc.c @@ -43,26 +43,86 @@ static pthread_mutex_t cmd_sk_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_t notif_th = 0; -static void notification_dispatch(struct hal_hdr *msg, int fd) +struct service_handler { + const struct hal_ipc_handler *handler; + uint8_t size; +}; + +static struct service_handler services[HAL_SERVICE_ID_MAX + 1]; + +void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers, + uint8_t size) { - switch (msg->service_id) { - case HAL_SERVICE_ID_BLUETOOTH: - bt_notify_adapter(msg->opcode, msg->payload, msg->len); - break; - case HAL_SERVICE_ID_HIDHOST: - bt_notify_hidhost(msg->opcode, msg->payload, msg->len); - break; - case HAL_SERVICE_ID_A2DP: - bt_notify_a2dp(msg->opcode, msg->payload, msg->len); - break; - case HAL_SERVICE_ID_PAN: - bt_notify_pan(msg->opcode, msg->payload, msg->len); - break; - default: - DBG("Unhandled notification service=%d opcode=0x%x", + services[service].handler = handlers; + services[service].size = size; +} + +void hal_ipc_unregister(uint8_t service) +{ + services[service].handler = NULL; + services[service].size = 0; +} + +static void handle_msg(void *buf, ssize_t len) +{ + struct hal_hdr *msg = buf; + const struct hal_ipc_handler *handler; + uint8_t opcode; + + if (len < (ssize_t) sizeof(*msg)) { + error("IPC: message too small (%zd bytes), aborting", len); + exit(EXIT_FAILURE); + } + + if (len != (ssize_t) (sizeof(*msg) + msg->len)) { + error("IPC: message malformed (%zd bytes), aborting", len); + exit(EXIT_FAILURE); + } + + /* if service is valid */ + if (msg->service_id > HAL_SERVICE_ID_MAX) { + error("IPC: unknown service (0x%x), aborting", + msg->service_id); + exit(EXIT_FAILURE); + } + + /* if service is registered */ + if (!services[msg->service_id].handler) { + error("IPC: unregistered service (0x%x), aborting", + msg->service_id); + exit(EXIT_FAILURE); + } + + /* if opcode fit valid range */ + if (msg->opcode < HAL_MINIMUM_EVENT) { + error("IPC: invalid opcode for service 0x%x (0x%x), aborting", msg->service_id, msg->opcode); - break; + exit(EXIT_FAILURE); } + + /* opcode is used as table offset and must be adjusted as events start + * with HAL_MINIMUM_EVENT offset */ + opcode = msg->opcode - HAL_MINIMUM_EVENT; + + /* if opcode is valid */ + if (opcode >= services[msg->service_id].size) { + error("IPC: invalid opcode for service 0x%x (0x%x), aborting", + msg->service_id, msg->opcode); + exit(EXIT_FAILURE); + } + + handler = &services[msg->service_id].handler[opcode]; + + /* if payload size is valid */ + if ((handler->var_len && handler->data_len > msg->len) || + (!handler->var_len && handler->data_len != msg->len)) { + error("IPC: message size invalid for service 0x%x opcode 0x%x " + "(%u bytes), aborting", + msg->service_id, msg->opcode, msg->len); + exit(EXIT_FAILURE); + } + + handler->handler(msg->payload, msg->len); } static void *notification_handler(void *data) @@ -72,7 +132,6 @@ static void *notification_handler(void *data) struct cmsghdr *cmsg; char cmsgbuf[CMSG_SPACE(sizeof(int))]; char buf[BLUEZ_HAL_MTU]; - struct hal_hdr *ev = (void *) buf; ssize_t ret; int fd; @@ -83,7 +142,7 @@ static void *notification_handler(void *data) memset(buf, 0, sizeof(buf)); memset(cmsgbuf, 0, sizeof(cmsgbuf)); - iv.iov_base = ev; + iv.iov_base = buf; iv.iov_len = sizeof(buf); msg.msg_iov = &iv; @@ -108,24 +167,6 @@ static void *notification_handler(void *data) exit(EXIT_FAILURE); } - if (ret < (ssize_t) sizeof(*ev)) { - error("Too small notification (%zd bytes), aborting", - ret); - exit(EXIT_FAILURE); - } - - if (ev->opcode < HAL_MINIMUM_EVENT) { - error("Invalid notification (0x%x), aborting", - ev->opcode); - exit(EXIT_FAILURE); - } - - if (ret != (ssize_t) (sizeof(*ev) + ev->len)) { - error("Malformed notification(%zd bytes), aborting", - ret); - exit(EXIT_FAILURE); - } - fd = -1; /* Receive auxiliary data in msg */ @@ -138,7 +179,7 @@ static void *notification_handler(void *data) } } - notification_dispatch(ev, fd); + handle_msg(buf, ret); } close(notif_sk); @@ -315,6 +356,12 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, exit(EXIT_FAILURE); } + /* socket was shutdown */ + if (ret == 0) { + error("Command socket closed, aborting"); + exit(EXIT_FAILURE); + } + memset(&msg, 0, sizeof(msg)); memset(&cmd, 0, sizeof(cmd)); @@ -367,6 +414,17 @@ int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, if (cmd.opcode == HAL_OP_STATUS) { struct hal_status *s = rsp; + + if (sizeof(*s) != cmd.len) { + error("Invalid status length, aborting"); + exit(EXIT_FAILURE); + } + + if (s->code == HAL_STATUS_SUCCESS) { + error("Invalid success status response, aborting"); + exit(EXIT_FAILURE); + } + return s->code; } diff --git a/android/hal-ipc.h b/android/hal-ipc.h index ea53e1c3..2fbf30f9 100644 --- a/android/hal-ipc.h +++ b/android/hal-ipc.h @@ -15,8 +15,18 @@ * */ +struct hal_ipc_handler { + void (*handler) (void *buf, uint16_t len); + bool var_len; + size_t data_len; +}; + bool hal_ipc_init(void); void hal_ipc_cleanup(void); int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, size_t *rsp_len, void *rsp, int *fd); + +void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers, + uint8_t size); +void hal_ipc_unregister(uint8_t service); diff --git a/android/hal-msg.h b/android/hal-msg.h index 44fd5c84..80c4a255 100644 --- a/android/hal-msg.h +++ b/android/hal-msg.h @@ -232,6 +232,8 @@ struct hal_cmd_sock_connect { uint8_t flags; } __attribute__((packed)); +/* Bluetooth HID Host HAL API */ + #define HAL_OP_HIDHOST_CONNECT 0x01 struct hal_cmd_hidhost_connect { uint8_t bdaddr[6]; diff --git a/android/hal-pan.c b/android/hal-pan.c index 2bc560e2..8c0f8d82 100644 --- a/android/hal-pan.c +++ b/android/hal-pan.c @@ -31,7 +31,7 @@ static bool interface_ready(void) return cbs != NULL; } -static void handle_conn_state(void *buf) +static void handle_conn_state(void *buf, uint16_t len) { struct hal_ev_pan_conn_state *ev = buf; @@ -41,7 +41,7 @@ static void handle_conn_state(void *buf) ev->local_role, ev->remote_role); } -static void handle_ctrl_state(void *buf) +static void handle_ctrl_state(void *buf, uint16_t len) { struct hal_ev_pan_ctrl_state *ev = buf; @@ -50,23 +50,20 @@ static void handle_ctrl_state(void *buf) ev->local_role, (char *)ev->name); } -void bt_notify_pan(uint8_t opcode, void *buf, uint16_t len) -{ - if (!interface_ready()) - return; - - switch (opcode) { - case HAL_EV_PAN_CONN_STATE: - handle_conn_state(buf); - break; - case HAL_EV_PAN_CTRL_STATE: - handle_ctrl_state(buf); - break; - default: - DBG("Unhandled callback opcode=0x%x", opcode); - break; - } -} +/* handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' */ +static const struct hal_ipc_handler ev_handlers[] = { + { /* HAL_EV_PAN_CTRL_STATE */ + .handler = handle_ctrl_state, + .var_len = false, + .data_len = sizeof(struct hal_ev_pan_ctrl_state), + }, + { /* HAL_EV_PAN_CONN_STATE */ + .handler = handle_conn_state, + .var_len = false, + .data_len = sizeof(struct hal_ev_pan_conn_state), + }, +}; static bt_status_t pan_enable(int local_role) { @@ -138,15 +135,29 @@ static bt_status_t pan_disconnect(const bt_bdaddr_t *bd_addr) static bt_status_t pan_init(const btpan_callbacks_t *callbacks) { struct hal_cmd_register_module cmd; + int ret; DBG(""); + if (interface_ready()) + return BT_STATUS_DONE; + cbs = callbacks; + hal_ipc_register(HAL_SERVICE_ID_PAN, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + cmd.service_id = HAL_SERVICE_ID_PAN; - return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, sizeof(cmd), &cmd, 0, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbs = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_PAN); + } + + return ret; } static void pan_cleanup() @@ -164,6 +175,8 @@ static void pan_cleanup() hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, sizeof(cmd), &cmd, 0, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_PAN); } static btpan_interface_t pan_if = { diff --git a/android/hal-sock.c b/android/hal-sock.c index b7bc88e9..f45be30e 100644 --- a/android/hal-sock.c +++ b/android/hal-sock.c @@ -34,12 +34,17 @@ static bt_status_t sock_listen_rfcomm(const char *service_name, DBG(""); + memset(&cmd, 0, sizeof(cmd)); + cmd.flags = flags; cmd.type = BTSOCK_RFCOMM; cmd.channel = chan; - memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); - memset(cmd.name, 0, sizeof(cmd.name)); - memcpy(cmd.name, service_name, strlen(service_name)); + + if (uuid) + memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); + + if (service_name) + memcpy(cmd.name, service_name, strlen(service_name)); return hal_ipc_cmd(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, sizeof(cmd), &cmd, NULL, NULL, sock); @@ -55,8 +60,8 @@ static bt_status_t sock_listen(btsock_type_t type, const char *service_name, return BT_STATUS_PARM_INVALID; } - DBG("uuid %s chan %d sock %p type %d service_name %s", - btuuid2str(uuid), chan, sock, type, service_name); + DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x", + btuuid2str(uuid), chan, sock, type, service_name, flags); switch (type) { case BTSOCK_RFCOMM: @@ -77,23 +82,28 @@ static bt_status_t sock_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type, struct hal_cmd_sock_connect cmd; if ((!uuid && chan <= 0) || !bdaddr || !sock) { - error("Invalid params: bd_addr %p, uuid %s, chan %d, sock %p", - bdaddr, btuuid2str(uuid), chan, sock); + error("Invalid params: bd_addr %s, uuid %s, chan %d, sock %p", + bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock); return BT_STATUS_PARM_INVALID; } - DBG("uuid %s chan %d sock %p type %d", btuuid2str(uuid), chan, sock, - type); + DBG("bdaddr %s uuid %s chan %d sock %p type %d flags 0x%02x", + bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock, type, flags); if (type != BTSOCK_RFCOMM) { error("Socket type %u not supported", type); return BT_STATUS_UNSUPPORTED; } + memset(&cmd, 0, sizeof(cmd)); + cmd.flags = flags; cmd.type = type; cmd.channel = chan; - memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); + + if (uuid) + memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); + memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr)); return hal_ipc_cmd(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT, diff --git a/android/hal-utils.c b/android/hal-utils.c index 4f44d989..e3c0c607 100644 --- a/android/hal-utils.c +++ b/android/hal-utils.c @@ -33,6 +33,9 @@ const char *bt_uuid_t2str(const uint8_t *uuid, char *buf) unsigned int i; int is_bt; + if (!uuid) + return strcpy(buf, "NULL"); + is_bt = !memcmp(&uuid[4], &BT_BASE_UUID[4], HAL_UUID_LEN - 4); for (i = 0; i < HAL_UUID_LEN; i++) { @@ -167,6 +170,9 @@ const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf) { const uint8_t *p = bd_addr->address; + if (!bd_addr) + return strcpy(buf, "NULL"); + snprintf(buf, MAX_ADDR_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2], p[3], p[4], p[5]); diff --git a/android/hal.h b/android/hal.h index 72090fe5..b4754114 100644 --- a/android/hal.h +++ b/android/hal.h @@ -26,9 +26,5 @@ bthh_interface_t *bt_get_hidhost_interface(void); btpan_interface_t *bt_get_pan_interface(void); btav_interface_t *bt_get_a2dp_interface(void); -void bt_notify_adapter(uint8_t opcode, void *buf, uint16_t len); void bt_thread_associate(void); void bt_thread_disassociate(void); -void bt_notify_hidhost(uint8_t opcode, void *buf, uint16_t len); -void bt_notify_a2dp(uint8_t opcode, void *buf, uint16_t len); -void bt_notify_pan(uint8_t opcode, void *buf, uint16_t len); diff --git a/android/hidhost.c b/android/hidhost.c index f5a607c2..8bfdfeda 100644 --- a/android/hidhost.c +++ b/android/hidhost.c @@ -78,7 +78,6 @@ static bdaddr_t adapter_addr; -static int notification_sk = -1; static GIOChannel *ctrl_io = NULL; static GIOChannel *intr_io = NULL; static GSList *devices = NULL; @@ -297,8 +296,8 @@ static void bt_hid_notify_state(struct hid_device *dev, uint8_t state) bdaddr2android(&dev->dst, ev.bdaddr); ev.state = state; - ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST, - HAL_EV_HIDHOST_CONN_STATE, sizeof(ev), &ev, -1); + ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_CONN_STATE, + sizeof(ev), &ev); } static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, @@ -356,8 +355,8 @@ static void bt_hid_notify_proto_mode(struct hid_device *dev, uint8_t *buf, ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL; } - ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST, - HAL_EV_HIDHOST_PROTO_MODE, sizeof(ev), &ev, -1); + ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_PROTO_MODE, + sizeof(ev), &ev); } static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf, @@ -399,8 +398,8 @@ static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf, } send: - ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST, - HAL_EV_HIDHOST_GET_REPORT, ev_len, ev, -1); + ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_GET_REPORT, + ev_len, ev); g_free(ev); } @@ -424,8 +423,8 @@ static void bt_hid_notify_virtual_unplug(struct hid_device *dev, ev.status = HAL_HIDHOST_STATUS_OK; } - ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST, - HAL_EV_HIDHOST_VIRTUAL_UNPLUG, sizeof(ev), &ev, -1); + ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_VIRTUAL_UNPLUG, + sizeof(ev), &ev); } @@ -509,21 +508,22 @@ static void bt_hid_set_info(struct hid_device *dev) memset(ev.descr, 0, sizeof(ev.descr)); memcpy(ev.descr, dev->rd_data, ev.descr_len); - ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO, - sizeof(ev), &ev, -1); + ipc_send_notif(HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO, sizeof(ev), + &ev); } static int uhid_create(struct hid_device *dev) { GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_NVAL; - GIOChannel *io; struct uhid_event ev; + GIOChannel *io; + int err; dev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC); if (dev->uhid_fd < 0) { + err = -errno; error("Failed to open uHID device: %s", strerror(errno)); - bt_hid_notify_state(dev, HAL_HIDHOST_STATE_NO_HID); - return -errno; + return err; } memset(&ev, 0, sizeof(ev)); @@ -538,11 +538,11 @@ static int uhid_create(struct hid_device *dev) ev.u.create.rd_data = dev->rd_data; if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0) { + err = -errno; error("Failed to create uHID device: %s", strerror(errno)); close(dev->uhid_fd); dev->uhid_fd = -1; - bt_hid_notify_state(dev, HAL_HIDHOST_STATE_NO_HID); - return -errno; + return err; } io = g_io_channel_unix_new(dev->uhid_fd); @@ -559,16 +559,20 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, gpointer user_data) { struct hid_device *dev = user_data; + uint8_t state; DBG(""); if (conn_err) { error("%s", conn_err->message); + state = HAL_HIDHOST_STATE_FAILED; goto failed; } - if (uhid_create(dev) < 0) + if (uhid_create(dev) < 0) { + state = HAL_HIDHOST_STATE_NO_HID; goto failed; + } dev->intr_watch = g_io_add_watch(dev->intr_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, @@ -579,6 +583,7 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, return; failed: + bt_hid_notify_state(dev, state); hid_device_free(dev); } @@ -715,10 +720,11 @@ fail: hid_device_free(dev); } -static uint8_t bt_hid_connect(struct hal_cmd_hidhost_connect *cmd, - uint16_t len) +static void bt_hid_connect(const void *buf, uint16_t len) { + const struct hal_cmd_hidhost_connect *cmd = buf; struct hid_device *dev; + uint8_t status; char addr[18]; bdaddr_t dst; GSList *l; @@ -726,14 +732,13 @@ static uint8_t bt_hid_connect(struct hal_cmd_hidhost_connect *cmd, DBG(""); - if (len < sizeof(*cmd)) - return HAL_STATUS_INVALID; - android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); - if (l) - return HAL_STATUS_FAILED; + if (l) { + status = HAL_STATUS_FAILED; + goto failed; + } dev = g_new0(struct hid_device, 1); bacpy(&dev->dst, &dst); @@ -747,32 +752,36 @@ static uint8_t bt_hid_connect(struct hal_cmd_hidhost_connect *cmd, hid_sdp_search_cb, dev, NULL) < 0) { error("Failed to search sdp details"); hid_device_free(dev); - return HAL_STATUS_FAILED; + status = HAL_STATUS_FAILED; + goto failed; } devices = g_slist_append(devices, dev); bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING); - return HAL_STATUS_SUCCESS; + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, status); } -static uint8_t bt_hid_disconnect(struct hal_cmd_hidhost_disconnect *cmd, - uint16_t len) +static void bt_hid_disconnect(const void *buf, uint16_t len) { + const struct hal_cmd_hidhost_disconnect *cmd = buf; struct hid_device *dev; + uint8_t status; GSList *l; bdaddr_t dst; DBG(""); - if (len < sizeof(*cmd)) - return HAL_STATUS_INVALID; - android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); - if (!l) - return HAL_STATUS_FAILED; + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } dev = l->data; @@ -785,33 +794,38 @@ static uint8_t bt_hid_disconnect(struct hal_cmd_hidhost_disconnect *cmd, bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); - return HAL_STATUS_SUCCESS; + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, status); } -static uint8_t bt_hid_virtual_unplug(struct hal_cmd_hidhost_virtual_unplug *cmd, - uint16_t len) +static void bt_hid_virtual_unplug(const void *buf, uint16_t len) { + const struct hal_cmd_hidhost_virtual_unplug *cmd = buf; struct hid_device *dev; GSList *l; + uint8_t status; bdaddr_t dst; uint8_t hdr; int fd; DBG(""); - if (len < sizeof(*cmd)) - return HAL_STATUS_INVALID; - android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); - if (!l) - return HAL_STATUS_FAILED; + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } dev = l->data; - if (!(dev->ctrl_io)) - return HAL_STATUS_FAILED; + if (!(dev->ctrl_io)) { + status = HAL_STATUS_FAILED; + goto failed; + } hdr = HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG; @@ -820,7 +834,8 @@ static uint8_t bt_hid_virtual_unplug(struct hal_cmd_hidhost_virtual_unplug *cmd, if (write(fd, &hdr, sizeof(hdr)) < 0) { error("error writing virtual unplug command: %s (%d)", strerror(errno), errno); - return HAL_STATUS_FAILED; + status = HAL_STATUS_FAILED; + goto failed; } /* Wait either channels to HUP */ @@ -832,10 +847,14 @@ static uint8_t bt_hid_virtual_unplug(struct hal_cmd_hidhost_virtual_unplug *cmd, bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); - return HAL_STATUS_SUCCESS; + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_VIRTUAL_UNPLUG, + status); } -static uint8_t bt_hid_info(struct hal_cmd_hidhost_set_info *cmd, uint16_t len) +static void bt_hid_info(const void *buf, uint16_t len) { /* Data from hal_cmd_hidhost_set_info is usefull only when we create * UHID device. Once device is created all the transactions will be @@ -843,33 +862,36 @@ static uint8_t bt_hid_info(struct hal_cmd_hidhost_set_info *cmd, uint16_t len) * once device is created with HID internals. */ DBG("Not supported"); - return HAL_STATUS_UNSUPPORTED; + ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO, + HAL_STATUS_UNSUPPORTED); } -static uint8_t bt_hid_get_protocol(struct hal_cmd_hidhost_get_protocol *cmd, - uint16_t len) +static void bt_hid_get_protocol(const void *buf, uint16_t len) { + const struct hal_cmd_hidhost_get_protocol *cmd = buf; struct hid_device *dev; GSList *l; bdaddr_t dst; int fd; uint8_t hdr; + uint8_t status; DBG(""); - if (len < sizeof(*cmd)) - return HAL_STATUS_INVALID; - android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); - if (!l) - return HAL_STATUS_FAILED; + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } dev = l->data; - if (dev->boot_dev) - return HAL_STATUS_UNSUPPORTED; + if (dev->boot_dev) { + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } hdr = HID_MSG_GET_PROTOCOL | cmd->mode; fd = g_io_channel_unix_get_fd(dev->ctrl_io); @@ -877,37 +899,45 @@ static uint8_t bt_hid_get_protocol(struct hal_cmd_hidhost_get_protocol *cmd, if (write(fd, &hdr, sizeof(hdr)) < 0) { error("error writing device_get_protocol: %s (%d)", strerror(errno), errno); - return HAL_STATUS_FAILED; + status = HAL_STATUS_FAILED; + goto failed; } dev->last_hid_msg = HID_MSG_GET_PROTOCOL; - return HAL_STATUS_SUCCESS; + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_PROTOCOL, + status); } -static uint8_t bt_hid_set_protocol(struct hal_cmd_hidhost_set_protocol *cmd, - uint16_t len) +static void bt_hid_set_protocol(const void *buf, uint16_t len) { + const struct hal_cmd_hidhost_set_protocol *cmd = buf; struct hid_device *dev; GSList *l; bdaddr_t dst; int fd; uint8_t hdr; + uint8_t status; DBG(""); - if (len < sizeof(*cmd)) - return HAL_STATUS_INVALID; - android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); - if (!l) - return HAL_STATUS_FAILED; + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } dev = l->data; - if (dev->boot_dev) - return HAL_STATUS_UNSUPPORTED; + if (dev->boot_dev) { + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } hdr = HID_MSG_SET_PROTOCOL | cmd->mode; fd = g_io_channel_unix_get_fd(dev->ctrl_io); @@ -915,39 +945,47 @@ static uint8_t bt_hid_set_protocol(struct hal_cmd_hidhost_set_protocol *cmd, if (write(fd, &hdr, sizeof(hdr)) < 0) { error("error writing device_set_protocol: %s (%d)", strerror(errno), errno); - return HAL_STATUS_FAILED; + status = HAL_STATUS_FAILED; + goto failed; } dev->last_hid_msg = HID_MSG_SET_PROTOCOL; - return HAL_STATUS_SUCCESS; + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_PROTOCOL, + status); } -static uint8_t bt_hid_get_report(struct hal_cmd_hidhost_get_report *cmd, - uint16_t len) +static void bt_hid_get_report(const void *buf, uint16_t len) { + const struct hal_cmd_hidhost_get_report *cmd = buf; struct hid_device *dev; GSList *l; bdaddr_t dst; int fd; uint8_t *req; uint8_t req_size; + uint8_t status; DBG(""); - if (len < sizeof(*cmd)) - return HAL_STATUS_INVALID; - android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); - if (!l) - return HAL_STATUS_FAILED; + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } dev = l->data; req_size = (cmd->buf_size > 0) ? 4 : 2; req = g_try_malloc0(req_size); - if (!req) - return HAL_STATUS_NOMEM; + if (!req) { + status = HAL_STATUS_NOMEM; + goto failed; + } req[0] = HID_MSG_GET_REPORT | cmd->type; req[1] = cmd->id; @@ -963,44 +1001,60 @@ static uint8_t bt_hid_get_report(struct hal_cmd_hidhost_get_report *cmd, error("error writing hid_get_report: %s (%d)", strerror(errno), errno); g_free(req); - return HAL_STATUS_FAILED; + status = HAL_STATUS_FAILED; + goto failed; } dev->last_hid_msg = HID_MSG_GET_REPORT; g_free(req); - return HAL_STATUS_SUCCESS; + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, status); } -static uint8_t bt_hid_set_report(struct hal_cmd_hidhost_set_report *cmd, - uint16_t len) +static void bt_hid_set_report(const void *buf, uint16_t len) { + const struct hal_cmd_hidhost_set_report *cmd = buf; struct hid_device *dev; GSList *l; bdaddr_t dst; int i, fd; uint8_t *req; uint8_t req_size; + uint8_t status; DBG(""); - if (len < sizeof(*cmd)) - return HAL_STATUS_INVALID; + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid hid set report size (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); - if (!l) - return HAL_STATUS_FAILED; + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } dev = l->data; - if (!(dev->ctrl_io)) - return HAL_STATUS_FAILED; + if (!(dev->ctrl_io)) { + status = HAL_STATUS_FAILED; + goto failed; + } req_size = 1 + (cmd->len / 2); req = g_try_malloc0(req_size); - if (!req) - return HAL_STATUS_NOMEM; + if (!req) { + status = HAL_STATUS_NOMEM; + goto failed; + } req[0] = HID_MSG_SET_REPORT | cmd->type; /* Report data coming to HAL is in ascii format, HAL sends @@ -1014,44 +1068,60 @@ static uint8_t bt_hid_set_report(struct hal_cmd_hidhost_set_report *cmd, error("error writing hid_set_report: %s (%d)", strerror(errno), errno); g_free(req); - return HAL_STATUS_FAILED; + status = HAL_STATUS_FAILED; + goto failed; } dev->last_hid_msg = HID_MSG_SET_REPORT; g_free(req); - return HAL_STATUS_SUCCESS; + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, status); } -static uint8_t bt_hid_send_data(struct hal_cmd_hidhost_send_data *cmd, - uint16_t len) +static void bt_hid_send_data(const void *buf, uint16_t len) { + const struct hal_cmd_hidhost_send_data *cmd = buf; struct hid_device *dev; GSList *l; bdaddr_t dst; int i, fd; uint8_t *req; uint8_t req_size; + uint8_t status; DBG(""); - if (len < sizeof(*cmd)) - return HAL_STATUS_INVALID; + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid hid send data size (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } android2bdaddr(&cmd->bdaddr, &dst); l = g_slist_find_custom(devices, &dst, device_cmp); - if (!l) - return HAL_STATUS_FAILED; + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } dev = l->data; - if (!(dev->intr_io)) - return HAL_STATUS_FAILED; + if (!(dev->intr_io)) { + status = HAL_STATUS_FAILED; + goto failed; + } req_size = 1 + (cmd->len / 2); req = g_try_malloc0(req_size); - if (!req) - return HAL_STATUS_NOMEM; + if (!req) { + status = HAL_STATUS_NOMEM; + goto failed; + } req[0] = HID_MSG_DATA | HID_DATA_TYPE_OUTPUT; /* Report data coming to HAL is in ascii format, HAL sends @@ -1065,53 +1135,42 @@ static uint8_t bt_hid_send_data(struct hal_cmd_hidhost_send_data *cmd, error("error writing data to HID device: %s (%d)", strerror(errno), errno); g_free(req); - return HAL_STATUS_FAILED; + status = HAL_STATUS_FAILED; + goto failed; } g_free(req); - return HAL_STATUS_SUCCESS; -} - -void bt_hid_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len) -{ - uint8_t status = HAL_STATUS_FAILED; - switch (opcode) { - case HAL_OP_HIDHOST_CONNECT: - status = bt_hid_connect(buf, len); - break; - case HAL_OP_HIDHOST_DISCONNECT: - status = bt_hid_disconnect(buf, len); - break; - case HAL_OP_HIDHOST_VIRTUAL_UNPLUG: - status = bt_hid_virtual_unplug(buf, len); - break; - case HAL_OP_HIDHOST_SET_INFO: - status = bt_hid_info(buf, len); - break; - case HAL_OP_HIDHOST_GET_PROTOCOL: - status = bt_hid_get_protocol(buf, len); - break; - case HAL_OP_HIDHOST_SET_PROTOCOL: - status = bt_hid_set_protocol(buf, len); - break; - case HAL_OP_HIDHOST_GET_REPORT: - status = bt_hid_get_report(buf, len); - break; - case HAL_OP_HIDHOST_SET_REPORT: - status = bt_hid_set_report(buf, len); - break; - case HAL_OP_HIDHOST_SEND_DATA: - status = bt_hid_send_data(buf, len); - break; - default: - DBG("Unhandled command, opcode 0x%x", opcode); - break; - } + status = HAL_STATUS_SUCCESS; - ipc_send_rsp(sk, HAL_SERVICE_ID_HIDHOST, status); +failed: + ipc_send_rsp(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, status); } +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_HIDHOST_CONNECT */ + { bt_hid_connect, false, sizeof(struct hal_cmd_hidhost_connect) }, + /* HAL_OP_HIDHOST_DISCONNECT */ + { bt_hid_disconnect, false, sizeof(struct hal_cmd_hidhost_disconnect) }, + /* HAL_OP_HIDHOST_VIRTUAL_UNPLUG */ + { bt_hid_virtual_unplug, false, + sizeof(struct hal_cmd_hidhost_virtual_unplug) }, + /* HAL_OP_HIDHOST_SET_INFO */ + { bt_hid_info, true, sizeof(struct hal_cmd_hidhost_set_info) }, + /* HAL_OP_HIDHOST_GET_PROTOCOL */ + { bt_hid_get_protocol, false, + sizeof(struct hal_cmd_hidhost_get_protocol) }, + /* HAL_OP_HIDHOST_SET_PROTOCOL */ + { bt_hid_set_protocol, false, + sizeof(struct hal_cmd_hidhost_get_protocol) }, + /* HAL_OP_HIDHOST_GET_REPORT */ + { bt_hid_get_report, false, sizeof(struct hal_cmd_hidhost_get_report) }, + /* HAL_OP_HIDHOST_SET_REPORT */ + { bt_hid_set_report, true, sizeof(struct hal_cmd_hidhost_set_report) }, + /* HAL_OP_HIDHOST_SEND_DATA */ + { bt_hid_send_data, true, sizeof(struct hal_cmd_hidhost_send_data) }, +}; + static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) { struct hid_device *dev; @@ -1184,7 +1243,7 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) } } -bool bt_hid_register(int sk, const bdaddr_t *addr) +bool bt_hid_register(const bdaddr_t *addr) { GError *err = NULL; @@ -1210,21 +1269,35 @@ bool bt_hid_register(int sk, const bdaddr_t *addr) BT_IO_OPT_INVALID); if (!intr_io) { error("Failed to listen on intr channel: %s", err->message); - g_io_channel_unref(ctrl_io); g_error_free(err); + + g_io_channel_shutdown(ctrl_io, TRUE, NULL); + g_io_channel_unref(ctrl_io); + ctrl_io = NULL; + return false; } - notification_sk = sk; + ipc_register(HAL_SERVICE_ID_HIDHOST, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); return true; } +static void free_hid_devices(gpointer data, gpointer user_data) +{ + struct hid_device *dev = data; + + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + hid_device_free(dev); +} + void bt_hid_unregister(void) { DBG(""); - notification_sk = -1; + g_slist_foreach(devices, free_hid_devices, NULL); + devices = NULL; if (ctrl_io) { g_io_channel_shutdown(ctrl_io, TRUE, NULL); @@ -1237,4 +1310,6 @@ void bt_hid_unregister(void) g_io_channel_unref(intr_io); intr_io = NULL; } + + ipc_unregister(HAL_SERVICE_ID_HIDHOST); } diff --git a/android/hidhost.h b/android/hidhost.h index 688086ae..ea144464 100644 --- a/android/hidhost.h +++ b/android/hidhost.h @@ -21,7 +21,5 @@ * */ -void bt_hid_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len); - -bool bt_hid_register(int sk, const bdaddr_t *addr); +bool bt_hid_register(const bdaddr_t *addr); void bt_hid_unregister(void); diff --git a/android/ipc.c b/android/ipc.c index 729f1577..6f940cdb 100644 --- a/android/ipc.c +++ b/android/ipc.c @@ -30,13 +30,225 @@ #include <stdint.h> #include <string.h> #include <signal.h> +#include <stdbool.h> #include <sys/socket.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <glib.h> #include "hal-msg.h" #include "ipc.h" #include "log.h" -void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, +struct service_handler { + const struct ipc_handler *handler; + uint8_t size; +}; + +static struct service_handler services[HAL_SERVICE_ID_MAX + 1]; + +static GIOChannel *cmd_io = NULL; +static GIOChannel *notif_io = NULL; + +static void ipc_handle_msg(const void *buf, ssize_t len) +{ + const struct hal_hdr *msg = buf; + const struct ipc_handler *handler; + + if (len < (ssize_t) sizeof(*msg)) { + error("IPC: message too small (%zd bytes), terminating", len); + raise(SIGTERM); + return; + } + + if (len != (ssize_t) (sizeof(*msg) + msg->len)) { + error("IPC: message malformed (%zd bytes), terminating", len); + raise(SIGTERM); + return; + } + + /* if service is valid */ + if (msg->service_id > HAL_SERVICE_ID_MAX) { + error("IPC: unknown service (0x%x), terminating", + msg->service_id); + raise(SIGTERM); + return; + } + + /* if service is registered */ + if (!services[msg->service_id].handler) { + error("IPC: unregistered service (0x%x), terminating", + msg->service_id); + raise(SIGTERM); + return; + } + + /* if opcode is valid */ + if (msg->opcode == HAL_OP_STATUS || + msg->opcode > services[msg->service_id].size) { + error("IPC: invalid opcode 0x%x for service 0x%x, terminating", + msg->opcode, msg->service_id); + raise(SIGTERM); + return; + } + + /* opcode is table offset + 1 */ + handler = &services[msg->service_id].handler[msg->opcode - 1]; + + /* if payload size is valid */ + if ((handler->var_len && handler->data_len > msg->len) || + (!handler->var_len && handler->data_len != msg->len)) { + error("IPC: size invalid opcode 0x%x service 0x%x, terminating", + msg->service_id, msg->opcode); + raise(SIGTERM); + return; + } + + handler->handler(msg->payload, msg->len); +} + +static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + char buf[BLUEZ_HAL_MTU]; + ssize_t ret; + int fd; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + info("IPC: command socket closed, terminating"); + goto fail; + } + + fd = g_io_channel_unix_get_fd(io); + + ret = read(fd, buf, sizeof(buf)); + if (ret < 0) { + error("IPC: command read failed, terminating (%s)", + strerror(errno)); + goto fail; + } + + ipc_handle_msg(buf, ret); + return TRUE; + +fail: + raise(SIGTERM); + return FALSE; +} + +static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + info("IPC: notification socket closed, terminating"); + raise(SIGTERM); + + return FALSE; +} + +static GIOChannel *connect_hal(GIOFunc connect_cb) +{ + struct sockaddr_un addr; + GIOCondition cond; + GIOChannel *io; + int sk; + + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + error("IPC: failed to create socket: %d (%s)", errno, + strerror(errno)); + return NULL; + } + + io = g_io_channel_unix_new(sk); + + g_io_channel_set_close_on_unref(io, TRUE); + g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)); + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + error("IPC: failed to connect HAL socket: %d (%s)", errno, + strerror(errno)); + g_io_channel_unref(io); + return NULL; + } + + cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + g_io_add_watch(io, cond, connect_cb, NULL); + + return io; +} + +static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + DBG(""); + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + error("IPC: notification socket connect failed, terminating"); + raise(SIGTERM); + return FALSE; + } + + cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + g_io_add_watch(io, cond, notif_watch_cb, NULL); + + cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + g_io_add_watch(cmd_io, cond, cmd_watch_cb, NULL); + + info("IPC: successfully connected"); + + return FALSE; +} + +static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + DBG(""); + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + error("IPC: command socket connect failed, terminating"); + raise(SIGTERM); + return FALSE; + } + + notif_io = connect_hal(notif_connect_cb); + if (!notif_io) + raise(SIGTERM); + + return FALSE; +} + +void ipc_init(void) +{ + cmd_io = connect_hal(cmd_connect_cb); + if (!cmd_io) + raise(SIGTERM); +} + +void ipc_cleanup(void) +{ + if (cmd_io) { + g_io_channel_shutdown(cmd_io, TRUE, NULL); + g_io_channel_unref(cmd_io); + cmd_io = NULL; + } + + if (notif_io) { + g_io_channel_shutdown(notif_io, TRUE, NULL); + g_io_channel_unref(notif_io); + notif_io = NULL; + } +} + +static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, void *param, int fd) { struct msghdr msg; @@ -47,6 +259,7 @@ void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, memset(&msg, 0, sizeof(msg)); memset(&m, 0, sizeof(m)); + memset(cmsgbuf, 0, sizeof(cmsgbuf)); m.service_id = service_id; m.opcode = opcode; @@ -80,11 +293,49 @@ void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, } } -void ipc_send_rsp(int sk, uint8_t service_id, uint8_t status) +void ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status) { struct hal_status s; + int sk; + + sk = g_io_channel_unix_get_fd(cmd_io); + + if (status == HAL_STATUS_SUCCESS) { + ipc_send(sk, service_id, opcode, 0, NULL, -1); + return; + } s.code = status; ipc_send(sk, service_id, HAL_OP_STATUS, sizeof(s), &s, -1); } + +void ipc_send_rsp_full(uint8_t service_id, uint8_t opcode, uint16_t len, + void *param, int fd) +{ + ipc_send(g_io_channel_unix_get_fd(cmd_io), service_id, opcode, len, + param, fd); +} + +void ipc_send_notif(uint8_t service_id, uint8_t opcode, uint16_t len, + void *param) +{ + if (!notif_io) + return; + + ipc_send(g_io_channel_unix_get_fd(notif_io), service_id, opcode, len, + param, -1); +} + +void ipc_register(uint8_t service, const struct ipc_handler *handlers, + uint8_t size) +{ + services[service].handler = handlers; + services[service].size = size; +} + +void ipc_unregister(uint8_t service) +{ + services[service].handler = NULL; + services[service].size = 0; +} diff --git a/android/ipc.h b/android/ipc.h index cf0f3d67..6cd102b0 100644 --- a/android/ipc.h +++ b/android/ipc.h @@ -21,6 +21,19 @@ * */ -void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, +struct ipc_handler { + void (*handler) (const void *buf, uint16_t len); + bool var_len; + size_t data_len; +}; +void ipc_init(void); +void ipc_cleanup(void); + +void ipc_send_rsp(uint8_t service_id, uint8_t opcode, uint8_t status); +void ipc_send_rsp_full(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, int fd); -void ipc_send_rsp(int sk, uint8_t service_id, uint8_t status); +void ipc_send_notif(uint8_t service_id, uint8_t opcode, uint16_t len, + void *param); +void ipc_register(uint8_t service, const struct ipc_handler *handlers, + uint8_t size); +void ipc_unregister(uint8_t service); diff --git a/android/main.c b/android/main.c index a4f5e847..5210b4bc 100644 --- a/android/main.c +++ b/android/main.c @@ -36,8 +36,6 @@ #include <unistd.h> #include <sys/signalfd.h> -#include <sys/socket.h> -#include <sys/un.h> #include <glib.h> @@ -69,68 +67,73 @@ static bdaddr_t adapter_bdaddr; static GMainLoop *event_loop; -static GIOChannel *hal_cmd_io = NULL; -static GIOChannel *hal_notif_io = NULL; - static bool services[HAL_SERVICE_ID_MAX + 1] = { false }; -static void service_register(void *buf, uint16_t len) +static void service_register(const void *buf, uint16_t len) { - struct hal_cmd_register_module *m = buf; - int sk = g_io_channel_unix_get_fd(hal_notif_io); + const struct hal_cmd_register_module *m = buf; + uint8_t status; - if (m->service_id > HAL_SERVICE_ID_MAX || services[m->service_id]) + if (m->service_id > HAL_SERVICE_ID_MAX || services[m->service_id]) { + status = HAL_STATUS_FAILED; goto failed; + } switch (m->service_id) { case HAL_SERVICE_ID_BLUETOOTH: - if (!bt_bluetooth_register(sk)) - goto failed; + bt_bluetooth_register(); break; case HAL_SERVICE_ID_SOCK: - if (!bt_socket_register(sk, &adapter_bdaddr)) - goto failed; + bt_socket_register(&adapter_bdaddr); break; case HAL_SERVICE_ID_HIDHOST: - if (!bt_hid_register(sk, &adapter_bdaddr)) + if (!bt_hid_register(&adapter_bdaddr)) { + status = HAL_STATUS_FAILED; goto failed; + } break; case HAL_SERVICE_ID_A2DP: - if (!bt_a2dp_register(sk, &adapter_bdaddr)) + if (!bt_a2dp_register(&adapter_bdaddr)) { + status = HAL_STATUS_FAILED; goto failed; + } break; case HAL_SERVICE_ID_PAN: - if (!bt_pan_register(sk, &adapter_bdaddr)) + if (!bt_pan_register(&adapter_bdaddr)) { + status = HAL_STATUS_FAILED; goto failed; + } break; default: DBG("service %u not supported", m->service_id); + status = HAL_STATUS_FAILED; goto failed; } services[m->service_id] = true; - ipc_send(g_io_channel_unix_get_fd(hal_cmd_io), HAL_SERVICE_ID_CORE, - HAL_OP_REGISTER_MODULE, 0, NULL, -1); + status = HAL_STATUS_SUCCESS; info("Service ID=%u registered", m->service_id); - return; + failed: - ipc_send_rsp(g_io_channel_unix_get_fd(hal_cmd_io), - HAL_SERVICE_ID_CORE, HAL_STATUS_FAILED); + ipc_send_rsp(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, status); } -static void service_unregister(void *buf, uint16_t len) +static void service_unregister(const void *buf, uint16_t len) { - struct hal_cmd_unregister_module *m = buf; + const struct hal_cmd_unregister_module *m = buf; + uint8_t status; - if (m->service_id > HAL_SERVICE_ID_MAX || !services[m->service_id]) + if (m->service_id > HAL_SERVICE_ID_MAX || !services[m->service_id]) { + status = HAL_STATUS_FAILED; goto failed; + } switch (m->service_id) { case HAL_SERVICE_ID_BLUETOOTH: @@ -152,36 +155,26 @@ static void service_unregister(void *buf, uint16_t len) /* This would indicate bug in HAL, as unregister should not be * called in init failed */ DBG("service %u not supported", m->service_id); + status = HAL_STATUS_FAILED; goto failed; } services[m->service_id] = false; - ipc_send(g_io_channel_unix_get_fd(hal_cmd_io), HAL_SERVICE_ID_CORE, - HAL_OP_UNREGISTER_MODULE, 0, NULL, -1); + status = HAL_STATUS_SUCCESS; info("Service ID=%u unregistered", m->service_id); - return; + failed: - ipc_send_rsp(g_io_channel_unix_get_fd(hal_cmd_io), - HAL_SERVICE_ID_CORE, HAL_STATUS_FAILED); + ipc_send_rsp(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, status); } -static void handle_service_core(uint8_t opcode, void *buf, uint16_t len) -{ - switch (opcode) { - case HAL_OP_REGISTER_MODULE: - service_register(buf, len); - break; - case HAL_OP_UNREGISTER_MODULE: - service_unregister(buf, len); - break; - default: - ipc_send_rsp(g_io_channel_unix_get_fd(hal_cmd_io), - HAL_SERVICE_ID_CORE, HAL_STATUS_FAILED); - break; - } -} +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_REGISTER_MODULE */ + { service_register, false, sizeof(struct hal_cmd_register_module) }, + /* HAL_OP_UNREGISTER_MODULE */ + { service_unregister, false, sizeof(struct hal_cmd_unregister_module) }, +}; static void bluetooth_stopped(void) { @@ -211,169 +204,6 @@ static void stop_bluetooth(void) g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, quit_eventloop, NULL); } -static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond, - gpointer user_data) -{ - char buf[BLUEZ_HAL_MTU]; - struct hal_hdr *msg = (void *) buf; - ssize_t ret; - int fd; - - if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - info("HAL command socket closed, terminating"); - goto fail; - } - - fd = g_io_channel_unix_get_fd(io); - - ret = read(fd, buf, sizeof(buf)); - if (ret < 0) { - error("HAL command read failed, terminating (%s)", - strerror(errno)); - goto fail; - } - - if (ret < (ssize_t) sizeof(*msg)) { - error("HAL command too small, terminating (%zd)", ret); - goto fail; - } - - if (ret != (ssize_t) (sizeof(*msg) + msg->len)) { - error("Malformed HAL command (%zd bytes), terminating", ret); - goto fail; - } - - DBG("service_id %u opcode %u len %u", msg->service_id, msg->opcode, - msg->len); - - if (msg->service_id > HAL_SERVICE_ID_MAX || - !services[msg->service_id]) { - error("HAL command for unregistered service %u, terminating", - msg->service_id); - goto fail; - } - - switch (msg->service_id) { - case HAL_SERVICE_ID_CORE: - handle_service_core(msg->opcode, msg->payload, msg->len); - break; - case HAL_SERVICE_ID_BLUETOOTH: - bt_bluetooth_handle_cmd(fd, msg->opcode, msg->payload, - msg->len); - break; - case HAL_SERVICE_ID_HIDHOST: - bt_hid_handle_cmd(fd, msg->opcode, msg->payload, msg->len); - break; - case HAL_SERVICE_ID_SOCK: - bt_sock_handle_cmd(fd, msg->opcode, msg->payload, msg->len); - break; - case HAL_SERVICE_ID_A2DP: - bt_a2dp_handle_cmd(fd, msg->opcode, msg->payload, msg->len); - break; - case HAL_SERVICE_ID_PAN: - bt_pan_handle_cmd(fd, msg->opcode, msg->payload, msg->len); - break; - default: - ipc_send_rsp(fd, msg->service_id, HAL_STATUS_FAILED); - break; - } - - return TRUE; - -fail: - stop_bluetooth(); - return FALSE; -} - -static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond, - gpointer user_data) -{ - info("HAL notification socket closed, terminating"); - stop_bluetooth(); - - return FALSE; -} - -static GIOChannel *connect_hal(GIOFunc connect_cb) -{ - struct sockaddr_un addr; - GIOCondition cond; - GIOChannel *io; - int sk; - - sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); - if (sk < 0) { - error("Failed to create socket: %d (%s)", errno, - strerror(errno)); - return NULL; - } - - io = g_io_channel_unix_new(sk); - - g_io_channel_set_close_on_unref(io, TRUE); - g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - - memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)); - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - error("Failed to connect HAL socket: %d (%s)", errno, - strerror(errno)); - g_io_channel_unref(io); - return NULL; - } - - cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; - - g_io_add_watch(io, cond, connect_cb, NULL); - - return io; -} - -static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond, - gpointer user_data) -{ - DBG(""); - - if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - stop_bluetooth(); - return FALSE; - } - - cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL; - - g_io_add_watch(io, cond, notif_watch_cb, NULL); - - cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; - - g_io_add_watch(hal_cmd_io, cond, cmd_watch_cb, NULL); - - info("Successfully connected to HAL"); - - return FALSE; -} - -static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond, - gpointer user_data) -{ - DBG(""); - - if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - stop_bluetooth(); - return FALSE; - } - - hal_notif_io = connect_hal(notif_connect_cb); - if (!hal_notif_io) { - error("Cannot connect to HAL, terminating"); - stop_bluetooth(); - } - - return FALSE; -} - static void adapter_ready(int err, const bdaddr_t *addr) { if (err < 0) { @@ -390,11 +220,7 @@ static void adapter_ready(int err, const bdaddr_t *addr) info("Adapter initialized"); - hal_cmd_io = connect_hal(cmd_connect_cb); - if (!hal_cmd_io) { - error("Cannot connect to HAL, terminating"); - stop_bluetooth(); - } + ipc_init(); } static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, @@ -477,18 +303,35 @@ static GOptionEntry options[] = { { NULL } }; -static void cleanup_hal_connection(void) +static void cleanup_services(void) { - if (hal_cmd_io) { - g_io_channel_shutdown(hal_cmd_io, TRUE, NULL); - g_io_channel_unref(hal_cmd_io); - hal_cmd_io = NULL; - } + int i; + + DBG(""); - if (hal_notif_io) { - g_io_channel_shutdown(hal_notif_io, TRUE, NULL); - g_io_channel_unref(hal_notif_io); - hal_notif_io = NULL; + for (i = HAL_SERVICE_ID_BLUETOOTH; i < HAL_SERVICE_ID_MAX; i++) { + if (!services[i]) + continue; + + switch (i) { + case HAL_SERVICE_ID_BLUETOOTH: + bt_bluetooth_unregister(); + break; + case HAL_SERVICE_ID_SOCK: + bt_socket_unregister(); + break; + case HAL_SERVICE_ID_HIDHOST: + bt_hid_unregister(); + break; + case HAL_SERVICE_ID_A2DP: + bt_a2dp_unregister(); + break; + case HAL_SERVICE_ID_PAN: + bt_pan_unregister(); + break; + } + + services[i] = false; } } @@ -501,7 +344,11 @@ static bool set_capabilities(void) header.version = _LINUX_CAPABILITY_VERSION; header.pid = 0; + /* CAP_NET_ADMIN: Allow use of MGMT interface + * CAP_NET_BIND_SERVICE: Allow use of privileged PSM + * CAP_NET_RAW: Allow use of bnep ioctl calls */ cap.effective = cap.permitted = + CAP_TO_MASK(CAP_NET_RAW) | CAP_TO_MASK(CAP_NET_ADMIN) | CAP_TO_MASK(CAP_NET_BIND_SERVICE); cap.inheritable = 0; @@ -531,9 +378,6 @@ int main(int argc, char *argv[]) GError *err = NULL; guint signal; - /* Core Service (ID=0) should always be considered registered */ - services[0] = true; - context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); @@ -554,40 +398,60 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); } - event_loop = g_main_loop_new(NULL, FALSE); signal = setup_signalfd(); if (!signal) return EXIT_FAILURE; __btd_log_init("*", 0); - if (!set_capabilities()) + if (!set_capabilities()) { + __btd_log_cleanup(); + g_source_remove(signal); return EXIT_FAILURE; + } bluetooth_start_timeout = g_timeout_add_seconds(STARTUP_GRACE_SECONDS, quit_eventloop, NULL); if (bluetooth_start_timeout == 0) { error("Failed to init startup timeout"); + __btd_log_cleanup(); + g_source_remove(signal); return EXIT_FAILURE; } - if (!bt_bluetooth_start(option_index, adapter_ready)) + if (!bt_bluetooth_start(option_index, adapter_ready)) { + __btd_log_cleanup(); + g_source_remove(bluetooth_start_timeout); + g_source_remove(signal); return EXIT_FAILURE; + } /* Use params: mtu = 0, flags = 0 */ start_sdp_server(0, 0); + ipc_register(HAL_SERVICE_ID_CORE, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + DBG("Entering main loop"); + event_loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(event_loop); g_source_remove(signal); - cleanup_hal_connection(); + if (bluetooth_start_timeout > 0) + g_source_remove(bluetooth_start_timeout); + + cleanup_services(); + + ipc_cleanup(); stop_sdp_server(); bt_bluetooth_cleanup(); g_main_loop_unref(event_loop); + ipc_unregister(HAL_SERVICE_ID_CORE); + info("Exit"); __btd_log_cleanup(); diff --git a/android/pan.c b/android/pan.c index 46b3700d..6b098b23 100644 --- a/android/pan.c +++ b/android/pan.c @@ -29,72 +29,333 @@ #include <fcntl.h> #include <glib.h> +#include "btio/btio.h" #include "lib/bluetooth.h" +#include "lib/bnep.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/glib-helper.h" +#include "profiles/network/bnep.h" + #include "log.h" #include "pan.h" #include "hal-msg.h" #include "ipc.h" +#include "utils.h" +#include "bluetooth.h" + +static bdaddr_t adapter_addr; +GSList *devices = NULL; +uint8_t local_role = HAL_PAN_ROLE_NONE; -static int notification_sk = -1; +struct pan_device { + char iface[16]; + bdaddr_t dst; + uint8_t conn_state; + uint8_t role; + GIOChannel *io; + guint watch; +}; -static uint8_t bt_pan_enable(struct hal_cmd_pan_enable *cmd, uint16_t len) +static int device_cmp(gconstpointer s, gconstpointer user_data) { - DBG("Not Implemented"); + const struct pan_device *dev = s; + const bdaddr_t *dst = user_data; - return HAL_STATUS_FAILED; + return bacmp(&dev->dst, dst); } -static uint8_t bt_pan_get_role(void *cmd, uint16_t len) +static void pan_device_free(struct pan_device *dev) { - DBG("Not Implemented"); + local_role = HAL_PAN_ROLE_NONE; + + if (dev->watch > 0) { + g_source_remove(dev->watch); + dev->watch = 0; + } + + if (dev->io) { + g_io_channel_unref(dev->io); + dev->io = NULL; + } + + devices = g_slist_remove(devices, dev); + g_free(dev); +} + +static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state) +{ + struct hal_ev_pan_conn_state ev; + char addr[18]; + + if (dev->conn_state == state) + return; - return HAL_STATUS_FAILED; + dev->conn_state = state; + ba2str(&dev->dst, addr); + DBG("device %s state %u", addr, state); + + bdaddr2android(&dev->dst, ev.bdaddr); + ev.state = state; + ev.local_role = local_role; + ev.remote_role = dev->role; + ev.status = HAL_STATUS_SUCCESS; + + ipc_send_notif(HAL_SERVICE_ID_PAN, HAL_EV_PAN_CONN_STATE, sizeof(ev), + &ev); } -static uint8_t bt_pan_connect(struct hal_cmd_pan_connect *cmd, uint16_t len) +static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state) { - DBG("Not Implemented"); + struct hal_ev_pan_ctrl_state ev; + + DBG(""); + + ev.state = state; + ev.local_role = local_role; + ev.status = HAL_STATUS_SUCCESS; + memset(ev.name, 0, sizeof(ev.name)); + memcpy(ev.name, dev->iface, sizeof(dev->iface)); - return HAL_STATUS_FAILED; + ipc_send_notif(HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE, sizeof(ev), + &ev); } -static uint8_t bt_pan_disconnect(struct hal_cmd_pan_connect *cmd, uint16_t len) +static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) { - DBG("Not Implemented"); + struct pan_device *dev = data; + + DBG("%s disconnected", dev->iface); - return HAL_STATUS_FAILED; + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); + pan_device_free(dev); + + return FALSE; } -void bt_pan_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len) +static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data) { - uint8_t status = HAL_STATUS_FAILED; + struct pan_device *dev = data; - switch (opcode) { - case HAL_OP_PAN_ENABLE: - status = bt_pan_enable(buf, len); - break; - case HAL_OP_PAN_GET_ROLE: - status = bt_pan_get_role(buf, len); + DBG(""); + + if (err < 0) { + error("bnep connect req failed: %s", strerror(-err)); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); + pan_device_free(dev); + return; + } + + memcpy(dev->iface, iface, sizeof(dev->iface)); + + DBG("%s connected", dev->iface); + + bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED); + + dev->watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + bnep_watchdog_cb, dev); + g_io_channel_unref(dev->io); + dev->io = NULL; +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer data) +{ + struct pan_device *dev = data; + uint16_t src, dst; + int perr, sk; + + DBG(""); + + if (err) { + error("%s", err->message); + goto fail; + } + + src = (local_role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU; + dst = (dev->role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU; + sk = g_io_channel_unix_get_fd(dev->io); + + perr = bnep_connect(sk, src, dst, bnep_conn_cb, dev); + if (perr < 0) { + error("bnep connect req failed: %s", strerror(-perr)); + goto fail; + } + + return; + +fail: + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); + pan_device_free(dev); +} + +static void bt_pan_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_pan_connect *cmd = buf; + struct pan_device *dev; + uint8_t status; + bdaddr_t dst; + char addr[18]; + GSList *l; + GError *gerr = NULL; + + DBG(""); + + switch (cmd->local_role) { + case HAL_PAN_ROLE_NAP: + if (cmd->remote_role != HAL_PAN_ROLE_PANU) { + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } break; - case HAL_OP_PAN_CONNECT: - status = bt_pan_connect(buf, len); + case HAL_PAN_ROLE_PANU: + if (cmd->remote_role != HAL_PAN_ROLE_NAP && + cmd->remote_role != HAL_PAN_ROLE_PANU) { + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } break; - case HAL_OP_PAN_DISCONNECT: - status = bt_pan_disconnect(buf, len); + default: + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = g_new0(struct pan_device, 1); + bacpy(&dev->dst, &dst); + local_role = cmd->local_role; + dev->role = cmd->remote_role; + + ba2str(&dev->dst, addr); + DBG("connecting to %s %s", addr, dev->iface); + + dev->io = bt_io_connect(connect_cb, dev, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, BNEP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_OMTU, BNEP_MTU, + BT_IO_OPT_IMTU, BNEP_MTU, + BT_IO_OPT_INVALID); + if (!dev->io) { + error("%s", gerr->message); + g_error_free(gerr); + g_free(dev); + status = HAL_STATUS_FAILED; + goto failed; + } + + devices = g_slist_append(devices, dev); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING); + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status); +} + +static void bt_pan_disconnect(const void *buf, uint16_t len) +{ + const struct hal_cmd_pan_disconnect *cmd = buf; + struct pan_device *dev; + uint8_t status; + GSList *l; + bdaddr_t dst; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = l->data; + + if (dev->watch) { + g_source_remove(dev->watch); + dev->watch = 0; + } + + bnep_if_down(dev->iface); + bnep_kill_connection(&dst); + + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); + pan_device_free(dev); + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, status); +} + +static void bt_pan_enable(const void *buf, uint16_t len) +{ + const struct hal_cmd_pan_enable *cmd = buf; + uint8_t status; + + switch (cmd->local_role) { + case HAL_PAN_ROLE_PANU: + case HAL_PAN_ROLE_NAP: + DBG("Not Implemented"); + status = HAL_STATUS_FAILED; break; default: - DBG("Unhandled command, opcode 0x%x", opcode); + status = HAL_STATUS_UNSUPPORTED; break; } - ipc_send_rsp(sk, HAL_SERVICE_ID_PAN, status); + ipc_send_rsp(HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status); } -bool bt_pan_register(int sk, const bdaddr_t *addr) +static void bt_pan_get_role(const void *buf, uint16_t len) { + struct hal_rsp_pan_get_role rsp; + DBG(""); - notification_sk = sk; + rsp.local_role = local_role; + ipc_send_rsp_full(HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, sizeof(rsp), + &rsp, -1); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_PAN_ENABLE */ + { bt_pan_enable, false, sizeof(struct hal_cmd_pan_enable) }, + /* HAL_OP_PAN_GET_ROLE */ + { bt_pan_get_role, false, 0 }, + /* HAL_OP_PAN_CONNECT */ + { bt_pan_connect, false, sizeof(struct hal_cmd_pan_connect) }, + /* HAL_OP_PAN_DISCONNECT */ + { bt_pan_disconnect, false, sizeof(struct hal_cmd_pan_disconnect) }, +}; + +bool bt_pan_register(const bdaddr_t *addr) +{ + int err; + + DBG(""); + + bacpy(&adapter_addr, addr); + + err = bnep_init(); + if (err) { + error("bnep init failed"); + return false; + } + + ipc_register(HAL_SERVICE_ID_PAN, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); return true; } @@ -103,5 +364,7 @@ void bt_pan_unregister(void) { DBG(""); - notification_sk = -1; + bnep_cleanup(); + + ipc_unregister(HAL_SERVICE_ID_PAN); } diff --git a/android/pan.h b/android/pan.h index 24303786..3178d88a 100644 --- a/android/pan.h +++ b/android/pan.h @@ -21,7 +21,5 @@ * */ -void bt_pan_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len); - -bool bt_pan_register(int sk, const bdaddr_t *addr); +bool bt_pan_register(const bdaddr_t *addr); void bt_pan_unregister(void); diff --git a/android/pics-did.txt b/android/pics-did.txt new file mode 100644 index 00000000..e8c914a5 --- /dev/null +++ b/android/pics-did.txt @@ -0,0 +1,42 @@ +DID PICS for the PTS tool. + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_DID_0_1 False Device ID 1.2 (C.1) +TSPC_DID_0_2 True Device ID 1.3 (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support one of the profile versions. +------------------------------------------------------------------------------- + + + SDP Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_DID_1_1 True Specification ID (M) +TSPC_DID_1_2 True Vendor ID (M) +TSPC_DID_1_3 True Product ID (M) +TSPC_DID_1_4 True Version (M) +TSPC_DID_1_5 True Primary Record (M) +TSPC_DID_1_6 True Vendor ID Source (M) +TSPC_ALL False Turns on all the test cases +------------------------------------------------------------------------------- + + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_ClientExecutableURL False +TSPX_ServiceDescription False +TSPX_DocumentationURL False +------------------------------------------------------------------------------- +Other should be set according to Tester's test environment. diff --git a/android/pics-gap.txt b/android/pics-gap.txt new file mode 100644 index 00000000..cd274e81 --- /dev/null +++ b/android/pics-gap.txt @@ -0,0 +1,714 @@ +GAP PICS for the PTS tool. + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Device Configuration +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_0_1 False BR/EDR (C.1) +TSPC_GAP_0_2 False LE (C.2) +TSPC_GAP_0_3 True (*) BR/EDR/LE (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if ('End Product' or 'Host Subsystem') and ('BR Host' or + 'BR/HS Host') are Supported ('End Product' or 'Host Subsystem' with 'BR' + or 'BR/HS Host' CC), otherwise excluded. Optional for + 'Component (Tested)' or 'Component (Non-Tested)'. +C.2: Mandatory if ('End Product' or 'Host Subsystem') and ('LE Host') are + Supported (End Product or Host Subsystem with LE Host CC), + otherwise excluded. Optional for 'Component (Tested)' or + 'Component (Non-Tested)'. +C.3: Mandatory if ('End Product' or 'Host Subsystem') and ('BR/LE Host' or + 'BR/HS/LE Host') are Supported (End Product or Host Subsystem with + BR/LE or BR/HS/LE Host CC), otherwise excluded. + Optional for 'Component (Tested)' or 'Component (Non-tested)'. +Note - Only one transport shall be supported. +------------------------------------------------------------------------------- + + + Version Configuration +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_0A_1 False Core Specification Addendum 3 (CSA3), + GAP Connection Parameters Changes, + Authentication and Lost Bond Changes, + Private Addressing Changes, + Dual Mode Addressing Changes, + Adopted 24 July 2012 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Modes +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_1_1 True (*) Non-discoverable mode (C.1) +TSPC_GAP_1_2 False Limited-discoverable Mode (O) +TSPC_GAP_1_3 True (*) General-discoverable mode (O) +TSPC_GAP_1_4 True (*) Non-connectable mode (O) +TSPC_GAP_1_5 True Connectable mode (M) +TSPC_GAP_1_6 False Non-bondable mode (O) +TSPC_GAP_1_7 True (*) Bondable mode (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_0_2 is supported, otherwise Optional. +C.2: Mandatory if TSPC_GAP_3_5 is supported, otherwise Optional. +------------------------------------------------------------------------------- + + + Security Aspects +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_2_1 True (*) Authentication procedure (C.1) +TSPC_GAP_2_2 True (*) Support of LMP-Authentication (M) +TSPC_GAP_2_3 True (*) Initiate LMP-Authentication (C.5) +TSPC_GAP_2_4 False Security mode 1 (C.2) +TSPC_GAP_2_5 True (*) Security mode 2 (O) +TSPC_GAP_2_6 False Security mode 3 (C.7) +TSPC_GAP_2_7 True (*) Security mode 4 (C.4) +TSPC_GAP_2_8 True (*) Support of Authenticated link key (C.6) +TSPC_GAP_2_9 True (*) Support of Unauthenticated link key (C.6) +TSPC_GAP_2_10 False No security (C.6) +------------------------------------------------------------------------------- +C.1: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6) is supported, otherwise + Optional. +Note 1: The Authentication Procedure in item GAP, TSPC_GAP_2_1 is the one + described in Fig. 5.1 on page 198 in the GAP Profile Specification and + not the LMP-Authenticaion. +C.2: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional. +C.5: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6 or TSPC_GAP_2_7) is supported, + otherwise Optional. +C.4: Mandatory if (Core Spec 2.1 or later) is supported, otherwise Excluded. +Note 2. If a Core 2.0 and earlier design claims to support secure communcation + it should support either Security mode 2 or 3. +Note 3. A Core 2.1 or later device shall always support secure communication + in Security Mode 4, and shall use that mode to connect with another + Core 2.1 or later device. It shall use Security Mode 2 only for + backward compatibility purposes with Core 2.0 and earlier devices. + Security Mode 1 is excluded for Core 2.1 or later devices based on + condition C.2. +C.6: If TSPC_GAP_2_7 is supported then at least one of (TSPC_GAP_2_8 or + TSPC_GAP_2_9 or TSPC_GAP_2_10) is Mandatory, otherwise Excluded. +C.7: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional. +------------------------------------------------------------------------------- + + + Idle Mode Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_3_1 True (*) Initiation of general inquiry (C.1) +TSPC_GAP_3_2 False Initiation of limited inquiry (C.1) +TSPC_GAP_3_3 True (*) Initiation of name discover (O) +TSPC_GAP_3_4 True (*) Initiation of device discovery (O) +TSPC_GAP_3_5 True (*) Initiation of general bonding (O) +TSPC_GAP_3_6 True (*) Initiation of dedicated bonding (O) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of TSPC_GAP_3_1 or TSPC_GAP_3_2 if + TSPC_GAP_3_5 is supported, otherwise Optional. +------------------------------------------------------------------------------- + + + Establishment Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_4_1 True Support link establishment as initiator (M) +TSPC_GAP_4_2 True Support link establishment as acceptor (M) +TSPC_GAP_4_3 True (*) Support channel establishment as initiator (O) +TSPC_GAP_4_4 True Support channel establishment as acceptor (M) +TSPC_GAP_4_5 True (*) Support connection establishment as initiator + (O) +TSPC_GAP_4_6 True (*) Support connection establishment as acceptor + (O) +------------------------------------------------------------------------------- + + + LE Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_5_1 False (*) Broadcaster (C.1) +TSPC_GAP_5_2 False Observer (C.1) +TSPC_GAP_5_3 False (*) Peripheral (C.1) +TSPC_GAP_5_4 True (*#) Central (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +Note: 'LE Roles' is applicable for LE-only configurations, but it appears that + PTS is checking this precondition also in some BR/EDR/LE tests. +------------------------------------------------------------------------------- + + + Broadcaster Physical Layer +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_6_1 False (*) Broadcaster: Transmitter (M) +TSPC_GAP_6_2 False Broadcaster: Receiver (O) +------------------------------------------------------------------------------- + + + Broadcaster Link Layer States +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_7_1 False (*) Broadcaster: Standby (M) +TSPC_GAP_7_2 False (*) Broadcaster: Advertising (M) +------------------------------------------------------------------------------- + + + Broadcaster Link Layer Advertising Event Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_8_1 False (*) Broadcaster: Non-Connectable Undirected Event + (M) +TSPC_GAP_8_2 False Broadcaster: Scannable Undirected Event (O) +------------------------------------------------------------------------------- + + + Broadcaster Link Layer Advertising Data Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_8A_1 False AD Type-Service UUID (O) +TSPC_GAP_8A_2 False AD Type-Local Name (O) +TSPC_GAP_8A_3 False (*) AD Type-Flags (M) +TSPC_GAP_8A_4 False AD Type-Manufacturer Specific Data (O) +TSPC_GAP_8A_5 False AD Type-TX Power Level (O) +TSPC_GAP_8A_6 False AD Type-Security Manager Out of Band (OOB) (C.1) +TSPC_GAP_8A_7 False AD Type-Security manager TK Value (O) +TSPC_GAP_8A_8 False AD Type-Slave Connection Interval Range (O) +TSPC_GAP_8A_9 False AD Type-Service Solicitation (O) +TSPC_GAP_8A_10 False AD Type-Service Data (O) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_SM_2_4 (OOB supported) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Broadcaster Connection Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_9_1 False (*) Broadcaster: Non-Connectable Mode +------------------------------------------------------------------------------- + + + Broadcaster Broadcasting and Observing Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_10_1 False (*) Broadcaster: Broadcast Mode +TSPC_GAP_11_1 False Broadcaster: Privacy Feature +TSPC_GAP_11_2 False Broadcaster: Resolvable Private Address + Generation Procedure +------------------------------------------------------------------------------- + + + Observer Physical Layer +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_12_1 True (#) Observer: Receiver +TSPC_GAP_12_2 False Observer: Transmitter +------------------------------------------------------------------------------- + + + Observer Link Layer States +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_13_1 True (#) Observer: Standby +TSPC_GAP_13_2 True (#) Observer: Scanning +------------------------------------------------------------------------------- + + + Observer Link Layer Scanning Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_14_1 True (#) Observer: Passive Scanning +TSPC_GAP_14_2 False Observer: Active Scanning +------------------------------------------------------------------------------- + + + Observer Connection Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_15_1 True (#) Observer: Non-Connectable Mode +------------------------------------------------------------------------------- + + + Observer Broadcasting and Observing Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_16_1 True (#) Observer: Observation Procedure +------------------------------------------------------------------------------- + + + Observer Privacy Feature +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_17_1 False Observer: Privacy Feature (O) +TSPC_GAP_17_2 False Observer: Non-Resolvable Private Address + Generation Procedure (C.1) +TSPC_GAP_17_3 False Observer: Resolvable Private Address Resolution + Procedure (C.2) +TSPC_GAP_17_4 False Observer: Resolvable Private Address Generation + Procedure (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_17_1 and TSPC_GAP_14_2 (Active Scanning) are + supported and TSPC_GAP_17_4 (Resolvable Private Address Generation + Procedure) is Not Supported; Optional if CSA3 or later and + TSPC_GAP_17_4 are supported, otherwise Excluded. +C.2: Optional if TSPC_GAP_17_1 is supported, otherwise Excluded. +C.3: Mandatory if CSA3 or later and TSPC_GAP_17_1 and TSPC_GAP_14_2 + (Active Scanning) are supported and TSPC_GAP_17_2 (Non-Resolvable + Private Address Generation Procedure) is not supported; Optional if + CSA3 or later and TSPC_GAP_17_2 (Non-Resolvable Private Address + Generation Procedure) are supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral Physical Layer +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_18_1 False Peripheral: Transmitter +TSPC_GAP_18_2 False Peripheral: Receiver +------------------------------------------------------------------------------- + + + Peripheral Link Layer States +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_19_1 False Peripheral: Standby +TSPC_GAP_19_2 False Peripheral: Advertising +TSPC_GAP_19_3 False Peripheral: Connection, Slave Role +------------------------------------------------------------------------------- + + + Peripheral Link Layer Advertising Event Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_20_1 False Peripheral: Connectable Undirected Event +TSPC_GAP_20_2 False Peripheral: Connectable Directed Event +TSPC_GAP_20_3 False Peripheral: Non-Connectable Undirected Event +TSPC_GAP_20_4 False Peripheral: Scannable Undirected Event +------------------------------------------------------------------------------- + + + Peripheral Link Layer Advertising Data Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_20A_1 False AD Type-Service UUID (C.1) +TSPC_GAP_20A_2 False AD Type-Local Name (C.1) +TSPC_GAP_20A_3 False AD Type-Flags (C.2) +TSPC_GAP_20A_4 False AD Type-Manufacturer Specific Data (C.1) +TSPC_GAP_20A_5 False AD Type-TX Power Level (C.1) +TSPC_GAP_20A_6 False AD Type-Security Manager Out of Band (OOB) (C.3) +TSPC_GAP_20A_7 False AD Type-Security manager TK Value (C.1) +TSPC_GAP_20A_8 False AD Type-Slave Connection Interval Range (C.1) +TSPC_GAP_20A_9 False AD Type-Service Solicitation (C.1) +TSPC_GAP_20A_10 False AD Type-Service Data (C.1) +------------------------------------------------------------------------------- +C.1: Optional if (TSPC_GAP_20_1 or TSPC_GAP_20_3 or TSPC_GAP_20_4) is + supported, otherwise Excluded. +C.2: Mandatory if TSPC_GAP_22_2 (Limited Discoverable Mode) or TSPC_GAP_22_3 + (General Discoverable Mode) is supported, otherwise Optional. +C.3: Optional if (TSPC_GAP_20_1 (Connectable Undirected Event) or TSPC_GAP_20_3 + (Non-Connectable Undirected Event) or TSPC_GAP_20_4 + (Scannable Undirected Event)) and TSPC_SM_2_4 (OOB supported) are + supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral Link Layer Control Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_21_1 False (*) Peripheral: Connection Update Procedure (M) +TSPC_GAP_21_2 False (*) Peripheral: Channel Map Update Procedure (M) +TSPC_GAP_21_3 False Peripheral: Encryption Procedure (O) +TSPC_GAP_21_4 False (*) Peripheral: Feature Exchange Procedure (M) +TSPC_GAP_21_5 False (*) Peripheral: Version Exchange Procedure (M) +TSPC_GAP_21_6 False (*) Peripheral: Termination Procedure (M) +------------------------------------------------------------------------------- + + + Peripheral Discovery Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_22_1 False Peripheral: Non-Discoverable Mode (C.2) +TSPC_GAP_22_2 False Peripheral: Limited Discoverable Mode (C.1) +TSPC_GAP_22_3 False Peripheral: General Discoverable Mode (C.1) +TSPC_GAP_22_4 False Peripheral: Name Discovery Procedure (C.3) +------------------------------------------------------------------------------- +C.1: Optional if (TSPC_GAP_5_3 OR TSPC_GAP_42_2), otherwise Excluded. +C.2: Mandatory if (TSPC_GAP_5_3 or TSPC_GAP_42_1) is supported, + otherwise Excluded. +C.3: Optional if TSPC_GAP_5_3 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral Connection Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_23_1 False Peripheral: Non-Connectable Mode (C.1) +TSPC_GAP_23_2 False Peripheral: Directed Connectable Mode (O) +TSPC_GAP_23_3 False Peripheral: Undirected Connectable Mode (M) +TSPC_GAP_23_4 False Peripheral: Connection Parameter Update + Procedure (O) +TSPC_GAP_23_5 False Peripheral: Terminate Connection Procedure (M) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_5_3 (LE Only – Peripheral role) OR TSPC_GAP_42_3 + (BR/EDR/LE – Non-Connectable Mode) OR TSPC_GAP_42_4 + (BR/EDR/LE – Connectable Mode) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral Bonding Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_24_1 False Peripheral: Non-Bondable Mode (M) +TSPC_GAP_24_2 False Peripheral: Bondable Mode (C.1) +TSPC_GAP_24_3 False Peripheral: Bonding Procedure (C.2) +TSPC_GAP_24_4 False Peripheral: Multiple Bonds (C.3) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_GAP_5_3 (LE Only – Peripheral role) OR (TSPC_GAP_38_3 + (BR/EDR/LE – Peripheral role) AND NOT TSPC_GAP_42_6 (BR.EDR/LE - + Bondable Mode)) is supported, Mandatory if TSPC_GAP_42_6 + (BR/EDR/LE – Bondable Mode) is supported, otherwise Excluded. +C.2: Optional if TSPC_GAP_24_2 (Bondable Mode) is supported, otherwise Excluded +------------------------------------------------------------------------------- + + + Peripheral Security Aspects Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_25_1 False Peripheral: Security Mode (O) +TSPC_GAP_25_2 False Peripheral: Security Mode 2 (O) +TSPC_GAP_25_3 False Peripheral: Authentication Procedure (C.2) +TSPC_GAP_25_4 False Peripheral: Authorization Procedure (O) +TSPC_GAP_25_5 False Peripheral: Connection Data Signing Procedure + (O) +TSPC_GAP_25_6 False Peripheral: Authenticate Signed Data Procedure + (O) +TSPC_GAP_25_7 False Peripheral: Authenticated Pairing + (LE security mode 1 level 3) (C.1) +TSPC_GAP_25_8 False Peripheral: Unauthenticated Pairing + (LE security mode 1 level 2) (C.1) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_GAP_25_1 is supported, otherwise Excluded. +C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported, + otherwise Optional. +------------------------------------------------------------------------------- + + + Peripheral Privacy Feature +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_26_1 False Peripheral: Privacy Feature (O) +TSPC_GAP_26_2 False Peripheral: Non-Resolvable Private Address + Generation Procedure (C.1) +TSPC_GAP_26_3 False Peripheral: Resolvable Private Address + Generation Procedure (C.2) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded. +C.2: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral GAP Characteristics +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_27_1 False (*) Peripheral: Device Name (M) +TSPC_GAP_27_2 False (*) Peripheral: Appearance (M) +TSPC_GAP_27_3 False Peripheral: Peripheral Privacy Flag (C.1) +TSPC_GAP_27_4 False Peripheral: Reconnection Address (C.2) +TSPC_GAP_27_5 False Peripheral: Peripheral Preferred Connection + Parameters (O) +TSPC_GAP_27_6 False Peripheral: Writeable Device Name (O) +TSPC_GAP_27_7 False Peripheral: Writeable Appearance (O) +TSPC_GAP_27_8 False Peripheral: Writeable Peripheral Privacy Flag + (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded. +C.2: Optional if TSPC_GAP_26_1 and TSPC_GAP_27_3 are supported, + otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Physical Layer +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_28_1 True (*#) Central: Transmitter (M) +TSPC_GAP_28_2 True (*#) Central: Receiver (M) +------------------------------------------------------------------------------- + + + Central Link Layer States +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_29_1 True (*#) Central: Standby (M) +TSPC_GAP_29_2 True (*#) Central: Scanning (M) +TSPC_GAP_29_3 True (*#) Central: Initiating (M) +TSPC_GAP_29_4 True (*#) Central: Connection, Master Role (M) +------------------------------------------------------------------------------- + + + Central Link Layer Scanning Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_30_1 True (*#) Central: Passive Scanning (O) +TSPC_GAP_30_2 True (*#) Central: Active Scanning (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_38_4) is supported. + Optional if TSPC_GAP_30_1 and (TSPC_GAP_5_4 OR TSPC_GAP_38_4) + is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Link Layer Control Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_31_1 True (*#) Central: Connection Update Procedure (M) +TSPC_GAP_31_2 True (*#) Central: Channel Map Update Procedure (M) +TSPC_GAP_31_3 True (*#) Central: Encryption Procedure (O) +TSPC_GAP_31_4 True (*#) Central: Feature Exchange Procedure (M) +TSPC_GAP_31_5 True (*#) Central: Version Exchange Procedure (M) +TSPC_GAP_31_6 True (*#) Central: Termination Procedure (M) +------------------------------------------------------------------------------- + + + Central Discovery Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_32_1 False Central: Limited Discovery Procedure (C.2) +TSPC_GAP_32_2 True (*#) Central: General Discovery Procedure (C.1) +TSPC_GAP_32_3 True (*#) Central: Name Discovery Procedure (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_1) is supported, else Excluded. +C.2: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_2) is supported, + otherwise Excluded. +C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_4) is supported, + otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Connection Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_33_1 True (*#) Central: Auto Connection Establishment + Procedure (C.3) +TSPC_GAP_33_2 True (*#) Central: General Connection Establishment + Procedure (C.1) +TSPC_GAP_33_3 True (*#) Central: Selective Connection Establishment + Procedure (C.3) +TSPC_GAP_33_4 True (*#) Central: Direct Connectin Establishment + Procedure (C.2) +TSPC_GAP_33_5 True (*#) Central: Connection Parameter Update Procedure + (C.2) +TSPC_GAP_33_6 True (*#) Central: Terminate Connection Procedure + (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) and TSPC_GAP_36_1 is + supported, otherwise Optional. +C.2: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported, + otherwise Excluded. +C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported, + otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Bonding Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_34_1 False Central: Non-Bondable Mode (C.1) +TSPC_GAP_34_2 True (*#) Central: Bondable Mode (C.2) +TSPC_GAP_34_3 True (*#) Central: Bonding Procedure (O) +------------------------------------------------------------------------------- +C.1: Mandatory if (TSPC_GAP_5_4 or 39/5) is supported, otherwise Excluded. +C.2: Optional if (TSPC_GAP_5_4 or 39/6) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Security Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_35_1 True (*#) Central: Security Mode 1 (O) +TSPC_GAP_35_2 True (*#) Central: Security Mode 2 (O) +TSPC_GAP_35_3 True (*#) Central: Authentication Procedure (O) +TSPC_GAP_35_4 True (*#) Central: Authorization Procedure (O) +TSPC_GAP_35_5 True (*#) Central: Connection Data Signing Procedure (O) +TSPC_GAP_35_6 True (*#) Central: Authenticate Signed Data Procedure (O) +TSPC_GAP_35_7 False Central: Authenticated Pairing + (LE security mode 1 level 3) (C.1) +TSPC_GAP_35_8 False Central: Unauthenticated Pairing + (LE security mode 1 level 2) (C.1) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Privacy Feature +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_36_1 False Central: Privacy Feature (C.3) +TSPC_GAP_36_2 False Central: Non-Resolvable Private Address + Generation Procedure (C.1) +TSPC_GAP_36_3 False Central: Resolvable Private Address Resolution + Procedure (C.2) +TSPC_GAP_36_4 False Central: Write to Privacy Characteristic + (Enable/Disable Privacy) (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_36_1 and TSPC_GAP_30_2 are supported, + otherwise Excluded. +C.2: Mandatory if TSPC_GAP_36_1 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Central GAP Characteristics +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_37_1 True (*#) Central: Device Name (M) +TSPC_GAP_37_2 True (*#) Central: Appearance (M) +------------------------------------------------------------------------------- + + + BR/EDR/LE Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_38_1 False BR/EDR/LE: Broadcaster (C.1) +TSPC_GAP_38_2 True (*#) BR/EDR/LE: Observer (C.1) +TSPC_GAP_38_3 False BR/EDR/LE: Peripheral (C.1) +TSPC_GAP_38_4 True (*#) BR/EDR/LE: Central (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +This table is applicable for BR/EDR/LE configurations. For LE-only +configurations, see 'LE Roles' table for role declarations. +------------------------------------------------------------------------------- + + + Central BR/EDR/LE Modes +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_39_1 True (*#) Central BR/EDR/LE: Non-Discoverable Mode (C.1) +TSPC_GAP_39_2 True (*#) Central BR/EDR/LE: Discoverable Mode (C.2) +TSPC_GAP_39_3 True (*#) Central BR/EDR/LE: Non-Connectable Mode (C.3) +TSPC_GAP_39_4 True (#) Central BR/EDR/LE: Connectable Mode (M) +TSPC_GAP_39_5 False Central BR/EDR/LE: Non-Bondable Mode (C.4) +TSPC_GAP_39_6 True (*#) Central BR/EDR/LE: Bondable Mode (C.5) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded. +C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR, + otherwise Excluded. +C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded. +C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded. +C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded. +------------------------------------------------------------------------------- + + + Central BR/EDR/LE Idle Mode Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_40_1 True (*#) Central BR/EDR/LE: General Discovery (C.1) +TSPC_GAP_40_2 False Central BR/EDR/LE: Limited Discovery (C.2) +TSPC_GAP_40_3 True (*#) Central BR/EDR/LE: Device Type Discovery (C.3) +TSPC_GAP_40_4 True (*#) Central BR/EDR/LE: Name Discovery (C.4) +TSPC_GAP_40_5 True (*#) Central BR/EDR/LE: Link Establishment (C.5) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_3_1 is supported over BR/EDR, otherwise Excluded. +C.2: Mandatory if TSPC_GAP_3_2 is supported over BR/EDR, otherwise Excluded. +C.3: Mandatory if (TSPC_GAP_3_1 or TSPC_GAP_3_2) is supported over BR/EDR, + otherwise Excluded. +C.4: Mandatory if TSPC_GAP_3_3 is supported over BR/EDR, otherwise Excluded. +C.5: Mandatory if (TSPC_GAP_4_1 or TSPC_GAP_4_2) is supported over BR/EDR, + otherwise Excluded. +------------------------------------------------------------------------------- + + + Central BR/EDR/LE Security Aspects +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_41_1 True (#) Central BR/EDR/LE: Security Aspects (M) +------------------------------------------------------------------------------- + + + Peripheral BR/EDR/LE Modes +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_42_1 False Peripheral BR/EDR/LE: Non-Discoverable Mode + (C.1) +TSPC_GAP_42_2 False Peripheral BR/EDR/LE: Discoverable Mode + (C.2) +TSPC_GAP_42_3 False Peripheral BR/EDR/LE: Non-Connectable Mode + (C.3) +TSPC_GAP_42_4 False (*) Peripheral BR/EDR/LE: Connectable Mode (M) +TSPC_GAP_42_5 False Peripheral BR/EDR/LE: Non-Bondable Mode (C.4) +TSPC_GAP_42_6 False Peripheral BR/EDR/LE: Bondable Mode (C.5) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded. +C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR, + otherwise Excluded. +C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded. +C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded. +C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral BR/EDR/LE Security Aspects +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_43_1 False (*) Peripheral BR/EDR/LE: Security Aspects (M) +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_1_1 False Master Role (Initiator) +TSPC_SM_1_2 False Slave Role (Responder) +TSPC_SM_2_4 False OOB supported (O) +TSPC_ALL False Turns on all +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +No special PIXIT settings required. All should be set according to Tester's +test environment. diff --git a/android/pics-hid.txt b/android/pics-hid.txt new file mode 100644 index 00000000..ba506266 --- /dev/null +++ b/android/pics-hid.txt @@ -0,0 +1,290 @@ +HID PICS for the PTS tool. + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_1_1 True (*) Role: Host, Report protocol (O.1) +TSPC_HID_1_2 False Role: HID Role (O.1) +TSPC_HID_1_3 False Role: Host, Boot protocol (O.1) +------------------------------------------------------------------------------- +O.1: It is Mandatory to support One of these roles. +------------------------------------------------------------------------------- + + + Application Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_2_1 True Host: Establish HID connection (M.1) +TSPC_HID_2_2 True Host: Accept HID connection (M.1) +TSPC_HID_2_3 True Host: Terminate HID connection (M.1) +TSPC_HID_2_4 True Host: Accept termination of HID connection (M.1) +TSPC_HID_2_5 True Host: Support for virtual cables (M.1) +TSPC_HID_2_6 True Host: HID initiated connection (M.1) +TSPC_HID_2_7 True Host: Host initiated connection (M.1) +TSPC_HID_2_8 True Host: Host data transfer to HID (C.1) +TSPC_HID_2_9 True Host: HID data transfer to Host (C.1) +TSPC_HID_2_10 False Host: Boot mode data transfer to Host (C.2) +TSPC_HID_2_11 False Host : Boot mode data transfer to HID (C.2) +TSPC_HID_2_12 False Host : Support for Application to send + GET_Report (O) +TSPC_HID_2_13 False Host : Support for Application to send + SET_REPORT (O) +TSPC_HID_2_14 False Host : Support for sending HCI_CONTROL with + VIRTUAL_CABLE_UNPLUG (C.3) +TSPC_HID_2_15 False Host : Support for receiving HCI_CONTROL with + VIRTUAL_CABLE_UNPLUG (C.2) +------------------------------------------------------------------------------- +M.1: Mandatory to support IF (TSPC_HID_1_1) supported. +C.1: Optional for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory + for Host Role (TSPC_HID_1_1). +C.2: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional. +C.3: Optional IF (TSPC_HID_2_5) supported, otherwise excluded. +------------------------------------------------------------------------------- + + + Device to Host Transfers +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_3_1 False Host : Data reports larger than host MTU on + Control channel (O) +TSPC_HID_3_2 True (*) Host : Data reports larger than host MTU on + Interrupt channel (C.1) +TSPC_HID_3_3 True (*) Host : Data reports to host (C.1) +TSPC_HID_3_4 False Host : Boot mode reports to host (C.2) +------------------------------------------------------------------------------- +C.1: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory if + (TSPC_HID_2_12), otherwise Optional. +C.2: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional. +------------------------------------------------------------------------------- + + + Host to Device Transfers +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_4_1 False Host : Data reports larger than device MTU on + Control channel (C.1) +TSPC_HID_4_2 False Host : Data reports larger than device MTU on + Interrupt channel (C.1) +TSPC_HID_4_3 True (*) Host : Data reports to device (C.2) +TSPC_HID_4_4 False Host : Boot mode reports to device (O) +------------------------------------------------------------------------------- +C.1: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional +C.2: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory for + Host Role (TSPC_HID_1_1). +------------------------------------------------------------------------------- + + + HID Control Commands +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_5_1 False Host : Set_Protocol command (C.1) +TSPC_HID_5_2 False Host : Get_Protocol command (C.1) +TSPC_HID_5_3 False Host : Set_Idle command (O) +TSPC_HID_5_4 False Host : Get_Idle command (O) +TSPC_HID_5_5 False (*) Host : Set_Report command (M.1) +TSPC_HID_5_6 False (*) Host : Get_Report command (M.2) +------------------------------------------------------------------------------- +M.1: Mandatory IF (TSPC_HID_1_1) supported AND (TSPC_HID_2_13) supported. +C.1: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional. + If either Set_Protocol or Get_Protocol supported, both are Mandatory. +M.2: Mandatory IF (TSPC_HID_1_1) Supported AND (TSPC_HID_2_12) Supported +------------------------------------------------------------------------------- + + + Host Link Manager Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_6_1 False Host : Initiate Authentication before + connection completed (C.1) +TSPC_HID_6_2 False Host : Initiate Authentication after connection + completed (C.1) +TSPC_HID_6_3 False Host : Initiate pairing before connection + completed (C.2) +TSPC_HID_6_4 False Host : Initiate pairing after connection + completed (C.2) +TSPC_HID_6_5 False Host : Encryption (O) +TSPC_HID_6_6 False Host : Initiate encryption (C.3) +TSPC_HID_6_7 False Host : Accept encryption requests (C.3) +TSPC_HID_6_8 True Host : Role switch (Master/Slave) (M.1) +TSPC_HID_6_9 True Host : Request Master Slave switch (M.1) +TSPC_HID_6_10 True Host : Accept Master Slave switch requests (M.1) +TSPC_HID_6_11 False Host : Hold mode (O) +TSPC_HID_6_12 True Host : Sniff mode (M.1) +TSPC_HID_6_13 False Host : Park mode (O) +------------------------------------------------------------------------------- +C.1: If Host Authentication supported, both (TSPC_HID_6_1) AND (TSPC_HID_6_2) + must be supported. +C.2: If Pairing supported both (TSPC_HID_6_3) AND (TSPC_HID_6_4) must + be supported. +M.1: Mandatory IF (TSPC_HID_1_1) supported. +C.3: Mandatory IF (TSPC_HID_6_5) encryption supported. +------------------------------------------------------------------------------- + + + Host Link Control Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_7_1 True Host : Supports inquiry, 79 channel (M.1) +TSPC_HID_7_2 False (*) Host : Supports inquiry scan, 79 channel (X) +------------------------------------------------------------------------------- +M.1: Mandatory to support IF (TSPC_HID_1_1) supported. +X: Feature should not be used by a Host, but can be supported in LM. +------------------------------------------------------------------------------- + + + HID Device Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_8_1 False Hid : Pointing HID (O.1) +TSPC_HID_8_2 False Hid : Keyboard HID (O.1) +TSPC_HID_8_3 False Hid : Identification HID (O.1) +TSPC_HID_8_4 False Hid : Other HID (O.1) +------------------------------------------------------------------------------- +O.1: It is Mandatory to support One of these roles IF (TSPC_HID_1_2) + is selected +------------------------------------------------------------------------------- + + + HID Application Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_9_1 False Hid : Establish HID connection (O) +TSPC_HID_9_2 False (*) Hid : Accept HID connection (M.1) +TSPC_HID_9_3 False Hid : Terminate HID connection (O) +TSPC_HID_9_4 False (*) Hid : Accept Termination of HID connection (M.1) +TSPC_HID_9_5 False Hid : Support for virtual cables (O) +TSPC_HID_9_6 False Hid : HID initiated reconnection (C.1) +TSPC_HID_9_7 False Hid : Host initiated reconnection (C.1) +TSPC_HID_9_8 False Hid : Host data transfer to HID (C.2) +TSPC_HID_9_9 False Hid : HID data transfer to Host (C.2) +TSPC_HID_9_10 False Hid : HID Boot mode data transfer to Host (C.3) +TSPC_HID_9_11 False Hid : Host Boot mode data transfer to HID (C.4) +TSPC_HID_9_12 False Hid : Output reports declared (C.4) +TSPC_HID_9_13 False Hid : Input reports declared (C.3) +TSPC_HID_9_14 False Hid : Feature reports declared (O) +TSPC_HID_9_15 False Hid : Support for sending HCI_CONTROL with + VIRTUAL_CABLE_UNPLUG (C.5) +TSPC_HID_9_16 False Hid : Support for receiving HCI_CONTROL with + VIRTUAL_CABLE_UNPLUG (C.5) +------------------------------------------------------------------------------- +M.1: Mandatory IF (TSPC_HID_1_2) supported. +C.1: One of these is Mandatory IF (TSPC_HID_9_5) is supported + (SDP attribute 0x204=True) +C.2: One of these is Mandatory. +C.3: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is selected +C.4: Mandatory IF (TSPC_HID_8_2) is supported (for status indicators) +C.5: Optional IF (TSPC_HID_9_2) supported, otherwise excluded. +------------------------------------------------------------------------------- + + + Device to Host Transfers +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_10_1 False Hid : Data reports larger than host MTU on + Control channel (O) +TSPC_HID_10_2 False Hid : Data reports larger than host MTU on + Interrupt channel (O) +TSPC_HID_10_3 False Hid : Data reports to host (O) +TSPC_HID_10_4 False Hid : Boot mode reports to host (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is supported. + Optional for other HIDs. +------------------------------------------------------------------------------- + + + Host to Device Transfers +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_11_1 False Hid : Data reports larger than device MTU on + Control channel (O) +TSPC_HID_11_2 False Hid : Data reports larger than device MTU on + Interrupt channel (O) +TSPC_HID_11_3 False Hid : Data reports to device (O) +TSPC_HID_11_4 False Hid : Boot mode reports to device (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory IF (TSPC_HID_8_2) is supported. Optional for other HIDs. +------------------------------------------------------------------------------- + + + HID Control Commands +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_12_1 False Hid : Set_Protocol command (C.1) +TSPC_HID_12_2 False Hid : Get_Protocol command (C.1) +TSPC_HID_12_3 False Hid : Set_Idle command (C.2) +TSPC_HID_12_4 False Hid : Get_Idle command (C.2) +TSPC_HID_12_5 False Hid : Set_Report command (C.3) +TSPC_HID_12_6 False Hid : Get_Report command (C.4) +------------------------------------------------------------------------------- +C.1: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is supported. + Optional for other HIDs. If either Set_Protocol or Get_Protocol + supported, both are Mandatory. +C.2: Mandatory IF (TSPC_HID_8_2) Keyboard is selected. Optional for other HIDs. +C.3: Mandatory IF (TSPC_HID_9_12) or (TSPC_HID_9_14) supported. +C.4: Mandatory IF (TSPC_HID_9_13) or (TSPC_HID_9_14) supported +------------------------------------------------------------------------------- + + + HID Link Manager Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_13_1 False Hid : Host initiated Authentication before + connection completed (C.1) +TSPC_HID_13_2 False Hid : Host initiated Authentication after + connection completed (C.1) +TSPC_HID_13_3 False Hid : Initiate pairing before connection + completed (X) +TSPC_HID_13_4 False Hid : Initiate pairing after connection + completed (X) +TSPC_HID_13_5 False Hid : Encryption (C.1) +TSPC_HID_13_6 False Hid : Initiate encryption (O) +TSPC_HID_13_7 False Hid : Accept encryption requests (C.2) +TSPC_HID_13_8 False Hid : Role switch (Master/Slave) (C.3) +TSPC_HID_13_9 False Hid : Request Master Slave switch (O) +TSPC_HID_13_10 False Hid : Accept Master Slave switch requests (C.3) +TSPC_HID_13_11 False Hid : Hold mode (O) +TSPC_HID_13_12 False Hid : Sniff mode (O) +TSPC_HID_13_13 False Hid : Park mode (O) +------------------------------------------------------------------------------- +C.1: Mandatory IF (TSPC_HID_8_2) OR (TSPC_HID_8_3) is selected. Optional + for other HIDs. +C.2: Mandatory IF (TSPC_HID_13_5) supported. +C.3: Mandatory IF (TSPC_HID_9_6) is supported. +X: Feature should not be used by a HID device, but can be supported in LM. +------------------------------------------------------------------------------- + + + HID Link Control Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_14_1 False Hid : Supports inquiry, 79 channel (O) +TSPC_HID_14_2 False (*) Hid : Supports inquiry scan, 79 channel (M.1) +TSPC_ALL False Enables all test cases when set to true. +------------------------------------------------------------------------------- +M.1: Mandatory IF (TSPC_HID_1_2) is supported. +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +No special PIXIT settings required. All should be set according to Tester's +test environment. diff --git a/android/pics-opp.txt b/android/pics-opp.txt new file mode 100644 index 00000000..11e61ff8 --- /dev/null +++ b/android/pics-opp.txt @@ -0,0 +1,189 @@ +OPP PICS for the PTS tool. + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_OPP_1_1 True (*) Role: Object Push Client. +TSPC_OPP_1_2 True (*) Role: Object Push Server. +------------------------------------------------------------------------------- +C.1: It is Mandatory to Support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Client Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_OPP_1b_1 True Client supports OPP version 1.1. (C.1) +TSPC_OPP_1b_2 False Client supports OPP version 1.2. (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the profile versions. +------------------------------------------------------------------------------- + + + Client Application Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_OPP_2_1 True Client: Perform Service Discovery request. + (M.1) +TSPC_OPP_2_2 True Client: Authentication/PIN exchange supported. + (M.1) +TSPC_OPP_2_2a True (*) Client: Require Authentication/PIN by default. + (O) +TSPC_OPP_2_3 True Client: Object Push is supported. (M.1) +TSPC_OPP_2_4 True (*) Client: vCard 2.1 format is supported for + Object Push. (C.3) +TSPC_OPP_2_5 False Client: vCalender 1.0 format is supported for + Object Push. (O) +TSPC_OPP_2_6 False Client: vMsg as defined in IrMC 1.1 is supported + for Object Push. (O) +TSPC_OPP_2_7 False Client: vNote as defined in IrMC 1.1 is + supported for Object Push. (O) +TSPC_OPP_2_8 True (*) Client: Support content formats other than those + declared in TSPC_OPP_2_44 through + TSPC_OPP_2_7. (O) +TSPC_OPP_2_8a False Client: Support specific set of other content + formats. (C.4) +TSPC_OPP_2_8b True (*) Client: Support all content formats. (C.4) +TSPC_OPP_2_9 True (*) Client: Push multiple vCard objects. (O) +TSPC_OPP_2_9a False Client: Push multiple vCard objects using + different PUT operations. (C.5) +TSPC_OPP_2_9b True (*) Client: Push multiple vCard objects using the + same PUT operation. (C.5) +TSPC_OPP_2_10 False Client: Push multiple vCalender objects. (O) +TSPC_OPP_2_10a False Client: Push multiple vMsg objects using + different PUT operations. (C.6) +TSPC_OPP_2_10b False Client: Push multiple vMsg objects using the + same PUT operation. (C.6) +TSPC_OPP_2_11 False Client: Push multiple vMsg objects. (O) +TSPC_OPP_2_11a False Client: Push multiple vMsg objects using + different PUT operations. (C.7) +TSPC_OPP_2_11b False Client: Push multiple vMsg objects using the + same PUT operation. (C.7) +TSPC_OPP_2_12 False Client: Push multiple vNote objects. (O) +TSPC_OPP_2_12a False Client: Push multiple vNote objects using + different PUT operations. (C.8) +TSPC_OPP_2_12b False Client: Push multiple vNote objects using the + same PUT operation. (C.8) +TSPC_OPP_2_13 False Client: Pull business card is supported. (O) +TSPC_OPP_2_14 False Client: vCard 2.1 format is supported for + Business Card Pull. (C.1) +TSPC_OPP_2_15 False Client: Exchange business card is supported. (O) +TSPC_OPP_2_16 False Client: vCard 2.1 format is supported for + Business Card Exchange (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory to Support IF (TSPC_OPP_2_13) Business Card Pull is supported. +C.2: Mandatory to Support IF (TSPC_OPP_2_15) Business Card Exchange is + supported. +M.1: Mandatory to Support IF (TSPC_OPP_1_1) supported. +C.3: vCard 2.1 support is required for devices containing phonebook + applications. vCard 2.1 support optional for other devices. +C.4: Mandatory to support one of TSPC_OPP_2_8a or TSPC_OPP_2_8b if TSPC_OPP_2_8 + is supported. Otherwise, both items are excluded. +C.5: Mandatory to support at least one of TSPC_OPP_2_9a and TSPC_OPP_2_9b if + TSPC_OPP_2_9 is supported. Otherwise, both items are excluded. +C.6: Mandatory to support at least one of TSPC_OPP_2_10a and TSPC_OPP_2_10b if + TSPC_OPP_2_10 is supported. Otherwise, both items are excluded. +C.7: Mandatory to support at least one of TSPC_OPP_2_11a and TSPC_OPP_2_11b if + TSPC_OPP_2_11 is supported. Otherwise, both items are excluded. +C.8: Mandatory to support at least one of TSPC_OPP_2_12a and TSPC_OPP_2_12b if + TSPC_OPP_2_12 is supported. Otherwise, both items are excluded. +------------------------------------------------------------------------------- + + + Server Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_OPP_2b_1 True Server supports OPP version 1.1. +TSPC_OPP_2b_2 False Server supports OPP version 1.2. +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the profile versions. +------------------------------------------------------------------------------- + + + Server Application Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_OPP_3_1 True Server: Provide information on supported + contents type on service discovery + request. (M) +TSPC_OPP_3_2 True Server: Authentication/PIN exchange supported. + (M) +TSPC_OPP_3_3 True Server: Object Push is supported. (M) +TSPC_OPP_3_3a True (*) Server: Receive multiple objects in the same + PUT operation. (O) +TSPC_OPP_3_4 True (*) Server: vCard 2.1 format is supported for Object + Push. (C.3) +TSPC_OPP_3_5 False Server: vCalender 1.0 format is supported for + Object Push. (O) +TSPC_OPP_3_6 False Server: vMsg as defined in IrMC 1.1 is supported + for Object Push. (O) +TSPC_OPP_3_7 False Server: vNote as defined in IrMC 1.1 is + supported for Object Push. (O) +TSPC_OPP_3_8 True (*) Server: Support content formats other than those + declared in TSPC_OPP_3_4 through + TSPC_OPP_3_7. (O) +TSPC_OPP_3_8a False Server: Support specific set of other content + formats. (C.4) +TSPC_OPP_3_8b True (*) Server: Support all content formats. (C.4) +TSPC_OPP_3_9 True (*) Server: Object Push vCard reject. (O) +TSPC_OPP_3_10 False Server: Object Push vCal rejectt. (O) +TSPC_OPP_3_11 False Server: Object Push vMsg reject. (O) +TSPC_OPP_3_12 False Server: Object Push vNote reject. (O) +TSPC_OPP_3_13 False Server: Business card pull is supported. (O.1) +TSPC_OPP_3_14 False Server: vCard 2.1 format is supported for + Business Card Pull. (C.1) +TSPC_OPP_3_15 False Server: Business card pull reject. (O) +TSPC_OPP_3_16 False Server: Business card exchange is supported. + (O.2) +TSPC_OPP_3_17 False Server: vCard 2.1 format is supported for + Business Card Exchange (C.2) +TSPC_OPP_3_18 False Server: Business card exchange reject. (O) +------------------------------------------------------------------------------- +M.1: Mandatory to Support IF (TSPC_OPP_1_2) supported. +C.2: Mandatory to Support IF (TSPC_OPP_3_16) Business Card Exchange is + supported. +O.1: IF NOT Supported, an error message must be sent on request for Business + Card Pull. +O.2: IF NOT Supported, an error message must be sent on request for Business + Card Exchange. +C.1: Mandatory to Support IF (TSPC_OPP_3_13) Business Card Pull is supported. +C.3: vCard 2.1 support is required for devices containing phonebook + applications. vCard 2.1 support optional for other devices. +C.4: Mandatory to support one of TSPC_OPP_3_8a or TSPC_OPP_3_8b if TSPC_OPP_3_8 + is supported. Otherwise, both items are excluded. +------------------------------------------------------------------------------- + + + Additional OPP Capabilities +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_OPP_4_1 False Abort-Push Operation is supported. (O) +TSPC_OPP_4_2 False Disconnect of OBEX session should be tested. +TSPC_OPP_4_3 False Multiple vCards transferred as a single vObject + is supported. (C.1) +TSPC_OPP_4_4 False Multiple vCards transfer is supported. (C.1) +TSPC_OPP_4_5 False vCards with multiple Phone Number Fields is + supported. (C.1) +TSPC_OPP_4_6 False Server supports Push vCal to Different Time + Zone. (C.1) +TSPC_ALL False Turn on all test cases. +------------------------------------------------------------------------------- +C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded. +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +No special PIXIT settings required. All should be set according to Tester's +test environment. diff --git a/android/pics-pan.txt b/android/pics-pan.txt new file mode 100644 index 00000000..406b6a9b --- /dev/null +++ b/android/pics-pan.txt @@ -0,0 +1,151 @@ +PAN PICS for the PTS tool. + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PAN_1_1 True (*#) Role: Network Access Point (O.1) +TSPC_PAN_1_2 False Role: Group Ad-hoc Network (O.1) +TSPC_PAN_1_3 True (*#) Role: PAN User (O.1) +TSPC_PAN_1a_1 True (#) BNEP: BNEP Connection Setup (M) +TSPC_PAN_1a_2 True (#) BNEP: BNEP Data Packet Reception (M) +TSPC_PAN_1a_3 True (#) BNEP: BNEP Data Packet Transmission (M) +TSPC_PAN_1a_4 True (#) BNEP: BNEP Control Message Processing (M) +TSPC_PAN_1a_5 True (#) BNEP: BNEP Extension Header Processing (M) +TSPC_PAN_1a_6 False BNEP: Network Protocol Filter Message + Transmission (O) +TSPC_PAN_1a_7 False BNEP: Multicast Address Filter Message + Transmission (O) +------------------------------------------------------------------------------- +O.1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Network Access Point Application Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PAN_2_1 True (#) NAP: Support BNEP (M) +TSPC_PAN_2_2 True (#) NAP: Support BNEP Forwarding (M) +TSPC_PAN_2_3 False NAP: Support Layer 2-Bridging between PAN and + External Network (C.1) +TSPC_PAN_2_4 True (*#) NAP: Support IP forwarding between PAN and + External Network (C.1) +TSPC_PAN_2_5 False NAP: Support BNEP Packet Filtering (O) +TSPC_PAN_2_6 True (*#) NAP: Support IPv4 (C.2) +TSPC_PAN_2_6a True (*#) NAP: Supports operable routable IPv4 address (O) +TSPC_PAN_2_6b False NAP: Support link-local address configuration + for IPv4 (C.4) +TSPC_PAN_2_7 False NAP: Support ping client for IPv4 (O) +TSPC_PAN_2_8 False NAP: Support DHCP Client for IPv4 (O) +TSPC_PAN_2_9 False NAP: Support DNS/LLMNR Resolver for IPv4 (O) +TSPC_PAN_2_9a True (#) NAP: Support LLMNR Sender for IPv4 (C.5) +TSPC_PAN_2_9b False NAP: Support LLMNR Responder for IPv4 (O) +TSPC_PAN_2_10 False NAP: Support HTTP Client for IPv4 (O) +TSPC_PAN_2_11 False NAP: Support WAP Client for IPv4 (O) +TSPC_PAN_2_12 False NAP: Support IPv6 (C.3) +TSPC_PAN_2_13 False NAP: Support ping client for IPv6 (O) +TSPC_PAN_2_14 False NAP: Support DNS/LLMNR Resolver for IPv6 (O) +TSPC_PAN_2_14a False (*) NAP: Support LLMNR Sender for IPv6 (C.6) +TSPC_PAN_2_14b False NAP: Support LLMNR Responder for IPv6 (O) +TSPC_PAN_2_15 False NAP: Support HTTP Client for IPv6 (O) +TSPC_PAN_2_16 False NAP: Support WAP Client for IPv6 (O) +TSPC_PAN_2_17 True (#) NAP: Supports Connectable Mode (M) +TSPC_PAN_2_18 True (#) NAP: NAP Service Record (M) +TSPC_PAN_2_19 False NAP: Support at least three PANUs (O) +TSPC_PAN_2_20 False NAP: Support at least two PANUs (O) +------------------------------------------------------------------------------- +Note that support for IP-related features only applies to the PAN interface of + the NAP (i.e. If the IP stack is accessible by PANUs). +C.1: Network Access Point devices MUST support either (TSPC_PAN_2_3) + OR (TSPC_PAN_2_4). +C.2: Mandatory to support IF any IPv4-based transport protocol OR + (TSPC_PAN_2_7-11) is supported, ELSE Optional. +C.3: Mandatory to support IF any IPv6-based transport protocol OR + (TSPC_PAN_2_13-16) is supported, ELSE Optional. +C.4: Mandatory if TSPC_PAN_2_6 is supported and TSPC_PAN_2_6a is not supported, + otherwise optional. +C.5: Mandatory if item (TSPC_PAN_2_6) or item supported. +C.6: Mandatory if item (TSPC_PAN_2_12) supported +------------------------------------------------------------------------------- + + + Group Ad-hoc Network Application Features + (GN Application Features) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PAN_3_1 False (*) GN: Support BNEP (M) +TSPC_PAN_3_2 False (*) GN: Support BNEP Forwarding (M) +TSPC_PAN_3_3 False GN: Support BNEP Packet Filtering (O) +TSPC_PAN_3_4 False GN: Support IPv4 (C.1) +TSPC_PAN_3_5 False GN: Support ping client for IPv4 (O) +TSPC_PAN_3_6 False GN: Support DHCP Client for IPv4 (O) +TSPC_PAN_3_7 False GN: Support DNS/LLMNR Resolver for IPv4 (O) +TSPC_PAN_3_7a False (*) GN: Support LLMNR Sender for IPv4 (C.3) +TSPC_PAN_3_7b False GN: Support LLMNR Responder for IPv4 (O) +TSPC_PAN_3_8 False GN: Support HTTP Client for IPv4 (O) +TSPC_PAN_3_9 False GN: Support WAP Client for IPv4 (O) +TSPC_PAN_3_10 False GN: Support IPv6 (C.2) +TSPC_PAN_3_11 False GN: Support ping client for IPv6 (O) +TSPC_PAN_3_12 False GN: Support DNS/LLMNR Resolver for IPv6 (O) +TSPC_PAN_3_12a False (*) GN: Support LLMNR Sender for IPv6 (C.4) +TSPC_PAN_3_12b False GN: Support LLMNR Responder for IPv6 (O) +TSPC_PAN_3_13 False GN: Support HTTP Client for IPv6 (O) +TSPC_PAN_3_14 False GN: Support WAP Client for IPv6 (O) +TSPC_PAN_3_15 False (*) GN: Supports Connectable Mode (M) +TSPC_PAN_3_16 False (*) GN: GN Service Record (M) +TSPC_PAN_3_17 False GN: Support at least three PANUs (O) +TSPC_PAN_3_18 False GN: Support at least two PANUs (O) +------------------------------------------------------------------------------- +C.1: Mandatory to support IF any IPv4-based transport protocol OR + (TSPC_PAN_3_5-9) is supported, ELSE Optional. +C.2: Mandatory to support IF any IPv6-based transport protocol OR + (TSPC_PAN_3_11-14) is supported, ELSE Optional. +C.3: Mandatory to support IF (TSPC_PAN_3_4) is supported. +C.4: Mandatory to support if (TSPC_PAN_3_10) is supported. +------------------------------------------------------------------------------- + + + PAN User Application Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PAN_4_1 True (#) PANU: Support BNEP (M) +TSPC_PAN_4_2 True (*#) PANU: Support IPv4 (C.1) +TSPC_PAN_4_3 True (*#) PANU: Support ping client for IPv4 (O) +TSPC_PAN_4_4 True (*#) PANU: Support DHCP client for IPv4 (O) +TSPC_PAN_4_5 False PANU: Support DNS/LLMNR Resolver for IPv4 (O) +TSPC_PAN_4_5a True (#) PANU: Support LLMNR Sender for IPv4 (C.2) +TSPC_PAN_4_5b False PANU: Support LLMNR Responder for IPv4 (O) +TSPC_PAN_4_6 False PANU: Support HTTP Client for IPv4 (O) +TSPC_PAN_4_7 False PANU: Support WAP Client for IPv4 (O) +TSPC_PAN_4_8 False PANU: Support IPv6 (C.1) +TSPC_PAN_4_9 False PANU: Support ping client for IPv6 (O) +TSPC_PAN_4_10 False PANU: Support DNS/LLMNR Resolver for IPv6 (O) +TSPC_PAN_4_10a False (*) PANU: Support LLMNR Sender for IPv6 (C.3) +TSPC_PAN_4_10b False PANU: Support LLMNR Responder for IPv6 (O) +TSPC_PAN_4_11 False PANU: Support HTTP Client for IPv6 (O) +TSPC_PAN_4_12 False PANU: Support WAP Client for IPv6 (O) +TSPC_PAN_4_13 False PANU: Support connections to multi-user + NAPs/GNs (O) +TSPC_PAN_4_14 False PANU: Supports Connectable Mode (O) +TSPC_PAN_4_15 False PANU: PANU Service Record (O) +TSPC_ALL False Turns on all the test cases +------------------------------------------------------------------------------- +C.1: PAN User devices must support at least One of items (TSPC_PAN_4_2) or + (TSPC_PAN_4_8). +C.2: Mandatory to support if (TSPC_PAN_4_2) is supported. +C.3: Mandatory to support if (TSPC_PAN_4_8) is supported. +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +No special PIXIT settings required. All should be set according to Tester's +test environment. diff --git a/android/pics-pbap.txt b/android/pics-pbap.txt new file mode 100644 index 00000000..2af5fd8d --- /dev/null +++ b/android/pics-pbap.txt @@ -0,0 +1,446 @@ +PBAP PICS for the PTS tool. + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Major Profile Version (X.Y) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_0_1 False Role: PBAP 1.0 (C.1) +TSPC_PBAP_0_2 True (*) Role: PBAP 1.1 (C.1) +TSPC_PBAP_0_3 False Role: Reserve +TSPC_PBAP_0_4 False (*) Role: PBAP 1.2 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support one and only one major profile version. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_1_1 False Role: PCE (C.1) +TSPC_PBAP_1_2 True (*) Role: PSE (C.1) +------------------------------------------------------------------------------- +C1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Supported features (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_2_1 False (*) PCE: Phone Book Download (C.1) +TSPC_PBAP_2_2 False (*) PCE: Phone Book Browsing (C.1) +TSPC_PBAP_2_3 False (*) PCE: Session Management (M) +TSPC_PBAP_2_4 False PCE: Able to Request Size of the Phonebook (O) +TSPC_PBAP_2_5 False PCE: Database Identifier (C.2) +TSPC_PBAP_2_6 False PCE: Folder Version Counters (C.2) +TSPC_PBAP_2_7 False PCE: vCard Selecting (C.2) +TSPC_PBAP_2_7a False PCE: Able to send vCardSelector (C.2) +TSPC_PBAP_2_7b False PCE: Able to send vCardSelectorOperator (C.2) +TSPC_PBAP_2_8 False (*) PCE: Enhanced Missed Calls (C.4) +TSPC_PBAP_2_8a False (*) PCE: Able to reset the missed Calls (C.2) +TSPC_PBAP_2_9 False PCE: X-BT-UCI vCard Field (C.2) +TSPC_PBAP_2_9a False PCE: Able to request X-BT-UCI Field (C.2) +TSPC_PBAP_2_10 False PCE: X-BT-UID vCard Field (C.2) +TSPC_PBAP_2_10a False PCE: Referencing Contacts (C.2) +TSPC_PBAP_2_12 False PCE: Contact Image Default Format (C.2) +TSPC_PBAP_2_12a False PCE: Able to request Contact Images (C.2) +TSPC_PBAP_2_13 False PCE: Supported Phonebook Objects (C.3) +TSPC_PBAP_2_13a False (*) PCE: Telecom/pb (C.3) +TSPC_PBAP_2_13b False PCE: Telecom/ich (C.3) +TSPC_PBAP_2_13c False PCE: Telecom/och (C.3) +TSPC_PBAP_2_13d False (*) PCE: Telecom/mch (C.3) +TSPC_PBAP_2_13e False (*) PCE: Telecom/cch (C.3) +TSPC_PBAP_2_13f False PCE: Telecom/spd (C.3) +TSPC_PBAP_2_13g False PCE: Telecom/fav (C.3) +TSPC_PBAP_2_13h False PCE: SIM1/Telecom/pb (C.3) +TSPC_PBAP_2_13i False PCE: SIM1/Telecom/ich (C.3) +TSPC_PBAP_2_13j False PCE: SIM1/Telecom/och (C.3) +TSPC_PBAP_2_13k False PCE: SIM1/Telecom/mch (C.3) +TSPC_PBAP_2_13l False PCE: SIM1/Telecom/cch (C.3) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined features. +C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +C.3: Mandatory to support at least one of the listed phonebook objects . +C.4: Optional if TSPC_PBAP_0_3 (PBAP 1.2) and any of the mch or cch folders + (13d,13e,13k,13l) are supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Supported Phone Book Download functions (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_3_1 False (*) PCE: Pull Phone Book (C.1) +------------------------------------------------------------------------------- +C1: Mandatory for PCE if Phone Book Download (TSPC_PBAP_2_1) is supported, + otherwise excluded. +------------------------------------------------------------------------------- + + + Supported Phone Book Browsing functions (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_4_1 False (*) PCE: Set Phone Book (C.1) +TSPC_PBAP_4_2 False (*) PCE: Pull vCard Listing (C.1) +TSPC_PBAP_4_3 False (*) PCE: Pull vCard Entry (C.1) +------------------------------------------------------------------------------- +C1: Mandatory for PCE if Phone Book Browsing TSPC_PBAP_2_2 is supported, + otherwise excluded. +------------------------------------------------------------------------------- + + + Used vCard formats (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_5_1 False (*) PCE: vCard 2.1 (C.1) +TSPC_PBAP_5_2 False (*) PCE: vCard 3.0 (C.1) +------------------------------------------------------------------------------- +C1: It is mandatory to support at least one of the defined versions if PCE + supported. +------------------------------------------------------------------------------- + + + OBEX Functions for PCE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_6_1 False (*) PCE: Connect (M) +TSPC_PBAP_6_2 False (*) PCE: Disconnect (M) +TSPC_PBAP_6_3 False (*) PCE: Get (M) +TSPC_PBAP_6_4 False (*) PCE: Abort (M) +TSPC_PBAP_6_5 False (*) PCE: SetPath (C.1) +TSPC_PBAP_6_6 False PCE: Support for OBEX authentication initiation + (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_PBAP_2_2 (Phone Book Browsing) is supported, + otherwise Excluded. +C.2: Optional to support initiation if TSPC_PBAP_0_1 (PBAP 1.0) or + TSPC_PBAP_0_2 (PBAP 1.1) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + PCE OBEX Header Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_7_1 False (*) PCE: Name (M) +TSPC_PBAP_7_2 False (*) PCE: Type (M) +TSPC_PBAP_7_3 False (*) PCE: Body (M) +TSPC_PBAP_7_4 False (*) PCE: End of Body (M) +TSPC_PBAP_7_5 False (*) PCE: Target (M) +TSPC_PBAP_7_6 False (*) PCE: Who (M) +TSPC_PBAP_7_7 False (*) PCE: Connection ID (M) +TSPC_PBAP_7_8 False (*) PCE: Authentication Challenge (M) +TSPC_PBAP_7_9 False (*) PCE: Authentication Response (M) +TSPC_PBAP_7_10 False (*) PCE: Application Parameters (M) +TSPC_PBAP_7_11 False PCE: Single Response Mode (C.1) +TSPC_PBAP_7_12 False PCE: Single Response Mode Parameter + (ability to parse) (C.1) +TSPC_PBAP_7_13 False PCE: Single Response Mode Parameter + (ability to send) (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + OBEX Error Codes for PCE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_8_1 False (*) PCE: Bad Request (M) +TSPC_PBAP_8_2 False (*) PCE: Not Implemented (M) +TSPC_PBAP_8_3 False (*) PCE: Unauthorized (M) +TSPC_PBAP_8_4 False (*) PCE: Precondition Failed (M) +TSPC_PBAP_8_5 False (*) PCE: Not Found (M) +TSPC_PBAP_8_6 False (*) PCE: Not Acceptable (M) +TSPC_PBAP_8_7 False (*) PCE: Service Unavailable (M) +TSPC_PBAP_8_8 False (*) PCE: Forbidden (M) +------------------------------------------------------------------------------- + + + Supported features ( PSE ) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_9_1 True PSE: Phone Book Download (M) +TSPC_PBAP_9_2 True PSE: Phone Book Browsing (M) +TSPC_PBAP_9_3 True PSE: Session Management (M) +TSPC_PBAP_9_4 True PSE: Able to request the size of the Phonebook + (M) +TSPC_PBAP_9_5 False PSE: Database Identifier (C.1) +TSPC_PBAP_9_5a False PSE: Able to keep a persistent Database + Identifier (C.2) +TSPC_PBAP_9_5b False PSE: Able to regenerate a Database Identifier + (C.2) +TSPC_PBAP_9_6 False PSE: Folder Version Counters (C.1) +TSPC_PBAP_9_6a False PSE: Able to Insert or Remove Entries (C.2) +TSPC_PBAP_9_6b False PSE: Able to Modify contact primary Fields (C.2) +TSPC_PBAP_9_6c False PSE: Able to Modify contact secondary Fields + (C.2) +TSPC_PBAP_9_7 False (*) PSE: vCard Selecting (C.1) +TSPC_PBAP_9_8 False (*) PSE: Enhanced Missed Calls (C.4) +TSPC_PBAP_9_9 False PSE: X-BT-UCI vCard Field (C.2) +TSPC_PBAP_9_10 False PSE: X-BT-UID vCard Field (C.2) +TSPC_PBAP_9_10a False PSE: Referencing Contacts (C.3) +TSPC_PBAP_9_12 False PSE: Contact Image Default Format (C.1) +TSPC_PBAP_9_12a False PSE: Able to request Contact Images (C.2) +TSPC_PBAP_9_13 False PSE: Supported Phonebook Objects +TSPC_PBAP_9_13a False (*) PSE: Telecom/pb (M) +TSPC_PBAP_9_13b False PSE: Telecom/ich (O) +TSPC_PBAP_9_13c False PSE: Telecom/och (O) +TSPC_PBAP_9_13d False PSE: Telecom/mch (O) +TSPC_PBAP_9_13e False (*) PSE: Telecom/cch (O) +TSPC_PBAP_9_13f False PSE: Telecom/spd (C.2) +TSPC_PBAP_9_13g False PSE: Telecom/fav (C.2) +TSPC_PBAP_9_13h False (*) PSE: SIM1/Telecom/pb (O) +TSPC_PBAP_9_13i False PSE: SIM1/Telecom/ich (O) +TSPC_PBAP_9_13j False PSE: SIM1/Telecom/och (O) +TSPC_PBAP_9_13k False (*) PSE: SIM1/Telecom/mch (O) +TSPC_PBAP_9_13l False PSE: SIM1/Telecom/cch (O) +TSPC_PBAP_9_14 False PSE: Deleted Handles Behavior +TSPC_PBAP_9_14a False (*) PSE: Error reporting (C.5) +TSPC_PBAP_9_14b False PSE: Change tracking (C.5) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +C.3: Optional if TSPC_PBAP_9_10 (X-BT-UID vCard Property) is supported, + otherwise Excluded. +C.4: Optional if TSPC_PBAP_0_3 (PBAP 1.2) and any of the mch or cch folders + (13d,13e,13k,13l) are supported, otherwise Excluded. +C.5: It is mandatory to support at least one of the defined deleted handles + behaviors. +------------------------------------------------------------------------------- + + + Supported Phone Book Download functions ( PSE ) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_10_1 True PSE: Pull Phone Book (M) +TSPC_PBAP_10_2 False PSE: Call History Function (O) +------------------------------------------------------------------------------- + + + Supported Phone Book Browsing functions ( PSE ) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_11_1 True PSE: Set Phone Book (M) +TSPC_PBAP_11_2 True PSE: Pull vCard Listing (M) +TSPC_PBAP_11_3 True PSE: Pull vCard Entry (M) +------------------------------------------------------------------------------- + + + Used vCard formats (PSE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_12_1 True PSE: vCard 2.1 (M) +TSPC_PBAP_12_2 True PSE: vCard 3.0 (M) +------------------------------------------------------------------------------- + + + OBEX Functions for PSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_13_1 True PSE: Connect (M) +TSPC_PBAP_13_2 True PSE: Disconnect (M) +TSPC_PBAP_13_3 True PSE: Get (M) +TSPC_PBAP_13_4 True PSE: Abort (M) +TSPC_PBAP_13_5 True PSE: SetPath (M) +TSPC_PBAP_13_6 False PSE: Support for OBEX authentication initiation + (C.1) +------------------------------------------------------------------------------- +C.1: Optional to support initiation if TSPC_PBAP_0_1 (PBAP 1.0) or + TSPC_PBAP_0_2 (PBAP 1.1) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + PSE OBEX Header Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_14_1 True PSE: Name (M) +TSPC_PBAP_14_2 True PSE: Type (M) +TSPC_PBAP_14_3 True PSE: Body (M) +TSPC_PBAP_14_4 True PSE: End of Body (M) +TSPC_PBAP_14_5 True PSE: Target (M) +TSPC_PBAP_14_6 True PSE: Who (M) +TSPC_PBAP_14_7 True PSE: Connection ID (M) +TSPC_PBAP_14_8 True PSE: Authentication Challenge (M) +TSPC_PBAP_14_9 True PSE: Authentication Response (M) +TSPC_PBAP_14_10 True PSE: Application Parameters (M) +TSPC_PBAP_14_11 False PSE: Single Response Mode (C.1) +TSPC_PBAP_14_12 False PSE: Single Response Mode Parameter + (ability to parse) (C.1) +TSPC_PBAP_14_13 False PSE: Single Response Mode Parameter + (ability to send) (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + OBEX Error Codes for PSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_15_1 True PSE: Bad Request (M) +TSPC_PBAP_15_2 True PSE: Not Implemented (M) +TSPC_PBAP_15_3 True (*) PSE: Unauthorized (O) +TSPC_PBAP_15_4 True (*) PSE: Precondition Failed (C.1) +TSPC_PBAP_15_5 True PSE: Not Found (M) +TSPC_PBAP_15_6 True (*) PSE: Not Acceptable (O) +TSPC_PBAP_15_7 True PSE: Service Unavailable (M) +TSPC_PBAP_15_8 True (*) PSE: Forbidden (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_PBAP_9_14a (Error reporting) is supported, otherwise + Optional. +------------------------------------------------------------------------------- + + + GAP Modes for PCE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_16_1 False (*) PCE: General discoverable mode (M) +TSPC_PBAP_16_2 False (*) PCE: Pairable mode (M) +------------------------------------------------------------------------------- + + + GAP Modes for PSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_17_1 True PSE: General discoverable mode (M) +TSPC_PBAP_17_2 True PSE: Pairable mode (M) +------------------------------------------------------------------------------- + + + GAP Security Modes for PCE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_18_1 False (*) PCE: Authentication Procedure (M) +TSPC_PBAP_18_2 False (*) PCE: Initiate LMP-Authentication (M) +TSPC_PBAP_18_3 False PCE: Security mode 1 (C.1) +TSPC_PBAP_18_4 False PCE: Security mode 2 (C.1) +TSPC_PBAP_18_5 False PCE: Security mode 3 (C.1) +TSPC_PBAP_18_6 False PCE: Security mode 4 (C.1) +------------------------------------------------------------------------------- +C.1: At least one of TSPC_PBAP_18_4, TSPC_PBAP_18_5 or TSPC_PBAP_18_6 + (security mode 2, 3, or 4) shall be supported. +------------------------------------------------------------------------------- + + + GAP Security Modes for PSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_19_1 True PSE: Authentication Procedure (M) +TSPC_PBAP_19_2 True PSE: Initiate LMP-Authentication (M) +TSPC_PBAP_19_3 False PSE: Security mode 1 (C.2) +TSPC_PBAP_19_4 False PSE: Security mode 2 (C.1) +TSPC_PBAP_19_5 False PSE: Security mode 3 (C.1) +TSPC_PBAP_19_6 False PSE: Security mode 4 (C.1) +------------------------------------------------------------------------------- +C.1: At least one of TSPC_PBAP_19_3, TSPC_PBAP_19_4, TSPC_PBAP_19_5 or + TSPC_PBAP_19_6 (security mode 2, 3, or 4) shall be supported. +C.2: Excluded in PSE. +------------------------------------------------------------------------------- + + + GAP Idle Modes for PSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_21_1 True PSE: Initiation of General Inquiry (M) +TSPC_PBAP_21_2 False PSE: Initiation of Limited Inquiry (O) +------------------------------------------------------------------------------- + + + SDP Attributes (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_22_1 False (*) PCE: BluetoothProfileDescriptorList (M) +------------------------------------------------------------------------------- + + SDP Attributes (PSE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_23_1 True PSE: ProtocolDescriptorList (M) +TSPC_PBAP_23_2 True PSE: BluetoothProfileDescriptorList (M) +------------------------------------------------------------------------------- + + + Additional PBAP Capabilities +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_24_1 False PCE: Retrieve Large Phone Book (C.1) +TSPC_PBAP_24_2 False PSE: Transfer Large Phone Book (C.2) +TSPC_PBAP_24_3 False PCE: Retrieve Empty Phone Book (C.1) +TSPC_PBAP_24_4 False PSE: Transfer Empty Phone Book (C.2) +TSPC_PBAP_24_5 False PSE: Return Phonebook – Limit number of entries + (C.2) +TSPC_PBAP_24_6 False PSE: Return vCard listing – Limit number of + entries (C.2) +TSPC_PBAP_24_7 False PSE: Phone Book Order (C.2) +TSPC_PBAP_24_8 False PSE: Call stack timestamps (C.3) +TSPC_PBAP_24_9 False PSE: No User Interaction (C.2) +TSPC_PBAP_24_10 False PSE: Special Character Handling (C.2) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_PBAP_2_1 is supported, otherwise excluded. +C.2: Optional if TSPC_PBAP_1_2 is supported, otherwise excluded. +C.3: Optional if TSPC_PBAP_10_2 is supported, otherwise excluded. +------------------------------------------------------------------------------- + + + GOEP 2.0 or later Features (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_25_1 False (*) PCE: GOEP v2.0 or later (M) +TSPC_PBAP_25_2 False (*) PCE: GOEP v2 Backwards Compatibility (M) +TSPC_PBAP_25_3 False PCE: OBEX over L2CAP (M) +TSPC_PBAP_25_4 False PCE: OBEX SRM (M) +TSPC_PBAP_25_5 False (*) PCE: Send OBEX SRMP header (C.1) +TSPC_PBAP_25_6 False PCE: Receive OBEX SRMP header (M) +------------------------------------------------------------------------------- +C.1: Optional to support if TSPC_PBAP_25_4 (OBEX SRM) is supported, + otherwise Excluded. +------------------------------------------------------------------------------- + + + GOEP 2.0 or later Features (PSE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_26_1 False (*) PSE: GOEP v2.0 or later (M) +TSPC_PBAP_26_2 False (*) PSE: GOEP v2 Backwards Compatibility (M) +TSPC_PBAP_26_3 False PSE: OBEX over L2CAP (M) +TSPC_PBAP_26_4 False PSE: OBEX SRM (M) +TSPC_PBAP_26_5 False (*) PSE: Send OBEX SRMP header (C.1) +TSPC_PBAP_26_6 False PSE: Receive OBEX SRMP header (M) +TSPC_ALL False (*) Turns on all the test cases +------------------------------------------------------------------------------- +C.1: Optional if TSPC_PBAP_26_4 (OBEX SRM) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +No special PIXIT settings required. All should be set according to Tester's +test environment. diff --git a/android/socket.c b/android/socket.c index c283c5f0..90208740 100644 --- a/android/socket.c +++ b/android/socket.c @@ -27,64 +27,919 @@ #include <glib.h> #include <stdbool.h> +#include <unistd.h> +#include <errno.h> #include "lib/bluetooth.h" +#include "btio/btio.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/sdp-client.h" +#include "src/sdpd.h" + +#include "bluetooth.h" #include "log.h" #include "hal-msg.h" #include "hal-ipc.h" #include "ipc.h" +#include "utils.h" #include "socket.h" +#define SPP_DEFAULT_CHANNEL 3 +#define OPP_DEFAULT_CHANNEL 9 +#define PBAP_DEFAULT_CHANNEL 15 +#define MAS_DEFAULT_CHANNEL 16 + +#define SVC_HINT_OBEX 0x10 + +static bdaddr_t adapter_addr; + +/* Simple list of RFCOMM server sockets */ +GList *servers = NULL; + +/* Simple list of RFCOMM connected sockets */ +GList *connections = NULL; + +struct rfcomm_sock { + int fd; /* descriptor for communication with Java framework */ + int real_sock; /* real RFCOMM socket */ + int channel; /* RFCOMM channel */ + + guint rfcomm_watch; + guint stack_watch; + + bdaddr_t dst; + uint32_t service_handle; -static int handle_listen(void *buf) + const struct profile_info *profile; +}; + +static struct rfcomm_sock *create_rfsock(int sock, int *hal_fd) { - DBG("Not implemented"); + int fds[2] = {-1, -1}; + struct rfcomm_sock *rfsock; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) { + error("socketpair(): %s", strerror(errno)); + *hal_fd = -1; + return NULL; + } + + rfsock = g_new0(struct rfcomm_sock, 1); + rfsock->fd = fds[0]; + *hal_fd = fds[1]; + rfsock->real_sock = sock; - return -1; + return rfsock; } -static int handle_connect(void *buf) +static void cleanup_rfsock(gpointer data) { - DBG("Not implemented"); + struct rfcomm_sock *rfsock = data; + + DBG("rfsock: %p fd %d real_sock %d chan %u", + rfsock, rfsock->fd, rfsock->real_sock, rfsock->channel); + + if (rfsock->fd >= 0) + if (close(rfsock->fd) < 0) + error("close() fd %d failed: %s", rfsock->fd, + strerror(errno)); + + if (rfsock->real_sock >= 0) + if (close(rfsock->real_sock) < 0) + error("close() fd %d: failed: %s", rfsock->real_sock, + strerror(errno)); + + if (rfsock->rfcomm_watch > 0) + if (!g_source_remove(rfsock->rfcomm_watch)) + error("rfcomm_watch source was not found"); - return -1; + if (rfsock->stack_watch > 0) + if (!g_source_remove(rfsock->stack_watch)) + error("stack_watch source was not found"); + + if (rfsock->service_handle) + bt_adapter_remove_record(rfsock->service_handle); + + g_free(rfsock); } -void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len) +static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name) { - int fd; + const char *service_name = "OBEX Object Push"; + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid; + sdp_profile_desc_t profile[1]; + sdp_list_t *aproto, *proto[3]; + uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff }; + void *dtds[sizeof(formats)], *values[sizeof(formats)]; + unsigned int i; + uint8_t dtd = SDP_UINT8; + sdp_data_t *sflist; + sdp_data_t *channel; + sdp_record_t *record; - switch (opcode) { - case HAL_OP_SOCK_LISTEN: - fd = handle_listen(buf); - if (fd < 0) - break; + record = sdp_record_alloc(); + if (!record) + return NULL; + + record->handle = sdp_next_handle(); + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &opush_uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID); + profile[0].version = 0x0100; + pfseq = sdp_list_append(NULL, profile); + sdp_set_profile_descs(record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap_uuid); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(NULL, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &chan); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + sdp_uuid16_create(&obex_uuid, OBEX_UUID); + proto[2] = sdp_list_append(NULL, &obex_uuid); + apseq = sdp_list_append(apseq, proto[2]); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + for (i = 0; i < sizeof(formats); i++) { + dtds[i] = &dtd; + values[i] = &formats[i]; + } + sflist = sdp_seq_alloc(dtds, values, sizeof(formats)); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist); + + if (svc_name) + service_name = svc_name; + + sdp_set_info_attr(record, service_name, NULL, NULL); + + sdp_data_free(channel); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(proto[2], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name) +{ + const char *service_name = "OBEX Phonebook Access Server"; + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, pbap_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid; + sdp_profile_desc_t profile[1]; + sdp_list_t *aproto, *proto[3]; + sdp_data_t *channel; + uint8_t formats[] = { 0x01 }; + uint8_t dtd = SDP_UINT8; + sdp_data_t *sflist; + sdp_record_t *record; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + record->handle = sdp_next_handle(); + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&pbap_uuid, PBAP_PSE_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &pbap_uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID); + profile[0].version = 0x0100; + pfseq = sdp_list_append(NULL, profile); + sdp_set_profile_descs(record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap_uuid); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(NULL, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &chan); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + sdp_uuid16_create(&obex_uuid, OBEX_UUID); + proto[2] = sdp_list_append(NULL, &obex_uuid); + apseq = sdp_list_append(apseq, proto[2]); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + sflist = sdp_data_alloc(dtd, formats); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_REPOSITORIES, sflist); + + if (svc_name) + service_name = svc_name; + + sdp_set_info_attr(record, service_name, NULL, NULL); + + sdp_data_free(channel); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(proto[2], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name) +{ + const char *service_name = "Serial Port"; + sdp_list_t *svclass_id, *apseq, *profiles, *root; + uuid_t root_uuid, sp_uuid, l2cap, rfcomm; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_data_t *channel; + sdp_record_t *record; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + record->handle = sdp_next_handle(); + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &sp_uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID); + profile.version = 0x0100; + profiles = sdp_list_append(NULL, &profile); + sdp_set_profile_descs(record, profiles); + + sdp_uuid16_create(&l2cap, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&rfcomm, RFCOMM_UUID); + proto[1] = sdp_list_append(NULL, &rfcomm); + channel = sdp_data_alloc(SDP_UINT8, &chan); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + sdp_add_lang_attr(record); + + if (svc_name) + service_name = svc_name; + + sdp_set_info_attr(record, service_name, "BlueZ", "COM Port"); + + sdp_set_url_attr(record, "http://www.bluez.org/", + "http://www.bluez.org/", "http://www.bluez.org/"); + + sdp_set_service_id(record, sp_uuid); + sdp_set_service_ttl(record, 0xffff); + sdp_set_service_avail(record, 0xff); + sdp_set_record_state(record, 0x00001234); + + sdp_data_free(channel); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + sdp_list_free(profiles, NULL); + + return record; +} + +static const struct profile_info { + uint8_t uuid[16]; + uint8_t channel; + uint8_t svc_hint; + BtIOSecLevel sec_level; + sdp_record_t * (*create_record)(uint8_t chan, const char *svc_name); +} profiles[] = { + { + .uuid = { + 0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = PBAP_DEFAULT_CHANNEL, + .svc_hint = SVC_HINT_OBEX, + .sec_level = BT_IO_SEC_MEDIUM, + .create_record = create_pbap_record + }, { + .uuid = { + 0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = OPP_DEFAULT_CHANNEL, + .svc_hint = SVC_HINT_OBEX, + .sec_level = BT_IO_SEC_LOW, + .create_record = create_opp_record + }, { + .uuid = { + 0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = MAS_DEFAULT_CHANNEL, + .svc_hint = 0, + .sec_level = BT_IO_SEC_MEDIUM, + .create_record = NULL + }, { + .uuid = { + 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = SPP_DEFAULT_CHANNEL, + .svc_hint = 0, + .sec_level = BT_IO_SEC_MEDIUM, + .create_record = create_spp_record + }, +}; + +static uint32_t sdp_service_register(const struct profile_info *profile, + const void *svc_name) +{ + sdp_record_t *record; + + if (!profile || !profile->create_record) + return 0; + + record = profile->create_record(profile->channel, svc_name); + if (!record) + return 0; + + if (bt_adapter_add_record(record, profile->svc_hint) < 0) { + error("Failed to register on SDP record"); + sdp_record_free(record); + return 0; + } + + return record->handle; +} + +static int bt_sock_send_fd(int sock_fd, const void *buf, int len, int send_fd) +{ + ssize_t ret; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iv; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + + DBG("len %d sock_fd %d send_fd %d", len, sock_fd, send_fd); + + if (sock_fd == -1 || send_fd == -1) + return -1; + + memset(&msg, 0, sizeof(msg)); + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd)); + + memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd)); + + iv.iov_base = (unsigned char *) buf; + iv.iov_len = len; + + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + + ret = sendmsg(sock_fd, &msg, MSG_NOSIGNAL); + if (ret < 0) { + error("sendmsg(): sock_fd %d send_fd %d: %s", + sock_fd, send_fd, strerror(errno)); + return ret; + } + + return ret; +} + +static const struct profile_info *get_profile_by_uuid(const uint8_t *uuid) +{ + unsigned int i; + + for (i = 0; i < G_N_ELEMENTS(profiles); i++) { + if (!memcmp(profiles[i].uuid, uuid, 16)) + return &profiles[i]; + } + + return NULL; +} + +static int try_write_all(int fd, unsigned char *buf, int len) +{ + int sent = 0; + + while (len > 0) { + int written; + + written = write(fd, buf, len); + if (written < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + + if (!written) + return 0; + + len -= written; buf += written; sent += written; + } + + return sent; +} + +static gboolean sock_stack_event_cb(GIOChannel *io, GIOCondition cond, + gpointer data) +{ + struct rfcomm_sock *rfsock = data; + unsigned char buf[1024]; + int len, sent; + + if (cond & G_IO_HUP) { + DBG("Socket %d hang up", g_io_channel_unix_get_fd(io)); + goto fail; + } + + if (cond & (G_IO_ERR | G_IO_NVAL)) { + error("Socket error: sock %d cond %d", + g_io_channel_unix_get_fd(io), cond); + goto fail; + } + + len = read(rfsock->fd, buf, sizeof(buf)); + if (len <= 0) { + error("read(): %s", strerror(errno)); + /* Read again */ + return TRUE; + } + + sent = try_write_all(rfsock->real_sock, buf, len); + if (sent < 0) { + error("write(): %s", strerror(errno)); + goto fail; + } + + return TRUE; +fail: + connections = g_list_remove(connections, rfsock); + cleanup_rfsock(rfsock); + + return FALSE; +} + +static gboolean sock_rfcomm_event_cb(GIOChannel *io, GIOCondition cond, + gpointer data) +{ + struct rfcomm_sock *rfsock = data; + unsigned char buf[1024]; + int len, sent; + + if (cond & G_IO_HUP) { + DBG("Socket %d hang up", g_io_channel_unix_get_fd(io)); + goto fail; + } + + if (cond & (G_IO_ERR | G_IO_NVAL)) { + error("Socket error: sock %d cond %d", + g_io_channel_unix_get_fd(io), cond); + goto fail; + } - ipc_send(sk, HAL_SERVICE_ID_SOCK, opcode, 0, NULL, fd); + len = read(rfsock->real_sock, buf, sizeof(buf)); + if (len <= 0) { + error("read(): %s", strerror(errno)); + /* Read again */ + return TRUE; + } + + sent = try_write_all(rfsock->fd, buf, len); + if (sent < 0) { + error("write(): %s", strerror(errno)); + goto fail; + } + + return TRUE; +fail: + connections = g_list_remove(connections, rfsock); + cleanup_rfsock(rfsock); + + return FALSE; +} + +static bool sock_send_accept(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr, + int fd_accepted) +{ + struct hal_sock_connect_signal cmd; + int len; + + DBG(""); + + cmd.size = sizeof(cmd); + bdaddr2android(bdaddr, cmd.bdaddr); + cmd.channel = rfsock->channel; + cmd.status = 0; + + len = bt_sock_send_fd(rfsock->fd, &cmd, sizeof(cmd), fd_accepted); + if (len != sizeof(cmd)) { + error("Error sending accept signal"); + return false; + } + + return true; +} + +static void accept_cb(GIOChannel *io, GError *err, gpointer user_data) +{ + struct rfcomm_sock *rfsock = user_data; + struct rfcomm_sock *rfsock_acc; + GIOChannel *io_stack; + GError *gerr = NULL; + bdaddr_t dst; + char address[18]; + int sock_acc; + int hal_fd; + guint id; + GIOCondition cond; + + if (err) { + error("%s", err->message); return; - case HAL_OP_SOCK_CONNECT: - fd = handle_connect(buf); - if (fd < 0) - break; + } + + bt_io_get(io, &gerr, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + g_io_channel_shutdown(io, TRUE, NULL); + return; + } - ipc_send(sk, HAL_SERVICE_ID_SOCK, opcode, 0, NULL, fd); + ba2str(&dst, address); + DBG("Incoming connection from %s rfsock %p", address, rfsock); + + sock_acc = g_io_channel_unix_get_fd(io); + rfsock_acc = create_rfsock(sock_acc, &hal_fd); + if (!rfsock_acc) { + g_io_channel_shutdown(io, TRUE, NULL); + return; + } + + DBG("rfsock: fd %d real_sock %d chan %u sock %d", + rfsock->fd, rfsock->real_sock, rfsock->channel, + sock_acc); + + if (!sock_send_accept(rfsock, &dst, hal_fd)) { + cleanup_rfsock(rfsock_acc); return; - default: - DBG("Unhandled command, opcode 0x%x", opcode); - break; } - ipc_send_rsp(sk, HAL_SERVICE_ID_SOCK, HAL_STATUS_FAILED); + connections = g_list_append(connections, rfsock_acc); + + /* Handle events from Android */ + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + io_stack = g_io_channel_unix_new(rfsock_acc->fd); + id = g_io_add_watch(io_stack, cond, sock_stack_event_cb, rfsock_acc); + g_io_channel_unref(io_stack); + + rfsock_acc->stack_watch = id; + + /* Handle rfcomm events */ + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + id = g_io_add_watch(io, cond, sock_rfcomm_event_cb, rfsock_acc); + g_io_channel_set_close_on_unref(io, FALSE); + + rfsock_acc->rfcomm_watch = id; + + DBG("rfsock %p rfsock_acc %p stack_watch %d rfcomm_watch %d", + rfsock, rfsock_acc, rfsock_acc->stack_watch, + rfsock_acc->rfcomm_watch); } -bool bt_socket_register(int sk, const bdaddr_t *addr) +static void handle_listen(const void *buf, uint16_t len) { + const struct hal_cmd_sock_listen *cmd = buf; + const struct profile_info *profile; + struct rfcomm_sock *rfsock = NULL; + BtIOSecLevel sec_level; + GIOChannel *io; + GError *err = NULL; + int hal_fd = -1; + int chan; + DBG(""); + profile = get_profile_by_uuid(cmd->uuid); + if (!profile) { + if (!cmd->channel) + goto failed; + + chan = cmd->channel; + sec_level = BT_IO_SEC_MEDIUM; + } else { + chan = profile->channel; + sec_level = profile->sec_level; + } + + DBG("rfcomm channel %d svc_name %s", chan, cmd->name); + + rfsock = create_rfsock(-1, &hal_fd); + if (!rfsock) + goto failed; + + io = bt_io_listen(accept_cb, NULL, rfsock, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_CHANNEL, chan, + BT_IO_OPT_SEC_LEVEL, sec_level, + BT_IO_OPT_INVALID); + if (!io) { + error("Failed listen: %s", err->message); + g_error_free(err); + goto failed; + } + + rfsock->real_sock = g_io_channel_unix_get_fd(io); + + g_io_channel_unref(io); + + DBG("real_sock %d fd %d hal_fd %d", rfsock->real_sock, rfsock->fd, + hal_fd); + + if (write(rfsock->fd, &chan, sizeof(chan)) != sizeof(chan)) { + error("Error sending RFCOMM channel"); + goto failed; + } + + rfsock->service_handle = sdp_service_register(profile, cmd->name); + + servers = g_list_append(servers, rfsock); + + ipc_send_rsp_full(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, 0, NULL, + hal_fd); + close(hal_fd); + return; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, + HAL_STATUS_FAILED); + + if (rfsock) + cleanup_rfsock(rfsock); + + if (hal_fd >= 0) + close(hal_fd); +} + +static bool sock_send_connect(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr) +{ + struct hal_sock_connect_signal cmd; + int len; + + DBG(""); + + memset(&cmd, 0, sizeof(cmd)); + cmd.size = sizeof(cmd); + bdaddr2android(bdaddr, cmd.bdaddr); + cmd.channel = rfsock->channel; + cmd.status = 0; + + len = write(rfsock->fd, &cmd, sizeof(cmd)); + if (len < 0) { + error("%s", strerror(errno)); + return false; + } + + if (len != sizeof(cmd)) { + error("Error sending connect signal"); + return false; + } + return true; } +static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) +{ + struct rfcomm_sock *rfsock = user_data; + bdaddr_t *dst = &rfsock->dst; + GIOChannel *io_stack; + char address[18]; + guint id; + GIOCondition cond; + + if (err) { + error("%s", err->message); + goto fail; + } + + ba2str(dst, address); + DBG("Connected to %s", address); + + DBG("rfsock: fd %d real_sock %d chan %u sock %d", + rfsock->fd, rfsock->real_sock, rfsock->channel, + g_io_channel_unix_get_fd(io)); + + if (!sock_send_connect(rfsock, dst)) + goto fail; + + /* Handle events from Android */ + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + io_stack = g_io_channel_unix_new(rfsock->fd); + id = g_io_add_watch(io_stack, cond, sock_stack_event_cb, rfsock); + g_io_channel_unref(io_stack); + + rfsock->stack_watch = id; + + /* Handle rfcomm events */ + cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + id = g_io_add_watch(io, cond, sock_rfcomm_event_cb, rfsock); + g_io_channel_set_close_on_unref(io, FALSE); + + rfsock->rfcomm_watch = id; + + return; +fail: + connections = g_list_remove(connections, rfsock); + cleanup_rfsock(rfsock); +} + +static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data) +{ + struct rfcomm_sock *rfsock = data; + BtIOSecLevel sec_level = BT_IO_SEC_MEDIUM; + GError *gerr = NULL; + sdp_list_t *list; + GIOChannel *io; + int chan; + + DBG(""); + + if (err < 0) { + error("Unable to get SDP record: %s", strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + error("No SDP records found"); + goto fail; + } + + for (list = recs; list != NULL; list = list->next) { + sdp_record_t *rec = list->data; + sdp_list_t *protos; + + if (sdp_get_access_protos(rec, &protos) < 0) { + error("Unable to get proto list"); + goto fail; + } + + chan = sdp_get_proto_port(protos, RFCOMM_UUID); + + sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, + NULL); + sdp_list_free(protos, NULL); + + if (chan) + break; + } + + if (chan <= 0) { + error("Could not get RFCOMM channel %d", chan); + goto fail; + } + + DBG("Got RFCOMM channel %d", chan); + + if (rfsock->profile) + sec_level = rfsock->profile->sec_level; + + io = bt_io_connect(connect_cb, rfsock, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &rfsock->dst, + BT_IO_OPT_CHANNEL, chan, + BT_IO_OPT_SEC_LEVEL, sec_level, + BT_IO_OPT_INVALID); + if (!io) { + error("Failed connect: %s", gerr->message); + g_error_free(gerr); + goto fail; + } + + if (write(rfsock->fd, &chan, sizeof(chan)) != sizeof(chan)) { + error("Error sending RFCOMM channel"); + goto fail; + } + + rfsock->real_sock = g_io_channel_unix_get_fd(io); + rfsock->channel = chan; + connections = g_list_append(connections, rfsock); + + g_io_channel_unref(io); + + return; +fail: + connections = g_list_remove(connections, rfsock); + cleanup_rfsock(rfsock); +} + +static void handle_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_sock_connect *cmd = buf; + struct rfcomm_sock *rfsock; + uuid_t uuid; + int hal_fd = -1; + + DBG(""); + + rfsock = create_rfsock(-1, &hal_fd); + if (!rfsock) + goto failed; + + android2bdaddr(cmd->bdaddr, &rfsock->dst); + + memset(&uuid, 0, sizeof(uuid)); + uuid.type = SDP_UUID128; + memcpy(&uuid.value.uuid128, cmd->uuid, sizeof(uint128_t)); + + rfsock->profile = get_profile_by_uuid(cmd->uuid); + + if (bt_search_service(&adapter_addr, &rfsock->dst, &uuid, + sdp_search_cb, rfsock, NULL) < 0) { + error("Failed to search SDP records"); + cleanup_rfsock(rfsock); + goto failed; + } + + ipc_send_rsp_full(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT, 0, NULL, + hal_fd); + close(hal_fd); + return; + +failed: + ipc_send_rsp(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_CONNECT, + HAL_STATUS_FAILED); + + if (hal_fd >= 0) + close(hal_fd); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_SOCK_LISTEN */ + { handle_listen, false, sizeof(struct hal_cmd_sock_listen) }, + /* HAL_OP_SOCK_CONNECT */ + { handle_connect, false, sizeof(struct hal_cmd_sock_connect) }, +}; + +void bt_socket_register(const bdaddr_t *addr) +{ + DBG(""); + + bacpy(&adapter_addr, addr); + ipc_register(HAL_SERVICE_ID_SOCK, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); +} + void bt_socket_unregister(void) { DBG(""); + + g_list_free_full(connections, cleanup_rfsock); + g_list_free_full(servers, cleanup_rfsock); + + ipc_unregister(HAL_SERVICE_ID_SOCK); } diff --git a/android/socket.h b/android/socket.h index 7aa55746..5150b890 100644 --- a/android/socket.h +++ b/android/socket.h @@ -21,7 +21,14 @@ * */ +struct hal_sock_connect_signal { + short size; + uint8_t bdaddr[6]; + int channel; + int status; +} __attribute__((packed)); + void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len); -bool bt_socket_register(int sk, const bdaddr_t *addr); +void bt_socket_register(const bdaddr_t *addr); void bt_socket_unregister(void); diff --git a/client/main.c b/client/main.c index 0dd15107..ebc85c68 100644 --- a/client/main.c +++ b/client/main.c @@ -538,6 +538,26 @@ static void cmd_devices(const char *arg) } } +static void cmd_paired_devices(const char *arg) +{ + GList *list; + + for (list = g_list_first(dev_list); list; list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + DBusMessageIter iter; + dbus_bool_t paired; + + if (g_dbus_proxy_get_property(proxy, "Paired", &iter) == FALSE) + continue; + + dbus_message_iter_get_basic(&iter, &paired); + if (!paired) + continue; + + print_device(proxy, NULL); + } +} + static void generic_callback(const DBusError *error, void *user_data) { char *str = user_data; @@ -1047,6 +1067,8 @@ static const struct { { "select", "<ctrl>", cmd_select, "Select default controller", ctrl_generator }, { "devices", NULL, cmd_devices, "List available devices" }, + { "paired-devices", NULL, cmd_paired_devices, + "List paired devices"}, { "system-alias", "<name>", cmd_system_alias }, { "reset-alias", NULL, cmd_reset_alias }, { "power", "<on/off>", cmd_power, "Set controller power" }, @@ -1197,12 +1219,16 @@ done: static gboolean input_handler(GIOChannel *channel, GIOCondition condition, gpointer user_data) { + if (condition & G_IO_IN) { + rl_callback_read_char(); + return TRUE; + } + if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { g_main_loop_quit(main_loop); return FALSE; } - rl_callback_read_char(); return TRUE; } @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for bluez 5.11. +# Generated by GNU Autoconf 2.69 for bluez 5.12. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='bluez' PACKAGE_TARNAME='bluez' -PACKAGE_VERSION='5.11' -PACKAGE_STRING='bluez 5.11' +PACKAGE_VERSION='5.12' +PACKAGE_STRING='bluez 5.12' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -636,6 +636,8 @@ LIBOBJS ANDROID_FALSE ANDROID_TRUE CONFIGDIR +SIXAXIS_FALSE +SIXAXIS_TRUE EXPERIMENTAL_FALSE EXPERIMENTAL_TRUE DATAFILES_FALSE @@ -833,6 +835,7 @@ with_systemdsystemunitdir with_systemduserunitdir enable_datafiles enable_experimental +enable_sixaxis enable_android ' ac_precious_vars='build_alias @@ -1397,7 +1400,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures bluez 5.11 to adapt to many kinds of systems. +\`configure' configures bluez 5.12 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1467,7 +1470,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bluez 5.11:";; + short | recursive ) echo "Configuration of bluez 5.12:";; esac cat <<\_ACEOF @@ -1501,6 +1504,7 @@ Optional Features: --disable-systemd disable systemd integration --disable-datafiles do not install configuration and data files --enable-experimental enable experimental plugins (SAP, NFC, ...) + --enable-sixaxis enable sixaxis plugin --enable-android enable BlueZ for Android Optional Packages: @@ -1615,7 +1619,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bluez configure 5.11 +bluez configure 5.12 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1980,7 +1984,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by bluez $as_me 5.11, which was +It was created by bluez $as_me 5.12, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2835,7 +2839,7 @@ fi # Define the identity of the package. PACKAGE='bluez' - VERSION='5.11' + VERSION='5.12' cat >>confdefs.h <<_ACEOF @@ -4596,6 +4600,8 @@ fi with_cflags="$with_cflags -Wredundant-decls" with_cflags="$with_cflags -Wcast-align" with_cflags="$with_cflags -DG_DISABLE_DEPRECATED" + with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28" + with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28" fi WARNING_CFLAGS=$with_cflags @@ -13560,6 +13566,21 @@ else fi +# Check whether --enable-sixaxis was given. +if test "${enable_sixaxis+set}" = set; then : + enableval=$enable_sixaxis; enable_sixaxis=${enableval} +fi + + if test "${enable_sixaxis}" = "yes" && + test "${enable_udev}" != "no"; then + SIXAXIS_TRUE= + SIXAXIS_FALSE='#' +else + SIXAXIS_TRUE='#' + SIXAXIS_FALSE= +fi + + if (test "${prefix}" = "NONE"); then if (test "$localstatedir" = '${prefix}/var'); then localstatedir='/var' @@ -13794,6 +13815,10 @@ if test -z "${EXPERIMENTAL_TRUE}" && test -z "${EXPERIMENTAL_FALSE}"; then as_fn_error $? "conditional \"EXPERIMENTAL\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${SIXAXIS_TRUE}" && test -z "${SIXAXIS_FALSE}"; then + as_fn_error $? "conditional \"SIXAXIS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${ANDROID_TRUE}" && test -z "${ANDROID_FALSE}"; then as_fn_error $? "conditional \"ANDROID\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -14195,7 +14220,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by bluez $as_me 5.11, which was +This file was extended by bluez $as_me 5.12, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14261,7 +14286,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -bluez config.status 5.11 +bluez config.status 5.12 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 949846ef..18d0b554 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT(bluez, 5.11) +AC_INIT(bluez, 5.12) AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules tar-pax no-dist-gzip dist-xz]) @@ -216,6 +216,11 @@ AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental], [enable_experimental=${enableval}]) AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes") +AC_ARG_ENABLE(sixaxis, AC_HELP_STRING([--enable-sixaxis], + [enable sixaxis plugin]), [enable_sixaxis=${enableval}]) +AM_CONDITIONAL(SIXAXIS, test "${enable_sixaxis}" = "yes" && + test "${enable_udev}" != "no") + if (test "${prefix}" = "NONE"); then dnl no prefix and no localstatedir, so default to /var if (test "$localstatedir" = '${prefix}/var'); then diff --git a/doc/device-api.txt b/doc/device-api.txt index 62017802..2d9fa3c9 100644 --- a/doc/device-api.txt +++ b/doc/device-api.txt @@ -85,6 +85,7 @@ Methods void Connect() Possible errors: org.bluez.Error.InvalidArguments org.bluez.Error.Failed + org.bluez.Error.AlreadyExists org.bluez.Error.AuthenticationCanceled org.bluez.Error.AuthenticationFailed org.bluez.Error.AuthenticationRejected diff --git a/emulator/btdev.c b/emulator/btdev.c index 5f04bd17..d0dff74e 100644 --- a/emulator/btdev.c +++ b/emulator/btdev.c @@ -108,6 +108,7 @@ struct btdev { uint8_t le_scan_enable; uint8_t le_filter_dup; uint8_t le_adv_enable; + uint8_t le_ltk[16]; uint16_t sync_train_interval; uint32_t sync_train_timeout; @@ -833,29 +834,26 @@ static void conn_request(struct btdev *btdev, const uint8_t *bdaddr) { struct btdev *remote = find_btdev_by_bdaddr(bdaddr); - if (remote) { - if (remote->scan_enable & 0x02) { - struct bt_hci_evt_conn_request cr; + if (remote && remote->scan_enable & 0x02) { + struct bt_hci_evt_conn_request cr; - memcpy(cr.bdaddr, btdev->bdaddr, 6); - memcpy(cr.dev_class, btdev->dev_class, 3); - cr.link_type = 0x01; + memcpy(cr.bdaddr, btdev->bdaddr, 6); + memcpy(cr.dev_class, btdev->dev_class, 3); + cr.link_type = 0x01; - send_event(remote, BT_HCI_EVT_CONN_REQUEST, - &cr, sizeof(cr)); - } else - conn_complete(btdev, bdaddr, BT_HCI_ERR_PAGE_TIMEOUT); - } else - conn_complete(btdev, bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID); + send_event(remote, BT_HCI_EVT_CONN_REQUEST, &cr, sizeof(cr)); + } else { + conn_complete(btdev, bdaddr, BT_HCI_ERR_PAGE_TIMEOUT); + } } static void disconnect_complete(struct btdev *btdev, uint16_t handle, uint8_t reason) { struct bt_hci_evt_disconnect_complete dc; - struct btdev *remote; + struct btdev *remote = btdev->conn; - if (!btdev) { + if (!remote) { dc.status = BT_HCI_ERR_UNKNOWN_CONN_ID; dc.handle = cpu_to_le16(handle); dc.reason = 0x00; @@ -869,8 +867,6 @@ static void disconnect_complete(struct btdev *btdev, uint16_t handle, dc.handle = cpu_to_le16(handle); dc.reason = reason; - remote = btdev->conn; - btdev->conn = NULL; remote->conn = NULL; @@ -1030,6 +1026,54 @@ static void le_set_scan_enable_complete(struct btdev *btdev) } } +static void le_start_encrypt_complete(struct btdev *btdev) +{ + char buf[1 + sizeof(struct bt_hci_evt_le_long_term_key_request)]; + struct bt_hci_evt_le_long_term_key_request *ev = (void *) &buf[1]; + struct btdev *remote = btdev->conn; + + if (!remote) { + cmd_status(btdev, BT_HCI_ERR_UNKNOWN_CONN_ID, + BT_HCI_CMD_LE_START_ENCRYPT); + return; + } + + cmd_status(btdev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_LE_START_ENCRYPT); + + memset(buf, 0, sizeof(buf)); + buf[0] = BT_HCI_EVT_LE_LONG_TERM_KEY_REQUEST; + ev->handle = cpu_to_le16(42); + + send_event(remote, BT_HCI_EVT_LE_META_EVENT, buf, sizeof(buf)); +} + +static void le_encrypt_complete(struct btdev *btdev) +{ + struct bt_hci_evt_encrypt_change ev; + struct bt_hci_rsp_le_ltk_req_reply rp; + struct btdev *remote = btdev->conn; + + memset(&rp, 0, sizeof(rp)); + rp.handle = cpu_to_le16(42); + + if (!remote) { + rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID; + cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_REPLY, &rp, + sizeof(rp)); + return; + } + + rp.status = BT_HCI_ERR_SUCCESS; + cmd_complete(btdev, BT_HCI_CMD_LE_LTK_REQ_REPLY, &rp, sizeof(rp)); + + memset(&ev, 0, sizeof(ev)); + ev.handle = cpu_to_le16(42); + ev.encr_mode = 0x01; + + send_event(btdev, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev)); + send_event(remote, BT_HCI_EVT_ENCRYPT_CHANGE, &ev, sizeof(ev)); +} + static void default_cmd(struct btdev *btdev, uint16_t opcode, const void *data, uint8_t len) { @@ -1058,6 +1102,8 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, const struct bt_hci_cmd_setup_sync_conn *ssc; const struct bt_hci_cmd_le_set_adv_enable *lsae; const struct bt_hci_cmd_le_set_scan_enable *lsse; + const struct bt_hci_cmd_le_start_encrypt *lse; + const struct bt_hci_cmd_le_ltk_req_reply *llrr; const struct bt_hci_cmd_read_local_amp_assoc *rlaa_cmd; struct bt_hci_rsp_read_default_link_policy rdlp; struct bt_hci_rsp_read_stored_link_key rslk; @@ -1826,6 +1872,22 @@ static void default_cmd(struct btdev *btdev, uint16_t opcode, cmd_complete(btdev, opcode, &lr, sizeof(lr)); break; + case BT_HCI_CMD_LE_START_ENCRYPT: + if (btdev->type == BTDEV_TYPE_BREDR) + goto unsupported; + lse = data; + memcpy(btdev->le_ltk, lse->ltk, 16); + le_start_encrypt_complete(btdev); + break; + + case BT_HCI_CMD_LE_LTK_REQ_REPLY: + if (btdev->type == BTDEV_TYPE_BREDR) + goto unsupported; + llrr = data; + memcpy(btdev->le_ltk, llrr->ltk, 16); + le_encrypt_complete(btdev); + break; + case BT_HCI_CMD_SETUP_SYNC_CONN: if (btdev->type == BTDEV_TYPE_LE) goto unsupported; diff --git a/emulator/bthost.c b/emulator/bthost.c index 0c5836f6..10e7a057 100644 --- a/emulator/bthost.c +++ b/emulator/bthost.c @@ -975,6 +975,48 @@ static bool l2cap_conn_param_rsp(struct bthost *bthost, struct btconn *conn, return true; } +static bool l2cap_le_conn_req(struct bthost *bthost, struct btconn *conn, + uint8_t ident, const void *data, uint16_t len) +{ + const struct bt_l2cap_pdu_le_conn_req *req = data; + struct bt_l2cap_pdu_le_conn_rsp rsp; + uint16_t psm; + + if (len < sizeof(*req)) + return false; + + psm = le16_to_cpu(req->psm); + + memset(&rsp, 0, sizeof(rsp)); + + rsp.mtu = 23; + rsp.mps = 23; + rsp.credits = 1; + + if (bthost->server_psm && bthost->server_psm == psm) + rsp.dcid = cpu_to_le16(conn->next_cid++); + else + rsp.result = cpu_to_le16(0x0002); /* PSM Not Supported */ + + l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_LE_CONN_RSP, ident, &rsp, + sizeof(rsp)); + + return true; +} + +static bool l2cap_le_conn_rsp(struct bthost *bthost, struct btconn *conn, + uint8_t ident, const void *data, uint16_t len) +{ + const struct bt_l2cap_pdu_le_conn_rsp *rsp = data; + + if (len < sizeof(*rsp)) + return false; + + bthost_add_l2cap_conn(bthost, conn, 0, le16_to_cpu(rsp->dcid)); + + return true; +} + static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn, const void *data, uint16_t len) { @@ -997,6 +1039,11 @@ static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn, data + sizeof(*hdr), hdr_len); break; + case BT_L2CAP_PDU_DISCONN_REQ: + ret = l2cap_disconn_req(bthost, conn, hdr->ident, + data + sizeof(*hdr), hdr_len); + break; + case BT_L2CAP_PDU_CONN_PARAM_REQ: ret = l2cap_conn_param_req(bthost, conn, hdr->ident, data + sizeof(*hdr), hdr_len); @@ -1007,6 +1054,16 @@ static void l2cap_le_sig(struct bthost *bthost, struct btconn *conn, data + sizeof(*hdr), hdr_len); break; + case BT_L2CAP_PDU_LE_CONN_REQ: + ret = l2cap_le_conn_req(bthost, conn, hdr->ident, + data + sizeof(*hdr), hdr_len); + break; + + case BT_L2CAP_PDU_LE_CONN_RSP: + ret = l2cap_le_conn_rsp(bthost, conn, hdr->ident, + data + sizeof(*hdr), hdr_len); + break; + default: printf("Unknown L2CAP code 0x%02x\n", hdr->code); ret = false; @@ -1159,6 +1216,20 @@ void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable) send_command(bthost, BT_HCI_CMD_LE_SET_ADV_ENABLE, &enable, 1); } +void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle, + const uint8_t ltk[16]) +{ + struct bt_hci_cmd_le_start_encrypt cmd; + + printf("bthost_le_start_encrypt(handle %u)\n", handle); + + memset(&cmd, 0, sizeof(cmd)); + cmd.handle = htobs(handle); + memcpy(cmd.ltk, ltk, 16); + + send_command(bthost, BT_HCI_CMD_LE_START_ENCRYPT, &cmd, sizeof(cmd)); +} + void bthost_set_server_psm(struct bthost *bthost, uint16_t psm) { bthost->server_psm = psm; diff --git a/emulator/bthost.h b/emulator/bthost.h index 32b10f9c..474ada91 100644 --- a/emulator/bthost.h +++ b/emulator/bthost.h @@ -72,6 +72,9 @@ void bthost_write_scan_enable(struct bthost *bthost, uint8_t scan); void bthost_set_adv_enable(struct bthost *bthost, uint8_t enable); +void bthost_le_start_encrypt(struct bthost *bthost, uint16_t handle, + const uint8_t ltk[16]); + void bthost_set_server_psm(struct bthost *bthost, uint16_t psm); void bthost_start(struct bthost *bthost); diff --git a/lib/bluetooth.c b/lib/bluetooth.c index 5598ccfd..17ee2c1d 100644 --- a/lib/bluetooth.c +++ b/lib/bluetooth.c @@ -793,6 +793,30 @@ const char *bt_compidtostr(int compid) return "Atus BV"; case 266: return "Codegate Ltd."; + case 267: + return "ERi, Inc."; + case 268: + return "Transducers Direct, LLC"; + case 269: + return "Fujitsu Ten Limited"; + case 270: + return "Audi AG"; + case 271: + return "HiSilicon Technologies Co., Ltd."; + case 272: + return "Nippon Seiki Co., Ltd."; + case 273: + return "Steelseries ApS"; + case 274: + return "vyzybl Inc."; + case 275: + return "Openbrain Technologies, Co., Ltd."; + case 276: + return "Xensr"; + case 277: + return "e.solutions"; + case 278: + return "1OAK Technologies"; case 65535: return "internal use"; default: diff --git a/lib/bluetooth.h b/lib/bluetooth.h index 012fde40..61c1f9ac 100644 --- a/lib/bluetooth.h +++ b/lib/bluetooth.h @@ -77,6 +77,13 @@ struct bt_security { #define BT_FLUSHABLE_OFF 0 #define BT_FLUSHABLE_ON 1 +#define BT_POWER 9 +struct bt_power { + uint8_t force_active; +}; +#define BT_POWER_FORCE_ACTIVE_OFF 0 +#define BT_POWER_FORCE_ACTIVE_ON 1 + #define BT_CHANNEL_POLICY 10 /* BR/EDR only (default policy) @@ -109,6 +116,9 @@ struct bt_voice { uint16_t setting; }; +#define BT_SNDMTU 12 +#define BT_RCVMTU 13 + #define BT_VOICE_TRANSPARENT 0x0003 #define BT_VOICE_CVSD_16BIT 0x0060 @@ -649,6 +649,7 @@ static hci_map ver_map[] = { { "2.1", 0x04 }, { "3.0", 0x05 }, { "4.0", 0x06 }, + { "4.1", 0x07 }, { NULL } }; diff --git a/monitor/bt.h b/monitor/bt.h index 2aec9504..849ed86f 100644 --- a/monitor/bt.h +++ b/monitor/bt.h @@ -290,8 +290,8 @@ struct bt_hci_cmd_setup_sync_conn { uint16_t pkt_type; } __attribute__ ((packed)); -#define BT_HCI_CMD_ACCEPT_SYNC_CONN 0x0429 -struct bt_hci_cmd_accept_sync_conn { +#define BT_HCI_CMD_ACCEPT_SYNC_CONN_REQUEST 0x0429 +struct bt_hci_cmd_accept_sync_conn_request { uint8_t bdaddr[6]; uint32_t tx_bandwidth; uint32_t rx_bandwidth; @@ -301,8 +301,8 @@ struct bt_hci_cmd_accept_sync_conn { uint16_t pkt_type; } __attribute__ ((packed)); -#define BT_HCI_CMD_REJECT_SYNC_CONN 0x042a -struct bt_hci_cmd_reject_sync_conn { +#define BT_HCI_CMD_REJECT_SYNC_CONN_REQUEST 0x042a +struct bt_hci_cmd_reject_sync_conn_request { uint8_t bdaddr[6]; uint8_t reason; } __attribute__ ((packed)); @@ -411,6 +411,73 @@ struct bt_hci_cmd_flow_spec_modify { uint8_t rx_flow_spec[16]; } __attribute__ ((packed)); +#define BT_HCI_CMD_TRUNCATED_PAGE 0x043f +struct bt_hci_cmd_truncated_page { + uint8_t bdaddr[6]; + uint8_t pscan_rep_mode; + uint16_t clock_offset; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_TRUNCATED_PAGE_CANCEL 0x0440 +struct bt_hci_cmd_truncated_page_cancel { + uint8_t bdaddr[6]; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_SET_SLAVE_BROADCAST 0x0441 +struct bt_hci_cmd_set_slave_broadcast { + uint8_t enable; + uint8_t lt_addr; + uint8_t lpo_allowed; + uint16_t pkt_type; + uint16_t min_interval; + uint16_t max_interval; + uint16_t timeout; +} __attribute__ ((packed)); +struct bt_hci_rsp_set_slave_broadcast { + uint8_t status; + uint8_t lt_addr; + uint16_t interval; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_SET_SLAVE_BROADCAST_RECEIVE 0x0442 +struct bt_hci_cmd_set_slave_broadcast_receive { + uint8_t enable; + uint8_t bdaddr[6]; + uint8_t lt_addr; + uint16_t interval; + uint32_t offset; + uint32_t instant; + uint16_t timeout; + uint8_t accuracy; + uint8_t skip; + uint16_t pkt_type; + uint8_t map[10]; +} __attribute__ ((packed)); +struct bt_hci_rsp_set_slave_broadcast_receive { + uint8_t status; + uint8_t bdaddr[6]; + uint8_t lt_addr; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_START_SYNC_TRAIN 0x0443 + +#define BT_HCI_CMD_RECEIVE_SYNC_TRAIN 0x0444 +struct bt_hci_cmd_receive_sync_train { + uint8_t bdaddr[6]; + uint16_t timeout; + uint16_t window; + uint16_t interval; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_REMOTE_OOB_EXT_DATA_REQUEST_REPLY 0x0445 +struct bt_hci_cmd_remote_oob_ext_data_request_reply { + uint8_t bdaddr[6]; + uint8_t hash192[16]; + uint8_t randomizer192[16]; + uint8_t hash256[16]; + uint8_t randomizer256[16]; +} __attribute__ ((packed)); + #define BT_HCI_CMD_HOLD_MODE 0x0801 struct bt_hci_cmd_hold_mode { uint16_t handle; @@ -1002,6 +1069,35 @@ struct bt_hci_cmd_write_le_host_supported { uint8_t simultaneous; } __attribute__ ((packed)); +#define BT_HCI_CMD_SET_RESERVED_LT_ADDR 0x0c74 +struct bt_hci_cmd_set_reserved_lt_addr { + uint8_t lt_addr; +} __attribute__ ((packed)); +struct bt_hci_rsp_set_reserved_lt_addr { + uint8_t status; + uint8_t lt_addr; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_DELETE_RESERVED_LT_ADDR 0x0c75 +struct bt_hci_cmd_delete_reserved_lt_addr { + uint8_t lt_addr; +} __attribute__ ((packed)); +struct bt_hci_rsp_delete_reserved_lt_addr { + uint8_t status; + uint8_t lt_addr; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_SET_SLAVE_BROADCAST_DATA 0x0c76 +struct bt_hci_cmd_set_slave_broadcast_data { + uint8_t lt_addr; + uint8_t fragment; + uint8_t length; +} __attribute__ ((packed)); +struct bt_hci_rsp_set_slave_broadcast_data { + uint8_t status; + uint8_t lt_addr; +} __attribute__ ((packed)); + #define BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS 0x0c77 struct bt_hci_rsp_read_sync_train_params { uint8_t status; @@ -1010,6 +1106,58 @@ struct bt_hci_rsp_read_sync_train_params { uint8_t service_data; } __attribute__ ((packed)); +#define BT_HCI_CMD_WRITE_SYNC_TRAIN_PARAMS 0x0c78 +struct bt_hci_cmd_write_sync_train_params { + uint16_t min_interval; + uint16_t max_interval; + uint32_t timeout; + uint8_t service_data; +} __attribute__ ((packed)); +struct bt_hci_rsp_write_sync_train_params { + uint8_t status; + uint16_t interval; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_READ_SECURE_CONN_SUPPORT 0x0c79 +struct bt_hci_rsp_read_secure_conn_support { + uint8_t status; + uint8_t support; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_WRITE_SECURE_CONN_SUPPORT 0x0c7a +struct bt_hci_cmd_write_secure_conn_support { + uint8_t support; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_READ_AUTH_PAYLOAD_TIMEOUT 0x0c7b +struct bt_hci_cmd_read_auth_payload_timeout { + uint16_t handle; +} __attribute__ ((packed)); +struct bt_hci_rsp_read_auth_payload_timeout { + uint8_t status; + uint16_t handle; + uint16_t timeout; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_WRITE_AUTH_PAYLOAD_TIMEOUT 0x0c7c +struct bt_hci_cmd_write_auth_payload_timeout { + uint16_t handle; + uint16_t timeout; +} __attribute__ ((packed)); +struct bt_hci_rsp_write_auth_payload_timeout { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA 0x0c7d +struct bt_hci_rsp_read_local_oob_ext_data { + uint8_t status; + uint8_t hash192[16]; + uint8_t randomizer192[16]; + uint8_t hash256[16]; + uint8_t randomizer256[16]; +} __attribute__ ((packed)); + #define BT_HCI_CMD_READ_LOCAL_VERSION 0x1001 struct bt_hci_rsp_read_local_version { uint8_t status; @@ -1830,6 +1978,58 @@ struct bt_hci_evt_amp_status_change { uint8_t amp_status; } __attribute__ ((packed)); +#define BT_HCI_EVT_SYNC_TRAIN_COMPLETE 0x4f +struct bt_hci_evt_sync_train_complete { + uint8_t status; +} __attribute__ ((packed)); + +#define BT_HCI_EVT_SYNC_TRAIN_RECEIVED 0x50 +struct bt_hci_evt_sync_train_received { + uint8_t status; + uint8_t bdaddr[6]; + uint32_t offset; + uint8_t map[10]; + uint8_t lt_addr; + uint32_t instant; + uint16_t interval; + uint8_t service_data; +} __attribute__ ((packed)); + +#define BT_HCI_EVT_SLAVE_BROADCAST_RECEIVE 0x51 +struct bt_hci_evt_slave_broadcast_receive { + uint8_t bdaddr[6]; + uint8_t lt_addr; + uint32_t clock; + uint32_t offset; + uint8_t status; + uint8_t fragment; + uint8_t length; +} __attribute__ ((packed)); + +#define BT_HCI_EVT_SLAVE_BROADCAST_TIMEOUT 0x52 +struct bt_hci_evt_slave_broadcast_timeout { + uint8_t bdaddr[6]; + uint8_t lt_addr; +} __attribute__ ((packed)); + +#define BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE 0x53 +struct bt_hci_evt_truncated_page_complete { + uint8_t status; + uint8_t bdaddr[6]; +} __attribute__ ((packed)); + +#define BT_HCI_EVT_SLAVE_PAGE_RESPONSE_TIMEOUT 0x54 + +#define BT_HCI_EVT_SLAVE_BROADCAST_CHANNEL_MAP_CHANGE 0x55 +struct bt_hci_evt_slave_broadcast_channel_map_change { + uint8_t map[10]; +} __attribute__ ((packed)); + +#define BT_HCI_EVT_AUTH_PAYLOAD_TIMEOUT_EXPIRED 0x57 +struct bt_hci_evt_auth_payload_timeout_expired { + uint16_t handle; +} __attribute__ ((packed)); + #define BT_HCI_EVT_LE_CONN_COMPLETE 0x01 struct bt_hci_evt_le_conn_complete { uint8_t status; @@ -2009,6 +2209,30 @@ struct bt_l2cap_pdu_conn_param_rsp { uint16_t result; } __attribute__ ((packed)); +#define BT_L2CAP_PDU_LE_CONN_REQ 0x14 +struct bt_l2cap_pdu_le_conn_req { + uint16_t psm; + uint16_t scid; + uint16_t mtu; + uint16_t mps; + uint16_t credits; +} __attribute__ ((packed)); + +#define BT_L2CAP_PDU_LE_CONN_RSP 0x15 +struct bt_l2cap_pdu_le_conn_rsp { + uint16_t dcid; + uint16_t mtu; + uint16_t mps; + uint16_t credits; + uint16_t result; +} __attribute__ ((packed)); + +#define BT_L2CAP_PDU_LE_FLOWCTL_CREDS 0x16 +struct bt_l2cap_pdu_le_flowctl_creds { + uint16_t cid; + uint16_t credits; +} __attribute__ ((packed)); + struct bt_l2cap_hdr_connless { uint16_t psm; } __attribute__ ((packed)); diff --git a/monitor/l2cap.c b/monitor/l2cap.c index ad6e64b0..c2158829 100644 --- a/monitor/l2cap.c +++ b/monitor/l2cap.c @@ -321,6 +321,12 @@ static void print_conn_result(uint16_t result) case 0x0004: str = "Connection refused - no resources available"; break; + case 0x0005: + str = "Insufficient Authentication"; + break; + case 0x0006: + str = "Insufficient Authorization"; + break; default: str = "Reserved"; break; @@ -1002,6 +1008,40 @@ static void sig_conn_param_rsp(const struct l2cap_frame *frame) print_conn_param_result(pdu->result); } +static void sig_le_conn_req(const struct l2cap_frame *frame) +{ + const struct bt_l2cap_pdu_le_conn_req *pdu = frame->data; + + print_psm(pdu->psm); + print_cid("Source", pdu->scid); + print_field("MTU: %u", btohs(pdu->mtu)); + print_field("MPS: %u", btohs(pdu->mps)); + print_field("Credits: %u", btohs(pdu->credits)); + + assign_scid(frame, btohs(pdu->scid), btohs(pdu->psm), 0); +} + +static void sig_le_conn_rsp(const struct l2cap_frame *frame) +{ + const struct bt_l2cap_pdu_le_conn_rsp *pdu = frame->data; + + print_cid("Destination", pdu->dcid); + print_field("MTU: %u", btohs(pdu->mtu)); + print_field("MPS: %u", btohs(pdu->mps)); + print_field("Credits: %u", btohs(pdu->credits)); + print_conn_result(pdu->result); + + /*assign_dcid(frame, btohs(pdu->dcid), btohs(pdu->scid));*/ +} + +static void sig_le_flowctl_creds(const struct l2cap_frame *frame) +{ + const struct bt_l2cap_pdu_le_flowctl_creds *pdu = frame->data; + + print_cid("Source", pdu->cid); + print_field("Credits: %u", btohs(pdu->credits)); +} + struct sig_opcode_data { uint8_t opcode; const char *str; @@ -1051,10 +1091,20 @@ static const struct sig_opcode_data bredr_sig_opcode_table[] = { static const struct sig_opcode_data le_sig_opcode_table[] = { { 0x01, "Command Reject", sig_cmd_reject, 2, false }, + { 0x06, "Disconnection Request", + sig_disconn_req, 4, true }, + { 0x07, "Disconnection Response", + sig_disconn_rsp, 4, true }, { 0x12, "Connection Parameter Update Request", sig_conn_param_req, 8, true }, { 0x13, "Connection Parameter Update Response", sig_conn_param_rsp, 2, true }, + { 0x14, "LE Connection Request", + sig_le_conn_req, 10, true }, + { 0x15, "LE Connection Response", + sig_le_conn_rsp, 10, true }, + { 0x16, "LE Flow Control Credit", + sig_le_flowctl_creds, 4, true }, { }, }; diff --git a/monitor/ll.c b/monitor/ll.c index e8b7a7a6..1a66572b 100644 --- a/monitor/ll.c +++ b/monitor/ll.c @@ -483,6 +483,8 @@ static const struct llcp_data llcp_table[] = { { 0x0b, "LL_PAUSE_ENC_RSP", null_pdu, 0, true }, { 0x0c, "LL_VERSION_IND", version_ind, 5, true }, { 0x0d, "LL_REJECT_IND", reject_ind, 1, true }, + { 0x12, "LL_PING_REQ", null_pdu, 0, true }, + { 0x13, "LL_PING_RSP", null_pdu, 0, true }, { } }; diff --git a/monitor/packet.c b/monitor/packet.c index 0efa60bc..6ec7d9be 100644 --- a/monitor/packet.c +++ b/monitor/packet.c @@ -66,6 +66,7 @@ #define COLOR_HCI_ACLDATA COLOR_CYAN #define COLOR_HCI_SCODATA COLOR_YELLOW +#define COLOR_UNKNOWN_ERROR COLOR_WHITE_BG #define COLOR_UNKNOWN_FEATURE_BIT COLOR_WHITE_BG #define COLOR_UNKNOWN_EVENT_MASK COLOR_WHITE_BG #define COLOR_UNKNOWN_LE_STATES COLOR_WHITE_BG @@ -345,6 +346,8 @@ static const struct { { 0x3d, "Connection Terminated due to MIC Failure" }, { 0x3e, "Connection Failed to be Established" }, { 0x3f, "MAC Connection Failed" }, + { 0x40, "Coarse Clock Adjustment Rejected " + "but Will Try to Adjust Using Clock Dragging" }, { } }; @@ -352,19 +355,24 @@ static void print_error(const char *label, uint8_t error) { const char *str = "Unknown"; const char *color_on, *color_off; + bool unknown = true; int i; for (i = 0; error2str_table[i].str; i++) { if (error2str_table[i].error == error) { str = error2str_table[i].str; + unknown = false; break; } } if (use_color()) { - if (error) - color_on = COLOR_RED; - else + if (error) { + if (unknown) + color_on = COLOR_UNKNOWN_ERROR; + else + color_on = COLOR_RED; + } else color_on = COLOR_GREEN; color_off = COLOR_OFF; } else { @@ -455,6 +463,11 @@ static void print_addr_type(const char *label, uint8_t addr_type) print_field("%s: %s (0x%2.2x)", label, str, addr_type); } +static void print_lt_addr(uint8_t lt_addr) +{ + print_field("LT address: %d", lt_addr); +} + static void print_handle(uint16_t handle) { print_field("Handle: %d", btohs(handle)); @@ -1002,7 +1015,6 @@ static void print_afh_mode(uint8_t mode) case 0x01: str = "Enabled"; break; - break; default: str = "Reserved"; break; @@ -1049,6 +1061,31 @@ static void print_ssp_debug_mode(uint8_t mode) print_field("Debug mode: %s (0x%2.2x)", str, mode); } +static void print_secure_conn_support(uint8_t support) +{ + const char *str; + + switch (support) { + case 0x00: + str = "Disabled"; + break; + case 0x01: + str = "Enabled"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Support: %s (0x%2.2x)", str, support); +} + +static void print_auth_payload_timeout(uint16_t timeout) +{ + print_field("Timeout: %d msec (0x%4.4x)", + btohs(timeout) * 10, btohs(timeout)); +} + static void print_pscan_rep_mode(uint8_t pscan_rep_mode) { const char *str; @@ -1138,6 +1175,31 @@ static void print_clock_accuracy(uint16_t accuracy) btohs(accuracy) * 0.3125, btohs(accuracy)); } +static void print_broadcast_fragment(uint8_t fragment) +{ + const char *str; + + switch (fragment) { + case 0x00: + str = "Continuation fragment"; + break; + case 0x01: + str = "Starting fragment"; + break; + case 0x02: + str = "Ending fragment"; + break; + case 0x03: + str = "No fragmentation"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Fragment: %s (0x%2.2x)", str, fragment); +} + static void print_link_type(uint8_t link_type) { const char *str; @@ -1203,6 +1265,9 @@ static void print_encr_mode_change(uint8_t encr_mode, uint16_t handle) break; } break; + case 0x02: + str = "Enabled with AES-CCM"; + break; default: str = "Reserved"; break; @@ -1283,14 +1348,20 @@ static void print_key_type(uint8_t key_type) str = "Debug Combination key"; break; case 0x04: - str = "Unauthenticated Combination key"; + str = "Unauthenticated Combination key from P-192"; break; case 0x05: - str = "Authenticated Combination key"; + str = "Authenticated Combination key from P-192"; break; case 0x06: str = "Changed Combination key"; break; + case 0x07: + str = "Unauthenticated Combination key from P-256"; + break; + case 0x08: + str = "Authenticated Combination key from P-256"; + break; default: str = "Reserved"; break; @@ -1388,7 +1459,13 @@ static void print_oob_data(uint8_t oob_data) str = "Authentication data not present"; break; case 0x01: - str = "Authentication data present"; + str = "P-192 authentication data present"; + break; + case 0x02: + str = "P-256 authentication data present"; + break; + case 0x03: + str = "P-192 and P-256 authentication data present"; break; default: str = "Reserved"; @@ -1898,6 +1975,7 @@ static const struct features_data features_page1[] = { { 0, "Secure Simple Pairing (Host Support)" }, { 1, "LE Supported (Host)" }, { 2, "Simultaneous LE and BR/EDR (Host)" }, + { 3, "Secure Connections (Host Support)" }, { } }; @@ -1907,11 +1985,20 @@ static const struct features_data features_page2[] = { { 2, "Synchronization Train" }, { 3, "Synchronization Scan" }, { 4, "Inquiry Response Notification Event" }, + { 5, "Generalized interlaced scan" }, + { 6, "Coarse Clock Adjustment" }, + { 8, "Secure Connections (Controller Support)" }, + { 9, "Ping" }, + { 11, "Train nudging" }, { } }; static const struct features_data features_le[] = { { 0, "LE Encryption" }, + { 1, "Connection Parameter Request Procedure" }, + { 2, "Extended Reject Indication" }, + { 3, "Slave-initiated Features Exchange" }, + { 4, "LE Ping" }, { } }; @@ -2180,6 +2267,7 @@ static const struct { { 20, "Slave Page Response Timeout" }, { 21, "Connectionless Slave Broadcast Channel Map Change" }, { 22, "Inquiry Response Notification" }, + { 23, "Authenticated Payload Timeout Expired" }, { } }; @@ -2211,11 +2299,12 @@ static const struct { uint8_t bit; const char *str; } events_le_table[] = { - { 0, "LE Connection Complete" }, - { 1, "LE Advertising Report" }, - { 2, "LE Connection Update Complete" }, - { 3, "LE Read Remote Used Features" }, - { 4, "LE Long Term Key Request" }, + { 0, "LE Connection Complete" }, + { 1, "LE Advertising Report" }, + { 2, "LE Connection Update Complete" }, + { 3, "LE Read Remote Used Features" }, + { 4, "LE Long Term Key Request" }, + { 5, "LE Remote Connection Parameter Request" }, { } }; @@ -3106,9 +3195,9 @@ static void setup_sync_conn_cmd(const void *data, uint8_t size) print_pkt_type(cmd->pkt_type); } -static void accept_sync_conn_cmd(const void *data, uint8_t size) +static void accept_sync_conn_request_cmd(const void *data, uint8_t size) { - const struct bt_hci_cmd_accept_sync_conn *cmd = data; + const struct bt_hci_cmd_accept_sync_conn_request *cmd = data; print_bdaddr(cmd->bdaddr); print_field("Transmit bandwidth: %d", btohl(cmd->tx_bandwidth)); @@ -3119,9 +3208,9 @@ static void accept_sync_conn_cmd(const void *data, uint8_t size) print_pkt_type(cmd->pkt_type); } -static void reject_sync_conn_cmd(const void *data, uint8_t size) +static void reject_sync_conn_request_cmd(const void *data, uint8_t size) { - const struct bt_hci_cmd_reject_sync_conn *cmd = data; + const struct bt_hci_cmd_reject_sync_conn_request *cmd = data; print_bdaddr(cmd->bdaddr); print_reason(cmd->reason); @@ -3271,6 +3360,91 @@ static void flow_spec_modify_cmd(const void *data, uint8_t size) print_flow_spec("RX", cmd->rx_flow_spec); } +static void truncated_page_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_truncated_page *cmd = data; + + print_bdaddr(cmd->bdaddr); + print_pscan_rep_mode(cmd->pscan_rep_mode); + print_clock_offset(cmd->clock_offset); +} + +static void truncated_page_cancel_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_truncated_page_cancel *cmd = data; + + print_bdaddr(cmd->bdaddr); +} + +static void set_slave_broadcast_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_set_slave_broadcast *cmd = data; + + print_field("Enable: 0x%2.2x", cmd->enable); + print_lt_addr(cmd->lt_addr); + print_field("LPO allowed: 0x%2.2x", cmd->lpo_allowed); + print_pkt_type(cmd->pkt_type); + print_slot_625("Min interval", cmd->min_interval); + print_slot_625("Max interval", cmd->max_interval); + print_slot_625("Supervision timeout", cmd->timeout); +} + +static void set_slave_broadcast_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_set_slave_broadcast *rsp = data; + + print_status(rsp->status); + print_lt_addr(rsp->lt_addr); + print_interval(rsp->interval); +} + +static void set_slave_broadcast_receive_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_set_slave_broadcast_receive *cmd = data; + + print_field("Enable: 0x%2.2x", cmd->enable); + print_bdaddr(cmd->bdaddr); + print_lt_addr(cmd->lt_addr); + print_interval(cmd->interval); + print_field("Offset: 0x%8.8x", btohl(cmd->offset)); + print_field("Next broadcast instant: 0x%4.4x", btohs(cmd->instant)); + print_slot_625("Supervision timeout", cmd->timeout); + print_field("Remote timing accuracy: %d ppm", cmd->accuracy); + print_field("Skip: 0x%2.2x", cmd->skip); + print_pkt_type(cmd->pkt_type); + print_channel_map(cmd->map); +} + +static void set_slave_broadcast_receive_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_set_slave_broadcast_receive *rsp = data; + + print_status(rsp->status); + print_bdaddr(rsp->bdaddr); + print_lt_addr(rsp->lt_addr); +} + +static void receive_sync_train_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_receive_sync_train *cmd = data; + + print_bdaddr(cmd->bdaddr); + print_timeout(cmd->timeout); + print_window(cmd->window); + print_interval(cmd->interval); +} + +static void remote_oob_ext_data_request_reply_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_remote_oob_ext_data_request_reply *cmd = data; + + print_bdaddr(cmd->bdaddr); + print_hash("P-192", cmd->hash192); + print_randomizer("P-192", cmd->randomizer192); + print_hash("P-256", cmd->hash256); + print_randomizer("P-256", cmd->randomizer256); +} + static void hold_mode_cmd(const void *data, uint8_t size) { const struct bt_hci_cmd_hold_mode *cmd = data; @@ -4211,6 +4385,59 @@ static void write_le_host_supported_cmd(const void *data, uint8_t size) print_field("Simultaneous: 0x%2.2x", cmd->simultaneous); } +static void set_reserved_lt_addr_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_set_reserved_lt_addr *cmd = data; + + print_lt_addr(cmd->lt_addr); +} + +static void set_reserved_lt_addr_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_set_reserved_lt_addr *rsp = data; + + print_status(rsp->status); + print_lt_addr(rsp->lt_addr); +} + +static void delete_reserved_lt_addr_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_delete_reserved_lt_addr *cmd = data; + + print_lt_addr(cmd->lt_addr); +} + +static void delete_reserved_lt_addr_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_delete_reserved_lt_addr *rsp = data; + + print_status(rsp->status); + print_lt_addr(rsp->lt_addr); +} + +static void set_slave_broadcast_data_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_set_slave_broadcast_data *cmd = data; + + print_lt_addr(cmd->lt_addr); + print_broadcast_fragment(cmd->fragment); + print_field("Length: %d", cmd->length); + + if (size - 3 != cmd->length) + print_text(COLOR_ERROR, "invalid data size (%d != %d)", + size - 3, cmd->length); + + packet_hexdump(data + 3, size - 3); +} + +static void set_slave_broadcast_data_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_set_slave_broadcast_data *rsp = data; + + print_status(rsp->status); + print_lt_addr(rsp->lt_addr); +} + static void read_sync_train_params_rsp(const void *data, uint8_t size) { const struct bt_hci_rsp_read_sync_train_params *rsp = data; @@ -4222,6 +4449,83 @@ static void read_sync_train_params_rsp(const void *data, uint8_t size) print_field("Service Data: 0x%2.2x", rsp->service_data); } +static void write_sync_train_params_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_write_sync_train_params *cmd = data; + + print_slot_625("Min interval", cmd->min_interval); + print_slot_625("Max interval", cmd->max_interval); + print_field("Timeout: %.3f msec (0x%8.8x)", + btohl(cmd->timeout) * 0.625, btohl(cmd->timeout)); + print_field("Service Data: 0x%2.2x", cmd->service_data); +} + +static void write_sync_train_params_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_write_sync_train_params *rsp = data; + + print_status(rsp->status); + print_interval(rsp->interval); +} + +static void read_secure_conn_support_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_read_secure_conn_support *rsp = data; + + print_status(rsp->status); + print_secure_conn_support(rsp->support); +} + +static void write_secure_conn_support_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_write_secure_conn_support *cmd = data; + + print_secure_conn_support(cmd->support); +} + +static void read_auth_payload_timeout_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_read_auth_payload_timeout *cmd = data; + + print_handle(cmd->handle); +} + +static void read_auth_payload_timeout_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_read_auth_payload_timeout *rsp = data; + + print_status(rsp->status); + print_handle(rsp->handle); + print_auth_payload_timeout(rsp->timeout); +} + +static void write_auth_payload_timeout_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_write_auth_payload_timeout *cmd = data; + + print_handle(cmd->handle); + print_auth_payload_timeout(cmd->timeout); +} + +static void write_auth_payload_timeout_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_write_auth_payload_timeout *rsp = data; + + print_status(rsp->status); + print_handle(rsp->handle); +} + +static void read_local_oob_ext_data_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_read_local_oob_ext_data *rsp = data; + + print_status(rsp->status); + print_hash("P-192", rsp->hash192); + print_randomizer("P-192", rsp->randomizer192); + print_hash("P-256", rsp->hash256); + print_randomizer("P-256", rsp->randomizer256); +} + static void read_local_version_rsp(const void *data, uint8_t size) { const struct bt_hci_rsp_read_local_version *rsp = data; @@ -4575,7 +4879,7 @@ static void le_set_adv_parameters_cmd(const void *data, uint8_t size) str = "Connectable undirected - ADV_IND"; break; case 0x01: - str = "Connectable directed - ADV_DIRECT_IND"; + str = "Connectable directed - ADV_DIRECT_IND (high duty cycle)"; break; case 0x02: str = "Scannable undirected - ADV_SCAN_IND"; @@ -4583,6 +4887,9 @@ static void le_set_adv_parameters_cmd(const void *data, uint8_t size) case 0x03: str = "Non connectable undirect - ADV_NONCONN_IND"; break; + case 0x04: + str = "Connectable directed - ADV_DIRECT_IND (low duty cycle)"; + break; default: str = "Reserved"; break; @@ -5047,10 +5354,10 @@ static const struct opcode_data opcode_table[] = { read_lmp_handle_rsp, 8, true }, { 0x0428, 131, "Setup Synchronous Connection", setup_sync_conn_cmd, 17, true }, - { 0x0429, 132, "Accept Synchronous Connection", - accept_sync_conn_cmd, 21, true }, - { 0x042a, 133, "Reject Synchronous Connection", - reject_sync_conn_cmd, 7, true }, + { 0x0429, 132, "Accept Synchronous Connection Request", + accept_sync_conn_request_cmd, 21, true }, + { 0x042a, 133, "Reject Synchronous Connection Request", + reject_sync_conn_request_cmd, 7, true }, { 0x042b, 151, "IO Capability Request Reply", io_capability_request_reply_cmd, 9, true, status_bdaddr_rsp, 7, true }, @@ -5093,13 +5400,25 @@ static const struct opcode_data opcode_table[] = { { 0x043c, 175, "Flow Specifcation Modify", flow_spec_modify_cmd, 34, true }, { 0x043d, 235, "Enhanced Setup Synchronous Connection" }, - { 0x043e, 236, "Enhanced Accept Synchronous Connection" }, - { 0x043f, 246, "Truncated Page" }, - { 0x0440, 247, "Truncated Page Cancel" }, - { 0x0441, 248, "Set Connectionless Slave Broadcast" }, - { 0x0442, 249, "Set Connectionless Slave Broadcast Receive" }, - { 0x0443, 250, "Start Synchronization Train" }, - { 0x0444, 251, "Receive Synchronization Train" }, + { 0x043e, 236, "Enhanced Accept Synchronous Connection Request" }, + { 0x043f, 246, "Truncated Page", + truncated_page_cmd, 9, true }, + { 0x0440, 247, "Truncated Page Cancel", + truncated_page_cancel_cmd, 6, true, + status_bdaddr_rsp, 7, true }, + { 0x0441, 248, "Set Connectionless Slave Broadcast", + set_slave_broadcast_cmd, 11, true, + set_slave_broadcast_rsp, 4, true }, + { 0x0442, 249, "Set Connectionless Slave Broadcast Receive", + set_slave_broadcast_receive_cmd, 34, true, + set_slave_broadcast_receive_rsp, 8, true }, + { 0x0443, 250, "Start Synchronization Train", + null_cmd, 0, true }, + { 0x0444, 251, "Receive Synchronization Train", + receive_sync_train_cmd, 12, true }, + { 0x0445, 257, "Remote OOB Extended Data Request Reply", + remote_oob_ext_data_request_reply_cmd, 70, true, + status_bdaddr_rsp, 7, true }, /* OGF 2 - Link Policy */ { 0x0801, 33, "Holde Mode", @@ -5379,13 +5698,40 @@ static const struct opcode_data opcode_table[] = { { 0x0c71, 241, "Set MWS Transport Layer" }, { 0x0c72, 242, "Set MWS Scan Frequency Table" }, { 0x0c73, 244, "Set MWS Pattern Configuration" }, - { 0x0c74, 252, "Set Reserved LT_ADDR" }, - { 0x0c75, 253, "Delete Reserved LT_ADDR" }, - { 0x0c76, 254, "Set Connectionless Slave Broadcast Data" }, + { 0x0c74, 252, "Set Reserved LT_ADDR", + set_reserved_lt_addr_cmd, 1, true, + set_reserved_lt_addr_rsp, 2, true }, + { 0x0c75, 253, "Delete Reserved LT_ADDR", + delete_reserved_lt_addr_cmd, 1, true, + delete_reserved_lt_addr_rsp, 2, true }, + { 0x0c76, 254, "Set Connectionless Slave Broadcast Data", + set_slave_broadcast_data_cmd, 3, false, + set_slave_broadcast_data_rsp, 2, true }, { 0x0c77, 255, "Read Synchronization Train Parameters", null_cmd, 0, true, read_sync_train_params_rsp, 8, true }, - { 0x0c78, 256, "Write Synchronization Train Parameters" }, + { 0x0c78, 256, "Write Synchronization Train Parameters", + write_sync_train_params_cmd, 9, true, + write_sync_train_params_rsp, 3, true }, + { 0x0c79, 258, "Read Secure Connections Host Support", + null_cmd, 0, true, + read_secure_conn_support_rsp, 2, true }, + { 0x0c7a, 259, "Write Secure Connections Host Support", + write_secure_conn_support_cmd, 1, true, + status_rsp, 1, true }, + { 0x0c7b, 260, "Read Authenticated Payload Timeout", + read_auth_payload_timeout_cmd, 2, true, + read_auth_payload_timeout_rsp, 5, true }, + { 0x0c7c, 261, "Write Authenticated Payload Timeout", + write_auth_payload_timeout_cmd, 4, true, + write_auth_payload_timeout_rsp, 3, true }, + { 0x0c7d, 262, "Read Local OOB Extended Data", + null_cmd, 0, true, + read_local_oob_ext_data_rsp, 65, true }, + { 0x0c7e, 264, "Read Extended Page Timeout" }, + { 0x0c7f, 265, "Write Extended Page Timeout" }, + { 0x0c80, 266, "Read Extended Inquiry Length" }, + { 0x0c81, 267, "Write Extended Inquiry Length" }, /* OGF 4 - Information Parameter */ { 0x1001, 115, "Read Local Version Information", @@ -5460,6 +5806,7 @@ static const struct opcode_data opcode_table[] = { { 0x1807, 189, "Enable AMP Receiver Reports" }, { 0x1808, 190, "AMP Test End" }, { 0x1809, 191, "AMP Test" }, + { 0x180a, 263, "Write Secure Connections Test Mode" }, /* OGF 8 - LE Control */ { 0x2001, 200, "LE Set Event Mask", @@ -5548,6 +5895,8 @@ static const struct opcode_data opcode_table[] = { { 0x201f, 230, "LE Test End", null_cmd, 0, true, le_test_end_rsp, 3, true }, + { 0x2020, 268, "LE Remote Connection Parameter Request Reply" }, + { 0x2021, 269, "LE Remote Connection Parameter Request Negative Reply" }, { } }; @@ -5563,11 +5912,11 @@ static const char *get_supported_command(int bit) return NULL; } -static void status_evt(const void *data, uint8_t size) +static void inquiry_complete_evt(const void *data, uint8_t size) { - uint8_t status = *((uint8_t *) data); + const struct bt_hci_evt_inquiry_complete *evt = data; - print_status(status); + print_status(evt->status); } static void inquiry_result_evt(const void *data, uint8_t size) @@ -6254,6 +6603,80 @@ static void amp_status_change_evt(const void *data, uint8_t size) print_amp_status(evt->amp_status); } +static void sync_train_complete_evt(const void *data, uint8_t size) +{ + const struct bt_hci_evt_sync_train_complete *evt = data; + + print_status(evt->status); +} + +static void sync_train_received_evt(const void *data, uint8_t size) +{ + const struct bt_hci_evt_sync_train_received *evt = data; + + print_status(evt->status); + print_bdaddr(evt->bdaddr); + print_field("Offset: 0x%8.8x", btohl(evt->offset)); + print_channel_map(evt->map); + print_lt_addr(evt->lt_addr); + print_field("Next broadcast instant: 0x%4.4x", btohs(evt->instant)); + print_interval(evt->interval); + print_field("Service Data: 0x%2.2x", evt->service_data); +} + +static void slave_broadcast_receive_evt(const void *data, uint8_t size) +{ + const struct bt_hci_evt_slave_broadcast_receive *evt = data; + + print_bdaddr(evt->bdaddr); + print_lt_addr(evt->lt_addr); + print_field("Clock: 0x%8.8x", btohl(evt->clock)); + print_field("Offset: 0x%8.8x", btohl(evt->offset)); + print_field("Receive status: 0x%2.2x", evt->status); + print_broadcast_fragment(evt->fragment); + print_field("Length: %d", evt->length); + + if (size - 18 != evt->length) + print_text(COLOR_ERROR, "invalid data size (%d != %d)", + size - 18, evt->length); + + packet_hexdump(data + 18, size - 18); +} + +static void slave_broadcast_timeout_evt(const void *data, uint8_t size) +{ + const struct bt_hci_evt_slave_broadcast_timeout *evt = data; + + print_bdaddr(evt->bdaddr); + print_lt_addr(evt->lt_addr); +} + +static void truncated_page_complete_evt(const void *data, uint8_t size) +{ + const struct bt_hci_evt_truncated_page_complete *evt = data; + + print_status(evt->status); + print_bdaddr(evt->bdaddr); +} + +static void slave_page_response_timeout_evt(const void *data, uint8_t size) +{ +} + +static void slave_broadcast_channel_map_change_evt(const void *data, uint8_t size) +{ + const struct bt_hci_evt_slave_broadcast_channel_map_change *evt = data; + + print_channel_map(evt->map); +} + +static void auth_payload_timeout_expired_evt(const void *data, uint8_t size) +{ + const struct bt_hci_evt_auth_payload_timeout_expired *evt = data; + + print_handle(evt->handle); +} + static void le_conn_complete_evt(const void *data, uint8_t size) { const struct bt_hci_evt_le_conn_complete *evt = data; @@ -6368,6 +6791,7 @@ static const struct subevent_data subevent_table[] = { le_remote_features_complete_evt, 11, true }, { 0x05, "LE Long Term Key Request", le_long_term_key_request_evt, 12, true }, + { 0x06, "LE Remote Connection Parameter Request" }, { } }; @@ -6436,7 +6860,7 @@ struct event_data { static const struct event_data event_table[] = { { 0x01, "Inquiry Complete", - status_evt, 1, true }, + inquiry_complete_evt, 1, true }, { 0x02, "Inquiry Result", inquiry_result_evt, 1, false }, { 0x03, "Connect Complete", @@ -6564,6 +6988,24 @@ static const struct event_data event_table[] = { short_range_mode_change_evt, 3, true }, { 0x4d, "AMP Status Change", amp_status_change_evt, 2, true }, + { 0x4e, "Triggered Clock Capture" }, + { 0x4f, "Synchronization Train Complete", + sync_train_complete_evt, 1, true }, + { 0x50, "Synchronization Train Received", + sync_train_received_evt, 29, true }, + { 0x51, "Connectionless Slave Broadcast Receive", + slave_broadcast_receive_evt, 18, false }, + { 0x52, "Connectionless Slave Broadcast Timeout", + slave_broadcast_timeout_evt, 7, true }, + { 0x53, "Truncated Page Complete", + truncated_page_complete_evt, 7, true }, + { 0x54, "Slave Page Response Timeout", + slave_page_response_timeout_evt, 0, true }, + { 0x55, "Connectionless Slave Broadcast Channel Map Change", + slave_broadcast_channel_map_change_evt, 10, true }, + { 0x56, "Inquiry Response Notification" }, + { 0x57, "Authenticated Payload Timeout Expired", + auth_payload_timeout_expired_evt, 2, true }, { 0xfe, "Testing" }, { 0xff, "Vendor", vendor_evt, 0, false }, { } diff --git a/plugins/autopair.c b/plugins/autopair.c index e6e5035e..8c98c12b 100644 --- a/plugins/autopair.c +++ b/plugins/autopair.c @@ -121,6 +121,14 @@ static ssize_t autopair_pincb(struct btd_adapter *adapter, } break; + case 0x06: /* Imaging */ + if (class & 0x80) { /* Printer */ + if (attempt > 1) + return 0; + memcpy(pinbuf, "0000", 4); + return 4; + } + break; } return 0; diff --git a/plugins/neard.c b/plugins/neard.c index ea91c4dd..35fdaeb5 100644 --- a/plugins/neard.c +++ b/plugins/neard.c @@ -227,7 +227,7 @@ static DBusMessage *create_request_oob_reply(struct btd_adapter *adapter, uint8_t *peir = eir; int len; - len = eir_create_oob(adapter_get_address(adapter), + len = eir_create_oob(btd_adapter_get_address(adapter), btd_adapter_get_name(adapter), btd_adapter_get_class(adapter), hash, randomizer, main_opts.did_vendor, @@ -640,7 +640,7 @@ static void store_params(struct btd_adapter *adapter, struct btd_device *device, if (params->name) { device_store_cached_name(device, params->name); - device_set_name(device, params->name); + btd_device_device_set_name(device, params->name); } /* TODO handle UUIDs? */ @@ -701,7 +701,8 @@ static DBusMessage *push_oob(DBusConnection *conn, DBusMessage *msg, void *data) return error_reply(msg, EINVAL); } - device = adapter_get_device(adapter, &remote.address, BDADDR_BREDR); + device = btd_adapter_get_device(adapter, &remote.address, + BDADDR_BREDR); err = check_device(device); if (err < 0) { @@ -769,7 +770,8 @@ static DBusMessage *request_oob(DBusConnection *conn, DBusMessage *msg, if (bacmp(&remote.address, BDADDR_ANY) == 0) goto read_local; - device = adapter_get_device(adapter, &remote.address, BDADDR_BREDR); + device = btd_adapter_get_device(adapter, &remote.address, + BDADDR_BREDR); err = check_device(device); if (err < 0) { diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c new file mode 100644 index 00000000..45fa1705 --- /dev/null +++ b/plugins/sixaxis.c @@ -0,0 +1,434 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2009 Bastien Nocera <hadess@hadess.net> + * Copyright (C) 2011 Antonio Ospite <ospite@studenti.unina.it> + * Copyright (C) 2013 Szymon Janc <szymon.janc@gmail.com> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stddef.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <linux/hidraw.h> +#include <linux/input.h> +#include <glib.h> +#include <libudev.h> + +#include "lib/bluetooth.h" +#include "lib/uuid.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/plugin.h" +#include "src/log.h" + +static const struct { + const char *name; + uint16_t source; + uint16_t vid; + uint16_t pid; + uint16_t version; +} devices[] = { + { + .name = "PLAYSTATION(R)3 Controller", + .source = 0x0002, + .vid = 0x054c, + .pid = 0x0268, + .version = 0x0000, + }, +}; + +static struct udev *ctx = NULL; +static struct udev_monitor *monitor = NULL; +static guint watch_id = 0; + +static int get_device_bdaddr(int fd, bdaddr_t *bdaddr) +{ + uint8_t buf[18]; + int ret; + + memset(buf, 0, sizeof(buf)); + + buf[0] = 0xf2; + + ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf); + if (ret < 0) { + error("sixaxis: failed to read device address (%s)", + strerror(errno)); + return ret; + } + + baswap(bdaddr, (bdaddr_t *) (buf + 4)); + + return 0; +} + +static int get_master_bdaddr(int fd, bdaddr_t *bdaddr) +{ + uint8_t buf[8]; + int ret; + + memset(buf, 0, sizeof(buf)); + + buf[0] = 0xf5; + + ret = ioctl(fd, HIDIOCGFEATURE(sizeof(buf)), buf); + if (ret < 0) { + error("sixaxis: failed to read master address (%s)", + strerror(errno)); + return ret; + } + + baswap(bdaddr, (bdaddr_t *) (buf + 2)); + + return 0; +} + +static int set_master_bdaddr(int fd, const bdaddr_t *bdaddr) +{ + uint8_t buf[8]; + int ret; + + buf[0] = 0xf5; + buf[1] = 0x01; + + baswap((bdaddr_t *) (buf + 2), bdaddr); + + ret = ioctl(fd, HIDIOCSFEATURE(sizeof(buf)), buf); + if (ret < 0) + error("sixaxis: failed to write master address (%s)", + strerror(errno)); + + return ret; +} + +static gboolean setup_leds(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + /* + * the total time the led is active (0xff means forever) + * | duty_length: cycle time in deciseconds (0 - "blink very fast") + * | | ??? (Maybe a phase shift or duty_length multiplier?) + * | | | % of duty_length led is off (0xff means 100%) + * | | | | % of duty_length led is on (0xff means 100%) + * | | | | | + * 0xff, 0x27, 0x10, 0x00, 0x32, + */ + uint8_t leds_report[] = { + 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, /* rumble values TBD */ + 0x00, 0x00, 0x00, 0x00, 0x00, /* LED_1=0x02, LED_2=0x04 ... */ + 0xff, 0x27, 0x10, 0x00, 0x32, /* LED_4 */ + 0xff, 0x27, 0x10, 0x00, 0x32, /* LED_3 */ + 0xff, 0x27, 0x10, 0x00, 0x32, /* LED_2 */ + 0xff, 0x27, 0x10, 0x00, 0x32, /* LED_1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, + }; + int number = GPOINTER_TO_INT(user_data); + int ret; + int fd; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) + return FALSE; + + DBG("number %d", number); + + /* TODO we could support up to 10 (1 + 2 + 3 + 4) */ + if (number > 7) + return FALSE; + + if (number > 4) { + leds_report[10] |= 0x10; + number -= 4; + } + + leds_report[10] |= 0x01 << number; + + fd = g_io_channel_unix_get_fd(channel); + + ret = write(fd, leds_report, sizeof(leds_report)); + if (ret == sizeof(leds_report)) + return FALSE; + + if (ret < 0) + error("sixaxis: failed to set LEDS (%s)", strerror(errno)); + else + error("sixaxis: failed to set LEDS (%d bytes written)", ret); + + return FALSE; +} + +static bool setup_device(int fd, int index, struct btd_adapter *adapter) +{ + char device_addr[18], master_addr[18], adapter_addr[18]; + bdaddr_t device_bdaddr, master_bdaddr; + const bdaddr_t *adapter_bdaddr; + struct btd_device *device; + + if (get_device_bdaddr(fd, &device_bdaddr) < 0) + return false; + + if (get_master_bdaddr(fd, &master_bdaddr) < 0) + return false; + + /* This can happen if controller was plugged while already connected + * eg. to charge up battery. + * Don't set LEDs in that case, hence return false */ + device = btd_adapter_find_device(adapter, &device_bdaddr); + if (device && btd_device_is_connected(device)) + return false; + + adapter_bdaddr = btd_adapter_get_address(adapter); + + if (bacmp(adapter_bdaddr, &master_bdaddr)) { + if (set_master_bdaddr(fd, adapter_bdaddr) < 0) + return false; + } + + ba2str(&device_bdaddr, device_addr); + ba2str(&master_bdaddr, master_addr); + ba2str(adapter_bdaddr, adapter_addr); + DBG("remote %s old_master %s new_master %s", + device_addr, master_addr, adapter_addr); + + device = btd_adapter_get_device(adapter, &device_bdaddr, BDADDR_BREDR); + + if (g_slist_find_custom(btd_device_get_uuids(device), HID_UUID, + (GCompareFunc)strcasecmp)) { + DBG("device %s already known, skipping", device_addr); + return true; + } + + info("sixaxis: setting up new device"); + + btd_device_device_set_name(device, devices[index].name); + btd_device_set_pnpid(device, devices[index].source, devices[index].vid, + devices[index].pid, devices[index].version); + btd_device_set_temporary(device, FALSE); + btd_device_set_trusted(device, TRUE); + + return true; +} + +static int get_js_number(struct udev_device *udevice) +{ + struct udev_list_entry *devices, *dev_list_entry; + struct udev_enumerate *enumerate; + struct udev_device *hid_parent; + const char *hidraw_node; + const char *hid_phys; + int number = 0; + + hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice, + "hid", NULL); + + hid_phys = udev_device_get_property_value(hid_parent, "HID_PHYS"); + hidraw_node = udev_device_get_devnode(udevice); + if (!hid_phys || !hidraw_node) + return 0; + + enumerate = udev_enumerate_new(udev_device_get_udev(udevice)); + udev_enumerate_add_match_sysname(enumerate, "js*"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + + udev_list_entry_foreach(dev_list_entry, devices) { + struct udev_device *input_parent; + struct udev_device *js_dev; + const char *input_phys; + const char *devname; + + devname = udev_list_entry_get_name(dev_list_entry); + js_dev = udev_device_new_from_syspath( + udev_device_get_udev(udevice), + devname); + + input_parent = udev_device_get_parent_with_subsystem_devtype( + js_dev, "input", NULL); + if (!input_parent) + goto next; + + /* check if this is the joystick relative to the hidraw device + * above */ + input_phys = udev_device_get_sysattr_value(input_parent, + "phys"); + if (!input_phys) + goto next; + + if (!strcmp(input_phys, hid_phys)) { + number = atoi(udev_device_get_sysnum(js_dev)); + + /* joystick numbers start from 0, leds from 1 */ + number++; + + udev_device_unref(js_dev); + break; + } +next: + udev_device_unref(js_dev); + } + + udev_enumerate_unref(enumerate); + + return number; +} + +static int get_supported_device(struct udev_device *udevice, uint16_t *bus) +{ + struct udev_device *hid_parent; + uint16_t vid, pid; + const char *hid_id; + int i; + + hid_parent = udev_device_get_parent_with_subsystem_devtype(udevice, + "hid", NULL); + if (!hid_parent) + return -1; + + hid_id = udev_device_get_property_value(hid_parent, "HID_ID"); + + if (sscanf(hid_id, "%hx:%hx:%hx", bus, &vid, &pid) != 3) + return -1; + + for (i = 0; G_N_ELEMENTS(devices); i++) { + if (devices[i].vid == vid && devices[i].pid == pid) + return i; + } + + return -1; +} + +static void device_added(struct udev_device *udevice) +{ + struct btd_adapter *adapter; + GIOChannel *io; + uint16_t bus; + int index; + int fd; + + adapter = btd_adapter_get_default(); + if (!adapter) + return; + + index = get_supported_device(udevice, &bus); + if (index < 0) + return; + + info("sixaxis: compatible device connected: %s (%04X:%04X)", + devices[index].name, devices[index].vid, + devices[index].pid); + + fd = open(udev_device_get_devnode(udevice), O_RDWR); + if (fd < 0) + return; + + io = g_io_channel_unix_new(fd); + + switch (bus) { + case BUS_USB: + if (!setup_device(fd, index, adapter)) + break; + + /* fall through */ + case BUS_BLUETOOTH: + /* wait for events before setting leds */ + g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + setup_leds, + GINT_TO_POINTER(get_js_number(udevice))); + + break; + default: + DBG("uknown bus type (%u)", bus); + break; + } + + g_io_channel_set_close_on_unref(io, TRUE); + g_io_channel_unref(io); +} + +static gboolean monitor_watch(GIOChannel *source, GIOCondition condition, + gpointer data) +{ + struct udev_device *udevice; + + udevice = udev_monitor_receive_device(monitor); + if (!udevice) + return TRUE; + + if (!g_strcmp0(udev_device_get_action(udevice), "add")) + device_added(udevice); + + udev_device_unref(udevice); + + return TRUE; +} + +static int sixaxis_init(void) +{ + GIOChannel *channel; + + DBG(""); + + ctx = udev_new(); + if (!ctx) + return -EIO; + + monitor = udev_monitor_new_from_netlink(ctx, "udev"); + if (!monitor) { + udev_unref(ctx); + ctx = NULL; + + return -EIO; + } + + /* Listen for newly connected hidraw interfaces */ + udev_monitor_filter_add_match_subsystem_devtype(monitor, "hidraw", + NULL); + udev_monitor_enable_receiving(monitor); + + channel = g_io_channel_unix_new(udev_monitor_get_fd(monitor)); + watch_id = g_io_add_watch(channel, G_IO_IN, monitor_watch, NULL); + g_io_channel_unref(channel); + + return 0; +} + +static void sixaxis_exit(void) +{ + DBG(""); + + g_source_remove(watch_id); + watch_id = 0; + + udev_monitor_unref(monitor); + monitor = NULL; + + udev_unref(ctx); + ctx = NULL; +} + +BLUETOOTH_PLUGIN_DEFINE(sixaxis, VERSION, BLUETOOTH_PLUGIN_PRIORITY_LOW, + sixaxis_init, sixaxis_exit) diff --git a/plugins/wiimote.c b/plugins/wiimote.c index 6cc21ee5..96a65695 100644 --- a/plugins/wiimote.c +++ b/plugins/wiimote.c @@ -106,7 +106,7 @@ static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device, found: DBG("Forcing fixed pin on detected wiimote %s", addr); - memcpy(pinbuf, adapter_get_address(adapter), 6); + memcpy(pinbuf, btd_adapter_get_address(adapter), 6); return 6; } diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c index 864cb188..29a15939 100644 --- a/profiles/audio/a2dp.c +++ b/profiles/audio/a2dp.c @@ -1887,7 +1887,7 @@ static int a2dp_source_disconnect(struct btd_service *service) DBG("path %s", path); - return source_disconnect(service, FALSE); + return source_disconnect(service); } static int a2dp_sink_connect(struct btd_service *service) @@ -1919,7 +1919,7 @@ static int a2dp_sink_disconnect(struct btd_service *service) DBG("path %s", path); - return sink_disconnect(service, FALSE); + return sink_disconnect(service); } static int a2dp_source_server_probe(struct btd_profile *p, diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c index dac7a66c..6669ddc0 100644 --- a/profiles/audio/avctp.c +++ b/profiles/audio/avctp.c @@ -1307,11 +1307,11 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan, avctp_set_state(session, AVCTP_STATE_CONNECTING); session->control = avctp_channel_create(session, chan, NULL); - src = adapter_get_address(device_get_adapter(dev)); + src = btd_adapter_get_address(device_get_adapter(dev)); dst = device_get_address(dev); session->auth_id = btd_request_authorization(src, dst, - AVRCP_TARGET_UUID, + AVRCP_REMOTE_UUID, auth_cb, session); if (session->auth_id == 0) goto drop; @@ -1371,7 +1371,7 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data) DBG("AVCTP: incoming connect from %s", address); - device = adapter_find_device(adapter_find(&src), &dst); + device = btd_adapter_find_device(adapter_find(&src), &dst); if (!device) return; @@ -1422,7 +1422,7 @@ static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master, int avctp_register(struct btd_adapter *adapter, gboolean master) { struct avctp_server *server; - const bdaddr_t *src = adapter_get_address(adapter); + const bdaddr_t *src = btd_adapter_get_address(adapter); server = g_new0(struct avctp_server, 1); @@ -1488,7 +1488,7 @@ static struct avctp_pending_req *pending_create(struct avctp_channel *chan, tmp = g_slist_copy(chan->processed); /* Find first unused transaction id */ - for (l = tmp; l; l = l->next) { + for (l = tmp; l; l = g_slist_next(l)) { struct avctp_pending_req *req = l->data; if (req->transaction == chan->transaction) { @@ -1937,6 +1937,7 @@ struct avctp *avctp_connect(struct btd_device *device) struct avctp *session; GError *err = NULL; GIOChannel *io; + const bdaddr_t *src; session = avctp_get_internal(device); if (!session) @@ -1947,9 +1948,10 @@ struct avctp *avctp_connect(struct btd_device *device) avctp_set_state(session, AVCTP_STATE_CONNECTING); + src = btd_adapter_get_address(session->server->adapter); + io = bt_io_connect(avctp_connect_cb, session, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, - adapter_get_address(session->server->adapter), + BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, device_get_address(session->device), BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, @@ -1971,6 +1973,7 @@ struct avctp *avctp_connect(struct btd_device *device) int avctp_connect_browsing(struct avctp *session) { + const bdaddr_t *src; GError *err = NULL; GIOChannel *io; @@ -1982,9 +1985,10 @@ int avctp_connect_browsing(struct avctp *session) avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING); + src = btd_adapter_get_address(session->server->adapter); + io = bt_io_connect(avctp_connect_browsing_cb, session, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, - adapter_get_address(session->server->adapter), + BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, device_get_address(session->device), BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c index 93783509..f866b392 100644 --- a/profiles/audio/avdtp.c +++ b/profiles/audio/avdtp.c @@ -394,9 +394,6 @@ struct avdtp { avdtp_session_state_t state; - /* True if the entire device is being disconnected */ - gboolean device_disconnect; - guint auth_id; GIOChannel *io; @@ -1200,11 +1197,6 @@ static void set_disconnect_timer(struct avdtp *session) if (session->dc_timer) remove_disconnect_timer(session); - if (session->device_disconnect) { - session->dc_timer = g_idle_add(disconnect_timeout, session); - return; - } - session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT, disconnect_timeout, session); @@ -2475,7 +2467,7 @@ static void avdtp_confirm_cb(GIOChannel *chan, gpointer data) DBG("AVDTP: incoming connect from %s", address); - device = adapter_find_device(adapter_find(&src), &dst); + device = btd_adapter_find_device(adapter_find(&src), &dst); if (!device) goto drop; @@ -2531,11 +2523,13 @@ static GIOChannel *l2cap_connect(struct avdtp *session) { GError *err = NULL; GIOChannel *io; + const bdaddr_t *src; + + src = btd_adapter_get_address(session->server->adapter); io = bt_io_connect(avdtp_connect_cb, session, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, - adapter_get_address(session->server->adapter), + BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, device_get_address(session->device), BT_IO_OPT_PSM, AVDTP_PSM, @@ -3183,8 +3177,8 @@ struct avdtp_service_capability *avdtp_stream_get_codec( return NULL; } -gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, - struct avdtp_service_capability *cap) +static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, + struct avdtp_service_capability *cap) { GSList *l; struct avdtp_service_capability *stream_cap; @@ -3219,7 +3213,16 @@ gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, struct avdtp_remote_sep *avdtp_stream_get_remote_sep( struct avdtp_stream *stream) { - return avdtp_get_remote_sep(stream->session, stream->rseid); + GSList *l; + + for (l = stream->session->seps; l; l = l->next) { + struct avdtp_remote_sep *sep = l->data; + + if (sep->seid == stream->rseid) + return sep; + } + + return NULL; } gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, @@ -3268,46 +3271,11 @@ static int process_queue(struct avdtp *session) return send_req(session, FALSE, req); } -struct avdtp_remote_sep *avdtp_get_remote_sep(struct avdtp *session, - uint8_t seid) -{ - GSList *l; - - for (l = session->seps; l; l = l->next) { - struct avdtp_remote_sep *sep = l->data; - - if (sep->seid == seid) - return sep; - } - - return NULL; -} - -uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep) -{ - return sep->seid; -} - -uint8_t avdtp_get_type(struct avdtp_remote_sep *sep) -{ - return sep->type; -} - struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep) { return sep->codec; } -gboolean avdtp_get_delay_reporting(struct avdtp_remote_sep *sep) -{ - return sep->delay_reporting; -} - -struct avdtp_stream *avdtp_get_stream(struct avdtp_remote_sep *sep) -{ - return sep->stream; -} - struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, void *data, int length) { @@ -3709,7 +3677,8 @@ static struct avdtp_server *avdtp_server_init(struct btd_adapter *adapter) server = g_new0(struct avdtp_server, 1); - server->io = avdtp_server_socket(adapter_get_address(adapter), TRUE); + server->io = avdtp_server_socket(btd_adapter_get_address(adapter), + TRUE); if (!server->io) { g_free(server); return NULL; @@ -3867,11 +3836,6 @@ gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream) return g_slist_find(session->streams, stream) ? TRUE : FALSE; } -void avdtp_set_device_disconnect(struct avdtp *session, gboolean dev_dc) -{ - session->device_disconnect = dev_dc; -} - unsigned int avdtp_add_state_cb(struct btd_device *dev, avdtp_session_state_cb cb, void *user_data) { diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h index 56065062..390c154e 100644 --- a/profiles/audio/avdtp.h +++ b/profiles/audio/avdtp.h @@ -221,19 +221,8 @@ struct avdtp *avdtp_ref(struct avdtp *session); struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, void *data, int size); -struct avdtp_remote_sep *avdtp_get_remote_sep(struct avdtp *session, - uint8_t seid); - -uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep); - -uint8_t avdtp_get_type(struct avdtp_remote_sep *sep); - struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); -gboolean avdtp_get_delay_reporting(struct avdtp_remote_sep *sep); - -struct avdtp_stream *avdtp_get_stream(struct avdtp_remote_sep *sep); - int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, void *user_data); @@ -251,8 +240,6 @@ gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, GSList **caps); struct avdtp_service_capability *avdtp_stream_get_codec( struct avdtp_stream *stream); -gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, - struct avdtp_service_capability *cap); gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, GSList *caps); struct avdtp_remote_sep *avdtp_stream_get_remote_sep( @@ -306,5 +293,3 @@ int avdtp_error_posix_errno(struct avdtp_error *err); struct btd_adapter *avdtp_get_adapter(struct avdtp *session); struct btd_device *avdtp_get_device(struct avdtp *session); - -void avdtp_set_device_disconnect(struct avdtp *session, gboolean dev_dc); diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c index 400af061..4f39622e 100644 --- a/profiles/audio/sink.c +++ b/profiles/audio/sink.c @@ -388,16 +388,13 @@ gboolean sink_new_stream(struct btd_service *service, struct avdtp *session, return TRUE; } -int sink_disconnect(struct btd_service *service, gboolean shutdown) +int sink_disconnect(struct btd_service *service) { struct sink *sink = btd_service_get_user_data(service); if (!sink->session) return -ENOTCONN; - if (shutdown) - avdtp_set_device_disconnect(sink->session, TRUE); - /* cancel pending connect */ if (sink->connect_id > 0) { a2dp_cancel(sink->connect_id); diff --git a/profiles/audio/sink.h b/profiles/audio/sink.h index 904a33d4..93c62a20 100644 --- a/profiles/audio/sink.h +++ b/profiles/audio/sink.h @@ -47,4 +47,4 @@ int sink_connect(struct btd_service *service); gboolean sink_new_stream(struct btd_service *service, struct avdtp *session, struct avdtp_stream *stream); gboolean sink_setup_stream(struct btd_service *service, struct avdtp *session); -int sink_disconnect(struct btd_service *service, gboolean shutdown); +int sink_disconnect(struct btd_service *service); diff --git a/profiles/audio/source.c b/profiles/audio/source.c index 24a43536..7b129b76 100644 --- a/profiles/audio/source.c +++ b/profiles/audio/source.c @@ -380,16 +380,13 @@ gboolean source_new_stream(struct btd_service *service, struct avdtp *session, return TRUE; } -int source_disconnect(struct btd_service *service, gboolean shutdown) +int source_disconnect(struct btd_service *service) { struct source *source = btd_service_get_user_data(service); if (!source->session) return -ENOTCONN; - if (shutdown) - avdtp_set_device_disconnect(source->session, TRUE); - /* cancel pending connect */ if (source->connect_id > 0) { a2dp_cancel(source->connect_id); diff --git a/profiles/audio/source.h b/profiles/audio/source.h index c16fb4bc..a014c68d 100644 --- a/profiles/audio/source.h +++ b/profiles/audio/source.h @@ -48,4 +48,4 @@ gboolean source_new_stream(struct btd_service *service, struct avdtp *session, struct avdtp_stream *stream); gboolean source_setup_stream(struct btd_service *service, struct avdtp *session); -int source_disconnect(struct btd_service *service, gboolean shutdown); +int source_disconnect(struct btd_service *service); diff --git a/profiles/health/hdp.c b/profiles/health/hdp.c index 7b4e7995..6203fa8a 100644 --- a/profiles/health/hdp.c +++ b/profiles/health/hdp.c @@ -1205,8 +1205,8 @@ static void mcl_connected(struct mcap_mcl *mcl, gpointer data) struct hdp_adapter *hdp_adapter = data; struct btd_device *device; - device = adapter_get_device(hdp_adapter->btd_adapter, &addr, - BDADDR_BREDR); + device = btd_adapter_get_device(hdp_adapter->btd_adapter, + &addr, BDADDR_BREDR); if (!device) return; hdp_device = create_health_device(device); @@ -1322,6 +1322,7 @@ static void release_adapter_instance(struct hdp_adapter *hdp_adapter) static gboolean update_adapter(struct hdp_adapter *hdp_adapter) { GError *err = NULL; + const bdaddr_t *src; if (applications == NULL) { release_adapter_instance(hdp_adapter); @@ -1331,8 +1332,9 @@ static gboolean update_adapter(struct hdp_adapter *hdp_adapter) if (hdp_adapter->mi != NULL) goto update; - hdp_adapter->mi = mcap_create_instance( - adapter_get_address(hdp_adapter->btd_adapter), + src = btd_adapter_get_address(hdp_adapter->btd_adapter); + + hdp_adapter->mi = mcap_create_instance(src, BT_IO_SEC_MEDIUM, 0, 0, mcl_connected, mcl_reconnected, mcl_disconnected, mcl_uncached, diff --git a/profiles/health/hdp_util.c b/profiles/health/hdp_util.c index 34e4671d..7de87a85 100644 --- a/profiles/health/hdp_util.c +++ b/profiles/health/hdp_util.c @@ -853,7 +853,7 @@ gboolean hdp_get_mdep(struct hdp_device *device, struct hdp_application *app, const bdaddr_t *dst; uuid_t uuid; - src = adapter_get_address(device_get_adapter(device->dev)); + src = btd_adapter_get_address(device_get_adapter(device->dev)); dst = device_get_address(device->dev); mdep_data = g_new0(struct get_mdep_data, 1); @@ -1080,7 +1080,7 @@ gboolean hdp_establish_mcl(struct hdp_device *device, const bdaddr_t *dst; uuid_t uuid; - src = adapter_get_address(device_get_adapter(device->dev)); + src = btd_adapter_get_address(device_get_adapter(device->dev)); dst = device_get_address(device->dev); conn_data = g_new0(struct conn_mcl_data, 1); @@ -1151,7 +1151,7 @@ gboolean hdp_get_dcpsm(struct hdp_device *device, hdp_continue_dcpsm_f func, const bdaddr_t *dst; uuid_t uuid; - src = adapter_get_address(device_get_adapter(device->dev)); + src = btd_adapter_get_address(device_get_adapter(device->dev)); dst = device_get_address(device->dev); dcpsm_data = g_new0(struct get_dcpsm_data, 1); diff --git a/profiles/input/device.c b/profiles/input/device.c index 65231612..62f6dbb2 100644 --- a/profiles/input/device.c +++ b/profiles/input/device.c @@ -667,7 +667,7 @@ static gboolean input_device_auto_reconnect(gpointer user_data) /* Stop the recurrent reconnection attempts if the device is reconnected * or is marked for removal. */ if (device_is_temporary(idev->device) || - device_is_connected(idev->device)) + btd_device_is_connected(idev->device)) return FALSE; /* Only attempt an auto-reconnect for at most 3 minutes (6 * 30s). */ @@ -713,7 +713,7 @@ static void input_device_enter_reconnect_mode(struct input_device *idev) /* If the device is temporary we are not required to reconnect with the * device. This is likely the case of a removing device. */ if (device_is_temporary(idev->device) || - device_is_connected(idev->device)) + btd_device_is_connected(idev->device)) return; if (idev->reconnect_timer > 0) @@ -811,8 +811,11 @@ static struct input_device *input_device_new(struct btd_service *service) struct input_device *idev; char name[HCI_MAX_NAME_LENGTH + 1]; + if (!rec) + return NULL; + idev = g_new0(struct input_device, 1); - bacpy(&idev->src, adapter_get_address(adapter)); + bacpy(&idev->src, btd_adapter_get_address(adapter)); bacpy(&idev->dst, device_get_address(device)); idev->service = btd_service_ref(service); idev->device = btd_device_ref(device); @@ -880,7 +883,7 @@ static struct input_device *find_device(const bdaddr_t *src, struct btd_device *device; struct btd_service *service; - device = adapter_find_device(adapter_find(src), dst); + device = btd_adapter_find_device(adapter_find(src), dst); if (device == NULL) return NULL; diff --git a/profiles/input/manager.c b/profiles/input/manager.c index 689ccddd..660043ee 100644 --- a/profiles/input/manager.c +++ b/profiles/input/manager.c @@ -46,13 +46,13 @@ static int hid_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { - return server_start(adapter_get_address(adapter)); + return server_start(btd_adapter_get_address(adapter)); } static void hid_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { - server_stop(adapter_get_address(adapter)); + server_stop(btd_adapter_get_address(adapter)); } static struct btd_profile input_profile = { diff --git a/profiles/input/server.c b/profiles/input/server.c index 21d4562b..f6f85a0d 100644 --- a/profiles/input/server.c +++ b/profiles/input/server.c @@ -63,6 +63,100 @@ static int server_cmp(gconstpointer s, gconstpointer user_data) return bacmp(&server->src, src); } +struct sixaxis_data { + GIOChannel *chan; + uint16_t psm; +}; + +static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data); + +static void sixaxis_sdp_cb(struct btd_device *dev, int err, void *user_data) +{ + struct sixaxis_data *data = user_data; + struct input_server *server; + GError *gerr = NULL; + const bdaddr_t *src; + GSList *l; + + DBG("err %d (%s)", err, strerror(-err)); + + if (err < 0) + goto fail; + + src = btd_adapter_get_address(device_get_adapter(dev)); + + l = g_slist_find_custom(servers, src, server_cmp); + if (!l) + goto fail; + + server = l->data; + + err = input_device_set_channel(src, device_get_address(dev), + data->psm, data->chan); + if (err < 0) + goto fail; + + if (server->confirm) { + if (!bt_io_accept(server->confirm, connect_event_cb, server, + NULL, &gerr)) { + error("bt_io_accept: %s", gerr->message); + g_error_free(gerr); + goto fail; + } + + g_io_channel_unref(server->confirm); + server->confirm = NULL; + } + + g_io_channel_unref(data->chan); + g_free(data); + + return; + +fail: + g_io_channel_shutdown(data->chan, TRUE, NULL); + g_io_channel_unref(data->chan); + g_free(data); +} + +static void sixaxis_browse_sdp(const bdaddr_t *src, const bdaddr_t *dst, + GIOChannel *chan, uint16_t psm) +{ + struct btd_device *device; + struct sixaxis_data *data; + + if (psm != L2CAP_PSM_HIDP_CTRL) + return; + + device = btd_adapter_find_device(adapter_find(src), dst); + if (!device) + return; + + data = g_new0(struct sixaxis_data, 1); + data->chan = g_io_channel_ref(chan); + data->psm = psm; + + device_discover_services(device); + device_wait_for_svc_complete(device, sixaxis_sdp_cb, data); +} + +static bool dev_is_sixaxis(const bdaddr_t *src, const bdaddr_t *dst) +{ + struct btd_device *device; + + device = btd_adapter_find_device(adapter_find(src), dst); + if (!device) + return false; + + if (btd_device_get_vendor(device) != 0x054c) + return false; + + if (btd_device_get_product(device) != 0x0268) + return false; + + return true; +} + static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data) { uint16_t psm; @@ -95,6 +189,11 @@ static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data) if (ret == 0) return; + if (ret == -ENOENT && dev_is_sixaxis(&src, &dst)) { + sixaxis_browse_sdp(&src, &dst, chan, psm); + return; + } + error("Refusing input device connect: %s (%d)", strerror(-ret), -ret); /* Send unplug virtual cable to unknown devices */ @@ -129,6 +228,9 @@ static void auth_callback(DBusError *derr, void *user_data) goto reject; } + if (!input_device_exists(&src, &dst) && dev_is_sixaxis(&src, &dst)) + return; + if (!bt_io_accept(server->confirm, connect_event_cb, server, NULL, &err)) { error("bt_io_accept: %s", err->message); @@ -175,7 +277,7 @@ static void confirm_event_cb(GIOChannel *chan, gpointer user_data) goto drop; } - if (!input_device_exists(&src, &dst)) { + if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst)) { error("Refusing connection from %s: unknown device", addr); goto drop; } diff --git a/profiles/network/common.c b/profiles/network/bnep.c index 0b291bdb..0a719a2e 100644 --- a/profiles/network/common.c +++ b/profiles/network/bnep.c @@ -43,9 +43,12 @@ #include <glib.h> #include "log.h" -#include "common.h" +#include "bnep.h" #include "lib/uuid.h" +#define CON_SETUP_RETRIES 3 +#define CON_SETUP_TO 9 + static int ctl; static struct { @@ -59,6 +62,35 @@ static struct { { NULL } }; +struct __service_16 { + uint16_t dst; + uint16_t src; +} __attribute__ ((packed)); + +struct bnep_conn { + GIOChannel *io; + uint16_t src; + uint16_t dst; + guint attempts; + guint setup_to; + void *data; + bnep_connect_cb conn_cb; +}; + +static void free_bnep_connect(struct bnep_conn *bc) +{ + if (!bc) + return; + + if (bc->io) { + g_io_channel_unref(bc->io); + bc->io = NULL; + } + + g_free(bc); + bc = NULL; +} + uint16_t bnep_service_id(const char *svc) { int i; @@ -149,9 +181,9 @@ int bnep_connadd(int sk, uint16_t role, char *dev) { struct bnep_connadd_req req; + memset(dev, 0, 16); memset(&req, 0, sizeof(req)); - strncpy(req.device, dev, 16); - req.device[15] = '\0'; + strcpy(req.device, "bnep%d"); req.sock = sk; req.role = role; if (ioctl(ctl, BNEPCONNADD, &req) < 0) { @@ -215,6 +247,165 @@ int bnep_if_down(const char *devname) return 0; } +static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct bnep_conn *bc = data; + struct bnep_control_rsp *rsp; + struct timeval timeo; + char pkt[BNEP_MTU]; + char iface[16]; + ssize_t r; + int sk; + + if (cond & G_IO_NVAL) + goto failed; + + if (bc->setup_to > 0) { + g_source_remove(bc->setup_to); + bc->setup_to = 0; + } + + if (cond & (G_IO_HUP | G_IO_ERR)) { + error("Hangup or error on l2cap server socket"); + goto failed; + } + + sk = g_io_channel_unix_get_fd(chan); + memset(pkt, 0, BNEP_MTU); + r = read(sk, pkt, sizeof(pkt) - 1); + if (r < 0) { + error("IO Channel read error"); + goto failed; + } + + if (r == 0) { + error("No packet received on l2cap socket"); + goto failed; + } + + errno = EPROTO; + + if ((size_t) r < sizeof(*rsp)) { + error("Packet received is not bnep type"); + goto failed; + } + + rsp = (void *) pkt; + if (rsp->type != BNEP_CONTROL) { + error("Packet received is not bnep type"); + goto failed; + } + + if (rsp->ctrl != BNEP_SETUP_CONN_RSP) + return TRUE; + + r = ntohs(rsp->resp); + if (r != BNEP_SUCCESS) { + error("bnep failed"); + goto failed; + } + + memset(&timeo, 0, sizeof(timeo)); + timeo.tv_sec = 0; + setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); + + sk = g_io_channel_unix_get_fd(bc->io); + if (bnep_connadd(sk, bc->src, iface)) { + error("bnep conn could not be added"); + goto failed; + } + + if (bnep_if_up(iface)) { + error("could not up %s", iface); + goto failed; + } + + bc->conn_cb(chan, iface, 0, bc->data); + free_bnep_connect(bc); + + return FALSE; + +failed: + bc->conn_cb(NULL, NULL, -EIO, bc->data); + free_bnep_connect(bc); + + return FALSE; +} + +static int bnep_setup_conn_req(struct bnep_conn *bc) +{ + struct bnep_setup_conn_req *req; + struct __service_16 *s; + unsigned char pkt[BNEP_MTU]; + int fd; + + /* Send request */ + req = (void *) pkt; + req->type = BNEP_CONTROL; + req->ctrl = BNEP_SETUP_CONN_REQ; + req->uuid_size = 2; /* 16bit UUID */ + s = (void *) req->service; + s->src = htons(bc->src); + s->dst = htons(bc->dst); + + fd = g_io_channel_unix_get_fd(bc->io); + if (write(fd, pkt, sizeof(*req) + sizeof(*s)) < 0) { + error("bnep connection req send failed: %s", strerror(errno)); + return -errno; + } + + bc->attempts++; + + return 0; +} + +static gboolean bnep_conn_req_to(gpointer user_data) +{ + struct bnep_conn *bc = user_data; + + if (bc->attempts == CON_SETUP_RETRIES) { + error("Too many bnep connection attempts"); + } else { + error("bnep connection setup TO, retrying..."); + if (bnep_setup_conn_req(bc) == 0) + return TRUE; + } + + bc->conn_cb(NULL, NULL, -ETIMEDOUT, bc->data); + free_bnep_connect(bc); + + return FALSE; +} + +int bnep_connect(int sk, uint16_t src, uint16_t dst, bnep_connect_cb conn_cb, + void *data) +{ + struct bnep_conn *bc; + int err; + + if (!conn_cb) + return -EINVAL; + + bc = g_new0(struct bnep_conn, 1); + bc->io = g_io_channel_unix_new(sk); + bc->attempts = 0; + bc->src = src; + bc->dst = dst; + bc->conn_cb = conn_cb; + bc->data = data; + + err = bnep_setup_conn_req(bc); + if (err < 0) + return err; + + bc->setup_to = g_timeout_add_seconds(CON_SETUP_TO, + bnep_conn_req_to, bc); + g_io_add_watch(bc->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + bnep_setup_cb, bc); + return 0; +} + int bnep_add_to_bridge(const char *devname, const char *bridge) { int ifindex; diff --git a/profiles/network/common.h b/profiles/network/bnep.h index 9a8caac0..9043e46f 100644 --- a/profiles/network/common.h +++ b/profiles/network/bnep.h @@ -35,3 +35,8 @@ int bnep_if_up(const char *devname); int bnep_if_down(const char *devname); int bnep_add_to_bridge(const char *devname, const char *bridge); int bnep_del_from_bridge(const char *devname, const char *bridge); + +typedef void (*bnep_connect_cb) (GIOChannel *chan, char *iface, int err, + void *data); +int bnep_connect(int sk, uint16_t src, uint16_t dst, bnep_connect_cb conn_cb, + void *data); diff --git a/profiles/network/connection.c b/profiles/network/connection.c index 960a1fe2..9aff3191 100644 --- a/profiles/network/connection.c +++ b/profiles/network/connection.c @@ -47,12 +47,10 @@ #include "service.h" #include "error.h" -#include "common.h" +#include "bnep.h" #include "connection.h" #define NETWORK_PEER_INTERFACE "org.bluez.Network1" -#define CON_SETUP_RETRIES 3 -#define CON_SETUP_TO 9 typedef enum { CONNECTED, @@ -73,16 +71,9 @@ struct network_conn { GIOChannel *io; guint dc_id; struct network_peer *peer; - guint attempt_cnt; - guint timeout_source; DBusMessage *connect; }; -struct __service_16 { - uint16_t dst; - uint16_t src; -} __attribute__ ((packed)); - static GSList *peers = NULL; static uint16_t get_service_id(struct btd_service *service) @@ -163,11 +154,6 @@ static void local_connect_cb(struct network_conn *nc, int err) static void cancel_connection(struct network_conn *nc, int err) { - if (nc->timeout_source > 0) { - g_source_remove(nc->timeout_source); - nc->timeout_source = 0; - } - btd_service_connecting_complete(nc->service, err); if (nc->connect) local_connect_cb(nc, err); @@ -200,83 +186,24 @@ static void disconnect_cb(struct btd_device *device, gboolean removal, connection_destroy(NULL, user_data); } -static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) +static void bnep_conn_cb(GIOChannel *chan, char *iface, int err, void *data) { struct network_conn *nc = data; - struct bnep_control_rsp *rsp; - struct timeval timeo; - char pkt[BNEP_MTU]; - ssize_t r; - int sk; const char *path; DBusConnection *conn; - DBG("cond %u", cond); - - if (cond & G_IO_NVAL) - return FALSE; - - if (nc->timeout_source > 0) { - g_source_remove(nc->timeout_source); - nc->timeout_source = 0; - } - - if (cond & (G_IO_HUP | G_IO_ERR)) { - error("Hangup or error on l2cap server socket"); - goto failed; - } - - sk = g_io_channel_unix_get_fd(chan); - - memset(pkt, 0, BNEP_MTU); - r = read(sk, pkt, sizeof(pkt) -1); - if (r < 0) { - error("IO Channel read error"); - goto failed; - } - - if (r == 0) { - error("No packet received on l2cap socket"); - goto failed; - } - - errno = EPROTO; - - if ((size_t) r < sizeof(*rsp)) { - error("Packet received is not bnep type"); - goto failed; - } - - rsp = (void *) pkt; - if (rsp->type != BNEP_CONTROL) { - error("Packet received is not bnep type"); - goto failed; - } - - if (rsp->ctrl != BNEP_SETUP_CONN_RSP) - return TRUE; - - r = ntohs(rsp->resp); - - if (r != BNEP_SUCCESS) { - error("bnep failed"); - goto failed; - } - - memset(&timeo, 0, sizeof(timeo)); - timeo.tv_sec = 0; - - setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); + DBG(""); - if (bnep_connadd(sk, BNEP_SVC_PANU, nc->dev)) { - error("%s could not be added", nc->dev); + if (err < 0) { + error("connect failed %s", strerror(-err)); goto failed; } - bnep_if_up(nc->dev); + info("%s connected", nc->dev); + memcpy(nc->dev, iface, sizeof(nc->dev)); btd_service_connecting_complete(nc->service, 0); + if (nc->connect) local_connect_cb(nc, 0); @@ -292,101 +219,30 @@ static gboolean bnep_setup_cb(GIOChannel *chan, GIOCondition cond, nc->state = CONNECTED; nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb, - nc, NULL); - - info("%s connected", nc->dev); - /* Start watchdog */ + nc, NULL); g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) bnep_watchdog_cb, nc); + bnep_watchdog_cb, nc); g_io_channel_unref(nc->io); nc->io = NULL; - return FALSE; + return; failed: cancel_connection(nc, -EIO); - - return FALSE; -} - -static int bnep_send_conn_req(struct network_conn *nc) -{ - struct bnep_setup_conn_req *req; - struct __service_16 *s; - unsigned char pkt[BNEP_MTU]; - int fd; - - DBG(""); - - /* Send request */ - req = (void *) pkt; - req->type = BNEP_CONTROL; - req->ctrl = BNEP_SETUP_CONN_REQ; - req->uuid_size = 2; /* 16bit UUID */ - s = (void *) req->service; - s->dst = htons(nc->id); - s->src = htons(BNEP_SVC_PANU); - - fd = g_io_channel_unix_get_fd(nc->io); - if (write(fd, pkt, sizeof(*req) + sizeof(*s)) < 0) { - int err = -errno; - error("bnep connection req send failed: %s", strerror(errno)); - return err; - } - - nc->attempt_cnt++; - - return 0; -} - -static gboolean bnep_conn_req_to(gpointer user_data) -{ - struct network_conn *nc; - - nc = user_data; - if (nc->attempt_cnt == CON_SETUP_RETRIES) { - error("Too many bnep connection attempts"); - } else { - error("bnep connection setup TO, retrying..."); - if (bnep_send_conn_req(nc) == 0) - return TRUE; - } - - cancel_connection(nc, -ETIMEDOUT); - - return FALSE; -} - -static int bnep_connect(struct network_conn *nc) -{ - int err; - - nc->attempt_cnt = 0; - - err = bnep_send_conn_req(nc); - if (err < 0) - return err; - - nc->timeout_source = g_timeout_add_seconds(CON_SETUP_TO, - bnep_conn_req_to, nc); - - g_io_add_watch(nc->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - bnep_setup_cb, nc); - - return 0; } static void connect_cb(GIOChannel *chan, GError *err, gpointer data) { struct network_conn *nc = data; - int perr; + int sk, perr; if (err) { error("%s", err->message); goto failed; } - perr = bnep_connect(nc); + sk = g_io_channel_unix_get_fd(nc->io); + perr = bnep_connect(sk, BNEP_SVC_PANU, nc->id, bnep_conn_cb, nc); if (perr < 0) { error("bnep connect(): %s (%d)", strerror(-perr), -perr); goto failed; @@ -452,7 +308,7 @@ int connection_connect(struct btd_service *service) if (nc->state != DISCONNECTED) return -EALREADY; - src = adapter_get_address(device_get_adapter(peer->device)); + src = btd_adapter_get_address(device_get_adapter(peer->device)); dst = device_get_address(peer->device); nc->io = bt_io_connect(connect_cb, nc, @@ -692,8 +548,6 @@ int connection_register(struct btd_service *service) nc = g_new0(struct network_conn, 1); nc->id = id; - memset(nc->dev, 0, sizeof(nc->dev)); - strcpy(nc->dev, "bnep%d"); nc->service = btd_service_ref(service); nc->state = DISCONNECTED; nc->peer = peer; diff --git a/profiles/network/manager.c b/profiles/network/manager.c index ab4224de..8ac2decb 100644 --- a/profiles/network/manager.c +++ b/profiles/network/manager.c @@ -43,7 +43,7 @@ #include "device.h" #include "profile.h" #include "service.h" -#include "common.h" +#include "bnep.h" #include "connection.h" #include "server.h" diff --git a/profiles/network/server.c b/profiles/network/server.c index 7b784e54..b3aab118 100644 --- a/profiles/network/server.c +++ b/profiles/network/server.c @@ -48,7 +48,7 @@ #include "error.h" #include "sdpd.h" -#include "common.h" +#include "bnep.h" #include "server.h" #define NETWORK_SERVER_INTERFACE "org.bluez.NetworkServer1" @@ -269,9 +269,6 @@ static int server_connadd(struct network_server *ns, char devname[16]; int err, nsk; - memset(devname, 0, sizeof(devname)); - strcpy(devname, "bnep%d"); - nsk = g_io_channel_unix_get_fd(session->io); err = bnep_connadd(nsk, dst_role, devname); if (err < 0) @@ -765,7 +762,7 @@ static struct network_adapter *create_adapter(struct btd_adapter *adapter) na->io = bt_io_listen(NULL, confirm_event, na, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, - adapter_get_address(adapter), + btd_adapter_get_address(adapter), BT_IO_OPT_PSM, BNEP_PSM, BT_IO_OPT_OMTU, BNEP_MTU, BT_IO_OPT_IMTU, BNEP_MTU, @@ -823,7 +820,7 @@ int server_register(struct btd_adapter *adapter, uint16_t id) path); done: - bacpy(&ns->src, adapter_get_address(adapter)); + bacpy(&ns->src, btd_adapter_get_address(adapter)); ns->id = id; ns->na = na; ns->record_id = 0; diff --git a/profiles/sap/server.c b/profiles/sap/server.c index 63314a77..119862df 100644 --- a/profiles/sap/server.c +++ b/profiles/sap/server.c @@ -1372,7 +1372,8 @@ int sap_server_register(struct btd_adapter *adapter) io = bt_io_listen(NULL, connect_confirm_cb, server, NULL, &gerr, - BT_IO_OPT_SOURCE_BDADDR, adapter_get_address(adapter), + BT_IO_OPT_SOURCE_BDADDR, + btd_adapter_get_address(adapter), BT_IO_OPT_CHANNEL, SAP_SERVER_CHANNEL, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH, BT_IO_OPT_MASTER, TRUE, diff --git a/src/adapter.c b/src/adapter.c index d904a563..94801038 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -690,7 +690,7 @@ int adapter_set_name(struct btd_adapter *adapter, const char *name) return set_name(adapter, name); } -struct btd_device *adapter_find_device(struct btd_adapter *adapter, +struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, const bdaddr_t *dst) { struct btd_device *device; @@ -1012,7 +1012,7 @@ static struct btd_device *adapter_create_device(struct btd_adapter *adapter, if (!device) return NULL; - device_set_temporary(device, TRUE); + btd_device_set_temporary(device, TRUE); adapter->devices = g_slist_append(adapter->devices, device); @@ -1072,7 +1072,7 @@ static void adapter_remove_device(struct btd_adapter *adapter, device_remove(dev, TRUE); } -struct btd_device *adapter_get_device(struct btd_adapter *adapter, +struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter, const bdaddr_t *addr, uint8_t addr_type) { @@ -1081,7 +1081,7 @@ struct btd_device *adapter_get_device(struct btd_adapter *adapter, if (!adapter) return NULL; - device = adapter_find_device(adapter, addr); + device = btd_adapter_find_device(adapter, addr); if (device) return device; @@ -2156,9 +2156,9 @@ static DBusMessage *remove_device(DBusConnection *conn, device = list->data; - device_set_temporary(device, TRUE); + btd_device_set_temporary(device, TRUE); - if (!device_is_connected(device)) { + if (!btd_device_is_connected(device)) { adapter_remove_device(adapter, device); return dbus_message_new_method_return(msg); } @@ -2196,11 +2196,6 @@ static const GDBusPropertyTable adapter_properties[] = { { } }; -struct adapter_keys { - struct btd_adapter *adapter; - GSList *keys; -}; - static int str2buf(const char *str, uint8_t *buf, size_t blen) { int i, dlen; @@ -2479,8 +2474,8 @@ static void load_devices(struct btd_adapter *adapter) { char filename[PATH_MAX + 1]; char srcaddr[18]; - struct adapter_keys keys = { adapter, NULL }; - struct adapter_keys ltks = { adapter, NULL }; + GSList *keys = NULL; + GSList *ltks = NULL; DIR *dir; struct dirent *entry; @@ -2514,11 +2509,11 @@ static void load_devices(struct btd_adapter *adapter) key_info = get_key_info(key_file, entry->d_name); if (key_info) - keys.keys = g_slist_append(keys.keys, key_info); + keys = g_slist_append(keys, key_info); ltk_info = get_ltk_info(key_file, entry->d_name); if (ltk_info) - ltks.keys = g_slist_append(ltks.keys, ltk_info); + ltks = g_slist_append(ltks, ltk_info); list = g_slist_find_custom(adapter->devices, entry->d_name, device_address_cmp); @@ -2532,12 +2527,12 @@ static void load_devices(struct btd_adapter *adapter) if (!device) goto free; - device_set_temporary(device, FALSE); + btd_device_set_temporary(device, FALSE); adapter->devices = g_slist_append(adapter->devices, device); /* TODO: register services from pre-loaded list of primaries */ - list = device_get_uuids(device); + list = btd_device_get_uuids(device); if (list) device_probe_profiles(device, list); @@ -2553,11 +2548,11 @@ free: closedir(dir); - load_link_keys(adapter, keys.keys, main_opts.debug_keys); - g_slist_free_full(keys.keys, g_free); + load_link_keys(adapter, keys, main_opts.debug_keys); + g_slist_free_full(keys, g_free); - load_ltks(adapter, ltks.keys); - g_slist_free_full(ltks.keys, g_free); + load_ltks(adapter, ltks); + g_slist_free_full(ltks, g_free); } int btd_adapter_block_address(struct btd_adapter *adapter, @@ -2726,7 +2721,8 @@ static void get_connections_complete(uint8_t status, uint16_t length, ba2str(&addr->bdaddr, address); DBG("Adding existing connection to %s", address); - device = adapter_get_device(adapter, &addr->bdaddr, addr->type); + device = btd_adapter_get_device(adapter, &addr->bdaddr, + addr->type); if (device) adapter_add_connection(adapter, device); } @@ -3990,7 +3986,7 @@ const char *adapter_get_path(struct btd_adapter *adapter) return adapter->path; } -const bdaddr_t *adapter_get_address(struct btd_adapter *adapter) +const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter) { return &adapter->bdaddr; } @@ -4087,16 +4083,11 @@ static void update_found_devices(struct btd_adapter *adapter, struct btd_device *dev; struct eir_data eir_data; char addr[18]; - int err; GSList *list; bool name_known; memset(&eir_data, 0, sizeof(eir_data)); - err = eir_parse(&eir_data, data, data_len); - if (err < 0) { - error("Error parsing EIR data: %s (%d)", strerror(-err), -err); - return; - } + eir_parse(&eir_data, data, data_len); /* Avoid creating LE device if it's not discoverable */ if (bdaddr_type != BDADDR_BREDR && @@ -4151,7 +4142,7 @@ static void update_found_devices(struct btd_adapter *adapter, name_known = device_name_known(dev); if (eir_data.name && (eir_data.name_complete || !name_known)) - device_set_name(dev, eir_data.name); + btd_device_device_set_name(dev, eir_data.name); if (eir_data.class != 0) device_set_class(dev, eir_data.class); @@ -4194,7 +4185,7 @@ connect_le: * connect_list stop passive scanning so that a connection * attempt to it can be made */ - if (device_is_le(dev) && !device_is_connected(dev) && + if (device_is_le(dev) && !btd_device_is_connected(dev) && g_slist_find(adapter->connect_list, dev)) { adapter->connect_le = dev; stop_passive_scanning(adapter); @@ -4350,9 +4341,12 @@ static void agent_auth_cb(struct agent *agent, DBusError *derr, void *user_data) { struct btd_adapter *adapter = user_data; - struct service_auth *auth = adapter->auths->head->data; + struct service_auth *auth = g_queue_pop_head(adapter->auths); - g_queue_pop_head(adapter->auths); + if (!auth) { + DBG("No pending authorization"); + return; + } auth->cb(derr, auth->user_data); @@ -4423,7 +4417,7 @@ static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst, struct btd_device *device; static guint id = 0; - device = adapter_find_device(adapter, dst); + device = btd_adapter_find_device(adapter, dst); if (!device) return 0; @@ -4629,7 +4623,7 @@ int btd_adapter_pincode_reply(struct btd_adapter *adapter, /* Since a pincode was requested, update the starting time to * the point where the pincode is provided. */ - device = adapter_find_device(adapter, bdaddr); + device = btd_adapter_find_device(adapter, bdaddr); device_bonding_restart_timer(device); id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_REPLY, @@ -4687,7 +4681,8 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length, ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s confirm_hint %u", adapter->dev_id, addr, ev->confirm_hint); - device = adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); + device = btd_adapter_get_device(adapter, &ev->addr.bdaddr, + ev->addr.type); if (!device) { error("Unable to get device object for %s", addr); return; @@ -4758,7 +4753,8 @@ static void user_passkey_request_callback(uint16_t index, uint16_t length, ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s", index, addr); - device = adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); + device = btd_adapter_get_device(adapter, &ev->addr.bdaddr, + ev->addr.type); if (!device) { error("Unable to get device object for %s", addr); return; @@ -4790,7 +4786,8 @@ static void user_passkey_notify_callback(uint16_t index, uint16_t length, ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s", index, addr); - device = adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); + device = btd_adapter_get_device(adapter, &ev->addr.bdaddr, + ev->addr.type); if (!device) { error("Unable to get device object for %s", addr); return; @@ -4872,7 +4869,8 @@ static void pin_code_request_callback(uint16_t index, uint16_t length, DBG("hci%u %s", adapter->dev_id, addr); - device = adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); + device = btd_adapter_get_device(adapter, &ev->addr.bdaddr, + ev->addr.type); if (!device) { error("Unable to get device object for %s", addr); return; @@ -4957,9 +4955,9 @@ static void bonding_complete(struct btd_adapter *adapter, struct btd_device *device; if (status == 0) - device = adapter_get_device(adapter, bdaddr, addr_type); + device = btd_adapter_get_device(adapter, bdaddr, addr_type); else - device = adapter_find_device(adapter, bdaddr); + device = btd_adapter_find_device(adapter, bdaddr); if (device != NULL) device_bonding_complete(device, status); @@ -4984,9 +4982,9 @@ static void bonding_attempt_complete(struct btd_adapter *adapter, addr_type, status); if (status == 0) - device = adapter_get_device(adapter, bdaddr, addr_type); + device = btd_adapter_get_device(adapter, bdaddr, addr_type); else - device = adapter_find_device(adapter, bdaddr); + device = btd_adapter_find_device(adapter, bdaddr); if (status == MGMT_STATUS_AUTH_FAILED && adapter->pincode_requested) { /* On faliure, issue a bonding_retry if possible. */ @@ -5145,7 +5143,7 @@ static void dev_disconnected(struct btd_adapter *adapter, DBG("Device %s disconnected, reason %u", dst, reason); - device = adapter_find_device(adapter, &addr->bdaddr); + device = btd_adapter_find_device(adapter, &addr->bdaddr); if (device) adapter_remove_connection(adapter, device); @@ -5220,7 +5218,7 @@ static void store_link_key(struct btd_adapter *adapter, char *str; int i; - ba2str(adapter_get_address(adapter), adapter_addr); + ba2str(btd_adapter_get_address(adapter), adapter_addr); ba2str(device_get_address(device), device_addr); snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr, @@ -5274,7 +5272,7 @@ static void new_link_key_callback(uint16_t index, uint16_t length, return; } - device = adapter_get_device(adapter, &addr->bdaddr, addr->type); + device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); if (!device) { error("Unable to get device object for %s", dst); return; @@ -5289,7 +5287,7 @@ static void new_link_key_callback(uint16_t index, uint16_t length, device_set_bonded(device, TRUE); if (device_is_temporary(device)) - device_set_temporary(device, FALSE); + btd_device_set_temporary(device, FALSE); } bonding_complete(adapter, &addr->bdaddr, addr->type, 0); @@ -5369,7 +5367,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length, DBG("hci%u new LTK for %s authenticated %u enc_size %u", adapter->dev_id, dst, ev->key.authenticated, ev->key.enc_size); - device = adapter_get_device(adapter, &addr->bdaddr, addr->type); + device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); if (!device) { error("Unable to get device object for %s", dst); return; @@ -5377,7 +5375,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length, if (ev->store_hint) { const struct mgmt_ltk_info *key = &ev->key; - const bdaddr_t *bdaddr = adapter_get_address(adapter); + const bdaddr_t *bdaddr = btd_adapter_get_address(adapter); store_longtermkey(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, key->master, @@ -5387,7 +5385,7 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length, device_set_bonded(device, TRUE); if (device_is_temporary(device)) - device_set_temporary(device, FALSE); + btd_device_set_temporary(device, FALSE); } if (ev->key.master) @@ -5720,7 +5718,8 @@ static void connected_callback(uint16_t index, uint16_t length, DBG("hci%u device %s connected eir_len %u", index, addr, eir_len); - device = adapter_get_device(adapter, &ev->addr.bdaddr, ev->addr.type); + device = btd_adapter_get_device(adapter, &ev->addr.bdaddr, + ev->addr.type); if (!device) { error("Unable to get device object for %s", addr); return; @@ -5737,7 +5736,7 @@ static void connected_callback(uint16_t index, uint16_t length, if (eir_data.name != NULL) { device_store_cached_name(device, eir_data.name); - device_set_name(device, eir_data.name); + btd_device_device_set_name(device, eir_data.name); } eir_data_free(&eir_data); @@ -5759,7 +5758,7 @@ static void device_blocked_callback(uint16_t index, uint16_t length, ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s blocked", index, addr); - device = adapter_find_device(adapter, &ev->addr.bdaddr); + device = btd_adapter_find_device(adapter, &ev->addr.bdaddr); if (device) device_block(device, TRUE); } @@ -5780,7 +5779,7 @@ static void device_unblocked_callback(uint16_t index, uint16_t length, ba2str(&ev->addr.bdaddr, addr); DBG("hci%u %s unblocked", index, addr); - device = adapter_find_device(adapter, &ev->addr.bdaddr); + device = btd_adapter_find_device(adapter, &ev->addr.bdaddr); if (device) device_unblock(device, FALSE, TRUE); } @@ -5802,7 +5801,7 @@ static void connect_failed_callback(uint16_t index, uint16_t length, DBG("hci%u %s status %u", index, addr, ev->status); - device = adapter_find_device(adapter, &ev->addr.bdaddr); + device = btd_adapter_find_device(adapter, &ev->addr.bdaddr); if (device) { /* If the device is in a bonding process cancel any auth request * sent to the agent before proceeding, but keep the bonding @@ -5848,15 +5847,15 @@ static void unpaired_callback(uint16_t index, uint16_t length, DBG("hci%u addr %s", index, addr); - device = adapter_find_device(adapter, &ev->addr.bdaddr); + device = btd_adapter_find_device(adapter, &ev->addr.bdaddr); if (!device) { warn("No device object for unpaired device %s", addr); return; } - device_set_temporary(device, TRUE); + btd_device_set_temporary(device, TRUE); - if (device_is_connected(device)) + if (btd_device_is_connected(device)) device_request_disconnect(device, NULL); else adapter_remove_device(adapter, device); diff --git a/src/adapter.h b/src/adapter.h index 80c5f772..de5b07dc 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -90,16 +90,16 @@ bool btd_adapter_get_connectable(struct btd_adapter *adapter); uint32_t btd_adapter_get_class(struct btd_adapter *adapter); const char *btd_adapter_get_name(struct btd_adapter *adapter); -struct btd_device *adapter_get_device(struct btd_adapter *adapter, +struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter, const bdaddr_t *addr, uint8_t addr_type); sdp_list_t *btd_adapter_get_services(struct btd_adapter *adapter); -struct btd_device *adapter_find_device(struct btd_adapter *adapter, +struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, const bdaddr_t *dst); const char *adapter_get_path(struct btd_adapter *adapter); -const bdaddr_t *adapter_get_address(struct btd_adapter *adapter); +const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter); int adapter_set_name(struct btd_adapter *adapter, const char *name); int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec); diff --git a/src/agent.c b/src/agent.c index 7880ba6a..4c63cb97 100644 --- a/src/agent.c +++ b/src/agent.c @@ -319,6 +319,9 @@ static void simple_agent_reply(DBusPendingCall *call, void *user_data) * is only called after a reply has been received */ message = dbus_pending_call_steal_reply(call); + /* Protect from the callback freeing the agent */ + agent_ref(agent); + dbus_error_init(&err); if (dbus_set_error_from_message(&err, message)) { DBG("agent error reply: %s, %s", err.name, err.message); @@ -330,6 +333,7 @@ static void simple_agent_reply(DBusPendingCall *call, void *user_data) agent_cancel(agent); dbus_message_unref(message); dbus_error_free(&err); + agent_unref(agent); return; } @@ -350,6 +354,7 @@ done: agent->request = NULL; agent_request_free(req, TRUE); + agent_unref(agent); } static int agent_call_authorize_service(struct agent_request *req, @@ -421,6 +426,9 @@ static void pincode_reply(DBusPendingCall *call, void *user_data) * is only called after a reply has been received */ message = dbus_pending_call_steal_reply(call); + /* Protect from the callback freeing the agent */ + agent_ref(agent); + dbus_error_init(&err); if (dbus_set_error_from_message(&err, message)) { error("Agent %s replied with an error: %s, %s", @@ -460,6 +468,7 @@ done: dbus_pending_call_cancel(req->call); agent->request = NULL; agent_request_free(req, TRUE); + agent_unref(agent); } static int pincode_request_new(struct agent_request *req, const char *device_path, diff --git a/src/attrib-server.c b/src/attrib-server.c index 2861a005..a6f1066e 100644 --- a/src/attrib-server.c +++ b/src/attrib-server.c @@ -156,7 +156,7 @@ static int adapter_cmp_addr(gconstpointer a, gconstpointer b) const struct gatt_server *server = a; const bdaddr_t *bdaddr = b; - return bacmp(adapter_get_address(server->adapter), bdaddr); + return bacmp(btd_adapter_get_address(server->adapter), bdaddr); } static int adapter_cmp(gconstpointer a, gconstpointer b) @@ -1137,7 +1137,7 @@ guint attrib_channel_attach(GAttrib *attrib) channel->server = server; - device = adapter_find_device(server->adapter, &channel->dst); + device = btd_adapter_find_device(server->adapter, &channel->dst); if (device == NULL) { error("Device object not found for attrib server"); g_free(channel); @@ -1310,7 +1310,7 @@ int btd_adapter_gatt_server_start(struct btd_adapter *adapter) server = g_new0(struct gatt_server, 1); server->adapter = btd_adapter_ref(adapter); - addr = adapter_get_address(server->adapter); + addr = btd_adapter_get_address(server->adapter); /* BR/EDR socket */ server->l2cap_io = bt_io_listen(connect_event, NULL, NULL, NULL, &gerr, diff --git a/src/bluetooth.ver b/src/bluetooth.ver index b71c70df..214fa8a6 100644 --- a/src/bluetooth.ver +++ b/src/bluetooth.ver @@ -5,6 +5,8 @@ info; error; debug; + baswap; + ba2str; local: *; }; diff --git a/src/device.c b/src/device.c index 82b66e65..18543ee2 100644 --- a/src/device.c +++ b/src/device.c @@ -263,7 +263,7 @@ static gboolean store_device_info_cb(gpointer user_data) device->store_id = 0; - ba2str(adapter_get_address(device->adapter), adapter_addr); + ba2str(btd_adapter_get_address(device->adapter), adapter_addr); ba2str(&device->bdaddr, device_addr); snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr, device_addr); @@ -409,7 +409,7 @@ void device_store_cached_name(struct btd_device *dev, const char *name) return; } - ba2str(adapter_get_address(dev->adapter), s_addr); + ba2str(btd_adapter_get_address(dev->adapter), s_addr); ba2str(&dev->bdaddr, d_addr); snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", s_addr, d_addr); filename[PATH_MAX] = '\0'; @@ -470,7 +470,7 @@ static void browse_request_cancel(struct browse_req *req) struct btd_device *device = req->device; struct btd_adapter *adapter = device->adapter; - bt_cancel_discovery(adapter_get_address(adapter), &device->bdaddr); + bt_cancel_discovery(btd_adapter_get_address(adapter), &device->bdaddr); attio_cleanup(device); @@ -809,7 +809,7 @@ static void set_trust(GDBusPendingPropertySet id, gboolean value, void *data) { struct btd_device *device = data; - device_set_trusted(device, value); + btd_device_set_trusted(device, value); g_dbus_pending_property_success(id); } @@ -997,7 +997,7 @@ int device_block(struct btd_device *device, gboolean update_only) store_device_info(device); - device_set_temporary(device, FALSE); + btd_device_set_temporary(device, FALSE); g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Blocked"); @@ -1100,7 +1100,8 @@ void device_request_disconnect(struct btd_device *device, DBusMessage *msg) } if (!device->connected) { - g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID); + if (msg) + g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID); return; } @@ -1151,7 +1152,7 @@ static void device_profile_connected(struct btd_device *dev, DBG("%s %s (%d)", profile->name, strerror(-err), -err); if (!err) - device_set_temporary(dev, FALSE); + btd_device_set_temporary(dev, FALSE); if (dev->pending == NULL) return; @@ -1307,7 +1308,7 @@ static DBusMessage *connect_profiles(struct btd_device *dev, DBusMessage *msg, if (!btd_adapter_get_powered(dev->adapter)) return btd_error_not_ready(msg); - device_set_temporary(dev, FALSE); + btd_device_set_temporary(dev, FALSE); if (!dev->svc_resolved) goto resolve_services; @@ -1354,10 +1355,10 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg, if (device_is_le(dev)) { int err; - if (device_is_connected(dev)) + if (btd_device_is_connected(dev)) return dbus_message_new_method_return(msg); - device_set_temporary(dev, FALSE); + btd_device_set_temporary(dev, FALSE); dev->disable_auto_connect = FALSE; @@ -1433,11 +1434,17 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg, if (!service) return btd_error_invalid_args(msg); + if (dev->disconnect) + return btd_error_in_progress(msg); + + dev->disconnect = dbus_message_ref(msg); + err = btd_service_disconnect(service); - if (err == 0) { - dev->disconnect = dbus_message_ref(msg); + if (err == 0) return NULL; - } + + dbus_message_unref(dev->disconnect); + dev->disconnect = NULL; if (err == -ENOTSUP) return btd_error_not_supported(msg); @@ -1608,7 +1615,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, uint8_t io_cap; int err; - device_set_temporary(device, FALSE); + btd_device_set_temporary(device, FALSE); if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) return btd_error_invalid_args(msg); @@ -1644,7 +1651,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, * channel first and only then start pairing (there's code for * this in the ATT connect callback) */ - if (device_is_le(device) && !device_is_connected(device)) + if (device_is_le(device) && !btd_device_is_connected(device)) err = device_connect_le(device); else err = adapter_create_bonding(adapter, &device->bdaddr, @@ -1790,7 +1797,7 @@ static const GDBusPropertyTable device_properties[] = { { } }; -gboolean device_is_connected(struct btd_device *device) +gboolean btd_device_is_connected(struct btd_device *device) { return device->connected; } @@ -2182,7 +2189,7 @@ struct btd_device *device_create_from_storage(struct btd_adapter *adapter, if (device == NULL) return NULL; - src = adapter_get_address(adapter); + src = btd_adapter_get_address(adapter); ba2str(src, srcaddr); load_info(device, srcaddr, address, key_file); @@ -2207,7 +2214,7 @@ struct btd_device *device_create(struct btd_adapter *adapter, return NULL; device->bdaddr_type = bdaddr_type; - sba = adapter_get_address(adapter); + sba = btd_adapter_get_address(adapter); ba2str(sba, src); str = load_cached_name(device, src, dst); @@ -2230,7 +2237,7 @@ char *btd_device_get_storage_path(struct btd_device *device, return NULL; } - ba2str(adapter_get_address(device->adapter), srcaddr); + ba2str(btd_adapter_get_address(device->adapter), srcaddr); ba2str(&device->bdaddr, dstaddr); if (!filename) @@ -2240,7 +2247,7 @@ char *btd_device_get_storage_path(struct btd_device *device, filename); } -void device_set_name(struct btd_device *device, const char *name) +void btd_device_device_set_name(struct btd_device *device, const char *name) { if (strncmp(name, device->name, MAX_NAME_LENGTH) == 0) return; @@ -2284,6 +2291,8 @@ void device_set_class(struct btd_device *device, uint32_t class) g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Class"); + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "Icon"); } uint32_t btd_device_get_class(struct btd_device *device) @@ -2341,7 +2350,7 @@ static void delete_folder_tree(const char *dirname) static void device_remove_stored(struct btd_device *device) { - const bdaddr_t *src = adapter_get_address(device->adapter); + const bdaddr_t *src = btd_adapter_get_address(device->adapter); uint8_t dst_type = device->bdaddr_type; char adapter_addr[18]; char device_addr[18]; @@ -2473,7 +2482,7 @@ static gboolean record_has_uuid(const sdp_record_t *rec, return FALSE; } -GSList *device_get_uuids(struct btd_device *device) +GSList *btd_device_get_uuids(struct btd_device *device) { return device->uuids; } @@ -2716,7 +2725,7 @@ static void update_bredr_services(struct browse_req *req, sdp_list_t *recs) char *data; gsize length = 0; - ba2str(adapter_get_address(device->adapter), srcaddr); + ba2str(btd_adapter_get_address(device->adapter), srcaddr); ba2str(&device->bdaddr, dstaddr); if (!device->temporary) { @@ -2960,7 +2969,7 @@ static void browse_cb(sdp_list_t *recs, int err, gpointer user_data) /* Search for mandatory uuids */ if (uuid_list[req->search_uuid]) { sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); - bt_search_service(adapter_get_address(adapter), + bt_search_service(btd_adapter_get_address(adapter), &device->bdaddr, &uuid, browse_cb, user_data, NULL); return; @@ -2993,7 +3002,7 @@ static void store_services(struct btd_device *device) if (prim_uuid == NULL) return; - ba2str(adapter_get_address(adapter), src_addr); + ba2str(btd_adapter_get_address(adapter), src_addr); ba2str(&device->bdaddr, dst_addr); snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", src_addr, @@ -3111,7 +3120,7 @@ static void register_all_services(struct browse_req *req, GSList *services) { struct btd_device *device = req->device; - device_set_temporary(device, FALSE); + btd_device_set_temporary(device, FALSE); update_gatt_services(req, device->primaries, services); g_slist_free_full(device->primaries, g_free); @@ -3369,7 +3378,8 @@ int device_connect_le(struct btd_device *dev) * pairing finishes */ io = bt_io_connect(att_connect_cb, attcb, NULL, &gerr, - BT_IO_OPT_SOURCE_BDADDR, adapter_get_address(adapter), + BT_IO_OPT_SOURCE_BDADDR, + btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, BT_IO_OPT_DEST_BDADDR, &dev->bdaddr, BT_IO_OPT_DEST_TYPE, dev->bdaddr_type, @@ -3452,7 +3462,7 @@ static int device_browse_primary(struct btd_device *device, DBusMessage *msg) device->att_io = bt_io_connect(att_connect_cb, attcb, NULL, NULL, BT_IO_OPT_SOURCE_BDADDR, - adapter_get_address(adapter), + btd_adapter_get_address(adapter), BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, BT_IO_OPT_DEST_BDADDR, &device->bdaddr, BT_IO_OPT_DEST_TYPE, device->bdaddr_type, @@ -3498,8 +3508,8 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg) req->device = device; sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); - err = bt_search_service(adapter_get_address(adapter), &device->bdaddr, - &uuid, browse_cb, req, NULL); + err = bt_search_service(btd_adapter_get_address(adapter), + &device->bdaddr, &uuid, browse_cb, req, NULL); if (err < 0) { browse_request_free(req); return err; @@ -3522,6 +3532,23 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg) return err; } +int device_discover_services(struct btd_device *device) +{ + int err; + + if (device_is_bredr(device)) + err = device_browse_sdp(device, NULL); + else + err = device_browse_primary(device, NULL); + + if (err == 0 && device->discov_timer) { + g_source_remove(device->discov_timer); + device->discov_timer = 0; + } + + return err; +} + struct btd_adapter *device_get_adapter(struct btd_device *device) { if (!device) @@ -3548,7 +3575,7 @@ gboolean device_is_temporary(struct btd_device *device) return device->temporary; } -void device_set_temporary(struct btd_device *device, gboolean temporary) +void btd_device_set_temporary(struct btd_device *device, gboolean temporary) { if (!device) return; @@ -3564,7 +3591,7 @@ void device_set_temporary(struct btd_device *device, gboolean temporary) device->temporary = temporary; } -void device_set_trusted(struct btd_device *device, gboolean trusted) +void btd_device_set_trusted(struct btd_device *device, gboolean trusted) { if (!device) return; @@ -4279,7 +4306,7 @@ static sdp_list_t *read_device_records(struct btd_device *device) sdp_list_t *recs = NULL; sdp_record_t *rec; - ba2str(adapter_get_address(device->adapter), local); + ba2str(btd_adapter_get_address(device->adapter), local); ba2str(&device->bdaddr, peer); snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer); diff --git a/src/device.h b/src/device.h index deec8d0e..3a33cb2b 100644 --- a/src/device.h +++ b/src/device.h @@ -33,7 +33,7 @@ struct btd_device *device_create_from_storage(struct btd_adapter *adapter, char *btd_device_get_storage_path(struct btd_device *device, const char *filename); -void device_set_name(struct btd_device *device, const char *name); +void btd_device_device_set_name(struct btd_device *device, const char *name); void device_store_cached_name(struct btd_device *dev, const char *name); void device_get_name(struct btd_device *device, char *name, size_t len); bool device_name_known(struct btd_device *device); @@ -46,7 +46,7 @@ uint16_t btd_device_get_version(struct btd_device *device); void device_remove(struct btd_device *device, gboolean remove_stored); int device_address_cmp(gconstpointer a, gconstpointer b); int device_bdaddr_cmp(gconstpointer a, gconstpointer b); -GSList *device_get_uuids(struct btd_device *device); +GSList *btd_device_get_uuids(struct btd_device *device); void device_probe_profiles(struct btd_device *device, GSList *profiles); const sdp_record_t *btd_device_get_record(struct btd_device *device, const char *uuid); @@ -69,12 +69,12 @@ gboolean device_is_paired(struct btd_device *device); gboolean device_is_bonded(struct btd_device *device); gboolean device_is_trusted(struct btd_device *device); void device_set_paired(struct btd_device *device, gboolean paired); -void device_set_temporary(struct btd_device *device, gboolean temporary); -void device_set_trusted(struct btd_device *device, gboolean trusted); +void btd_device_set_temporary(struct btd_device *device, gboolean temporary); +void btd_device_set_trusted(struct btd_device *device, gboolean trusted); void device_set_bonded(struct btd_device *device, gboolean bonded); void device_set_legacy(struct btd_device *device, bool legacy); void device_set_rssi(struct btd_device *device, int8_t rssi); -gboolean device_is_connected(struct btd_device *device); +gboolean btd_device_is_connected(struct btd_device *device); bool device_is_retrying(struct btd_device *device); void device_bonding_complete(struct btd_device *device, uint8_t status); gboolean device_is_bonding(struct btd_device *device, const char *sender); @@ -131,5 +131,7 @@ bool device_remove_svc_complete_callback(struct btd_device *dev, struct btd_service *btd_device_get_service(struct btd_device *dev, const char *remote_uuid); +int device_discover_services(struct btd_device *device); + void btd_device_init(void); void btd_device_cleanup(void); @@ -129,7 +129,7 @@ static char *name2utf8(const uint8_t *name, uint8_t len) return g_strdup(utf8_name); } -int eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len) +void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len) { uint16_t len = 0; @@ -138,7 +138,7 @@ int eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len) /* No EIR data to parse */ if (eir_data == NULL) - return 0; + return; while (len < eir_len - 1) { uint8_t field_len = eir_data[0]; @@ -226,8 +226,6 @@ int eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len) eir_data += field_len + 1; } - - return 0; } int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len) @@ -248,7 +246,7 @@ int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len) /* optional OOB EIR data */ if (eir_len > 0) - return eir_parse(eir, eir_data, eir_len); + eir_parse(eir, eir_data, eir_len); return 0; } @@ -52,7 +52,7 @@ struct eir_data { }; void eir_data_free(struct eir_data *eir); -int eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len); +void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len); int eir_parse_oob(struct eir_data *eir, uint8_t *eir_data, uint16_t eir_len); int eir_create_oob(const bdaddr_t *addr, const char *name, uint32_t cod, const uint8_t *hash, const uint8_t *randomizer, diff --git a/src/profile.c b/src/profile.c index d8cbe6a8..3c0d27c5 100644 --- a/src/profile.c +++ b/src/profile.c @@ -417,6 +417,11 @@ </sequence> \ </sequence> \ </attribute> \ + <attribute id=\"0x0005\"> \ + <sequence> \ + <uuid value=\"0x1002\" /> \ + </sequence> \ + </attribute> \ <attribute id=\"0x0009\"> \ <sequence> \ <sequence> \ @@ -458,6 +463,11 @@ </sequence> \ </sequence> \ </attribute> \ + <attribute id=\"0x0005\"> \ + <sequence> \ + <uuid value=\"0x1002\" /> \ + </sequence> \ + </attribute> \ <attribute id=\"0x0009\"> \ <sequence> \ <sequence> \ @@ -496,6 +506,11 @@ </sequence> \ </sequence> \ </attribute> \ + <attribute id=\"0x0005\"> \ + <sequence> \ + <uuid value=\"0x1002\" /> \ + </sequence> \ + </attribute> \ <attribute id=\"0x0009\"> \ <sequence> \ <sequence> \ @@ -703,7 +718,7 @@ static void ext_io_destroy(gpointer p) } if (ext_io->resolving) - bt_cancel_discovery(adapter_get_address(ext_io->adapter), + bt_cancel_discovery(btd_adapter_get_address(ext_io->adapter), device_get_address(ext_io->device)); if (ext_io->adapter) @@ -1032,7 +1047,7 @@ static struct ext_io *create_conn(struct ext_io *server, GIOChannel *io, GIOCondition cond; char addr[18]; - device = adapter_find_device(server->adapter, dst); + device = btd_adapter_find_device(server->adapter, dst); if (device == NULL) { ba2str(dst, addr); error("%s device %s not found", server->ext->name, addr); @@ -1242,7 +1257,7 @@ static uint32_t ext_start_servers(struct ext_profile *ext, io = bt_io_listen(connect, confirm, l2cap, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, - adapter_get_address(adapter), + btd_adapter_get_address(adapter), BT_IO_OPT_MODE, ext->mode, BT_IO_OPT_PSM, psm, BT_IO_OPT_SEC_LEVEL, ext->sec_level, @@ -1280,7 +1295,7 @@ static uint32_t ext_start_servers(struct ext_profile *ext, io = bt_io_listen(connect, confirm, rfcomm, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, - adapter_get_address(adapter), + btd_adapter_get_address(adapter), BT_IO_OPT_CHANNEL, chan, BT_IO_OPT_SEC_LEVEL, ext->sec_level, BT_IO_OPT_INVALID); @@ -1565,7 +1580,7 @@ static void record_cb(sdp_list_t *recs, int err, gpointer user_data) goto failed; } - err = connect_io(conn, adapter_get_address(conn->adapter), + err = connect_io(conn, btd_adapter_get_address(conn->adapter), device_get_address(conn->device)); if (err < 0) { error("Connecting %s failed: %s", ext->name, strerror(-err)); @@ -1622,10 +1637,10 @@ static int ext_connect_dev(struct btd_service *service) if (ext->remote_psm || ext->remote_chan) { conn->psm = ext->remote_psm; conn->chan = ext->remote_chan; - err = connect_io(conn, adapter_get_address(adapter), + err = connect_io(conn, btd_adapter_get_address(adapter), device_get_address(dev)); } else { - err = resolve_service(conn, adapter_get_address(adapter), + err = resolve_service(conn, btd_adapter_get_address(adapter), device_get_address(dev)); } diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c index acfd7c34..505ac794 100644 --- a/tools/l2cap-tester.c +++ b/tools/l2cap-tester.c @@ -315,6 +315,31 @@ static const struct l2cap_server_data l2cap_server_nval_cid_test2 = { .expect_rsp_len = sizeof(l2cap_nval_cfg_rsp), }; +static const struct l2cap_client_data le_client_connect_success_test = { + .client_psm = 0x0080, + .server_psm = 0x0080, +}; + +static const struct l2cap_client_data le_client_connect_nval_psm_test = { + .client_psm = 0x0080, + .expect_err = ECONNREFUSED, +}; + +static const uint8_t le_connect_req[] = { 0x80, 0x00, /* PSM */ + 0x41, 0x00, /* SCID */ + 0x20, 0x00, /* MTU */ + 0x20, 0x00, /* MPS */ + 0x05, 0x00, /* Credits */ +}; + +static const struct l2cap_server_data le_server_success_test = { + .server_psm = 0x0080, + .send_req_code = BT_L2CAP_PDU_LE_CONN_REQ, + .send_req = le_connect_req, + .send_req_len = sizeof(le_connect_req), + .expect_rsp_code = BT_L2CAP_PDU_LE_CONN_RSP, +}; + static void client_connectable_complete(uint16_t opcode, uint8_t status, const void *param, uint8_t len, void *user_data) @@ -737,5 +762,14 @@ int main(int argc, char *argv[]) &l2cap_server_nval_cid_test2, setup_powered_server, test_server); + test_l2cap_le("L2CAP LE Client - Success", + &le_client_connect_success_test, + setup_powered_client, test_connect); + test_l2cap_le("L2CAP LE Client - Invalid PSM", + &le_client_connect_nval_psm_test, + setup_powered_client, test_connect); + test_l2cap_le("L2CAP LE Server - Success", &le_server_success_test, + setup_powered_server, test_server); + return tester_run(); } diff --git a/tools/l2test.c b/tools/l2test.c index c5c87631..9a7c809a 100644 --- a/tools/l2test.c +++ b/tools/l2test.c @@ -28,6 +28,7 @@ #endif #include <stdio.h> +#include <stdbool.h> #include <errno.h> #include <ctype.h> #include <fcntl.h> @@ -48,6 +49,9 @@ #define NIBBLE_TO_ASCII(c) ((c) < 0x0a ? (c) + 0x30 : (c) + 0x57) +#define BREDR_DEFAULT_PSM 0x1011 +#define LE_DEFAULT_PSM 0x0080 + /* Test modes */ enum { SEND, @@ -87,7 +91,7 @@ static long buffer_size = 2048; /* Default addr and psm and cid */ static bdaddr_t bdaddr; -static unsigned short psm = 0x1011; +static unsigned short psm = 0; static unsigned short cid = 0; /* Default number of frames to send (-1 = infinite) */ @@ -258,6 +262,37 @@ static void hexdump(unsigned char *s, unsigned long l) } } +static int getopts(int sk, struct l2cap_options *opts, bool connected) +{ + socklen_t optlen; + int err; + + memset(opts, 0, sizeof(*opts)); + + if (bdaddr_type == BDADDR_BREDR || cid) { + optlen = sizeof(*opts); + return getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts, &optlen); + } + + optlen = sizeof(opts->imtu); + err = getsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, &opts->imtu, &optlen); + if (err < 0 || !connected) + return err; + + optlen = sizeof(opts->omtu); + return getsockopt(sk, SOL_BLUETOOTH, BT_SNDMTU, &opts->omtu, &optlen); +} + +static int setopts(int sk, struct l2cap_options *opts) +{ + if (bdaddr_type == BDADDR_BREDR || cid) + return setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, opts, + sizeof(opts)); + + return setsockopt(sk, SOL_BLUETOOTH, BT_RCVMTU, &opts->imtu, + sizeof(opts->imtu)); +} + static int do_connect(char *svr) { struct sockaddr_l2 addr; @@ -290,12 +325,9 @@ static int do_connect(char *svr) } /* Get default options */ - memset(&opts, 0, sizeof(opts)); - optlen = sizeof(opts); - - if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) { + if (getopts(sk, &opts, false) < 0) { syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)", - strerror(errno), errno); + strerror(errno), errno); goto error; } @@ -308,7 +340,7 @@ static int do_connect(char *svr) opts.txwin_size = txwin_size; opts.max_tx = max_transmit; - if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) { + if (setopts(sk, &opts) < 0) { syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)", strerror(errno), errno); goto error; @@ -400,10 +432,7 @@ static int do_connect(char *svr) } /* Get current options */ - memset(&opts, 0, sizeof(opts)); - optlen = sizeof(opts); - - if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) { + if (getopts(sk, &opts, true) < 0) { syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)", strerror(errno), errno); goto error; @@ -531,10 +560,7 @@ static void do_listen(void (*handler)(int sk)) } /* Get default options */ - memset(&opts, 0, sizeof(opts)); - optlen = sizeof(opts); - - if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) { + if (getopts(sk, &opts, false) < 0) { syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)", strerror(errno), errno); goto error; @@ -550,7 +576,7 @@ static void do_listen(void (*handler)(int sk)) opts.txwin_size = txwin_size; opts.max_tx = max_transmit; - if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) { + if (setopts(sk, &opts) < 0) { syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)", strerror(errno), errno); goto error; @@ -629,10 +655,7 @@ static void do_listen(void (*handler)(int sk)) } /* Get current options */ - memset(&opts, 0, sizeof(opts)); - optlen = sizeof(opts); - - if (getsockopt(nsk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) { + if (getopts(nsk, &opts, true) < 0) { syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)", strerror(errno), errno); if (!defer_setup) { @@ -1525,6 +1548,13 @@ int main(int argc, char *argv[]) } } + if (!psm) { + if (bdaddr_type == BDADDR_BREDR) + psm = BREDR_DEFAULT_PSM; + else + psm = LE_DEFAULT_PSM; + } + if (need_addr && !(argc - optind)) { usage(); exit(1); diff --git a/tools/obex-server-tool.c b/tools/obex-server-tool.c index e37c56fd..86c22713 100644 --- a/tools/obex-server-tool.c +++ b/tools/obex-server-tool.c @@ -427,7 +427,7 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if (option_root && chdir(option_root) > 0) { + if (option_root && chdir(option_root) < 0) { perror("chdir:"); exit(EXIT_FAILURE); } diff --git a/tools/sdptool.c b/tools/sdptool.c index f985b1e8..b4b65ec3 100644 --- a/tools/sdptool.c +++ b/tools/sdptool.c @@ -1174,18 +1174,15 @@ static int add_sp(sdp_session_t *session, svc_info_t *si) sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(0, &root_uuid); sdp_set_browse_groups(&record, root); - sdp_list_free(root, 0); sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID); svclass_id = sdp_list_append(0, &sp_uuid); sdp_set_service_classes(&record, svclass_id); - sdp_list_free(svclass_id, 0); sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID); profile.version = 0x0100; profiles = sdp_list_append(0, &profile); sdp_set_profile_descs(&record, profiles); - sdp_list_free(profiles, 0); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(0, &l2cap); @@ -1226,6 +1223,9 @@ end: sdp_list_free(proto[1], 0); sdp_list_free(apseq, 0); sdp_list_free(aproto, 0); + sdp_list_free(root, 0); + sdp_list_free(svclass_id, 0); + sdp_list_free(profiles, 0); return ret; } @@ -1813,7 +1813,10 @@ end: sdp_list_free(proto[1], 0); sdp_list_free(proto[2], 0); sdp_list_free(apseq, 0); + sdp_list_free(pfseq, 0); sdp_list_free(aproto, 0); + sdp_list_free(root, 0); + sdp_list_free(svclass_id, NULL); return ret; } @@ -1885,7 +1888,10 @@ end: sdp_list_free(proto[1], 0); sdp_list_free(proto[2], 0); sdp_list_free(apseq, 0); + sdp_list_free(pfseq, 0); sdp_list_free(aproto, 0); + sdp_list_free(root, 0); + sdp_list_free(svclass_id, 0); return ret; } diff --git a/tools/smp-tester.c b/tools/smp-tester.c index 46dc65a7..05d620a6 100644 --- a/tools/smp-tester.c +++ b/tools/smp-tester.c @@ -29,10 +29,12 @@ #include <unistd.h> #include <errno.h> #include <stdbool.h> +#include <sys/socket.h> #include <glib.h> #include "lib/bluetooth.h" +#include "lib/hci.h" #include "lib/mgmt.h" #include "monitor/bt.h" @@ -42,6 +44,40 @@ #include "src/shared/mgmt.h" #include "src/shared/hciemu.h" +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif + +#ifndef AF_ALG +#define AF_ALG 38 +#define PF_ALG AF_ALG + +#include <linux/types.h> + +struct sockaddr_alg { + __u16 salg_family; + __u8 salg_type[14]; + __u32 salg_feat; + __u32 salg_mask; + __u8 salg_name[64]; +}; + +struct af_alg_iv { + __u32 ivlen; + __u8 iv[0]; +}; + +#define ALG_SET_KEY 1 +#define ALG_SET_IV 2 +#define ALG_SET_OP 3 + +#define ALG_OP_DECRYPT 0 +#define ALG_OP_ENCRYPT 1 + +#else +#include <linux/if_alg.h> +#endif + #define SMP_CID 0x0006 struct test_data { @@ -51,23 +87,203 @@ struct test_data { struct hciemu *hciemu; enum hciemu_type hciemu_type; unsigned int io_id; + uint8_t ia[6]; + uint8_t ia_type; + uint8_t ra[6]; + uint8_t ra_type; + bool out; uint16_t handle; + size_t counter; + int alg_sk; + uint8_t smp_tk[16]; + uint8_t smp_prnd[16]; + uint8_t smp_rrnd[16]; + uint8_t smp_pcnf[16]; + uint8_t smp_preq[7]; + uint8_t smp_prsp[7]; + uint8_t smp_ltk[16]; }; -struct smp_server_data { - const void *send_req; - uint16_t send_req_len; - const void *expect_rsp; - uint16_t expect_rsp_len; +struct smp_req_rsp { + const void *send; + uint16_t send_len; + const void *expect; + uint16_t expect_len; }; -struct smp_client_data { - const void *expect_req; - uint16_t expect_req_len; - const void *send_rsp; - uint16_t send_rsp_len; +struct smp_data { + const struct smp_req_rsp *req; + size_t req_count; }; +static int alg_setup(void) +{ + struct sockaddr_alg salg; + int sk; + + sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); + if (sk < 0) { + fprintf(stderr, "socket(AF_ALG): %s\n", strerror(errno)); + return -1; + } + + memset(&salg, 0, sizeof(salg)); + salg.salg_family = AF_ALG; + strcpy((char *) salg.salg_type, "skcipher"); + strcpy((char *) salg.salg_name, "ecb(aes)"); + + if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) { + fprintf(stderr, "bind(AF_ALG): %s\n", strerror(errno)); + close(sk); + return -1; + } + + return sk; +} + +static int alg_new(int alg_sk, const uint8_t *key) +{ + int sk; + + if (setsockopt(alg_sk, SOL_ALG, ALG_SET_KEY, key, 16) < 0) { + tester_warn("setsockopt(ALG_SET_KEY): %s", strerror(errno)); + return -1; + } + + sk = accept4(alg_sk, NULL, 0, SOCK_CLOEXEC); + if (sk < 0) { + tester_warn("accept4(AF_ALG): %s", strerror(errno)); + return -1; + } + + return sk; +} + +static int alg_encrypt(int sk, uint8_t in[16], uint8_t out[16]) +{ + __u32 alg_op = ALG_OP_ENCRYPT; + char cbuf[CMSG_SPACE(sizeof(alg_op))]; + struct cmsghdr *cmsg; + struct msghdr msg; + struct iovec iov; + int ret; + + memset(cbuf, 0, sizeof(cbuf)); + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_ALG; + cmsg->cmsg_type = ALG_SET_OP; + cmsg->cmsg_len = CMSG_LEN(sizeof(alg_op)); + memcpy(CMSG_DATA(cmsg), &alg_op, sizeof(alg_op)); + + iov.iov_base = in; + iov.iov_len = 16; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ret = sendmsg(sk, &msg, 0); + if (ret < 0) { + tester_warn("sendmsg(AF_ALG): %s", strerror(errno)); + return ret; + } + + ret = read(sk, out, 16); + if (ret < 0) + tester_warn("read(AF_ALG): %s", strerror(errno)); + + return 0; +} + +static int smp_e(uint8_t key[16], uint8_t in[16], uint8_t out[16]) +{ + struct test_data *data = tester_get_data(); + int sk, err; + + sk = alg_new(data->alg_sk, key); + if (sk < 0) + return sk; + + err = alg_encrypt(sk, in, out); + + close(sk); + + return err; +} + +static inline void swap128(const uint8_t src[16], uint8_t dst[16]) +{ + int i; + for (i = 0; i < 16; i++) + dst[15 - i] = src[i]; +} + +static inline void swap56(const uint8_t src[7], uint8_t dst[7]) +{ + int i; + for (i = 0; i < 7; i++) + dst[6 - i] = src[i]; +} + +typedef struct { + uint64_t a, b; +} u128; + +static inline void u128_xor(u128 *r, const u128 *p, const u128 *q) +{ + r->a = p->a ^ q->a; + r->b = p->b ^ q->b; +} + +static int smp_c1(uint8_t r[16], uint8_t res[16]) +{ + struct test_data *data = tester_get_data(); + uint8_t p1[16], p2[16]; + int err; + + memset(p1, 0, 16); + + /* p1 = pres || preq || _rat || _iat */ + swap56(data->smp_prsp, p1); + swap56(data->smp_preq, p1 + 7); + p1[14] = data->ra_type; + p1[15] = data->ia_type; + + memset(p2, 0, 16); + + /* p2 = padding || ia || ra */ + baswap((bdaddr_t *) (p2 + 4), (bdaddr_t *) data->ia); + baswap((bdaddr_t *) (p2 + 10), (bdaddr_t *) data->ra); + + /* res = r XOR p1 */ + u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); + + /* res = e(k, res) */ + err = smp_e(data->smp_tk, res, res); + if (err) + return err; + + /* res = res XOR p2 */ + u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); + + /* res = e(k, res) */ + return smp_e(data->smp_tk, res, res); +} + +static int smp_s1(uint8_t r1[16], uint8_t r2[16], uint8_t res[16]) +{ + struct test_data *data = tester_get_data(); + + memcpy(res, r1 + 8, 8); + memcpy(res + 8, r2 + 8, 8); + + return smp_e(data->smp_tk, res, res); +} + static void mgmt_debug(const char *str, void *user_data) { const char *prefix = user_data; @@ -180,6 +396,13 @@ static void test_pre_setup(const void *test_data) { struct test_data *data = tester_get_data(); + data->alg_sk = alg_setup(); + if (data->alg_sk < 0) { + tester_warn("Failed to setup AF_ALG socket"); + tester_pre_setup_failed(); + return; + } + data->mgmt = mgmt_new_default(); if (!data->mgmt) { tester_warn("Failed to setup management interface"); @@ -203,6 +426,11 @@ static void test_post_teardown(const void *test_data) data->io_id = 0; } + if (data->alg_sk >= 0) { + close(data->alg_sk); + data->alg_sk = -1; + } + hciemu_unref(data->hciemu); data->hciemu = NULL; } @@ -217,11 +445,11 @@ static void test_data_free(void *test_data) #define test_smp(name, data, setup, func) \ do { \ struct test_data *user; \ - user = malloc(sizeof(struct test_data)); \ + user = calloc(1, sizeof(struct test_data)); \ if (!user) \ break; \ user->hciemu_type = HCIEMU_TYPE_LE; \ - user->io_id = 0; \ + user->alg_sk = -1; \ user->test_data = data; \ tester_add_full(name, data, \ test_pre_setup, setup, func, NULL, \ @@ -231,21 +459,27 @@ static void test_data_free(void *test_data) static const uint8_t smp_nval_req_1[] = { 0x0b, 0x00 }; static const uint8_t smp_nval_req_1_rsp[] = { 0x05, 0x07 }; -static const struct smp_server_data smp_server_nval_req_1_test = { - .send_req = smp_nval_req_1, - .send_req_len = sizeof(smp_nval_req_1), - .expect_rsp = smp_nval_req_1_rsp, - .expect_rsp_len = sizeof(smp_nval_req_1_rsp), +static const struct smp_req_rsp nval_req_1[] = { + { smp_nval_req_1, sizeof(smp_nval_req_1), + smp_nval_req_1_rsp, sizeof(smp_nval_req_1_rsp) }, +}; + +static const struct smp_data smp_server_nval_req_1_test = { + .req = nval_req_1, + .req_count = G_N_ELEMENTS(nval_req_1), }; static const uint8_t smp_nval_req_2[7] = { 0x01 }; static const uint8_t smp_nval_req_2_rsp[] = { 0x05, 0x06 }; -static const struct smp_server_data smp_server_nval_req_2_test = { - .send_req = smp_nval_req_2, - .send_req_len = sizeof(smp_nval_req_2), - .expect_rsp = smp_nval_req_2_rsp, - .expect_rsp_len = sizeof(smp_nval_req_2_rsp), +static const struct smp_req_rsp srv_nval_req_1[] = { + { smp_nval_req_2, sizeof(smp_nval_req_2), + smp_nval_req_2_rsp, sizeof(smp_nval_req_2_rsp) }, +}; + +static const struct smp_data smp_server_nval_req_2_test = { + .req = srv_nval_req_1, + .req_count = G_N_ELEMENTS(srv_nval_req_1), }; static const uint8_t smp_basic_req_1[] = { 0x01, /* Pairing Request */ @@ -255,28 +489,45 @@ static const uint8_t smp_basic_req_1[] = { 0x01, /* Pairing Request */ 0x10, /* Max key size */ 0x00, /* Init. key dist. */ 0x01, /* Rsp. key dist. */ - }; static const uint8_t smp_basic_req_1_rsp[] = { 0x02, /* Pairing Response */ 0x03, /* NoInputNoOutput */ 0x00, /* OOB Flag */ - 0x00, /* SMP auth none */ + 0x01, /* Bonding - no MITM */ 0x10, /* Max key size */ 0x00, /* Init. key dist. */ - 0x00, /* Rsp. key dist. */ + 0x01, /* Rsp. key dist. */ +}; + +static const uint8_t smp_confirm_req_1[17] = { 0x03 }; +static const uint8_t smp_random_req_1[17] = { 0x04 }; + +static const struct smp_req_rsp srv_basic_req_1[] = { + { smp_basic_req_1, sizeof(smp_basic_req_1), + smp_basic_req_1_rsp, sizeof(smp_basic_req_1_rsp) }, + { smp_confirm_req_1, sizeof(smp_confirm_req_1), + smp_confirm_req_1, sizeof(smp_confirm_req_1) }, + { smp_random_req_1, sizeof(smp_random_req_1), + smp_random_req_1, sizeof(smp_random_req_1) }, +}; +static const struct smp_data smp_server_basic_req_1_test = { + .req = srv_basic_req_1, + .req_count = G_N_ELEMENTS(srv_basic_req_1), }; -static const struct smp_server_data smp_server_basic_req_1_test = { - .send_req = smp_basic_req_1, - .send_req_len = sizeof(smp_basic_req_1), - .expect_rsp = smp_basic_req_1_rsp, - .expect_rsp_len = sizeof(smp_basic_req_1_rsp), +static const struct smp_req_rsp cli_basic_req_1[] = { + { NULL, 0, smp_basic_req_1, sizeof(smp_basic_req_1) }, + { smp_basic_req_1_rsp, sizeof(smp_basic_req_1_rsp), + smp_confirm_req_1, sizeof(smp_confirm_req_1) }, + { smp_confirm_req_1, sizeof(smp_confirm_req_1), + smp_random_req_1, sizeof(smp_random_req_1) }, + { smp_random_req_1, sizeof(smp_random_req_1), NULL, 0 }, }; -static const struct smp_client_data smp_client_basic_req_1_test = { - .expect_req = smp_basic_req_1, - .expect_req_len = sizeof(smp_basic_req_1), +static const struct smp_data smp_client_basic_req_1_test = { + .req = cli_basic_req_1, + .req_count = G_N_ELEMENTS(cli_basic_req_1), }; static void client_connectable_complete(uint16_t opcode, uint8_t status, @@ -341,63 +592,182 @@ static void pair_device_complete(uint8_t status, uint16_t length, tester_test_passed(); } -static void smp_server(const void *data, uint16_t len, void *user_data) +static const void *get_pdu(const uint8_t *data) { struct test_data *test_data = tester_get_data(); - const struct smp_client_data *cli = test_data->test_data; + uint8_t opcode = data[0]; + static uint8_t buf[17]; + uint8_t res[16]; + + switch (opcode) { + case 0x01: /* Pairing Request */ + memcpy(test_data->smp_preq, data, sizeof(test_data->smp_preq)); + break; + case 0x02: /* Pairing Response */ + memcpy(test_data->smp_prsp, data, sizeof(test_data->smp_prsp)); + break; + case 0x03: /* Pairing Confirm */ + buf[0] = data[0]; + smp_c1(test_data->smp_prnd, res); + swap128(res, &buf[1]); + return buf; + case 0x04: /* Pairing Random */ + buf[0] = data[0]; + swap128(test_data->smp_prnd, &buf[1]); + return buf; + default: + break; + } + + return data; +} + +static bool verify_random(const uint8_t rnd[16]) +{ + struct test_data *data = tester_get_data(); + uint8_t confirm[16], res[16], key[16]; + int err; + + err = smp_c1(data->smp_rrnd, res); + if (err < 0) + return false; - tester_print("Received SMP request"); + swap128(res, confirm); - if (!cli->expect_req) { + if (memcmp(data->smp_pcnf, confirm, sizeof(data->smp_pcnf) != 0)) { + tester_warn("Confirmation values don't match"); + return false; + } + + if (data->out) { + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + smp_s1(data->smp_rrnd, data->smp_prnd, key); + swap128(key, data->smp_ltk); + bthost_le_start_encrypt(bthost, data->handle, data->smp_ltk); + } else { + smp_s1(data->smp_prnd, data->smp_rrnd, key); + swap128(key, data->smp_ltk); + } + + return true; +} + +static void smp_server(const void *data, uint16_t len, void *user_data) +{ + struct test_data *test_data = user_data; + struct bthost *bthost = hciemu_client_get_host(test_data->hciemu); + const struct smp_data *smp = test_data->test_data; + const struct smp_req_rsp *req; + const void *pdu; + uint8_t opcode; + + if (len < 1) { + tester_warn("Received too small SMP PDU"); + goto failed; + } + + opcode = *((const uint8_t *) data); + + tester_print("Received SMP opcode 0x%02x", opcode); + + if (test_data->counter >= smp->req_count) { tester_test_passed(); return; } - if (cli->expect_req_len != len) { - tester_warn("Unexpected SMP request length (%u != %u)", - len, cli->expect_req_len); + req = &smp->req[test_data->counter++]; + if (!req->expect) + goto next; + + if (req->expect_len != len) { + tester_warn("Unexpected SMP PDU length (%u != %u)", + len, req->expect_len); goto failed; } - if (memcmp(cli->expect_req, data, len) != 0) { - tester_warn("Unexpected SMP request"); - goto failed; + switch (opcode) { + case 0x01: /* Pairing Request */ + memcpy(test_data->smp_preq, data, sizeof(test_data->smp_preq)); + break; + case 0x02: /* Pairing Response */ + memcpy(test_data->smp_prsp, data, sizeof(test_data->smp_prsp)); + break; + case 0x03: /* Pairing Confirm */ + memcpy(test_data->smp_pcnf, data + 1, 16); + goto next; + case 0x04: /* Pairing Random */ + swap128(data + 1, test_data->smp_rrnd); + if (!verify_random(data + 1)) + goto failed; + goto next; + default: + break; } - if (cli->send_rsp) { - struct bthost *bthost; + if (memcmp(req->expect, data, len) != 0) { + tester_warn("Unexpected SMP PDU"); + goto failed; + } - bthost = hciemu_client_get_host(test_data->hciemu); - bthost_send_cid(bthost, test_data->handle, SMP_CID, - cli->send_rsp, cli->send_rsp_len); +next: + if (smp->req_count == test_data->counter) { + tester_test_passed(); return; } - tester_test_passed(); + req = &smp->req[test_data->counter]; + + pdu = get_pdu(req->send); + bthost_send_cid(bthost, test_data->handle, SMP_CID, pdu, + req->send_len); + + if (!req->expect) + tester_test_passed(); + return; failed: tester_test_failed(); } -static void smp_server_new_conn(uint16_t handle, void *user_data) +static void smp_new_conn(uint16_t handle, void *user_data) { struct test_data *data = user_data; + const struct smp_data *smp = data->test_data; struct bthost *bthost = hciemu_client_get_host(data->hciemu); + const struct smp_req_rsp *req; + const void *pdu; - tester_print("New server connection with handle 0x%04x", handle); + tester_print("New SMP client connection with handle 0x%04x", handle); data->handle = handle; - bthost_add_cid_hook(bthost, handle, SMP_CID, smp_server, NULL); + bthost_add_cid_hook(bthost, handle, SMP_CID, smp_server, data); + + if (smp->req_count == data->counter) + return; + + req = &smp->req[data->counter]; + + if (!req->send) + return; + + tester_print("Sending SMP PDU"); + + pdu = get_pdu(req->send); + bthost_send_cid(bthost, handle, SMP_CID, pdu, req->send_len); } -static void test_client(const void *test_data) +static void init_bdaddr(struct test_data *data) { - struct test_data *data = tester_get_data(); - const uint8_t *client_bdaddr; - struct mgmt_cp_pair_device cp; - struct bthost *bthost; + const uint8_t *master_bdaddr, *client_bdaddr; + + master_bdaddr = hciemu_get_master_bdaddr(data->hciemu); + if (!master_bdaddr) { + tester_warn("No master bdaddr"); + tester_test_failed(); + return; + } client_bdaddr = hciemu_get_client_bdaddr(data->hciemu); if (!client_bdaddr) { @@ -406,10 +776,30 @@ static void test_client(const void *test_data) return; } + data->ia_type = LE_PUBLIC_ADDRESS; + data->ra_type = LE_PUBLIC_ADDRESS; + + if (data->out) { + memcpy(data->ia, client_bdaddr, sizeof(data->ia)); + memcpy(data->ra, master_bdaddr, sizeof(data->ra)); + } else { + memcpy(data->ia, master_bdaddr, sizeof(data->ia)); + memcpy(data->ra, client_bdaddr, sizeof(data->ra)); + } +} + +static void test_client(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct mgmt_cp_pair_device cp; + struct bthost *bthost; + + init_bdaddr(data); + bthost = hciemu_client_get_host(data->hciemu); - bthost_set_connect_cb(bthost, smp_server_new_conn, data); + bthost_set_connect_cb(bthost, smp_new_conn, data); - memcpy(&cp.addr.bdaddr, client_bdaddr, sizeof(bdaddr_t)); + memcpy(&cp.addr.bdaddr, data->ra, sizeof(data->ra)); cp.addr.type = BDADDR_LE_PUBLIC; cp.io_cap = 0x03; /* NoInputNoOutput */ @@ -441,6 +831,8 @@ static void setup_powered_server(const void *test_data) mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, sizeof(param), param, NULL, NULL, NULL); + mgmt_send(data->mgmt, MGMT_OP_SET_PAIRABLE, data->mgmt_index, + sizeof(param), param, NULL, NULL, NULL); mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index, sizeof(param), param, NULL, NULL, NULL); mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, @@ -448,72 +840,19 @@ static void setup_powered_server(const void *test_data) NULL, NULL); } -static void smp_client(const void *data, uint16_t len, void *user_data) -{ - struct test_data *test_data = user_data; - const struct smp_server_data *srv = test_data->test_data; - - tester_print("SMP client received response"); - - if (!srv->expect_rsp) { - tester_test_passed(); - return; - } - - if (srv->expect_rsp_len != len) { - tester_warn("Unexpected SMP response length (%u != %u)", - len, srv->expect_rsp_len); - goto failed; - } - - if (memcmp(srv->expect_rsp, data, len) != 0) { - tester_warn("Unexpected SMP response"); - goto failed; - } - - tester_test_passed(); - return; - -failed: - tester_test_failed(); -} - -static void smp_client_new_conn(uint16_t handle, void *user_data) -{ - struct test_data *data = user_data; - const struct smp_server_data *srv = data->test_data; - struct bthost *bthost = hciemu_client_get_host(data->hciemu); - - tester_print("New SMP client connection with handle 0x%04x", handle); - - bthost_add_cid_hook(bthost, handle, SMP_CID, smp_client, data); - - if (!srv->send_req) - return; - - tester_print("Sending SMP Request from client"); - - bthost_send_cid(bthost, handle, SMP_CID, srv->send_req, - srv->send_req_len); -} - static void test_server(const void *test_data) { struct test_data *data = tester_get_data(); - const uint8_t *master_bdaddr; struct bthost *bthost; - master_bdaddr = hciemu_get_master_bdaddr(data->hciemu); - if (!master_bdaddr) { - tester_warn("No master bdaddr"); - tester_test_failed(); - return; - } + data->out = true; + + init_bdaddr(data); bthost = hciemu_client_get_host(data->hciemu); - bthost_set_connect_cb(bthost, smp_client_new_conn, data); + bthost_set_connect_cb(bthost, smp_new_conn, data); - bthost_hci_connect(bthost, master_bdaddr, BDADDR_LE_PUBLIC); + bthost_hci_connect(bthost, data->ra, BDADDR_LE_PUBLIC); } int main(int argc, char *argv[]) diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c new file mode 100644 index 00000000..fb555b80 --- /dev/null +++ b/unit/test-avdtp.c @@ -0,0 +1,1090 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <unistd.h> +#include <stdlib.h> +#include <stdbool.h> +#include <inttypes.h> +#include <string.h> +#include <fcntl.h> +#include <sys/socket.h> + +#include <glib.h> + +#include "src/shared/util.h" +#include "src/log.h" +#include "android/avdtp.h" + +struct test_pdu { + bool valid; + const uint8_t *data; + size_t size; +}; + +struct test_data { + char *test_name; + struct test_pdu *pdu_list; +}; + +#define data(args...) ((const unsigned char[]) { args }) + +#define raw_pdu(args...) \ + { \ + .valid = true, \ + .data = data(args), \ + .size = sizeof(data(args)), \ + } + +#define define_test(name, function, args...) \ + do { \ + const struct test_pdu pdus[] = { \ + args, { }, { } \ + }; \ + static struct test_data data; \ + data.test_name = g_strdup(name); \ + data.pdu_list = g_malloc(sizeof(pdus)); \ + memcpy(data.pdu_list, pdus, sizeof(pdus)); \ + g_test_add_data_func(name, &data, function); \ + } while (0) + +struct context { + GMainLoop *main_loop; + struct avdtp *session; + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + guint source; + int fd; + int mtu; + gboolean pending_open; + gboolean pending_suspend; + unsigned int pdu_offset; + const struct test_data *data; +}; + +static void test_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + g_print("%s%s\n", prefix, str); +} + +static void test_free(gconstpointer user_data) +{ + const struct test_data *data = user_data; + + g_free(data->test_name); + g_free(data->pdu_list); +} + +static gboolean context_quit(gpointer user_data) +{ + struct context *context = user_data; + + g_main_loop_quit(context->main_loop); + + return FALSE; +} + +static gboolean send_pdu(gpointer user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + ssize_t len; + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + len = write(context->fd, pdu->data, pdu->size); + + if (g_test_verbose()) + util_hexdump('<', pdu->data, len, test_debug, "AVDTP: "); + + g_assert(len == (ssize_t) pdu->size); + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-02-C")) + g_timeout_add_seconds(1, context_quit, context); + + return FALSE; +} + +static void context_process(struct context *context) +{ + if (!context->data->pdu_list[context->pdu_offset].valid) { + context_quit(context); + return; + } + + g_idle_add(send_pdu, context); +} + +static gboolean transport_open(struct avdtp_stream *stream) +{ + int fd; + + fd = open("/dev/null", O_RDWR, 0); + if (fd < 0) + g_assert_not_reached(); + + return avdtp_stream_set_transport(stream, fd, 672, 672); +} + +static gboolean test_handler(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_pdu *pdu; + unsigned char buf[512]; + ssize_t len; + int fd; + + pdu = &context->data->pdu_list[context->pdu_offset++]; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) + return FALSE; + + fd = g_io_channel_unix_get_fd(channel); + + len = read(fd, buf, sizeof(buf)); + + g_assert(len > 0); + + if (g_test_verbose()) + util_hexdump('>', buf, len, test_debug, "AVDTP: "); + + g_assert((size_t) len == pdu->size); + + g_assert(memcmp(buf, pdu->data, pdu->size) == 0); + + if (context->pending_open) { + context->pending_open = FALSE; + g_assert(transport_open(context->stream)); + } + + if (context->pending_suspend) { + int ret; + + context->pending_suspend = FALSE; + ret = avdtp_suspend(context->session, context->stream); + g_assert_cmpint(ret, ==, 0); + } + + context_process(context); + + return TRUE; +} + +static struct context *create_context(uint16_t version, gconstpointer data) +{ + struct context *context = g_new0(struct context, 1); + GIOChannel *channel; + int err, sv[2]; + + context->main_loop = g_main_loop_new(NULL, FALSE); + g_assert(context->main_loop); + + err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv); + g_assert(err == 0); + + context->session = avdtp_new(sv[0], 672, 672, version); + g_assert(context->session != NULL); + + channel = g_io_channel_unix_new(sv[1]); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + context->source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + test_handler, context); + g_assert(context->source > 0); + + g_io_channel_unref(channel); + + context->fd = sv[1]; + context->data = data; + + return context; +} + +static void execute_context(struct context *context) +{ + g_main_loop_run(context->main_loop); + + g_source_remove(context->source); + avdtp_unref(context->session); + + g_main_loop_unref(context->main_loop); + + test_free(context->data); + g_free(context); +} + +static gboolean sep_getcap_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + gboolean get_all, GSList **caps, + uint8_t *err, void *user_data) +{ + struct avdtp_service_capability *media_transport, *media_codec; + struct avdtp_media_codec_capability *codec_caps; + uint8_t cap[4] = { 0xff, 0xff, 2, 64 }; + + *caps = NULL; + + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, + NULL, 0); + + *caps = g_slist_append(*caps, media_transport); + + codec_caps = g_malloc0(sizeof(*codec_caps) + sizeof(cap)); + codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO; + codec_caps->media_codec_type = 0x00; + memcpy(codec_caps->data, cap, sizeof(cap)); + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps, + sizeof(*codec_caps) + sizeof(cap)); + + *caps = g_slist_append(*caps, media_codec); + g_free(codec_caps); + + return TRUE; +} + +static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data) +{ + struct context *context = user_data; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-18-C")) { + *err = 0xc0; + return FALSE; + } + + context->pending_open = TRUE; + context->stream = stream; + + return TRUE; +} + +static gboolean sep_setconf_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + GSList *caps, + avdtp_set_configuration_cb cb, + void *user_data) +{ + struct context *context = user_data; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-09-C")) + return FALSE; + + cb(session, stream, NULL); + + return TRUE; +} + +static gboolean sep_start_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err, + void *user_data) +{ + struct context *context = user_data; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-21-C")) { + *err = 0xc0; + return FALSE; + } + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-25-C")) + context->pending_suspend = TRUE; + + return TRUE; +} + +static gboolean sep_close_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err, + void *user_data) +{ + struct context *context = user_data; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-24-C")) { + *err = 0xc0; + return FALSE; + } + + return TRUE; +} + +static gboolean sep_suspend_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err, + void *user_data) +{ + struct context *context = user_data; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-27-C")) { + *err = 0xc0; + return FALSE; + } + + return TRUE; +} + +static struct avdtp_sep_ind sep_ind = { + .get_capability = sep_getcap_ind, + .set_configuration = sep_setconf_ind, + .open = sep_open_ind, + .close = sep_close_ind, + .start = sep_start_ind, + .suspend = sep_suspend_ind, +}; + +static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data) +{ + struct context *context = user_data; + int ret; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-07-C")) { + g_assert(err != NULL); + g_assert_cmpint(avdtp_error_error_code(err), ==, 0x13); + context_quit(context); + return; + } + + g_assert(err == NULL); + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-11-C") || + g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-10-C")) + ret = avdtp_get_configuration(session, stream); + else if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-23-C")) + ret = avdtp_abort(session, stream); + else + ret = avdtp_open(session, stream); + + g_assert_cmpint(ret, ==, 0); +} + +static void sep_getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data) +{ + struct context *context = user_data; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-10-C")) { + g_assert(err != NULL); + g_assert_cmpint(avdtp_error_error_code(err), ==, 0x12); + } else + g_assert(err == NULL); + + context_quit(context); +} + +static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + int ret; + + g_assert(err == NULL); + + g_assert(transport_open(stream)); + + ret = avdtp_start(session, stream); + g_assert_cmpint(ret, ==, 0); +} + +static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct context *context = user_data; + int ret; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-19-C") || + g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-22-C")) { + g_assert(err != NULL); + g_assert_cmpint(avdtp_error_error_code(err), ==, 0x31); + context_quit(context); + return; + } + + g_assert(err == NULL); + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-19-C")) + ret = avdtp_close(session, stream, FALSE); + else + ret = avdtp_suspend(session, stream); + + g_assert_cmpint(ret, ==, 0); +} + +static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct context *context = user_data; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-25-C")) { + g_assert(err != NULL); + g_assert_cmpint(avdtp_error_error_code(err), ==, 0x31); + context_quit(context); + } +} + +static struct avdtp_sep_cfm sep_cfm = { + .set_configuration = sep_setconf_cfm, + .get_configuration = sep_getconf_cfm, + .open = sep_open_cfm, + .start = sep_start_cfm, + .suspend = sep_suspend_cfm, +}; + +static void test_server(gconstpointer data) +{ + struct context *context = create_context(0x0100, data); + struct avdtp_local_sep *sep; + + sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO, + 0x00, FALSE, &sep_ind, &sep_cfm, + context); + + g_idle_add(send_pdu, context); + + execute_context(context); + + avdtp_unregister_sep(sep); +} + +static void test_server_1_3(gconstpointer data) +{ + struct context *context = create_context(0x0103, data); + struct avdtp_local_sep *sep; + + sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO, + 0x00, TRUE, &sep_ind, NULL, context); + + g_idle_add(send_pdu, context); + + execute_context(context); + + avdtp_unregister_sep(sep); +} + +static void test_server_0_sep(gconstpointer data) +{ + struct context *context = create_context(0x0100, data); + + g_idle_add(send_pdu, context); + + execute_context(context); +} + +static void discover_cb(struct avdtp *session, GSList *seps, + struct avdtp_error *err, void *user_data) +{ + struct context *context = user_data; + struct avdtp_stream *stream; + struct avdtp_remote_sep *rsep; + struct avdtp_service_capability *media_transport, *media_codec; + struct avdtp_media_codec_capability *cap; + GSList *caps; + uint8_t data[4] = { 0x21, 0x02, 2, 32 }; + int ret; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-05-C") || + g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-07-C") || + g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-25-C")) + return; + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-01-C")) { + g_assert(err != NULL); + g_assert_cmpint(avdtp_error_error_code(err), ==, 0x01); + context_quit(context); + return; + } + + if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-04-C") || + g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-32-C")) { + g_assert(err != NULL); + g_assert_cmpint(avdtp_error_error_code(err), ==, 0x11); + context_quit(context); + return; + } + + g_assert(err == NULL); + g_assert_cmpint(g_slist_length(seps), !=, 0); + + rsep = avdtp_find_remote_sep(session, context->sep); + g_assert(rsep != NULL); + + media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, + NULL, 0); + + caps = g_slist_append(NULL, media_transport); + + cap = g_malloc0(sizeof(*cap) + sizeof(data)); + cap->media_type = AVDTP_MEDIA_TYPE_AUDIO; + cap->media_codec_type = 0x00; + memcpy(cap->data, data, sizeof(data)); + + media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap, + sizeof(*cap) + sizeof(data)); + + caps = g_slist_append(caps, media_codec); + g_free(cap); + + ret = avdtp_set_configuration(session, rsep, context->sep, caps, + &stream); + g_assert_cmpint(ret, ==, 0); + + g_slist_free_full(caps, g_free); +} + +static void test_client(gconstpointer data) +{ + struct context *context = create_context(0x0100, data); + struct avdtp_local_sep *sep; + + sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, + 0x00, FALSE, NULL, &sep_cfm, + context); + context->sep = sep; + + avdtp_discover(context->session, discover_cb, context); + + execute_context(context); + + avdtp_unregister_sep(sep); +} + +static void test_client_1_3(gconstpointer data) +{ + struct context *context = create_context(0x0103, data); + struct avdtp_local_sep *sep; + + sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, + 0x00, TRUE, NULL, &sep_cfm, + context); + context->sep = sep; + + avdtp_discover(context->session, discover_cb, context); + + execute_context(context); + + avdtp_unregister_sep(sep); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + if (g_test_verbose()) + __btd_log_init("*", 0); + + /* + * Stream Management Service + * + * To verify that the following procedures are implemented according to + * their specification in AVDTP. + */ + define_test("/TP/SIG/SMG/BV-05-C", test_client, + raw_pdu(0x00, 0x01)); + define_test("/TP/SIG/SMG/BV-06-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00)); + define_test("/TP/SIG/SMG/BV-07-C", test_client, + raw_pdu(0x10, 0x01), + raw_pdu(0x12, 0x01, 0x04, 0x00), + raw_pdu(0x20, 0x02, 0x04)); + define_test("/TP/SIG/SMG/BV-08-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40)); + define_test("/TP/SIG/SMG/BV-09-C", test_client, + raw_pdu(0x30, 0x01), + raw_pdu(0x32, 0x01, 0x04, 0x00), + raw_pdu(0x40, 0x02, 0x04), + raw_pdu(0x42, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20)); + define_test("/TP/SIG/SMG/BV-10-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03)); + define_test("/TP/SIG/SMG/BV-11-C", test_client, + raw_pdu(0x60, 0x01), + raw_pdu(0x62, 0x01, 0x04, 0x00), + raw_pdu(0x70, 0x02, 0x04), + raw_pdu(0x72, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x80, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x82, 0x03), + raw_pdu(0x90, 0x04, 0x04)); + define_test("/TP/SIG/SMG/BV-12-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x04, 0x04), + raw_pdu(0x32, 0x04, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0x21, 0x02, 0x02, 0x20)); + define_test("/TP/SIG/SMG/BV-15-C", test_client, + raw_pdu(0xa0, 0x01), + raw_pdu(0xa2, 0x01, 0x04, 0x00), + raw_pdu(0xb0, 0x02, 0x04), + raw_pdu(0xb2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0xc0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0xc2, 0x03), + raw_pdu(0xd0, 0x06, 0x04)); + define_test("/TP/SIG/SMG/BV-16-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06)); + define_test("/TP/SIG/SMG/BV-17-C", test_client, + raw_pdu(0xe0, 0x01), + raw_pdu(0xe2, 0x01, 0x04, 0x00), + raw_pdu(0xf0, 0x02, 0x04), + raw_pdu(0xf2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x00, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x02, 0x03), + raw_pdu(0x10, 0x06, 0x04), + raw_pdu(0x12, 0x06), + raw_pdu(0x20, 0x07, 0x04)); + define_test("/TP/SIG/SMG/BV-18-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x07, 0x04), + raw_pdu(0x42, 0x07)); + define_test("/TP/SIG/SMG/BV-19-C", test_client, + raw_pdu(0x30, 0x01), + raw_pdu(0x32, 0x01, 0x04, 0x00), + raw_pdu(0x40, 0x02, 0x04), + raw_pdu(0x42, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x52, 0x03), + raw_pdu(0x60, 0x06, 0x04), + raw_pdu(0x62, 0x06), + raw_pdu(0x70, 0x07, 0x04), + raw_pdu(0x72, 0x07), + raw_pdu(0x80, 0x08, 0x04)); + define_test("/TP/SIG/SMG/BV-20-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x07, 0x04), + raw_pdu(0x42, 0x07), + raw_pdu(0x50, 0x08, 0x04), + raw_pdu(0x52, 0x08)); + define_test("/TP/SIG/SMG/BV-21-C", test_client, + raw_pdu(0x90, 0x01), + raw_pdu(0x92, 0x01, 0x04, 0x00), + raw_pdu(0xa0, 0x02, 0x04), + raw_pdu(0xa2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0xb0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0xb2, 0x03), + raw_pdu(0xc0, 0x06, 0x04), + raw_pdu(0xc2, 0x06), + raw_pdu(0xd0, 0x07, 0x04), + raw_pdu(0xd2, 0x07), + raw_pdu(0xe0, 0x09, 0x04)); + define_test("/TP/SIG/SMG/BV-22-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x07, 0x04), + raw_pdu(0x42, 0x07), + raw_pdu(0x50, 0x09, 0x04), + raw_pdu(0x52, 0x09)); + define_test("/TP/SIG/SMG/BV-23-C", test_client, + raw_pdu(0xf0, 0x01), + raw_pdu(0xf2, 0x01, 0x04, 0x00), + raw_pdu(0x00, 0x02, 0x04), + raw_pdu(0x02, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x10, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x12, 0x03), + raw_pdu(0x20, 0x0a, 0x04)); + define_test("/TP/SIG/SMG/BV-24-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x0a, 0x04), + raw_pdu(0x32, 0x0a)); + define_test("/TP/SIG/SMG/BV-25-C", test_client_1_3, + raw_pdu(0x30, 0x01), + raw_pdu(0x32, 0x01, 0x04, 0x00), + raw_pdu(0x40, 0x0c, 0x04)); + define_test("/TP/SIG/SMG/BV-26-C", test_server_1_3, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x0c, 0x04), + raw_pdu(0x12, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40)); + define_test("/TP/SIG/SMG/BV-27-C", test_server_1_3, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40)); + define_test("/TP/SIG/SMG/BV-28-C", test_client_1_3, + raw_pdu(0x50, 0x01), + raw_pdu(0x52, 0x01, 0x04, 0x00), + raw_pdu(0x60, 0x0c, 0x04), + raw_pdu(0x62, 0x0c, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40, 0x0f, 0x00), + raw_pdu(0x70, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20)); + define_test("/TP/SIG/SMG/BV-31-C", test_client_1_3, + raw_pdu(0x80, 0x01), + raw_pdu(0x82, 0x01, 0x04, 0x00), + raw_pdu(0x90, 0x0c, 0x04), + raw_pdu(0x92, 0x0c, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x06, + 0x00, 0x00, 0xff, 0xff, 0x02, 0x40, 0x08, 0x00), + raw_pdu(0xa0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08, + 0x00)); + define_test("/TP/SIG/SMG/BI-01-C", test_client, + raw_pdu(0xb0, 0x01), + raw_pdu(0xb3, 0x01, 0x01)); + define_test("/TP/SIG/SMG/BI-02-C", test_server, + raw_pdu(0x01, 0x01)); + define_test("/TP/SIG/SMG/BI-03-C", test_server_0_sep, + raw_pdu(0x00, 0x01), + raw_pdu(0x03, 0x01, 0x19)); + define_test("/TP/SIG/SMG/BI-04-C", test_client, + raw_pdu(0xc0, 0x01), + raw_pdu(0xc2, 0x01, 0x04, 0x00), + raw_pdu(0xd0, 0x02, 0x04), + raw_pdu(0xd3, 0x02, 0x11)); + define_test("/TP/SIG/SMG/BI-05-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02), + raw_pdu(0x13, 0x02, 0x11)); + define_test("/TP/SIG/SMG/BI-06-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x00), + raw_pdu(0x13, 0x02, 0x12)); + define_test("/TP/SIG/SMG/BI-07-C", test_client, + raw_pdu(0xe0, 0x01), + raw_pdu(0xe2, 0x01, 0x04, 0x00), + raw_pdu(0xf0, 0x02, 0x04), + raw_pdu(0xf2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x00, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x03, 0x03, 0x00, 0x13)); + define_test("/TP/SIG/SMG/BI-08-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x33, 0x03, 0x00, 0x13)); + define_test("/TP/SIG/SMG/BI-09-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x23, 0x03, 0x00, 0x29)); + define_test("/TP/SIG/SMG/BI-10-C", test_client, + raw_pdu(0x10, 0x01), + raw_pdu(0x12, 0x01, 0x04, 0x00), + raw_pdu(0x20, 0x02, 0x04), + raw_pdu(0x22, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x30, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x32, 0x03), + raw_pdu(0x40, 0x04, 0x04), + raw_pdu(0x43, 0x04, 0x12)); + define_test("/TP/SIG/SMG/BI-11-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x04, 0x00), + raw_pdu(0x33, 0x04, 0x12)); + define_test("/TP/SIG/SMG/BI-17-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x33, 0x06, 0x31)); + define_test("/TP/SIG/SMG/BI-18-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x33, 0x06, 0xc0)); + define_test("/TP/SIG/SMG/BI-19-C", test_client, + raw_pdu(0x50, 0x01), + raw_pdu(0x52, 0x01, 0x04, 0x00), + raw_pdu(0x60, 0x02, 0x04), + raw_pdu(0x62, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x70, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x72, 0x03), + raw_pdu(0x80, 0x06, 0x04), + raw_pdu(0x82, 0x06), + raw_pdu(0x90, 0x07, 0x04), + raw_pdu(0x93, 0x07, 0x04, 0x31)); + define_test("/TP/SIG/SMG/BI-20-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x07, 0x04), + raw_pdu(0x33, 0x07, 0x04, 0x31)); + define_test("/TP/SIG/SMG/BI-21-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x07, 0x04), + raw_pdu(0x43, 0x07, 0x04, 0xc0)); + define_test("/TP/SIG/SMG/BI-22-C", test_client, + raw_pdu(0xa0, 0x01), + raw_pdu(0xa2, 0x01, 0x04, 0x00), + raw_pdu(0xb0, 0x02, 0x04), + raw_pdu(0xb2, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0xc0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0xc2, 0x03), + raw_pdu(0xd0, 0x06, 0x04), + raw_pdu(0xd2, 0x06), + raw_pdu(0xe0, 0x07, 0x04), + raw_pdu(0xe3, 0x07, 0x04, 0x31)); + define_test("/TP/SIG/SMG/BI-23-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x08, 0x00), + raw_pdu(0x43, 0x08, 0x12)); + define_test("/TP/SIG/SMG/BI-24-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x08, 0x04), + raw_pdu(0x43, 0x08, 0xc0)); + define_test("/TP/SIG/SMG/BI-25-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x07, 0x04), + raw_pdu(0x42, 0x07), + raw_pdu(0xf0, 0x09, 0x04), + raw_pdu(0xf3, 0x09, 0x04, 0x31)); + define_test("/TP/SIG/SMG/BI-26-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x09, 0x04), + raw_pdu(0x43, 0x09, 0x04, 0x31)); + define_test("/TP/SIG/SMG/BI-27-C", test_server, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00, + 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20), + raw_pdu(0x22, 0x03), + raw_pdu(0x30, 0x06, 0x04), + raw_pdu(0x32, 0x06), + raw_pdu(0x40, 0x07, 0x04), + raw_pdu(0x42, 0x07), + raw_pdu(0x50, 0x09, 0x04), + raw_pdu(0x53, 0x09, 0x04, 0xc0)); + define_test("/TP/SIG/SMG/BI-28-C", test_server, + raw_pdu(0x00, 0xff), + raw_pdu(0x01, 0x3f)); + define_test("/TP/SIG/SMG/BI-30-C", test_client, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x02, 0x04), + raw_pdu(0x12, 0x02, 0xee, 0x01, 0x00, 0x01, 0x00, 0x07, + 0x06, 0x00, 0x00, 0xff, 0xff, 0x02, 0x40), + raw_pdu(0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20)); + define_test("/TP/SIG/SMG/ESR04/BI-28-C", test_server, + raw_pdu(0x00, 0x3f), + raw_pdu(0x01, 0x3f)); + define_test("/TP/SIG/SMG/BI-32-C", test_client_1_3, + raw_pdu(0x30, 0x01), + raw_pdu(0x32, 0x01, 0x04, 0x00), + raw_pdu(0x40, 0x0c, 0x04), + raw_pdu(0x43, 0x0c, 0x11)); + define_test("/TP/SIG/SMG/BI-33-C", test_server_1_3, + raw_pdu(0x00, 0x01), + raw_pdu(0x02, 0x01, 0x04, 0x00), + raw_pdu(0x10, 0x0c), + raw_pdu(0x13, 0x0c, 0x11)); + define_test("/TP/SIG/SMG/BI-35-C", test_client_1_3, + raw_pdu(0x50, 0x01), + raw_pdu(0x52, 0x01, 0x04, 0x00), + raw_pdu(0x60, 0x0c, 0x04), + raw_pdu(0x62, 0x0c, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x06, + 0x00, 0x00, 0xff, 0xff, 0x02, 0x40, 0x08, 0x00), + raw_pdu(0x70, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20, 0x08, + 0x00)); + define_test("/TP/SIG/SMG/BI-36-C", test_client_1_3, + raw_pdu(0x80, 0x01), + raw_pdu(0x82, 0x01, 0x04, 0x00), + raw_pdu(0x90, 0x0c, 0x04), + raw_pdu(0x92, 0x0c, 0xee, 0x01, 0x00, 0x01, 0x00, 0x07, + 0x06, 0x00, 0x00, 0xff, 0xff, 0x02, 0x40), + raw_pdu(0xa0, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06, + 0x00, 0x00, 0x21, 0x02, 0x02, 0x20)); + + return g_test_run(); +} diff --git a/unit/test-eir.c b/unit/test-eir.c index 1a6e1c99..6d9d554f 100644 --- a/unit/test-eir.c +++ b/unit/test-eir.c @@ -537,13 +537,11 @@ static void test_basic(void) { struct eir_data data; unsigned char buf[HCI_MAX_EIR_LENGTH]; - int err; memset(buf, 0, sizeof(buf)); memset(&data, 0, sizeof(data)); - err = eir_parse(&data, buf, HCI_MAX_EIR_LENGTH); - g_assert(err == 0); + eir_parse(&data, buf, HCI_MAX_EIR_LENGTH); g_assert(data.services == NULL); g_assert(data.name == NULL); @@ -554,12 +552,10 @@ static void test_parsing(gconstpointer data) { const struct test_data *test = data; struct eir_data eir; - int err; memset(&eir, 0, sizeof(eir)); - err = eir_parse(&eir, test->eir_data, test->eir_size); - g_assert(err == 0); + eir_parse(&eir, test->eir_data, test->eir_size); if (g_test_verbose() == TRUE) { GSList *list; |