From: Wu Zheng Date: Wed, 27 Nov 2013 02:36:00 +0000 (-0500) Subject: Imported Upstream version 5.11 X-Git-Tag: upstream/5.11^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a704ad586e3a3480a8e0c60da2d9b772dbd0c887;p=platform%2Fupstream%2Fbluez.git Imported Upstream version 5.11 --- diff --git a/ChangeLog b/ChangeLog index fc9b3d7..ca48336 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +ver 5.11: + Fix issue with connection attempt when not powered. + Fix issue with assigning player to AVRCP target role. + Fix issue with OBEX default cache directory. + Fix issue with SDP search error handling. + Fix issue with processing of SDP records. + Fix issue with HID to HCI switching utility. + Fix issue with mgmt end-to-end testing tool. + Fix issue with L2CAP end-to-end testing tool. + Add support for SMP end-to-end testing tool. + Add support for more Wii controllers. + ver 5.10: Fix issue with discoverable timeout handling. Fix issue with MAP messages and record version. diff --git a/Makefile.am b/Makefile.am index 8aed6f7..328ccdb 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:1:17 +lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:2:17 lib_libbluetooth_la_DEPENDENCIES = $(local_headers) endif @@ -179,7 +179,7 @@ test_scripts = include Makefile.tools include Makefile.obexd -include Makefile.android +include android/Makefile.am if HID2HCI rulesdir = @UDEV_DIR@/rules.d diff --git a/Makefile.android b/Makefile.android deleted file mode 100644 index 3ceefd8..0000000 --- a/Makefile.android +++ /dev/null @@ -1,10 +0,0 @@ -if ANDROID -noinst_PROGRAMS += android/bluetoothd - -android_bluetoothd_SOURCES = android/main.c src/log.c -android_bluetoothd_LDADD = @GLIB_LIBS@ -endif - -EXTRA_DIST += android/Android.mk android/log.c - -EXTRA_DIST += android/hal-ipc-api.txt diff --git a/Makefile.in b/Makefile.in index 677ad3e..9556969 100644 --- a/Makefile.in +++ b/Makefile.in @@ -63,11 +63,11 @@ libexec_PROGRAMS = src/bluetoothd$(EXEEXT) obexd/src/obexd$(EXEEXT) @LIBRARY_TRUE@am__append_1 = $(lib_headers) @LIBRARY_TRUE@am__append_2 = lib/libbluetooth.la DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \ - $(dist_man_MANS) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.android $(srcdir)/Makefile.in \ + $(dist_man_MANS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(srcdir)/Makefile.obexd $(srcdir)/Makefile.plugins \ - $(srcdir)/Makefile.tools $(srcdir)/config.h.in \ - $(top_srcdir)/configure $(top_srcdir)/lib/bluez.pc.in \ + $(srcdir)/Makefile.tools $(srcdir)/android/Makefile.am \ + $(srcdir)/config.h.in $(top_srcdir)/configure \ + $(top_srcdir)/lib/bluez.pc.in \ $(top_srcdir)/src/bluetoothd.8.in AUTHORS COPYING COPYING.LIB \ ChangeLog INSTALL NEWS TODO compile config.guess config.sub \ depcomp install-sh ltmain.sh missing @@ -114,11 +114,12 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \ @EXPERIMENTAL_TRUE@am__append_15 = emulator/btvirt emulator/b1ee \ @EXPERIMENTAL_TRUE@ tools/mgmt-tester tools/gap-tester \ @EXPERIMENTAL_TRUE@ tools/l2cap-tester tools/sco-tester \ -@EXPERIMENTAL_TRUE@ tools/bdaddr tools/avinfo tools/avtest \ -@EXPERIMENTAL_TRUE@ tools/scotest tools/amptest tools/hwdb \ -@EXPERIMENTAL_TRUE@ tools/hcieventmask tools/hcisecfilter \ -@EXPERIMENTAL_TRUE@ tools/btmgmt tools/btinfo tools/btattach \ -@EXPERIMENTAL_TRUE@ tools/btsnoop tools/btiotest tools/cltest \ +@EXPERIMENTAL_TRUE@ tools/smp-tester tools/bdaddr tools/avinfo \ +@EXPERIMENTAL_TRUE@ tools/avtest tools/scotest tools/amptest \ +@EXPERIMENTAL_TRUE@ tools/hwdb tools/hcieventmask \ +@EXPERIMENTAL_TRUE@ tools/hcisecfilter tools/btmgmt \ +@EXPERIMENTAL_TRUE@ tools/btinfo tools/btattach tools/btsnoop \ +@EXPERIMENTAL_TRUE@ tools/btiotest tools/cltest \ @EXPERIMENTAL_TRUE@ tools/mpris-player @TOOLS_TRUE@am__append_16 = tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \ @TOOLS_TRUE@ tools/rfcomm tools/rctest tools/l2test tools/l2ping \ @@ -151,8 +152,10 @@ DIST_COMMON = README $(am__configure_deps) $(am__include_HEADERS_DIST) \ @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/bluetoothd -@HID2HCI_TRUE@am__append_29 = $(rules_DATA) +@ANDROID_TRUE@am__append_28 = 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) TESTS = $(am__EXEEXT_8) subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -221,14 +224,36 @@ am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" \ "$(DESTDIR)$(systemduserunitdir)" "$(DESTDIR)$(includedir)" LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) \ $(plugin_LTLIBRARIES) +android_libhal_internal_la_LIBADD = +am__android_libhal_internal_la_SOURCES_DIST = android/hal.h \ + android/hal-bluetooth.c android/hal-sock.c \ + android/hal-hidhost.c android/hal-pan.c android/hal-a2dp.c \ + android/hardware/bluetooth.h android/hardware/bt_av.h \ + android/hardware/bt_gatt.h android/hardware/bt_gatt_client.h \ + android/hardware/bt_gatt_server.h \ + android/hardware/bt_gatt_types.h android/hardware/bt_hf.h \ + android/hardware/bt_hh.h android/hardware/bt_hl.h \ + android/hardware/bt_pan.h android/hardware/bt_rc.h \ + android/hardware/bt_sock.h android/hardware/hardware.h \ + android/cutils/properties.h android/hal-log.h \ + android/hal-ipc.h android/hal-ipc.c +@ANDROID_TRUE@am_android_libhal_internal_la_OBJECTS = android/android_libhal_internal_la-hal-bluetooth.lo \ +@ANDROID_TRUE@ android/android_libhal_internal_la-hal-sock.lo \ +@ANDROID_TRUE@ android/android_libhal_internal_la-hal-hidhost.lo \ +@ANDROID_TRUE@ android/android_libhal_internal_la-hal-pan.lo \ +@ANDROID_TRUE@ android/android_libhal_internal_la-hal-a2dp.lo \ +@ANDROID_TRUE@ android/android_libhal_internal_la-hal-ipc.lo +android_libhal_internal_la_OBJECTS = \ + $(am_android_libhal_internal_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +@ANDROID_TRUE@am_android_libhal_internal_la_rpath = gdbus_libgdbus_internal_la_LIBADD = am_gdbus_libgdbus_internal_la_OBJECTS = gdbus/mainloop.lo \ gdbus/watch.lo gdbus/object.lo gdbus/client.lo gdbus/polkit.lo gdbus_libgdbus_internal_la_OBJECTS = \ $(am_gdbus_libgdbus_internal_la_OBJECTS) -AM_V_lt = $(am__v_lt_@AM_V@) -am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) -am__v_lt_0 = --silent lib_libbluetooth_internal_la_LIBADD = am__objects_1 = am__objects_2 = lib/bluetooth.lo lib/hci.lo lib/sdp.lo @@ -275,6 +300,7 @@ plugins_external_dummy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ @EXPERIMENTAL_TRUE@ tools/gap-tester$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/l2cap-tester$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/sco-tester$(EXEEXT) \ +@EXPERIMENTAL_TRUE@ tools/smp-tester$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/bdaddr$(EXEEXT) tools/avinfo$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/avtest$(EXEEXT) \ @EXPERIMENTAL_TRUE@ tools/scotest$(EXEEXT) \ @@ -293,7 +319,9 @@ plugins_external_dummy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ @READLINE_TRUE@ tools/bluetooth-player$(EXEEXT) \ @READLINE_TRUE@ tools/obexctl$(EXEEXT) @EXPERIMENTAL_TRUE@am__EXEEXT_6 = profiles/iap/iapd$(EXEEXT) -@ANDROID_TRUE@am__EXEEXT_7 = android/bluetoothd$(EXEEXT) +@ANDROID_TRUE@am__EXEEXT_7 = android/system-emulator$(EXEEXT) \ +@ANDROID_TRUE@ android/bluetoothd$(EXEEXT) \ +@ANDROID_TRUE@ android/haltest$(EXEEXT) am__EXEEXT_8 = unit/test-eir$(EXEEXT) unit/test-uuid$(EXEEXT) \ unit/test-textfile$(EXEEXT) unit/test-crc$(EXEEXT) \ unit/test-mgmt$(EXEEXT) unit/test-sdp$(EXEEXT) \ @@ -304,11 +332,71 @@ am__EXEEXT_8 = unit/test-eir$(EXEEXT) unit/test-uuid$(EXEEXT) \ unit/test-gobex-apparam$(EXEEXT) unit/test-lib$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(cups_PROGRAMS) $(libexec_PROGRAMS) \ $(noinst_PROGRAMS) $(udev_PROGRAMS) -am__android_bluetoothd_SOURCES_DIST = android/main.c src/log.c +am__android_bluetoothd_SOURCES_DIST = android/main.c src/log.c \ + android/hal-msg.h android/utils.h src/sdpd-database.c \ + src/sdpd-server.c src/sdpd-service.c src/sdpd-request.c \ + src/glib-helper.h src/glib-helper.c src/eir.h src/eir.c \ + 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_TRUE@am_android_bluetoothd_OBJECTS = android/main.$(OBJEXT) \ -@ANDROID_TRUE@ src/log.$(OBJEXT) +@ANDROID_TRUE@ src/log.$(OBJEXT) src/sdpd-database.$(OBJEXT) \ +@ANDROID_TRUE@ src/sdpd-server.$(OBJEXT) \ +@ANDROID_TRUE@ src/sdpd-service.$(OBJEXT) \ +@ANDROID_TRUE@ src/sdpd-request.$(OBJEXT) \ +@ANDROID_TRUE@ src/glib-helper.$(OBJEXT) src/eir.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/util.$(OBJEXT) \ +@ANDROID_TRUE@ src/shared/mgmt.$(OBJEXT) \ +@ANDROID_TRUE@ android/bluetooth.$(OBJEXT) \ +@ANDROID_TRUE@ android/hidhost.$(OBJEXT) android/ipc.$(OBJEXT) \ +@ANDROID_TRUE@ android/a2dp.$(OBJEXT) android/socket.$(OBJEXT) \ +@ANDROID_TRUE@ android/pan.$(OBJEXT) btio/btio.$(OBJEXT) \ +@ANDROID_TRUE@ src/sdp-client.$(OBJEXT) android_bluetoothd_OBJECTS = $(am_android_bluetoothd_OBJECTS) -android_bluetoothd_DEPENDENCIES = +@ANDROID_TRUE@android_bluetoothd_DEPENDENCIES = \ +@ANDROID_TRUE@ lib/libbluetooth-internal.la +am__android_haltest_SOURCES_DIST = android/client/haltest.c \ + android/client/pollhandler.h android/client/pollhandler.c \ + android/client/terminal.h android/client/terminal.c \ + android/client/history.h android/client/history.c \ + android/client/tabcompletion.c android/client/if-main.h \ + android/client/if-av.c android/client/if-bt.c \ + android/client/if-gatt.c android/client/if-hf.c \ + android/client/if-hh.c android/client/if-pan.c \ + android/client/if-sock.c android/client/hwmodule.c \ + android/hal-utils.h android/hal-utils.c +@ANDROID_TRUE@am_android_haltest_OBJECTS = android/client/android_haltest-haltest.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-pollhandler.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-terminal.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-history.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-tabcompletion.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-av.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-bt.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-gatt.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-hf.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-hh.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-pan.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-if-sock.$(OBJEXT) \ +@ANDROID_TRUE@ android/client/android_haltest-hwmodule.$(OBJEXT) \ +@ANDROID_TRUE@ android/android_haltest-hal-utils.$(OBJEXT) +android_haltest_OBJECTS = $(am_android_haltest_OBJECTS) +@ANDROID_TRUE@android_haltest_DEPENDENCIES = \ +@ANDROID_TRUE@ android/libhal-internal.la +android_haltest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(android_haltest_CFLAGS) $(CFLAGS) $(android_haltest_LDFLAGS) \ + $(LDFLAGS) -o $@ +am__android_system_emulator_SOURCES_DIST = android/system-emulator.c \ + monitor/mainloop.h monitor/mainloop.c +@ANDROID_TRUE@am_android_system_emulator_OBJECTS = \ +@ANDROID_TRUE@ android/system-emulator.$(OBJEXT) \ +@ANDROID_TRUE@ monitor/mainloop.$(OBJEXT) +android_system_emulator_OBJECTS = \ + $(am_android_system_emulator_OBJECTS) +android_system_emulator_LDADD = $(LDADD) am__attrib_gatttool_SOURCES_DIST = attrib/gatttool.c attrib/att.c \ attrib/gatt.c attrib/gattrib.c btio/btio.c attrib/gatttool.h \ attrib/interactive.c attrib/utils.c src/log.c client/display.c \ @@ -906,6 +994,22 @@ am__tools_sdptool_SOURCES_DIST = tools/sdptool.c src/sdp-xml.h \ @TOOLS_TRUE@ src/sdp-xml.$(OBJEXT) tools_sdptool_OBJECTS = $(am_tools_sdptool_OBJECTS) @TOOLS_TRUE@tools_sdptool_DEPENDENCIES = lib/libbluetooth-internal.la +am__tools_smp_tester_SOURCES_DIST = tools/smp-tester.c monitor/bt.h \ + emulator/btdev.h emulator/btdev.c emulator/bthost.h \ + emulator/bthost.c src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c src/shared/hciemu.h \ + src/shared/hciemu.c src/shared/tester.h src/shared/tester.c +@EXPERIMENTAL_TRUE@am_tools_smp_tester_OBJECTS = \ +@EXPERIMENTAL_TRUE@ tools/smp-tester.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/btdev.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ emulator/bthost.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/util.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/mgmt.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/hciemu.$(OBJEXT) \ +@EXPERIMENTAL_TRUE@ src/shared/tester.$(OBJEXT) +tools_smp_tester_OBJECTS = $(am_tools_smp_tester_OBJECTS) +@EXPERIMENTAL_TRUE@tools_smp_tester_DEPENDENCIES = \ +@EXPERIMENTAL_TRUE@ lib/libbluetooth-internal.la am_unit_test_crc_OBJECTS = unit/test-crc.$(OBJEXT) \ monitor/crc.$(OBJEXT) unit_test_crc_OBJECTS = $(am_unit_test_crc_OBJECTS) @@ -983,11 +1087,13 @@ AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(profiles_sap_libsap_a_SOURCES) \ + $(android_libhal_internal_la_SOURCES) \ $(gdbus_libgdbus_internal_la_SOURCES) \ $(lib_libbluetooth_internal_la_SOURCES) \ $(lib_libbluetooth_la_SOURCES) \ $(plugins_external_dummy_la_SOURCES) \ - $(android_bluetoothd_SOURCES) $(attrib_gatttool_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) \ @@ -1010,20 +1116,23 @@ 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) \ - $(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_crc_SOURCES) \ + $(unit_test_eir_SOURCES) $(unit_test_gdbus_client_SOURCES) \ + $(unit_test_gobex_SOURCES) $(unit_test_gobex_apparam_SOURCES) \ $(unit_test_gobex_header_SOURCES) \ $(unit_test_gobex_packet_SOURCES) \ $(unit_test_gobex_transfer_SOURCES) $(unit_test_lib_SOURCES) \ $(unit_test_mgmt_SOURCES) $(unit_test_sdp_SOURCES) \ $(unit_test_textfile_SOURCES) $(unit_test_uuid_SOURCES) DIST_SOURCES = $(am__profiles_sap_libsap_a_SOURCES_DIST) \ + $(am__android_libhal_internal_la_SOURCES_DIST) \ $(gdbus_libgdbus_internal_la_SOURCES) \ $(lib_libbluetooth_internal_la_SOURCES) \ $(am__lib_libbluetooth_la_SOURCES_DIST) \ $(am__plugins_external_dummy_la_SOURCES_DIST) \ $(am__android_bluetoothd_SOURCES_DIST) \ + $(am__android_haltest_SOURCES_DIST) \ + $(am__android_system_emulator_SOURCES_DIST) \ $(am__attrib_gatttool_SOURCES_DIST) \ $(am__client_bluetoothctl_SOURCES_DIST) \ $(am__emulator_b1ee_SOURCES_DIST) \ @@ -1055,9 +1164,9 @@ 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) \ - $(unit_test_crc_SOURCES) $(unit_test_eir_SOURCES) \ - $(unit_test_gdbus_client_SOURCES) $(unit_test_gobex_SOURCES) \ - $(unit_test_gobex_apparam_SOURCES) \ + $(am__tools_smp_tester_SOURCES_DIST) $(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) \ @@ -1254,19 +1363,19 @@ 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 + gdbus/libgdbus-internal.la $(am__append_29) dist_man_MANS = $(am__append_17) $(am__append_19) dist_noinst_MANS = CLEANFILES = $(builtin_files) src/bluetooth.service \ obexd/src/builtin.h $(builtin_files) obexd/src/obex.service \ - $(am__append_29) + $(am__append_30) 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 \ obexd/src/org.bluez.obex.service obexd/src/genbuiltin \ - android/Android.mk android/log.c android/hal-ipc-api.txt \ + 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 \ @@ -1303,7 +1412,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:1:17 +@LIBRARY_TRUE@lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 20:2:17 @LIBRARY_TRUE@lib_libbluetooth_la_DEPENDENCIES = $(local_headers) lib_libbluetooth_internal_la_SOURCES = $(lib_headers) $(lib_sources) \ $(extra_headers) $(extra_sources) @@ -1463,6 +1572,15 @@ test_scripts = test/sap_client.py test/bluezutils.py test/dbusdef.py \ @EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c @EXPERIMENTAL_TRUE@tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ +@EXPERIMENTAL_TRUE@tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \ +@EXPERIMENTAL_TRUE@ emulator/btdev.h emulator/btdev.c \ +@EXPERIMENTAL_TRUE@ emulator/bthost.h emulator/bthost.c \ +@EXPERIMENTAL_TRUE@ src/shared/util.h src/shared/util.c \ +@EXPERIMENTAL_TRUE@ src/shared/mgmt.h src/shared/mgmt.c \ +@EXPERIMENTAL_TRUE@ src/shared/hciemu.h src/shared/hciemu.c \ +@EXPERIMENTAL_TRUE@ src/shared/tester.h src/shared/tester.c + +@EXPERIMENTAL_TRUE@tools_smp_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ @EXPERIMENTAL_TRUE@tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \ @EXPERIMENTAL_TRUE@ emulator/btdev.h emulator/btdev.c \ @EXPERIMENTAL_TRUE@ emulator/bthost.h emulator/bthost.c \ @@ -1658,8 +1776,76 @@ obexd_src_obexd_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/obexd/src \ obexd_src_obexd_SHORTNAME = obexd obexd_builtin_files = obexd/src/builtin.h $(obexd_builtin_nodist) nodist_obexd_src_obexd_SOURCES = $(obexd_builtin_files) -@ANDROID_TRUE@android_bluetoothd_SOURCES = android/main.c src/log.c -@ANDROID_TRUE@android_bluetoothd_LDADD = @GLIB_LIBS@ +@ANDROID_TRUE@android_system_emulator_SOURCES = android/system-emulator.c \ +@ANDROID_TRUE@ monitor/mainloop.h monitor/mainloop.c + +@ANDROID_TRUE@android_bluetoothd_SOURCES = android/main.c \ +@ANDROID_TRUE@ src/log.c \ +@ANDROID_TRUE@ android/hal-msg.h \ +@ANDROID_TRUE@ android/utils.h \ +@ANDROID_TRUE@ src/sdpd-database.c src/sdpd-server.c \ +@ANDROID_TRUE@ src/sdpd-service.c src/sdpd-request.c \ +@ANDROID_TRUE@ src/glib-helper.h src/glib-helper.c \ +@ANDROID_TRUE@ src/eir.h src/eir.c \ +@ANDROID_TRUE@ src/shared/util.h src/shared/util.c \ +@ANDROID_TRUE@ src/shared/mgmt.h src/shared/mgmt.c \ +@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/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@android_bluetoothd_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ +@ANDROID_TRUE@android_libhal_internal_la_SOURCES = android/hal.h android/hal-bluetooth.c \ +@ANDROID_TRUE@ android/hal-sock.c \ +@ANDROID_TRUE@ android/hal-hidhost.c \ +@ANDROID_TRUE@ android/hal-pan.c \ +@ANDROID_TRUE@ android/hal-a2dp.c \ +@ANDROID_TRUE@ android/hardware/bluetooth.h \ +@ANDROID_TRUE@ android/hardware/bt_av.h \ +@ANDROID_TRUE@ android/hardware/bt_gatt.h \ +@ANDROID_TRUE@ android/hardware/bt_gatt_client.h \ +@ANDROID_TRUE@ android/hardware/bt_gatt_server.h \ +@ANDROID_TRUE@ android/hardware/bt_gatt_types.h \ +@ANDROID_TRUE@ android/hardware/bt_hf.h \ +@ANDROID_TRUE@ android/hardware/bt_hh.h \ +@ANDROID_TRUE@ android/hardware/bt_hl.h \ +@ANDROID_TRUE@ android/hardware/bt_pan.h \ +@ANDROID_TRUE@ android/hardware/bt_rc.h \ +@ANDROID_TRUE@ android/hardware/bt_sock.h \ +@ANDROID_TRUE@ android/hardware/hardware.h \ +@ANDROID_TRUE@ android/cutils/properties.h \ +@ANDROID_TRUE@ android/hal-log.h \ +@ANDROID_TRUE@ android/hal-ipc.h android/hal-ipc.c + +@ANDROID_TRUE@android_libhal_internal_la_CPPFLAGS = -I$(srcdir)/android +@ANDROID_TRUE@android_haltest_SOURCES = android/client/haltest.c \ +@ANDROID_TRUE@ android/client/pollhandler.h \ +@ANDROID_TRUE@ android/client/pollhandler.c \ +@ANDROID_TRUE@ android/client/terminal.h \ +@ANDROID_TRUE@ android/client/terminal.c \ +@ANDROID_TRUE@ android/client/history.h \ +@ANDROID_TRUE@ android/client/history.c \ +@ANDROID_TRUE@ android/client/tabcompletion.c \ +@ANDROID_TRUE@ android/client/if-main.h \ +@ANDROID_TRUE@ android/client/if-av.c \ +@ANDROID_TRUE@ android/client/if-bt.c \ +@ANDROID_TRUE@ android/client/if-gatt.c \ +@ANDROID_TRUE@ android/client/if-hf.c \ +@ANDROID_TRUE@ android/client/if-hh.c \ +@ANDROID_TRUE@ android/client/if-pan.c \ +@ANDROID_TRUE@ android/client/if-sock.c \ +@ANDROID_TRUE@ android/client/hwmodule.c \ +@ANDROID_TRUE@ android/hal-utils.h android/hal-utils.c + +@ANDROID_TRUE@android_haltest_LDADD = android/libhal-internal.la +@ANDROID_TRUE@android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \ +@ANDROID_TRUE@ -DPLATFORM_SDK_VERSION=19 + +@ANDROID_TRUE@android_haltest_LDFLAGS = -pthread @HID2HCI_TRUE@rulesdir = @UDEV_DIR@/rules.d @HID2HCI_TRUE@rules_DATA = tools/97-hid2hci.rules @TEST_TRUE@testdir = $(pkglibdir)/test @@ -1739,7 +1925,7 @@ all: $(BUILT_SOURCES) config.h .SUFFIXES: .c .lo .o .obj am--refresh: Makefile @: -$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.plugins $(srcdir)/Makefile.tools $(srcdir)/Makefile.obexd $(srcdir)/Makefile.android $(am__configure_deps) +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/Makefile.plugins $(srcdir)/Makefile.tools $(srcdir)/Makefile.obexd $(srcdir)/android/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ @@ -1762,7 +1948,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; -$(srcdir)/Makefile.plugins $(srcdir)/Makefile.tools $(srcdir)/Makefile.obexd $(srcdir)/Makefile.android: +$(srcdir)/Makefile.plugins $(srcdir)/Makefile.tools $(srcdir)/Makefile.obexd $(srcdir)/android/Makefile.am: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck @@ -1879,6 +2065,26 @@ clean-pluginLTLIBRARIES: echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done +android/$(am__dirstamp): + @$(MKDIR_P) android + @: > android/$(am__dirstamp) +android/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) android/$(DEPDIR) + @: > android/$(DEPDIR)/$(am__dirstamp) +android/android_libhal_internal_la-hal-bluetooth.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android_libhal_internal_la-hal-sock.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android_libhal_internal_la-hal-hidhost.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android_libhal_internal_la-hal-pan.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android_libhal_internal_la-hal-a2dp.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/android_libhal_internal_la-hal-ipc.lo: \ + android/$(am__dirstamp) android/$(DEPDIR)/$(am__dirstamp) +android/libhal-internal.la: $(android_libhal_internal_la_OBJECTS) $(android_libhal_internal_la_DEPENDENCIES) $(EXTRA_android_libhal_internal_la_DEPENDENCIES) android/$(am__dirstamp) + $(AM_V_CCLD)$(LINK) $(am_android_libhal_internal_la_rpath) $(android_libhal_internal_la_OBJECTS) $(android_libhal_internal_la_LIBADD) $(LIBS) gdbus/$(am__dirstamp): @$(MKDIR_P) gdbus @: > gdbus/$(am__dirstamp) @@ -2110,12 +2316,6 @@ clean-udevPROGRAMS: list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list -android/$(am__dirstamp): - @$(MKDIR_P) android - @: > android/$(am__dirstamp) -android/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) android/$(DEPDIR) - @: > android/$(DEPDIR)/$(am__dirstamp) android/main.$(OBJEXT): android/$(am__dirstamp) \ android/$(DEPDIR)/$(am__dirstamp) src/$(am__dirstamp): @@ -2125,9 +2325,115 @@ src/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) src/$(DEPDIR) @: > src/$(DEPDIR)/$(am__dirstamp) src/log.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) +src/sdpd-database.$(OBJEXT): src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) +src/sdpd-server.$(OBJEXT): src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) +src/sdpd-service.$(OBJEXT): src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) +src/sdpd-request.$(OBJEXT): src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) +src/glib-helper.$(OBJEXT): src/$(am__dirstamp) \ + src/$(DEPDIR)/$(am__dirstamp) +src/eir.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) +src/shared/$(am__dirstamp): + @$(MKDIR_P) src/shared + @: > src/shared/$(am__dirstamp) +src/shared/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/shared/$(DEPDIR) + @: > src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/util.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +src/shared/mgmt.$(OBJEXT): src/shared/$(am__dirstamp) \ + src/shared/$(DEPDIR)/$(am__dirstamp) +android/bluetooth.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/hidhost.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/ipc.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/a2dp.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/socket.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/pan.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +btio/$(am__dirstamp): + @$(MKDIR_P) btio + @: > btio/$(am__dirstamp) +btio/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) btio/$(DEPDIR) + @: > btio/$(DEPDIR)/$(am__dirstamp) +btio/btio.$(OBJEXT): btio/$(am__dirstamp) \ + btio/$(DEPDIR)/$(am__dirstamp) +src/sdp-client.$(OBJEXT): src/$(am__dirstamp) \ + src/$(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) +android/client/$(am__dirstamp): + @$(MKDIR_P) android/client + @: > android/client/$(am__dirstamp) +android/client/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) android/client/$(DEPDIR) + @: > android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-haltest.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-pollhandler.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-terminal.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-history.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-tabcompletion.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-if-av.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-if-bt.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-if-gatt.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-if-hf.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-if-hh.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-if-pan.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-if-sock.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/client/android_haltest-hwmodule.$(OBJEXT): \ + android/client/$(am__dirstamp) \ + android/client/$(DEPDIR)/$(am__dirstamp) +android/android_haltest-hal-utils.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +android/haltest$(EXEEXT): $(android_haltest_OBJECTS) $(android_haltest_DEPENDENCIES) $(EXTRA_android_haltest_DEPENDENCIES) android/$(am__dirstamp) + @rm -f android/haltest$(EXEEXT) + $(AM_V_CCLD)$(android_haltest_LINK) $(android_haltest_OBJECTS) $(android_haltest_LDADD) $(LIBS) +android/system-emulator.$(OBJEXT): android/$(am__dirstamp) \ + android/$(DEPDIR)/$(am__dirstamp) +monitor/$(am__dirstamp): + @$(MKDIR_P) monitor + @: > monitor/$(am__dirstamp) +monitor/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) monitor/$(DEPDIR) + @: > monitor/$(DEPDIR)/$(am__dirstamp) +monitor/mainloop.$(OBJEXT): monitor/$(am__dirstamp) \ + monitor/$(DEPDIR)/$(am__dirstamp) +android/system-emulator$(EXEEXT): $(android_system_emulator_OBJECTS) $(android_system_emulator_DEPENDENCIES) $(EXTRA_android_system_emulator_DEPENDENCIES) android/$(am__dirstamp) + @rm -f android/system-emulator$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(android_system_emulator_OBJECTS) $(android_system_emulator_LDADD) $(LIBS) attrib/$(am__dirstamp): @$(MKDIR_P) attrib @: > attrib/$(am__dirstamp) @@ -2142,14 +2448,6 @@ attrib/gatt.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) attrib/gattrib.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) -btio/$(am__dirstamp): - @$(MKDIR_P) btio - @: > btio/$(am__dirstamp) -btio/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) btio/$(DEPDIR) - @: > btio/$(DEPDIR)/$(am__dirstamp) -btio/btio.$(OBJEXT): btio/$(am__dirstamp) \ - btio/$(DEPDIR)/$(am__dirstamp) attrib/interactive.$(OBJEXT): attrib/$(am__dirstamp) \ attrib/$(DEPDIR)/$(am__dirstamp) attrib/utils.$(OBJEXT): attrib/$(am__dirstamp) \ @@ -2169,12 +2467,6 @@ client/main.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) client/agent.$(OBJEXT): client/$(am__dirstamp) \ client/$(DEPDIR)/$(am__dirstamp) -monitor/$(am__dirstamp): - @$(MKDIR_P) monitor - @: > monitor/$(am__dirstamp) -monitor/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) monitor/$(DEPDIR) - @: > monitor/$(DEPDIR)/$(am__dirstamp) monitor/uuid.$(OBJEXT): monitor/$(am__dirstamp) \ monitor/$(DEPDIR)/$(am__dirstamp) client/bluetoothctl$(EXEEXT): $(client_bluetoothctl_OBJECTS) $(client_bluetoothctl_DEPENDENCIES) $(EXTRA_client_bluetoothctl_DEPENDENCIES) client/$(am__dirstamp) @@ -2188,8 +2480,6 @@ emulator/$(DEPDIR)/$(am__dirstamp): @: > emulator/$(DEPDIR)/$(am__dirstamp) emulator/b1ee.$(OBJEXT): emulator/$(am__dirstamp) \ emulator/$(DEPDIR)/$(am__dirstamp) -monitor/mainloop.$(OBJEXT): monitor/$(am__dirstamp) \ - monitor/$(DEPDIR)/$(am__dirstamp) emulator/b1ee$(EXEEXT): $(emulator_b1ee_OBJECTS) $(emulator_b1ee_DEPENDENCIES) $(EXTRA_emulator_b1ee_DEPENDENCIES) emulator/$(am__dirstamp) @rm -f emulator/b1ee$(EXEEXT) $(AM_V_CCLD)$(LINK) $(emulator_b1ee_OBJECTS) $(emulator_b1ee_LDADD) $(LIBS) @@ -2649,12 +2939,6 @@ src/bluetoothd-dbus-common.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) src/bluetoothd-eir.$(OBJEXT): src/$(am__dirstamp) \ src/$(DEPDIR)/$(am__dirstamp) -src/shared/$(am__dirstamp): - @$(MKDIR_P) src/shared - @: > src/shared/$(am__dirstamp) -src/shared/$(DEPDIR)/$(am__dirstamp): - @$(MKDIR_P) src/shared/$(DEPDIR) - @: > src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/bluetoothd-util.$(OBJEXT): src/shared/$(am__dirstamp) \ src/shared/$(DEPDIR)/$(am__dirstamp) src/shared/bluetoothd-mgmt.$(OBJEXT): src/shared/$(am__dirstamp) \ @@ -2730,13 +3014,6 @@ tools/btiotest$(EXEEXT): $(tools_btiotest_OBJECTS) $(tools_btiotest_DEPENDENCIES $(AM_V_CCLD)$(LINK) $(tools_btiotest_OBJECTS) $(tools_btiotest_LDADD) $(LIBS) tools/btmgmt.$(OBJEXT): tools/$(am__dirstamp) \ tools/$(DEPDIR)/$(am__dirstamp) -src/glib-helper.$(OBJEXT): src/$(am__dirstamp) \ - src/$(DEPDIR)/$(am__dirstamp) -src/eir.$(OBJEXT): src/$(am__dirstamp) src/$(DEPDIR)/$(am__dirstamp) -src/shared/util.$(OBJEXT): src/shared/$(am__dirstamp) \ - src/shared/$(DEPDIR)/$(am__dirstamp) -src/shared/mgmt.$(OBJEXT): src/shared/$(am__dirstamp) \ - src/shared/$(DEPDIR)/$(am__dirstamp) tools/btmgmt$(EXEEXT): $(tools_btmgmt_OBJECTS) $(tools_btmgmt_DEPENDENCIES) $(EXTRA_tools_btmgmt_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/btmgmt$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_btmgmt_OBJECTS) $(tools_btmgmt_LDADD) $(LIBS) @@ -2953,6 +3230,11 @@ src/sdp-xml.$(OBJEXT): src/$(am__dirstamp) \ tools/sdptool$(EXEEXT): $(tools_sdptool_OBJECTS) $(tools_sdptool_DEPENDENCIES) $(EXTRA_tools_sdptool_DEPENDENCIES) tools/$(am__dirstamp) @rm -f tools/sdptool$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tools_sdptool_OBJECTS) $(tools_sdptool_LDADD) $(LIBS) +tools/smp-tester.$(OBJEXT): tools/$(am__dirstamp) \ + tools/$(DEPDIR)/$(am__dirstamp) +tools/smp-tester$(EXEEXT): $(tools_smp_tester_OBJECTS) $(tools_smp_tester_DEPENDENCIES) $(EXTRA_tools_smp_tester_DEPENDENCIES) tools/$(am__dirstamp) + @rm -f tools/smp-tester$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tools_smp_tester_OBJECTS) $(tools_smp_tester_LDADD) $(LIBS) unit/$(am__dirstamp): @$(MKDIR_P) unit @: > unit/$(am__dirstamp) @@ -3013,12 +3295,6 @@ unit/test-mgmt$(EXEEXT): $(unit_test_mgmt_OBJECTS) $(unit_test_mgmt_DEPENDENCIES $(AM_V_CCLD)$(LINK) $(unit_test_mgmt_OBJECTS) $(unit_test_mgmt_LDADD) $(LIBS) unit/test-sdp.$(OBJEXT): unit/$(am__dirstamp) \ unit/$(DEPDIR)/$(am__dirstamp) -src/sdpd-database.$(OBJEXT): src/$(am__dirstamp) \ - src/$(DEPDIR)/$(am__dirstamp) -src/sdpd-service.$(OBJEXT): src/$(am__dirstamp) \ - src/$(DEPDIR)/$(am__dirstamp) -src/sdpd-request.$(OBJEXT): src/$(am__dirstamp) \ - src/$(DEPDIR)/$(am__dirstamp) unit/test-sdp$(EXEEXT): $(unit_test_sdp_OBJECTS) $(unit_test_sdp_DEPENDENCIES) $(EXTRA_unit_test_sdp_DEPENDENCIES) unit/$(am__dirstamp) @rm -f unit/test-sdp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(unit_test_sdp_OBJECTS) $(unit_test_sdp_LDADD) $(LIBS) @@ -3072,7 +3348,40 @@ uninstall-testSCRIPTS: mostlyclean-compile: -rm -f *.$(OBJEXT) + -rm -f android/a2dp.$(OBJEXT) + -rm -f android/android_haltest-hal-utils.$(OBJEXT) + -rm -f android/android_libhal_internal_la-hal-a2dp.$(OBJEXT) + -rm -f android/android_libhal_internal_la-hal-a2dp.lo + -rm -f android/android_libhal_internal_la-hal-bluetooth.$(OBJEXT) + -rm -f android/android_libhal_internal_la-hal-bluetooth.lo + -rm -f android/android_libhal_internal_la-hal-hidhost.$(OBJEXT) + -rm -f android/android_libhal_internal_la-hal-hidhost.lo + -rm -f android/android_libhal_internal_la-hal-ipc.$(OBJEXT) + -rm -f android/android_libhal_internal_la-hal-ipc.lo + -rm -f android/android_libhal_internal_la-hal-pan.$(OBJEXT) + -rm -f android/android_libhal_internal_la-hal-pan.lo + -rm -f android/android_libhal_internal_la-hal-sock.$(OBJEXT) + -rm -f android/android_libhal_internal_la-hal-sock.lo + -rm -f android/bluetooth.$(OBJEXT) + -rm -f android/client/android_haltest-haltest.$(OBJEXT) + -rm -f android/client/android_haltest-history.$(OBJEXT) + -rm -f android/client/android_haltest-hwmodule.$(OBJEXT) + -rm -f android/client/android_haltest-if-av.$(OBJEXT) + -rm -f android/client/android_haltest-if-bt.$(OBJEXT) + -rm -f android/client/android_haltest-if-gatt.$(OBJEXT) + -rm -f android/client/android_haltest-if-hf.$(OBJEXT) + -rm -f android/client/android_haltest-if-hh.$(OBJEXT) + -rm -f android/client/android_haltest-if-pan.$(OBJEXT) + -rm -f android/client/android_haltest-if-sock.$(OBJEXT) + -rm -f android/client/android_haltest-pollhandler.$(OBJEXT) + -rm -f android/client/android_haltest-tabcompletion.$(OBJEXT) + -rm -f android/client/android_haltest-terminal.$(OBJEXT) + -rm -f android/hidhost.$(OBJEXT) + -rm -f android/ipc.$(OBJEXT) -rm -f android/main.$(OBJEXT) + -rm -f android/pan.$(OBJEXT) + -rm -f android/socket.$(OBJEXT) + -rm -f android/system-emulator.$(OBJEXT) -rm -f attrib/att.$(OBJEXT) -rm -f attrib/bluetoothd-att.$(OBJEXT) -rm -f attrib/bluetoothd-gatt-service.$(OBJEXT) @@ -3258,9 +3567,11 @@ mostlyclean-compile: -rm -f src/glib-helper.$(OBJEXT) -rm -f src/log.$(OBJEXT) -rm -f src/oui.$(OBJEXT) + -rm -f src/sdp-client.$(OBJEXT) -rm -f src/sdp-xml.$(OBJEXT) -rm -f src/sdpd-database.$(OBJEXT) -rm -f src/sdpd-request.$(OBJEXT) + -rm -f src/sdpd-server.$(OBJEXT) -rm -f src/sdpd-service.$(OBJEXT) -rm -f src/shared/bluetoothd-mgmt.$(OBJEXT) -rm -f src/shared/bluetoothd-util.$(OBJEXT) @@ -3342,6 +3653,7 @@ mostlyclean-compile: -rm -f tools/sco-tester.$(OBJEXT) -rm -f tools/scotest.$(OBJEXT) -rm -f tools/sdptool.$(OBJEXT) + -rm -f tools/smp-tester.$(OBJEXT) -rm -f tools/ubcsp.$(OBJEXT) -rm -f unit/test-crc.$(OBJEXT) -rm -f unit/test-eir.$(OBJEXT) @@ -3361,7 +3673,34 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/a2dp.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_haltest-hal-utils.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/bluetooth.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/hidhost.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/ipc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/pan.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/socket.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/$(DEPDIR)/system-emulator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-haltest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-history.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-hwmodule.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-av.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-bt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-gatt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-hf.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-hh.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-pan.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-if-sock.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-pollhandler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-tabcompletion.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@android/client/$(DEPDIR)/android_haltest-terminal.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/att.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-att.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@attrib/$(DEPDIR)/bluetoothd-gatt-service.Po@am__quote@ @@ -3537,9 +3876,11 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/glib-helper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/log.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/oui.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdp-client.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdp-xml.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-database.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-request.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-server.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/sdpd-service.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/textfile.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/shared/$(DEPDIR)/bluetoothd-mgmt.Po@am__quote@ @@ -3597,6 +3938,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sco-tester.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/scotest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/sdptool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/smp-tester.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/$(DEPDIR)/ubcsp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/amp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@tools/parser/$(DEPDIR)/att.Po@am__quote@ @@ -3661,6 +4003,48 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< +android/android_libhal_internal_la-hal-bluetooth.lo: android/hal-bluetooth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-bluetooth.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Tpo -c -o android/android_libhal_internal_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-bluetooth.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-bluetooth.c' object='android/android_libhal_internal_la-hal-bluetooth.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-bluetooth.lo `test -f 'android/hal-bluetooth.c' || echo '$(srcdir)/'`android/hal-bluetooth.c + +android/android_libhal_internal_la-hal-sock.lo: android/hal-sock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-sock.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Tpo -c -o android/android_libhal_internal_la-hal-sock.lo `test -f 'android/hal-sock.c' || echo '$(srcdir)/'`android/hal-sock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-sock.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-sock.c' object='android/android_libhal_internal_la-hal-sock.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-sock.lo `test -f 'android/hal-sock.c' || echo '$(srcdir)/'`android/hal-sock.c + +android/android_libhal_internal_la-hal-hidhost.lo: android/hal-hidhost.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-hidhost.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Tpo -c -o android/android_libhal_internal_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-hidhost.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-hidhost.c' object='android/android_libhal_internal_la-hal-hidhost.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-hidhost.lo `test -f 'android/hal-hidhost.c' || echo '$(srcdir)/'`android/hal-hidhost.c + +android/android_libhal_internal_la-hal-pan.lo: android/hal-pan.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-pan.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Tpo -c -o android/android_libhal_internal_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-pan.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-pan.c' object='android/android_libhal_internal_la-hal-pan.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-pan.lo `test -f 'android/hal-pan.c' || echo '$(srcdir)/'`android/hal-pan.c + +android/android_libhal_internal_la-hal-a2dp.lo: android/hal-a2dp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-a2dp.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Tpo -c -o android/android_libhal_internal_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-a2dp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-a2dp.c' object='android/android_libhal_internal_la-hal-a2dp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-a2dp.lo `test -f 'android/hal-a2dp.c' || echo '$(srcdir)/'`android/hal-a2dp.c + +android/android_libhal_internal_la-hal-ipc.lo: android/hal-ipc.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT android/android_libhal_internal_la-hal-ipc.lo -MD -MP -MF android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Tpo -c -o android/android_libhal_internal_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Tpo android/$(DEPDIR)/android_libhal_internal_la-hal-ipc.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-ipc.c' object='android/android_libhal_internal_la-hal-ipc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(android_libhal_internal_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o android/android_libhal_internal_la-hal-ipc.lo `test -f 'android/hal-ipc.c' || echo '$(srcdir)/'`android/hal-ipc.c + plugins/plugins_external_dummy_la-external-dummy.lo: plugins/external-dummy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(plugins_external_dummy_la_CFLAGS) $(CFLAGS) -MT plugins/plugins_external_dummy_la-external-dummy.lo -MD -MP -MF plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Tpo -c -o plugins/plugins_external_dummy_la-external-dummy.lo `test -f 'plugins/external-dummy.c' || echo '$(srcdir)/'`plugins/external-dummy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Tpo plugins/$(DEPDIR)/plugins_external_dummy_la-external-dummy.Plo @@ -3668,6 +4052,202 @@ 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 +android/client/android_haltest-haltest.o: android/client/haltest.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-haltest.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-haltest.Tpo -c -o android/client/android_haltest-haltest.o `test -f 'android/client/haltest.c' || echo '$(srcdir)/'`android/client/haltest.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-haltest.Tpo android/client/$(DEPDIR)/android_haltest-haltest.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/haltest.c' object='android/client/android_haltest-haltest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-haltest.o `test -f 'android/client/haltest.c' || echo '$(srcdir)/'`android/client/haltest.c + +android/client/android_haltest-haltest.obj: 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.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-haltest.Tpo -c -o android/client/android_haltest-haltest.obj `if test -f 'android/client/haltest.c'; then $(CYGPATH_W) 'android/client/haltest.c'; else $(CYGPATH_W) '$(srcdir)/android/client/haltest.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-haltest.Tpo android/client/$(DEPDIR)/android_haltest-haltest.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/haltest.c' object='android/client/android_haltest-haltest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-haltest.obj `if test -f 'android/client/haltest.c'; then $(CYGPATH_W) 'android/client/haltest.c'; else $(CYGPATH_W) '$(srcdir)/android/client/haltest.c'; fi` + +android/client/android_haltest-pollhandler.o: android/client/pollhandler.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-pollhandler.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-pollhandler.Tpo -c -o android/client/android_haltest-pollhandler.o `test -f 'android/client/pollhandler.c' || echo '$(srcdir)/'`android/client/pollhandler.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-pollhandler.Tpo android/client/$(DEPDIR)/android_haltest-pollhandler.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/pollhandler.c' object='android/client/android_haltest-pollhandler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-pollhandler.o `test -f 'android/client/pollhandler.c' || echo '$(srcdir)/'`android/client/pollhandler.c + +android/client/android_haltest-pollhandler.obj: android/client/pollhandler.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-pollhandler.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-pollhandler.Tpo -c -o android/client/android_haltest-pollhandler.obj `if test -f 'android/client/pollhandler.c'; then $(CYGPATH_W) 'android/client/pollhandler.c'; else $(CYGPATH_W) '$(srcdir)/android/client/pollhandler.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-pollhandler.Tpo android/client/$(DEPDIR)/android_haltest-pollhandler.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/pollhandler.c' object='android/client/android_haltest-pollhandler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-pollhandler.obj `if test -f 'android/client/pollhandler.c'; then $(CYGPATH_W) 'android/client/pollhandler.c'; else $(CYGPATH_W) '$(srcdir)/android/client/pollhandler.c'; fi` + +android/client/android_haltest-terminal.o: android/client/terminal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-terminal.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-terminal.Tpo -c -o android/client/android_haltest-terminal.o `test -f 'android/client/terminal.c' || echo '$(srcdir)/'`android/client/terminal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-terminal.Tpo android/client/$(DEPDIR)/android_haltest-terminal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/terminal.c' object='android/client/android_haltest-terminal.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-terminal.o `test -f 'android/client/terminal.c' || echo '$(srcdir)/'`android/client/terminal.c + +android/client/android_haltest-terminal.obj: android/client/terminal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-terminal.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-terminal.Tpo -c -o android/client/android_haltest-terminal.obj `if test -f 'android/client/terminal.c'; then $(CYGPATH_W) 'android/client/terminal.c'; else $(CYGPATH_W) '$(srcdir)/android/client/terminal.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-terminal.Tpo android/client/$(DEPDIR)/android_haltest-terminal.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/terminal.c' object='android/client/android_haltest-terminal.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-terminal.obj `if test -f 'android/client/terminal.c'; then $(CYGPATH_W) 'android/client/terminal.c'; else $(CYGPATH_W) '$(srcdir)/android/client/terminal.c'; fi` + +android/client/android_haltest-history.o: android/client/history.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-history.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-history.Tpo -c -o android/client/android_haltest-history.o `test -f 'android/client/history.c' || echo '$(srcdir)/'`android/client/history.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-history.Tpo android/client/$(DEPDIR)/android_haltest-history.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/history.c' object='android/client/android_haltest-history.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-history.o `test -f 'android/client/history.c' || echo '$(srcdir)/'`android/client/history.c + +android/client/android_haltest-history.obj: android/client/history.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-history.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-history.Tpo -c -o android/client/android_haltest-history.obj `if test -f 'android/client/history.c'; then $(CYGPATH_W) 'android/client/history.c'; else $(CYGPATH_W) '$(srcdir)/android/client/history.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-history.Tpo android/client/$(DEPDIR)/android_haltest-history.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/history.c' object='android/client/android_haltest-history.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-history.obj `if test -f 'android/client/history.c'; then $(CYGPATH_W) 'android/client/history.c'; else $(CYGPATH_W) '$(srcdir)/android/client/history.c'; fi` + +android/client/android_haltest-tabcompletion.o: android/client/tabcompletion.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-tabcompletion.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-tabcompletion.Tpo -c -o android/client/android_haltest-tabcompletion.o `test -f 'android/client/tabcompletion.c' || echo '$(srcdir)/'`android/client/tabcompletion.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-tabcompletion.Tpo android/client/$(DEPDIR)/android_haltest-tabcompletion.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/tabcompletion.c' object='android/client/android_haltest-tabcompletion.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-tabcompletion.o `test -f 'android/client/tabcompletion.c' || echo '$(srcdir)/'`android/client/tabcompletion.c + +android/client/android_haltest-tabcompletion.obj: android/client/tabcompletion.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-tabcompletion.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-tabcompletion.Tpo -c -o android/client/android_haltest-tabcompletion.obj `if test -f 'android/client/tabcompletion.c'; then $(CYGPATH_W) 'android/client/tabcompletion.c'; else $(CYGPATH_W) '$(srcdir)/android/client/tabcompletion.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-tabcompletion.Tpo android/client/$(DEPDIR)/android_haltest-tabcompletion.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/tabcompletion.c' object='android/client/android_haltest-tabcompletion.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-tabcompletion.obj `if test -f 'android/client/tabcompletion.c'; then $(CYGPATH_W) 'android/client/tabcompletion.c'; else $(CYGPATH_W) '$(srcdir)/android/client/tabcompletion.c'; fi` + +android/client/android_haltest-if-av.o: android/client/if-av.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-av.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-av.Tpo -c -o android/client/android_haltest-if-av.o `test -f 'android/client/if-av.c' || echo '$(srcdir)/'`android/client/if-av.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-av.Tpo android/client/$(DEPDIR)/android_haltest-if-av.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-av.c' object='android/client/android_haltest-if-av.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-av.o `test -f 'android/client/if-av.c' || echo '$(srcdir)/'`android/client/if-av.c + +android/client/android_haltest-if-av.obj: android/client/if-av.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-av.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-av.Tpo -c -o android/client/android_haltest-if-av.obj `if test -f 'android/client/if-av.c'; then $(CYGPATH_W) 'android/client/if-av.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-av.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-av.Tpo android/client/$(DEPDIR)/android_haltest-if-av.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-av.c' object='android/client/android_haltest-if-av.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-av.obj `if test -f 'android/client/if-av.c'; then $(CYGPATH_W) 'android/client/if-av.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-av.c'; fi` + +android/client/android_haltest-if-bt.o: android/client/if-bt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-bt.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-bt.Tpo -c -o android/client/android_haltest-if-bt.o `test -f 'android/client/if-bt.c' || echo '$(srcdir)/'`android/client/if-bt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-bt.Tpo android/client/$(DEPDIR)/android_haltest-if-bt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-bt.c' object='android/client/android_haltest-if-bt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-bt.o `test -f 'android/client/if-bt.c' || echo '$(srcdir)/'`android/client/if-bt.c + +android/client/android_haltest-if-bt.obj: android/client/if-bt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-bt.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-bt.Tpo -c -o android/client/android_haltest-if-bt.obj `if test -f 'android/client/if-bt.c'; then $(CYGPATH_W) 'android/client/if-bt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-bt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-bt.Tpo android/client/$(DEPDIR)/android_haltest-if-bt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-bt.c' object='android/client/android_haltest-if-bt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-bt.obj `if test -f 'android/client/if-bt.c'; then $(CYGPATH_W) 'android/client/if-bt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-bt.c'; fi` + +android/client/android_haltest-if-gatt.o: android/client/if-gatt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-gatt.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-gatt.Tpo -c -o android/client/android_haltest-if-gatt.o `test -f 'android/client/if-gatt.c' || echo '$(srcdir)/'`android/client/if-gatt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-gatt.Tpo android/client/$(DEPDIR)/android_haltest-if-gatt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-gatt.c' object='android/client/android_haltest-if-gatt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-gatt.o `test -f 'android/client/if-gatt.c' || echo '$(srcdir)/'`android/client/if-gatt.c + +android/client/android_haltest-if-gatt.obj: android/client/if-gatt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-gatt.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-gatt.Tpo -c -o android/client/android_haltest-if-gatt.obj `if test -f 'android/client/if-gatt.c'; then $(CYGPATH_W) 'android/client/if-gatt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-gatt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-gatt.Tpo android/client/$(DEPDIR)/android_haltest-if-gatt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-gatt.c' object='android/client/android_haltest-if-gatt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-gatt.obj `if test -f 'android/client/if-gatt.c'; then $(CYGPATH_W) 'android/client/if-gatt.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-gatt.c'; fi` + +android/client/android_haltest-if-hf.o: android/client/if-hf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hf.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hf.Tpo -c -o android/client/android_haltest-if-hf.o `test -f 'android/client/if-hf.c' || echo '$(srcdir)/'`android/client/if-hf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hf.Tpo android/client/$(DEPDIR)/android_haltest-if-hf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hf.c' object='android/client/android_haltest-if-hf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hf.o `test -f 'android/client/if-hf.c' || echo '$(srcdir)/'`android/client/if-hf.c + +android/client/android_haltest-if-hf.obj: android/client/if-hf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hf.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hf.Tpo -c -o android/client/android_haltest-if-hf.obj `if test -f 'android/client/if-hf.c'; then $(CYGPATH_W) 'android/client/if-hf.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hf.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hf.Tpo android/client/$(DEPDIR)/android_haltest-if-hf.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hf.c' object='android/client/android_haltest-if-hf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hf.obj `if test -f 'android/client/if-hf.c'; then $(CYGPATH_W) 'android/client/if-hf.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hf.c'; fi` + +android/client/android_haltest-if-hh.o: android/client/if-hh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hh.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hh.Tpo -c -o android/client/android_haltest-if-hh.o `test -f 'android/client/if-hh.c' || echo '$(srcdir)/'`android/client/if-hh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hh.Tpo android/client/$(DEPDIR)/android_haltest-if-hh.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hh.c' object='android/client/android_haltest-if-hh.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hh.o `test -f 'android/client/if-hh.c' || echo '$(srcdir)/'`android/client/if-hh.c + +android/client/android_haltest-if-hh.obj: android/client/if-hh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-hh.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-hh.Tpo -c -o android/client/android_haltest-if-hh.obj `if test -f 'android/client/if-hh.c'; then $(CYGPATH_W) 'android/client/if-hh.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hh.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-hh.Tpo android/client/$(DEPDIR)/android_haltest-if-hh.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-hh.c' object='android/client/android_haltest-if-hh.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-hh.obj `if test -f 'android/client/if-hh.c'; then $(CYGPATH_W) 'android/client/if-hh.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-hh.c'; fi` + +android/client/android_haltest-if-pan.o: android/client/if-pan.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-pan.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-pan.Tpo -c -o android/client/android_haltest-if-pan.o `test -f 'android/client/if-pan.c' || echo '$(srcdir)/'`android/client/if-pan.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-pan.Tpo android/client/$(DEPDIR)/android_haltest-if-pan.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-pan.c' object='android/client/android_haltest-if-pan.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-pan.o `test -f 'android/client/if-pan.c' || echo '$(srcdir)/'`android/client/if-pan.c + +android/client/android_haltest-if-pan.obj: android/client/if-pan.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-pan.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-pan.Tpo -c -o android/client/android_haltest-if-pan.obj `if test -f 'android/client/if-pan.c'; then $(CYGPATH_W) 'android/client/if-pan.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-pan.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-pan.Tpo android/client/$(DEPDIR)/android_haltest-if-pan.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-pan.c' object='android/client/android_haltest-if-pan.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-pan.obj `if test -f 'android/client/if-pan.c'; then $(CYGPATH_W) 'android/client/if-pan.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-pan.c'; fi` + +android/client/android_haltest-if-sock.o: android/client/if-sock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-sock.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-sock.Tpo -c -o android/client/android_haltest-if-sock.o `test -f 'android/client/if-sock.c' || echo '$(srcdir)/'`android/client/if-sock.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-sock.Tpo android/client/$(DEPDIR)/android_haltest-if-sock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-sock.c' object='android/client/android_haltest-if-sock.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-sock.o `test -f 'android/client/if-sock.c' || echo '$(srcdir)/'`android/client/if-sock.c + +android/client/android_haltest-if-sock.obj: android/client/if-sock.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-if-sock.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-if-sock.Tpo -c -o android/client/android_haltest-if-sock.obj `if test -f 'android/client/if-sock.c'; then $(CYGPATH_W) 'android/client/if-sock.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sock.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-if-sock.Tpo android/client/$(DEPDIR)/android_haltest-if-sock.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/if-sock.c' object='android/client/android_haltest-if-sock.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-if-sock.obj `if test -f 'android/client/if-sock.c'; then $(CYGPATH_W) 'android/client/if-sock.c'; else $(CYGPATH_W) '$(srcdir)/android/client/if-sock.c'; fi` + +android/client/android_haltest-hwmodule.o: android/client/hwmodule.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-hwmodule.o -MD -MP -MF android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo -c -o android/client/android_haltest-hwmodule.o `test -f 'android/client/hwmodule.c' || echo '$(srcdir)/'`android/client/hwmodule.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo android/client/$(DEPDIR)/android_haltest-hwmodule.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/hwmodule.c' object='android/client/android_haltest-hwmodule.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-hwmodule.o `test -f 'android/client/hwmodule.c' || echo '$(srcdir)/'`android/client/hwmodule.c + +android/client/android_haltest-hwmodule.obj: android/client/hwmodule.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/client/android_haltest-hwmodule.obj -MD -MP -MF android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo -c -o android/client/android_haltest-hwmodule.obj `if test -f 'android/client/hwmodule.c'; then $(CYGPATH_W) 'android/client/hwmodule.c'; else $(CYGPATH_W) '$(srcdir)/android/client/hwmodule.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/client/$(DEPDIR)/android_haltest-hwmodule.Tpo android/client/$(DEPDIR)/android_haltest-hwmodule.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/client/hwmodule.c' object='android/client/android_haltest-hwmodule.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/client/android_haltest-hwmodule.obj `if test -f 'android/client/hwmodule.c'; then $(CYGPATH_W) 'android/client/hwmodule.c'; else $(CYGPATH_W) '$(srcdir)/android/client/hwmodule.c'; fi` + +android/android_haltest-hal-utils.o: android/hal-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/android_haltest-hal-utils.o -MD -MP -MF android/$(DEPDIR)/android_haltest-hal-utils.Tpo -c -o android/android_haltest-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_haltest-hal-utils.Tpo android/$(DEPDIR)/android_haltest-hal-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-utils.c' object='android/android_haltest-hal-utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/android_haltest-hal-utils.o `test -f 'android/hal-utils.c' || echo '$(srcdir)/'`android/hal-utils.c + +android/android_haltest-hal-utils.obj: android/hal-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -MT android/android_haltest-hal-utils.obj -MD -MP -MF android/$(DEPDIR)/android_haltest-hal-utils.Tpo -c -o android/android_haltest-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) android/$(DEPDIR)/android_haltest-hal-utils.Tpo android/$(DEPDIR)/android_haltest-hal-utils.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='android/hal-utils.c' object='android/android_haltest-hal-utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(android_haltest_CFLAGS) $(CFLAGS) -c -o android/android_haltest-hal-utils.obj `if test -f 'android/hal-utils.c'; then $(CYGPATH_W) 'android/hal-utils.c'; else $(CYGPATH_W) '$(srcdir)/android/hal-utils.c'; fi` + btio/obexd-btio.o: btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(obexd_src_obexd_CPPFLAGS) $(CPPFLAGS) $(obexd_src_obexd_CFLAGS) $(CFLAGS) -MT btio/obexd-btio.o -MD -MP -MF btio/$(DEPDIR)/obexd-btio.Tpo -c -o btio/obexd-btio.o `test -f 'btio/btio.c' || echo '$(srcdir)/'`btio/btio.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) btio/$(DEPDIR)/obexd-btio.Tpo btio/$(DEPDIR)/obexd-btio.Po @@ -6028,6 +6608,8 @@ distclean-generic: -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f android/$(DEPDIR)/$(am__dirstamp) -rm -f android/$(am__dirstamp) + -rm -f android/client/$(DEPDIR)/$(am__dirstamp) + -rm -f android/client/$(am__dirstamp) -rm -f attrib/$(DEPDIR)/$(am__dirstamp) -rm -f attrib/$(am__dirstamp) -rm -f btio/$(DEPDIR)/$(am__dirstamp) @@ -6111,7 +6693,7 @@ clean-am: clean-binPROGRAMS clean-cupsPROGRAMS clean-generic \ distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf android/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR) + -rm -rf android/$(DEPDIR) android/client/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags @@ -6166,7 +6748,7 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache - -rm -rf android/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR) + -rm -rf android/$(DEPDIR) android/client/$(DEPDIR) attrib/$(DEPDIR) btio/$(DEPDIR) client/$(DEPDIR) emulator/$(DEPDIR) gdbus/$(DEPDIR) gobex/$(DEPDIR) lib/$(DEPDIR) monitor/$(DEPDIR) obexd/client/$(DEPDIR) obexd/plugins/$(DEPDIR) obexd/src/$(DEPDIR) plugins/$(DEPDIR) profiles/alert/$(DEPDIR) profiles/audio/$(DEPDIR) profiles/cups/$(DEPDIR) profiles/cyclingspeed/$(DEPDIR) profiles/deviceinfo/$(DEPDIR) profiles/gatt/$(DEPDIR) profiles/health/$(DEPDIR) profiles/heartrate/$(DEPDIR) profiles/iap/$(DEPDIR) profiles/input/$(DEPDIR) profiles/network/$(DEPDIR) profiles/proximity/$(DEPDIR) profiles/sap/$(DEPDIR) profiles/scanparam/$(DEPDIR) profiles/thermometer/$(DEPDIR) profiles/time/$(DEPDIR) src/$(DEPDIR) src/shared/$(DEPDIR) tools/$(DEPDIR) tools/parser/$(DEPDIR) unit/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/Makefile.tools b/Makefile.tools index 840b95c..78034f5 100644 --- a/Makefile.tools +++ b/Makefile.tools @@ -33,7 +33,8 @@ endif if EXPERIMENTAL noinst_PROGRAMS += emulator/btvirt emulator/b1ee \ tools/mgmt-tester tools/gap-tester \ - tools/l2cap-tester tools/sco-tester + tools/l2cap-tester tools/sco-tester \ + tools/smp-tester emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \ monitor/mainloop.h monitor/mainloop.c \ @@ -63,6 +64,15 @@ tools_l2cap_tester_SOURCES = tools/l2cap-tester.c monitor/bt.h \ src/shared/tester.h src/shared/tester.c tools_l2cap_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ +tools_smp_tester_SOURCES = tools/smp-tester.c monitor/bt.h \ + emulator/btdev.h emulator/btdev.c \ + emulator/bthost.h emulator/bthost.c \ + src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/hciemu.h src/shared/hciemu.c \ + src/shared/tester.h src/shared/tester.c +tools_smp_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + tools_gap_tester_SOURCES = tools/gap-tester.c monitor/bt.h \ emulator/btdev.h emulator/btdev.c \ emulator/bthost.h emulator/bthost.c \ diff --git a/android/Android.mk b/android/Android.mk index 0e025ac..616a338 100644 --- a/android/Android.mk +++ b/android/Android.mk @@ -6,6 +6,13 @@ BLUEZ_VERSION := $(shell grep ^AC_INIT $(LOCAL_PATH)/../configure.ac | cpp -P -D # Specify pathmap for glib pathmap_INCL += glib:external/bluetooth/glib +# Specify common compiler flags +BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \ + -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION) + +# Disable warnings enabled by Android but not enabled in autotools build +BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith -Wno-missing-field-initializers + # # Android BlueZ daemon (bluetoothd) # @@ -14,18 +21,55 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ main.c \ - log.c \ + bluetooth.c \ + hidhost.c \ + socket.c \ + ipc.c ipc.h \ + a2dp.c \ + pan.c \ + ../src/log.c \ + ../src/shared/mgmt.c \ + ../src/shared/util.c \ + ../src/sdpd-database.c \ + ../src/sdpd-service.c \ + ../src/sdpd-request.c \ + ../src/sdpd-server.c \ + ../src/glib-helper.c \ + ../src/eir.c \ + ../lib/sdp.c \ + ../lib/bluetooth.c \ + ../lib/hci.c \ + ../btio/btio.c \ + ../src/sdp-client.c \ LOCAL_C_INCLUDES := \ $(call include-path-for, glib) \ $(call include-path-for, glib)/glib \ + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/../ \ $(LOCAL_PATH)/../src \ + $(LOCAL_PATH)/../lib \ -LOCAL_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) LOCAL_SHARED_LIBRARIES := \ libglib \ +lib_headers := \ + bluetooth.h \ + hci.h \ + hci_lib.h \ + l2cap.h \ + sdp_lib.h \ + sdp.h \ + rfcomm.h \ + sco.h \ + +$(shell mkdir -p $(LOCAL_PATH)/../lib/bluetooth) + +$(foreach file,$(lib_headers), $(shell ln -sf ../$(file) $(LOCAL_PATH)/../lib/bluetooth/$(file))) + LOCAL_MODULE := bluetoothd include $(BUILD_EXECUTABLE) @@ -37,15 +81,122 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - hal_bluetooth.c \ - hal_bt_sock.c \ + hal-ipc.c \ + hal-bluetooth.c \ + hal-sock.c \ + hal-hidhost.c \ + hal-pan.c \ + hal-a2dp.c \ + hal-utils.c \ + +LOCAL_C_INCLUDES += \ + $(call include-path-for, system-core) \ + $(call include-path-for, libhardware) \ LOCAL_SHARED_LIBRARIES := \ libcutils \ +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) \ + LOCAL_MODULE := bluetooth.default LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_REQUIRED_MODULES := haltest bluetoothd include $(BUILD_SHARED_LIBRARY) + +# +# haltest +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + client/haltest.c \ + client/pollhandler.c \ + client/terminal.c \ + client/history.c \ + client/tabcompletion.c \ + client/if-av.c \ + client/if-bt.c \ + client/if-hf.c \ + client/if-hh.c \ + client/if-pan.c \ + client/if-sock.c \ + hal-utils.c \ + +ANDROID_4_3_OR_ABOVE := $(shell echo 0 | awk -v v=$(PLATFORM_SDK_VERSION) 'END {print (v > 17) ? 1 : 0}') + +ifeq ($(ANDROID_4_3_OR_ABOVE), 1) +LOCAL_SRC_FILES += \ + client/if-gatt.c +endif + +LOCAL_C_INCLUDES += \ + $(call include-path-for, system-core) \ + $(call include-path-for, libhardware) \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_SHARED_LIBRARIES := libhardware + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := haltest + +include $(BUILD_EXECUTABLE) + +# +# btmon +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + ../monitor/main.c \ + ../monitor/bt.h \ + ../monitor/mainloop.h \ + ../monitor/mainloop.c \ + ../monitor/display.h \ + ../monitor/display.c \ + ../monitor/hcidump.h \ + ../monitor/hcidump.c \ + ../monitor/btsnoop.h \ + ../monitor/btsnoop.c \ + ../monitor/control.h \ + ../monitor/control.c \ + ../monitor/packet.h \ + ../monitor/packet.c \ + ../monitor/l2cap.h \ + ../monitor/l2cap.c \ + ../monitor/uuid.h \ + ../monitor/uuid.c \ + ../monitor/sdp.h \ + ../monitor/sdp.c \ + ../monitor/vendor.h \ + ../monitor/vendor.c \ + ../monitor/lmp.h \ + ../monitor/lmp.c \ + ../monitor/crc.h \ + ../monitor/crc.c \ + ../monitor/ll.h \ + ../monitor/ll.c \ + ../lib/hci.c \ + ../lib/bluetooth.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/.. \ + $(LOCAL_PATH)/../lib \ + $(LOCAL_PATH)/../src/shared \ + +LOCAL_C_INCLUDES += \ + $(call include-path-for, glib) \ + $(call include-path-for, glib)/glib \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE := btmon + +include $(BUILD_EXECUTABLE) diff --git a/android/Makefile.am b/android/Makefile.am new file mode 100644 index 0000000..4e0c9ed --- /dev/null +++ b/android/Makefile.am @@ -0,0 +1,86 @@ +if ANDROID +noinst_PROGRAMS += android/system-emulator + +android_system_emulator_SOURCES = android/system-emulator.c \ + monitor/mainloop.h monitor/mainloop.c + +noinst_PROGRAMS += android/bluetoothd + +android_bluetoothd_SOURCES = android/main.c \ + src/log.c \ + android/hal-msg.h \ + android/utils.h \ + src/sdpd-database.c src/sdpd-server.c \ + src/sdpd-service.c src/sdpd-request.c \ + src/glib-helper.h src/glib-helper.c \ + src/eir.h src/eir.c \ + 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_bluetoothd_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + +noinst_LTLIBRARIES += android/libhal-internal.la + +android_libhal_internal_la_SOURCES = android/hal.h android/hal-bluetooth.c \ + android/hal-sock.c \ + android/hal-hidhost.c \ + android/hal-pan.c \ + android/hal-a2dp.c \ + android/hardware/bluetooth.h \ + android/hardware/bt_av.h \ + android/hardware/bt_gatt.h \ + android/hardware/bt_gatt_client.h \ + android/hardware/bt_gatt_server.h \ + android/hardware/bt_gatt_types.h \ + android/hardware/bt_hf.h \ + android/hardware/bt_hh.h \ + android/hardware/bt_hl.h \ + android/hardware/bt_pan.h \ + android/hardware/bt_rc.h \ + android/hardware/bt_sock.h \ + android/hardware/hardware.h \ + android/cutils/properties.h \ + android/hal-log.h \ + android/hal-ipc.h android/hal-ipc.c + +android_libhal_internal_la_CPPFLAGS = -I$(srcdir)/android + +noinst_PROGRAMS += android/haltest + +android_haltest_SOURCES = android/client/haltest.c \ + android/client/pollhandler.h \ + android/client/pollhandler.c \ + android/client/terminal.h \ + android/client/terminal.c \ + android/client/history.h \ + android/client/history.c \ + android/client/tabcompletion.c \ + android/client/if-main.h \ + android/client/if-av.c \ + android/client/if-bt.c \ + android/client/if-gatt.c \ + android/client/if-hf.c \ + android/client/if-hh.c \ + android/client/if-pan.c \ + android/client/if-sock.c \ + android/client/hwmodule.c \ + android/hal-utils.h android/hal-utils.c + +android_haltest_LDADD = android/libhal-internal.la + +android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \ + -DPLATFORM_SDK_VERSION=19 + +android_haltest_LDFLAGS = -pthread + +endif + +EXTRA_DIST += android/Android.mk android/hal-ipc-api.txt android/README diff --git a/android/README b/android/README new file mode 100644 index 0000000..6c2c53f --- /dev/null +++ b/android/README @@ -0,0 +1,90 @@ +BlueZ for Android +***************** + +Since Android 4.2 there exists a well standardized HAL interface that the +Bluetooth stack is expected to provide and which enables the easy replacement +of the stack of choice on Android. Android BlueZ is intended as a drop-in +replacement to Android provided Bluetooth stack. + +More details about BlueZ for Android architecture and components can be found +in android/hal-apc-api.txt file. + +=============================== +Building and running on Android +=============================== + +Build requirements +================== + +- GLib - Android 4.2 or later don't provide GLib and one must provide it in +'external/bluetooth/glib' folder of Android tree. Sample Android GLib port +is available at https://code.google.com/p/android-bluez.glib/ + +- Bionic support - BlueZ requires signalfd and timerfd APIs to be provided +by libc library. Currently only 'master' branch available at +https://android.googlesource.com/platform/bionic provides all required +functionality and running BlueZ on older branch requires backporting missing +features. Sample Bionic for Android on Intel Architecture (Android-IA) with all +required features backported is available at +https://code.google.com/p/android-bluez.bionic/ + +Runtime requirements +==================== + +BlueZ HAL library requires 'bluetoothd' service to be available on Android +system. This can be done by defining service in init.rc file of targeted board: + +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd + class main + group bluetooth net_admin + disabled + oneshot + +It is required that bluetooth user could start and stop bluetoothd service by +setting 'ctl.start' or 'ctl.stop' property. This can be achieved by +whitelisting bluetooth user and bluetoothd service in init source code. + +Required Android init system modifications can be found at +https://code.google.com/p/android-bluez.system-core/ + +Downloading and building +======================== + +Building for Android requires full Android AOSP source tree. Sample Android-IA +tree with all required components present is available at +http://code.google.com/p/android-bluez/ + +Downloading: +repo init -u https://code.google.com/p/android-bluez.manifest/ -m topics/bluez +repo sync + +Build for Intel ultrabook: +'source build/envsetup.sh' +'lunch core_mesa-eng' +'make allimages -j8' + +After full build is done it is possible to rebuild only BlueZ: +'cd external/bluetooth/bluez/android/' +'mm' (or 'mm -B' to force rebuilding of all files) +'adb sync' to update target device. + +============================= +Building and running on Linux +============================= + +It is possible to build and test BlueZ for Android daemon on Linux (eg. PC). +Simply follow instructions available at README file in BlueZ top directory. +Android daemon binary is located at android/bluetoothd. See next section on +how to test Android daemon on Linux. + +============ +Testing tool +============ + +BT HAL test tools located in android/haltest is provided for HAL level testing +of both Android daemon and HAL library. Start it 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. diff --git a/android/a2dp.c b/android/a2dp.c new file mode 100644 index 0000000..936c28e --- /dev/null +++ b/android/a2dp.c @@ -0,0 +1,378 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "btio/btio.h" +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "log.h" +#include "a2dp.h" +#include "hal-msg.h" +#include "ipc.h" +#include "utils.h" +#include "bluetooth.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; +static uint32_t record_id = 0; + +struct a2dp_device { + bdaddr_t dst; + uint8_t state; + GIOChannel *io; + guint watch; +}; + +static int device_cmp(gconstpointer s, gconstpointer user_data) +{ + const struct a2dp_device *dev = s; + const bdaddr_t *dst = user_data; + + return bacmp(&dev->dst, dst); +} + +static void a2dp_device_free(struct a2dp_device *dev) +{ + if (dev->watch > 0) + g_source_remove(dev->watch); + + if (dev->io) + g_io_channel_unref(dev->io); + + devices = g_slist_remove(devices, dev); + g_free(dev); +} + +static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst) +{ + struct a2dp_device *dev; + + dev = g_new0(struct a2dp_device, 1); + bacpy(&dev->dst, dst); + devices = g_slist_prepend(devices, dev); + + return dev; +} + +static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state) +{ + struct hal_ev_a2dp_conn_state ev; + char address[18]; + + if (dev->state == state) + return; + + dev->state = state; + + ba2str(&dev->dst, address); + DBG("device %s state %u", address, 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); +} + +static gboolean watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data) +{ + struct a2dp_device *dev = data; + + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); + + a2dp_device_free(dev); + + return FALSE; +} + +static void signaling_connect_cb(GIOChannel *chan, GError *err, + gpointer user_data) +{ + struct a2dp_device *dev = user_data; + + if (err) { + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); + error("%s", err->message); + a2dp_device_free(dev); + return; + } + + 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) +{ + struct a2dp_device *dev; + char addr[18]; + bdaddr_t dst; + GSList *l; + GError *err = NULL; + + 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; + + dev = a2dp_device_new(&dst); + dev->io = bt_io_connect(signaling_connect_cb, dev, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, L2CAP_PSM_AVDTP, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + a2dp_device_free(dev); + return HAL_STATUS_FAILED; + } + + ba2str(&dev->dst, addr); + DBG("connecting to %s", addr); + + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t bt_a2dp_disconnect(struct hal_cmd_a2dp_connect *cmd, + uint16_t len) +{ + 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; + + dev = l->data; + + /* Wait signaling channel to HUP */ + if (dev->io) + g_io_channel_shutdown(dev->io, TRUE, NULL); + + 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; + } + + ipc_send_rsp(sk, HAL_SERVICE_ID_A2DP, status); +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + struct a2dp_device *dev; + bdaddr_t src, dst; + char address[18]; + GError *gerr = NULL; + GSList *l; + + if (err) { + error("%s", err->message); + return; + } + + bt_io_get(chan, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + g_io_channel_shutdown(chan, TRUE, NULL); + return; + } + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (l) + return; + + ba2str(&dst, address); + DBG("Incoming connection from %s", address); + + dev = a2dp_device_new(&dst); + signaling_connect_cb(chan, err, dev); +} + +static sdp_record_t *a2dp_record(void) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid; + sdp_profile_desc_t profile[1]; + sdp_list_t *aproto, *proto[2]; + sdp_record_t *record; + sdp_data_t *psm, *version, *features; + uint16_t lp = AVDTP_UUID; + uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(0, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID); + svclass_id = sdp_list_append(0, &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]); + sdp_set_profile_descs(record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(0, &l2cap_uuid); + psm = sdp_data_alloc(SDP_UINT16, &lp); + proto[0] = sdp_list_append(proto[0], psm); + apseq = sdp_list_append(0, proto[0]); + + sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID); + proto[1] = sdp_list_append(0, &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); + 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); + + 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); + + return record; +} + +bool bt_a2dp_register(int sk, const bdaddr_t *addr) +{ + GError *err = NULL; + sdp_record_t *rec; + + DBG(""); + + bacpy(&adapter_addr, addr); + + server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_PSM, L2CAP_PSM_AVDTP, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (!server) { + error("Failed to listen on AVDTP channel: %s", err->message); + g_error_free(err); + return false; + } + + rec = a2dp_record(); + if (bt_adapter_add_record(rec, SVC_HINT_CAPTURING) < 0) { + error("Failed to register on A2DP record"); + sdp_record_free(rec); + g_io_channel_shutdown(server, TRUE, NULL); + g_io_channel_unref(server); + server = NULL; + return false; + } + record_id = rec->handle; + + notification_sk = sk; + + return true; +} + +void bt_a2dp_unregister(void) +{ + DBG(""); + + notification_sk = -1; + + bt_adapter_remove_record(record_id); + record_id = 0; + + if (server) { + g_io_channel_shutdown(server, TRUE, NULL); + g_io_channel_unref(server); + server = NULL; + } +} diff --git a/android/a2dp.h b/android/a2dp.h new file mode 100644 index 0000000..3531618 --- /dev/null +++ b/android/a2dp.h @@ -0,0 +1,27 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +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); +void bt_a2dp_unregister(void); diff --git a/android/bluetooth.c b/android/bluetooth.c new file mode 100644 index 0000000..83f20e2 --- /dev/null +++ b/android/bluetooth.c @@ -0,0 +1,2313 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/mgmt.h" +#include "src/shared/mgmt.h" +#include "src/glib-helper.h" +#include "src/eir.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/sdp-client.h" +#include "src/sdpd.h" +#include "log.h" +#include "hal-msg.h" +#include "ipc.h" +#include "utils.h" +#include "bluetooth.h" + +#define DEVICE_ID_SOURCE 0x0002 /* USB */ +#define DEVICE_ID_VENDOR 0x1d6b /* Linux Foundation */ +#define DEVICE_ID_PRODUCT 0x0247 /* BlueZ for Android */ + +/* Default to DisplayYesNo */ +#define DEFAULT_IO_CAPABILITY 0x01 +/* Default discoverable timeout 120sec as in Android */ +#define DEFAULT_DISCOVERABLE_TIMEOUT 120 + +#define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \ + + (sizeof(struct hal_property))) + +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 */ +static GSList *browse_reqs; + +static struct mgmt *mgmt_if = NULL; + +static struct { + uint16_t index; + + bdaddr_t bdaddr; + uint32_t dev_class; + + char *name; + + uint32_t current_settings; + + bool discovering; + uint32_t discoverable_timeout; + + GSList *uuids; +} adapter = { + .index = MGMT_INDEX_NONE, + .dev_class = 0, + .name = NULL, + .current_settings = 0, + .discovering = false, + .discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT, + .uuids = NULL, +}; + +struct device { + bdaddr_t bdaddr; + int bond_state; + char *name; +}; + +struct browse_req { + bdaddr_t bdaddr; + GSList *uuids; + int search_uuid; + int reconnect_attempt; +}; + +static const uint16_t uuid_list[] = { + L2CAP_UUID, + PNP_INFO_SVCLASS_ID, + PUBLIC_BROWSE_GROUP, + 0 +}; + +static GSList *found_devices = NULL; +static GSList *devices = NULL; + +static void adapter_name_changed(const uint8_t *name) +{ + struct hal_ev_adapter_props_changed *ev; + size_t len = strlen((const char *) name); + uint8_t buf[BASELEN_PROP_CHANGED + len]; + + memset(buf, 0, sizeof(buf)); + ev = (void *) buf; + + ev->num_props = 1; + ev->status = HAL_STATUS_SUCCESS; + ev->props[0].type = HAL_PROP_ADAPTER_NAME; + /* Android expects value without NULL terminator */ + ev->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); +} + +static void adapter_set_name(const uint8_t *name) +{ + if (!g_strcmp0(adapter.name, (const char *) name)) + return; + + DBG("%s", name); + + g_free(adapter.name); + adapter.name = g_strdup((const char *) name); + + adapter_name_changed(name); +} + +static void mgmt_local_name_changed_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_cp_set_local_name *rp = param; + + if (length < sizeof(*rp)) { + error("Wrong size of local name changed parameters"); + return; + } + + adapter_set_name(rp->name); + + /* TODO Update services if needed */ +} + +static void powered_changed(void) +{ + struct hal_ev_adapter_state_changed ev; + + ev.state = (adapter.current_settings & MGMT_SETTING_POWERED) ? + HAL_POWER_ON : HAL_POWER_OFF; + + DBG("%u", ev.state); + + ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev, -1); +} + +static uint8_t settings2scan_mode(void) +{ + bool connectable, discoverable; + + connectable = adapter.current_settings & MGMT_SETTING_CONNECTABLE; + discoverable = adapter.current_settings & MGMT_SETTING_DISCOVERABLE; + + if (connectable && discoverable) + return HAL_ADAPTER_SCAN_MODE_CONN_DISC; + + if (connectable) + return HAL_ADAPTER_SCAN_MODE_CONN; + + return HAL_ADAPTER_SCAN_MODE_NONE; +} + +static void scan_mode_changed(void) +{ + uint8_t buf[BASELEN_PROP_CHANGED + 1]; + struct hal_ev_adapter_props_changed *ev = (void *) buf; + uint8_t *mode; + + ev->num_props = 1; + ev->status = HAL_STATUS_SUCCESS; + + ev->props[0].type = HAL_PROP_ADAPTER_SCAN_MODE; + ev->props[0].len = 1; + + mode = ev->props[0].val; + *mode = settings2scan_mode(); + + DBG("mode %u", *mode); + + ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf, -1); +} + +static void adapter_class_changed(void) +{ + uint8_t buf[BASELEN_PROP_CHANGED + sizeof(uint32_t)]; + struct hal_ev_adapter_props_changed *ev = (void *) buf; + + ev->num_props = 1; + ev->status = HAL_STATUS_SUCCESS; + + ev->props[0].type = HAL_PROP_ADAPTER_CLASS; + ev->props[0].len = sizeof(uint32_t); + memcpy(ev->props->val, &adapter.dev_class, sizeof(uint32_t)); + + ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf, -1); +} + +static void settings_changed(uint32_t settings) +{ + uint32_t changed_mask; + uint32_t scan_mode_mask; + + changed_mask = adapter.current_settings ^ settings; + + adapter.current_settings = settings; + + DBG("0x%08x", changed_mask); + + if (changed_mask & MGMT_SETTING_POWERED) + powered_changed(); + + + scan_mode_mask = MGMT_SETTING_CONNECTABLE | + MGMT_SETTING_DISCOVERABLE; + + /* + * Only when powered, the connectable and discoverable + * state changes should be communicated. + */ + if (adapter.current_settings & MGMT_SETTING_POWERED) + if (changed_mask & scan_mode_mask) + scan_mode_changed(); +} + +static void new_settings_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + uint32_t settings; + + if (length < sizeof(settings)) { + error("Wrong size of new settings parameters"); + return; + } + + settings = bt_get_le32(param); + + DBG("settings: 0x%8.8x -> 0x%8.8x", adapter.current_settings, + settings); + + if (settings == adapter.current_settings) + return; + + settings_changed(settings); +} + +static void mgmt_dev_class_changed_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_cod *rp = param; + uint32_t dev_class; + + if (length < sizeof(*rp)) { + error("Wrong size of class of device changed parameters"); + return; + } + + dev_class = rp->val[0] | (rp->val[1] << 8) | (rp->val[2] << 16); + + if (dev_class == adapter.dev_class) + return; + + DBG("Class: 0x%06x", dev_class); + + adapter.dev_class = dev_class; + + adapter_class_changed(); + + /* TODO: Gatt attrib set*/ +} + +static void store_link_key(const bdaddr_t *dst, const uint8_t *key, + uint8_t type, uint8_t pin_length) +{ + /* TODO store link key */ + +} + +static int bdaddr_cmp(gconstpointer a, gconstpointer b) +{ + const bdaddr_t *bda = a; + const bdaddr_t *bdb = b; + + return bacmp(bdb, bda); +} + +static void send_bond_state_change(const bdaddr_t *addr, uint8_t status, + uint8_t state) +{ + struct hal_ev_bond_state_changed ev; + + ev.status = 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); +} + +static void cache_device_name(const bdaddr_t *addr, const char *name) +{ + struct device *dev = NULL; + GSList *l; + + l = g_slist_find_custom(devices, addr, bdaddr_cmp); + if (l) + dev = l->data; + + if (!dev) { + dev = g_new0(struct device, 1); + bacpy(&dev->bdaddr, addr); + dev->bond_state = HAL_BOND_STATE_NONE; + devices = g_slist_prepend(devices, dev); + } + + if (!g_strcmp0(dev->name, name)) + return; + + g_free(dev->name); + dev->name = g_strdup(name); + /*TODO: Do some real caching here */ +} + +static void set_device_bond_state(const bdaddr_t *addr, uint8_t status, + int state) { + + struct device *dev = NULL; + GSList *l; + + l = g_slist_find_custom(devices, addr, bdaddr_cmp); + if (l) + dev = l->data; + + if (!dev) { + dev = g_new0(struct device, 1); + bacpy(&dev->bdaddr, addr); + dev->bond_state = HAL_BOND_STATE_NONE; + devices = g_slist_prepend(devices, dev); + } + + if (dev->bond_state != state) { + dev->bond_state = state; + send_bond_state_change(&dev->bdaddr, status, state); + } +} + +static void browse_req_free(struct browse_req *req) +{ + g_slist_free_full(req->uuids, g_free); + g_free(req); +} + +static void fill_uuids(GSList *list, void *buf) +{ + for (; list; list = g_slist_next(list)) { + memcpy(buf, list->data, sizeof(uint128_t)); + buf += sizeof(uint128_t); + } +} + +static void remote_uuids_callback(struct browse_req *req) +{ + struct hal_ev_remote_device_props *ev; + int len; + + len = sizeof(*ev) + sizeof(struct hal_property) + (sizeof(uint128_t) * + g_slist_length(req->uuids)); + ev = g_malloc(len); + + ev->status = HAL_STATUS_SUCCESS; + bdaddr2android(&req->bdaddr, &ev->bdaddr); + ev->num_props = 1; + ev->props[0].type = HAL_PROP_DEVICE_UUIDS; + ev->props[0].len = sizeof(uint128_t) * g_slist_length(req->uuids); + fill_uuids(req->uuids, ev->props[0].val); + + ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_REMOTE_DEVICE_PROPS, len, ev, -1); + + g_free(ev); +} + +static int uuid_128_cmp(gconstpointer a, gconstpointer b) +{ + return memcmp(a, b, sizeof(uint128_t)); +} + +static void update_records(struct browse_req *req, sdp_list_t *recs) +{ + for (; recs; recs = recs->next) { + sdp_record_t *rec = (sdp_record_t *) recs->data; + sdp_list_t *svcclass = NULL; + uuid_t uuid128; + uuid_t *tmp; + uint8_t *new_uuid; + + if (!rec) + break; + + if (sdp_get_service_classes(rec, &svcclass) < 0) + continue; + + if (!svcclass) + continue; + + tmp = svcclass->data; + + switch (tmp->type) { + case SDP_UUID16: + sdp_uuid16_to_uuid128(&uuid128, tmp); + break; + case SDP_UUID32: + sdp_uuid32_to_uuid128(&uuid128, tmp); + break; + case SDP_UUID128: + memcpy(&uuid128, tmp, sizeof(uuid_t)); + break; + default: + continue; + } + + new_uuid = g_malloc(16);/* size of 128 bit uuid */ + memcpy(new_uuid, &uuid128.value.uuid128, + sizeof(uuid128.value.uuid128)); + + /* Check if uuid is already added */ + if (g_slist_find_custom(req->uuids, new_uuid, uuid_128_cmp)) + g_free(new_uuid); + else + req->uuids = g_slist_append(req->uuids, new_uuid); + + sdp_list_free(svcclass, free); + } +} + +static void browse_cb(sdp_list_t *recs, int err, gpointer user_data) +{ + struct browse_req *req = user_data; + uuid_t uuid; + + /* If we have a valid response and req->search_uuid == 2, then L2CAP + * UUID & PNP searching was successful -- we are done */ + if (err < 0 || req->search_uuid == 2) { + if (err == -ECONNRESET && req->reconnect_attempt < 1) { + req->search_uuid--; + req->reconnect_attempt++; + } else { + goto done; + } + } + + update_records(req, recs); + + /* Search for mandatory uuids */ + if (uuid_list[req->search_uuid]) { + sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); + bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid, + browse_cb, user_data, NULL); + return; + } + +done: + remote_uuids_callback(req); + + browse_reqs = g_slist_remove(browse_reqs, req); + browse_req_free(req); +} + +static int req_cmp(gconstpointer a, gconstpointer b) +{ + const struct browse_req *req = a; + const bdaddr_t *bdaddr = b; + + return bacmp(&req->bdaddr, bdaddr); +} + +static uint8_t browse_remote_sdp(const bdaddr_t *addr) +{ + struct browse_req *req; + uuid_t uuid; + + if (g_slist_find_custom(browse_reqs, addr, req_cmp)) + return HAL_STATUS_DONE; + + req = g_new0(struct browse_req, 1); + bacpy(&req->bdaddr, addr); + sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); + + if (bt_search_service(&adapter.bdaddr, + &req->bdaddr, &uuid, browse_cb, req, NULL) < 0) { + browse_req_free(req); + return false; + } + + browse_reqs = g_slist_append(browse_reqs, req); + + return HAL_STATUS_SUCCESS; +} + +static void new_link_key_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_new_link_key *ev = param; + const struct mgmt_addr_info *addr = &ev->key.addr; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small new link key event"); + return; + } + + ba2str(&addr->bdaddr, dst); + + DBG("new key for %s type %u pin_len %u", + dst, ev->key.type, ev->key.pin_len); + + if (ev->key.pin_len > 16) { + error("Invalid PIN length (%u) in new_key event", + ev->key.pin_len); + return; + } + + if (ev->store_hint) { + const struct mgmt_link_key_info *key = &ev->key; + + store_link_key(&addr->bdaddr, key->val, key->type, + key->pin_len); + } + + set_device_bond_state(&addr->bdaddr, HAL_STATUS_SUCCESS, + HAL_BOND_STATE_BONDED); + + browse_remote_sdp(&addr->bdaddr); +} + +static const char *get_device_name(const bdaddr_t *addr) +{ + GSList *l; + + l = g_slist_find_custom(devices, addr, bdaddr_cmp); + if (l) { + struct device *dev = l->data; + return dev->name; + } + + return NULL; +} + +static void send_remote_device_name_prop(const bdaddr_t *bdaddr) +{ + struct hal_ev_remote_device_props *ev; + const char *name; + size_t ev_len; + char dst[18]; + + /* Use cached name or bdaddr string */ + name = get_device_name(bdaddr); + if (!name) + name = dst; + + ev_len = BASELEN_REMOTE_DEV_PROP + strlen(name); + ev = g_malloc0(ev_len); + + ev->status = HAL_STATUS_SUCCESS; + bdaddr2android(bdaddr, ev->bdaddr); + ev->num_props = 1; + ev->props[0].type = HAL_PROP_DEVICE_NAME; + ev->props[0].len = strlen(name); + memcpy(&ev->props[0].val, name, strlen(name)); + + ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_REMOTE_DEVICE_PROPS, sizeof(ev), ev, -1); + + g_free(ev); +} + +static void pin_code_request_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_pin_code_request *ev = param; + struct hal_ev_pin_request hal_ev; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small PIN code request event"); + return; + } + + ba2str(&ev->addr.bdaddr, dst); + + /* Workaround for Android Bluetooth.apk issue: send remote + * device property */ + send_remote_device_name_prop(&ev->addr.bdaddr); + + set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS, + HAL_BOND_STATE_BONDING); + + DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure); + + /* TODO CoD of remote devices should probably be cached + * Name we already send in remote device prop */ + 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); +} + +static void send_ssp_request(const bdaddr_t *addr, uint8_t variant, + uint32_t passkey) +{ + struct hal_ev_ssp_request ev; + + /* It is ok to have empty name and CoD of remote devices here since + * those information has been already provided on device_connected event + * or during device scaning. Android will use that instead. + */ + memset(&ev, 0, sizeof(ev)); + bdaddr2android(addr, ev.bdaddr); + ev.pairing_variant = variant; + ev.passkey = passkey; + + ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST, + sizeof(ev), &ev, -1); +} + +static void user_confirm_request_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_user_confirm_request *ev = param; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small user confirm request event"); + return; + } + + ba2str(&ev->addr.bdaddr, dst); + DBG("%s confirm_hint %u", dst, ev->confirm_hint); + + set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS, + HAL_BOND_STATE_BONDING); + + if (ev->confirm_hint) + send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_CONSENT, 0); + else + send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_CONFIRM, + ev->value); +} + +static void user_passkey_request_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_user_passkey_request *ev = param; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small passkey request event"); + return; + } + + ba2str(&ev->addr.bdaddr, dst); + DBG("%s", dst); + + set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS, + HAL_BOND_STATE_BONDING); + + send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_ENTRY, 0); +} + +static void user_passkey_notify_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_passkey_notify *ev = param; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small passkey notify event"); + return; + } + + ba2str(&ev->addr.bdaddr, dst); + DBG("%s entered %u", dst, ev->entered); + + /* HAL seems to not support entered characters */ + if (ev->entered) + return; + + set_device_bond_state(&ev->addr.bdaddr, HAL_STATUS_SUCCESS, + HAL_BOND_STATE_BONDING); + + send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_NOTIF, + ev->passkey); +} + +static void mgmt_discovering_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_discovering *ev = param; + struct hal_ev_discovery_state_changed cp; + + if (length < sizeof(*ev)) { + error("Too small discovering event"); + return; + } + + DBG("hci%u type %u discovering %u", index, ev->type, + ev->discovering); + + if (adapter.discovering == !!ev->discovering) + return; + + adapter.discovering = !!ev->discovering; + + DBG("new discovering state %u", ev->discovering); + + if (adapter.discovering) { + cp.state = HAL_DISCOVERY_STATE_STARTED; + } else { + g_slist_free_full(found_devices, g_free); + found_devices = NULL; + + cp.state = HAL_DISCOVERY_STATE_STOPPED; + } + + ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp), &cp, -1); +} + +static void confirm_device_name(const bdaddr_t *addr, uint8_t addr_type) +{ + struct mgmt_cp_confirm_name cp; + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, addr); + cp.addr.type = addr_type; + + if (mgmt_reply(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) == 0) + 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++; + } + + /* 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++; + } + + return num_props; +} + +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; + struct eir_data eir; + GSList *l; + bdaddr_t *remote = NULL; + int err; + + 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; + + if (!remote) { + char addr[18]; + + remote = g_new0(bdaddr_t, 1); + bacpy(remote, bdaddr); + + found_devices = g_slist_prepend(found_devices, remote); + is_new_dev = true; + + ba2str(remote, 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); + } + + if (is_new_dev) { + struct hal_ev_device_found *ev = NULL; + struct hal_property *prop = NULL; + + /* with new device we also send bdaddr prop */ + props_size += sizeof(struct hal_property) + sizeof(eir.addr); + + buff_size = sizeof(struct hal_ev_device_found) + props_size; + buf = g_new0(char, buff_size); + ev = buf; + prop = ev->props; + + /* 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; + + /* 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); + } else { + struct hal_ev_remote_device_props *ev = NULL; + + buff_size = sizeof(*ev) + props_size; + buf = g_new0(char, buff_size); + ev = buf; + + ev->num_props = fill_device_props(ev->props, remote, eir.class, + rssi, eir.name); + + ev->status = HAL_STATUS_SUCCESS; + bdaddr2android(bdaddr, ev->bdaddr); + + ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_REMOTE_DEVICE_PROPS, buff_size, ev, -1); + g_free(buf); + } + + if (confirm) { + char addr[18]; + + ba2str(bdaddr, addr); + info("Device %s needs name confirmation.", addr); + confirm_device_name(bdaddr, bdaddr_type); + } + + eir_data_free(&eir); +} + +static void mgmt_device_found_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_device_found *ev = param; + const uint8_t *eir; + uint16_t eir_len; + uint32_t flags; + bool confirm_name; + char addr[18]; + + if (length < sizeof(*ev)) { + error("Too short device found event (%u bytes)", length); + return; + } + + eir_len = btohs(ev->eir_len); + if (length != sizeof(*ev) + eir_len) { + error("Device found event size mismatch (%u != %zu)", + length, sizeof(*ev) + eir_len); + return; + } + + if (eir_len == 0) + eir = NULL; + else + eir = ev->eir; + + 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); + + confirm_name = flags & MGMT_DEV_FOUND_CONFIRM_NAME; + + update_found_device(&ev->addr.bdaddr, ev->addr.type, ev->rssi, + confirm_name, eir, eir_len); +} + +static void mgmt_device_connected_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_device_connected *ev = param; + struct hal_ev_acl_state_changed hal_ev; + + if (length < sizeof(*ev)) { + error("Too short device connected event (%u bytes)", length); + return; + } + + update_found_device(&ev->addr.bdaddr, ev->addr.type, 0, false, + &ev->eir[0], btohs(ev->eir_len)); + + hal_ev.status = HAL_STATUS_SUCCESS; + 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); +} + +static void mgmt_device_disconnected_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_device_disconnected *ev = param; + struct hal_ev_acl_state_changed hal_ev; + + if (length < sizeof(*ev)) { + error("Too short device disconnected event (%u bytes)", length); + return; + } + + hal_ev.status = HAL_STATUS_SUCCESS; + 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); +} + +static uint8_t status_mgmt2hal(uint8_t mgmt) +{ + switch (mgmt) { + case MGMT_STATUS_SUCCESS: + return HAL_STATUS_SUCCESS; + case MGMT_STATUS_NO_RESOURCES: + return HAL_STATUS_NOMEM; + case MGMT_STATUS_BUSY: + return HAL_STATUS_BUSY; + case MGMT_STATUS_NOT_SUPPORTED: + return HAL_STATUS_UNSUPPORTED; + case MGMT_STATUS_INVALID_PARAMS: + return HAL_STATUS_INVALID; + case MGMT_STATUS_AUTH_FAILED: + return HAL_STATUS_AUTH_FAILURE; + case MGMT_STATUS_NOT_CONNECTED: + return HAL_STATUS_REMOTE_DEVICE_DOWN; + default: + return HAL_STATUS_FAILED; + } +} + +static void mgmt_connect_failed_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_connect_failed *ev = param; + + DBG(""); + + /* In case security mode 3 pairing we will get connect failed event + * in case e.g wrong PIN code entered. Let's check if device is + * bonding, if so update bond state */ + set_device_bond_state(&ev->addr.bdaddr, status_mgmt2hal(ev->status), + HAL_BOND_STATE_NONE); +} + +static void mgmt_auth_failed_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_auth_failed *ev = param; + + DBG(""); + + set_device_bond_state(&ev->addr.bdaddr, status_mgmt2hal(ev->status), + HAL_BOND_STATE_NONE); +} + +static void mgmt_device_unpaired_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + DBG(""); +} + +static void register_mgmt_handlers(void) +{ + mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index, + new_settings_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index, + mgmt_dev_class_changed_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index, + mgmt_local_name_changed_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index, + new_link_key_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index, + pin_code_request_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index, + user_confirm_request_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index, + user_passkey_request_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index, + user_passkey_notify_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index, + mgmt_discovering_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index, + mgmt_device_found_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index, + mgmt_device_connected_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index, + mgmt_device_disconnected_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index, + mgmt_connect_failed_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_AUTH_FAILED, adapter.index, + mgmt_auth_failed_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_UNPAIRED, adapter.index, + mgmt_device_unpaired_event, NULL, NULL); +} + +static void load_link_keys_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + bt_bluetooth_ready cb = user_data; + int err; + + if (status) { + error("Failed to load link keys for index %u: %s (0x%02x)", + adapter.index, mgmt_errstr(status), status); + err = -EIO; + goto failed; + } + + DBG("status %u", status); + + cb(0, &adapter.bdaddr); + return; + +failed: + cb(err, NULL); +} + +static void load_link_keys(GSList *keys, bt_bluetooth_ready cb) +{ + struct mgmt_cp_load_link_keys *cp; + struct mgmt_link_key_info *key; + size_t key_count, cp_size; + unsigned int id; + + key_count = g_slist_length(keys); + + DBG("keys %zu ", key_count); + + cp_size = sizeof(*cp) + (key_count * sizeof(*key)); + + cp = g_malloc0(cp_size); + + /* + * Even if the list of stored keys is empty, it is important to + * load an empty list into the kernel. That way it is ensured + * that no old keys from a previous daemon are present. + */ + cp->key_count = htobs(key_count); + + for (key = cp->keys; keys != NULL; keys = g_slist_next(keys), key++) + memcpy(key, keys->data, sizeof(*key)); + + id = mgmt_send(mgmt_if, MGMT_OP_LOAD_LINK_KEYS, adapter.index, + cp_size, cp, load_link_keys_complete, cb, NULL); + + g_free(cp); + + if (id == 0) { + error("Failed to load link keys"); + cb(-EIO, NULL); + } +} + +/* output uint128 is in host order */ +static void uuid16_to_uint128(uint16_t uuid, uint128_t *u128) +{ + uuid_t uuid16, uuid128; + + sdp_uuid16_create(&uuid16, uuid); + sdp_uuid16_to_uuid128(&uuid128, &uuid16); + + ntoh128(&uuid128.value.uuid128, u128); +} + +static bool get_uuids(void) +{ + struct hal_ev_adapter_props_changed *ev; + GSList *list = adapter.uuids; + unsigned int uuid_count = g_slist_length(list); + int len = uuid_count * sizeof(uint128_t); + uint8_t buf[BASELEN_PROP_CHANGED + len]; + uint8_t *p; + int i; + + memset(buf, 0, sizeof(buf)); + ev = (void *) buf; + + ev->num_props = 1; + ev->status = HAL_STATUS_SUCCESS; + + ev->props[0].type = HAL_PROP_ADAPTER_UUIDS; + ev->props[0].len = len; + p = ev->props->val; + + for (; list; list = g_slist_next(list)) { + uint16_t uuid = GPOINTER_TO_UINT(list->data); + uint128_t uint128; + + uuid16_to_uint128(uuid, &uint128); + + /* Android expects swapped bytes in uuid */ + for (i = 0; i < 16; i++) + p[15 - i] = uint128.data[i]; + + p += sizeof(uint128_t); + } + + ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev, -1); + + return true; +} + +static void remove_uuid_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + error("Failed to remove UUID: %s (0x%02x)", + mgmt_errstr(status), status); + return; + } + + mgmt_dev_class_changed_event(adapter.index, length, param, NULL); + + /* send notification only if bluetooth service is registered */ + if (notification_sk >= 0) + get_uuids(); +} + +static void remove_uuid(uint16_t uuid) +{ + uint128_t uint128; + struct mgmt_cp_remove_uuid cp; + + uuid16_to_uint128(uuid, &uint128); + htob128(&uint128, (uint128_t *) cp.uuid); + + mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), &cp, + remove_uuid_complete, NULL, NULL); +} + +static void add_uuid_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + error("Failed to add UUID: %s (0x%02x)", + mgmt_errstr(status), status); + return; + } + + mgmt_dev_class_changed_event(adapter.index, length, param, NULL); + + /* send notification only if bluetooth service is registered */ + if (notification_sk >= 0) + get_uuids(); +} + +static void add_uuid(uint8_t svc_hint, uint16_t uuid) +{ + uint128_t uint128; + struct mgmt_cp_add_uuid cp; + + uuid16_to_uint128(uuid, &uint128); + + htob128(&uint128, (uint128_t *) cp.uuid); + cp.svc_hint = svc_hint; + + mgmt_send(mgmt_if, MGMT_OP_ADD_UUID, adapter.index, sizeof(cp), &cp, + add_uuid_complete, NULL, NULL); +} + +int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint) +{ + uint16_t uuid; + + /* TODO support all types? */ + if (rec->svclass.type != SDP_UUID16) { + warn("Ignoring unsupported UUID type"); + return -EINVAL; + } + + uuid = rec->svclass.value.uuid16; + + if (g_slist_find(adapter.uuids, GUINT_TO_POINTER(uuid))) { + DBG("UUID 0x%x already added", uuid); + return -EALREADY; + } + + adapter.uuids = g_slist_prepend(adapter.uuids, GUINT_TO_POINTER(uuid)); + + add_uuid(svc_hint, uuid); + + return add_record_to_server(&adapter.bdaddr, rec); +} + +void bt_adapter_remove_record(uint32_t handle) +{ + sdp_record_t *rec; + GSList *uuid_found; + uint16_t uuid; + + rec = sdp_record_find(handle); + if (!rec) + return; + + uuid = rec->svclass.value.uuid16; + + uuid_found = g_slist_find(adapter.uuids, GUINT_TO_POINTER(uuid)); + if (uuid_found) { + remove_uuid(uuid); + + adapter.uuids = g_slist_remove(adapter.uuids, + uuid_found->data); + } + + remove_record_from_server(handle); +} + +static void set_mode_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + error("Failed to set mode: %s (0x%02x)", + mgmt_errstr(status), status); + return; + } + + /* + * The parameters are identical and also the task that is + * required in both cases. So it is safe to just call the + * event handling functions here. + */ + new_settings_callback(adapter.index, length, param, NULL); +} + +static bool set_mode(uint16_t opcode, uint8_t mode) +{ + struct mgmt_mode cp; + + memset(&cp, 0, sizeof(cp)); + cp.val = mode; + + DBG("opcode=0x%x mode=0x%x", opcode, mode); + + if (mgmt_send(mgmt_if, opcode, adapter.index, sizeof(cp), &cp, + set_mode_complete, NULL, NULL) > 0) + return true; + + error("Failed to set mode"); + + return false; +} + +static void set_io_capability(void) +{ + struct mgmt_cp_set_io_capability cp; + + memset(&cp, 0, sizeof(cp)); + cp.io_capability = DEFAULT_IO_CAPABILITY; + + if (mgmt_send(mgmt_if, MGMT_OP_SET_IO_CAPABILITY, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) == 0) + error("Failed to set IO capability"); +} + +static void set_device_id(void) +{ + struct mgmt_cp_set_device_id cp; + uint8_t major, minor; + uint16_t version; + + if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2) + return; + + version = major << 8 | minor; + + memset(&cp, 0, sizeof(cp)); + cp.source = htobs(DEVICE_ID_SOURCE); + cp.vendor = htobs(DEVICE_ID_VENDOR); + cp.product = htobs(DEVICE_ID_PRODUCT); + cp.version = htobs(version); + + if (mgmt_send(mgmt_if, MGMT_OP_SET_DEVICE_ID, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) == 0) + error("Failed to set device id"); + + register_device_id(DEVICE_ID_SOURCE, DEVICE_ID_VENDOR, + DEVICE_ID_PRODUCT, version); + + bt_adapter_add_record(sdp_record_find(0x10000), 0x00); +} + +static void set_adapter_name_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_cp_set_local_name *rp = param; + + if (status != MGMT_STATUS_SUCCESS) { + error("Failed to set name: %s (0x%02x)", + mgmt_errstr(status), status); + return; + } + + adapter_set_name(rp->name); +} + +static uint8_t set_adapter_name(uint8_t *name, uint16_t len) +{ + struct mgmt_cp_set_local_name cp; + + memset(&cp, 0, sizeof(cp)); + memcpy(cp.name, name, len); + + if (mgmt_send(mgmt_if, MGMT_OP_SET_LOCAL_NAME, adapter.index, + sizeof(cp), &cp, set_adapter_name_complete, + NULL, NULL) > 0) + return HAL_STATUS_SUCCESS; + + error("Failed to set name"); + + return HAL_STATUS_FAILED; +} + +static uint8_t set_discoverable_timeout(uint8_t *timeout) +{ + /* Android handles discoverable timeout in Settings app. + * There is no need to use kernel feature for that. + * Just need to store this value here */ + + /* TODO: This should be in some storage */ + memcpy(&adapter.discoverable_timeout, timeout, sizeof(uint32_t)); + + return HAL_STATUS_SUCCESS; +} + +static void clear_uuids(void) +{ + struct mgmt_cp_remove_uuid cp; + + memset(&cp, 0, sizeof(cp)); + + mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL); +} + +static void read_info_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_read_info *rp = param; + bt_bluetooth_ready cb = user_data; + uint32_t missing_settings, supported_settings; + int err; + + DBG(""); + + if (status) { + error("Failed to read info for index %u: %s (0x%02x)", + adapter.index, mgmt_errstr(status), status); + err = -EIO; + goto failed; + } + + if (length < sizeof(*rp)) { + error("Too small read info complete response"); + err = -EIO; + goto failed; + } + + if (!bacmp(&rp->bdaddr, BDADDR_ANY)) { + error("No Bluetooth address"); + err = -ENODEV; + goto failed; + } + + /* Store adapter information */ + bacpy(&adapter.bdaddr, &rp->bdaddr); + adapter.dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) | + (rp->dev_class[2] << 16); + adapter.name = g_strdup((const char *) rp->name); + + supported_settings = btohs(rp->supported_settings); + adapter.current_settings = btohs(rp->current_settings); + + /* TODO: Read discoverable timeout from storage here */ + + /* TODO: Register all event notification handlers */ + register_mgmt_handlers(); + + clear_uuids(); + + load_link_keys(NULL, cb); + + set_io_capability(); + set_device_id(); + + missing_settings = adapter.current_settings ^ supported_settings; + + if (missing_settings & MGMT_SETTING_SSP) + set_mode(MGMT_OP_SET_SSP, 0x01); + + if (missing_settings & MGMT_SETTING_PAIRABLE) + set_mode(MGMT_OP_SET_PAIRABLE, 0x01); + + return; + +failed: + cb(err, NULL); +} + +static void mgmt_index_added_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + bt_bluetooth_ready cb = user_data; + + DBG("index %u", index); + + if (adapter.index != MGMT_INDEX_NONE) { + DBG("skip event for index %u", index); + return; + } + + if (option_index != MGMT_INDEX_NONE && option_index != index) { + DBG("skip event for index %u (option %u)", index, option_index); + return; + } + + adapter.index = index; + + if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL, + read_info_complete, cb, NULL) == 0) { + cb(-EIO, NULL); + return; + } +} + +static void mgmt_index_removed_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + DBG("index %u", index); + + if (index != adapter.index) + return; + + error("Adapter was removed. Exiting."); + raise(SIGTERM); +} + +static void read_index_list_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_read_index_list *rp = param; + bt_bluetooth_ready cb = user_data; + uint16_t num; + int i; + + DBG(""); + + if (status) { + error("%s: Failed to read index list: %s (0x%02x)", + __func__, mgmt_errstr(status), status); + goto failed; + } + + if (length < sizeof(*rp)) { + error("%s: Wrong size of read index list response", __func__); + goto failed; + } + + num = btohs(rp->num_controllers); + + DBG("Number of controllers: %u", num); + + if (num * sizeof(uint16_t) + sizeof(*rp) != length) { + error("%s: Incorrect pkt size for index list rsp", __func__); + goto failed; + } + + if (adapter.index != MGMT_INDEX_NONE) + return; + + for (i = 0; i < num; i++) { + uint16_t index = btohs(rp->index[i]); + + if (option_index != MGMT_INDEX_NONE && option_index != index) + continue; + + if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL, + read_info_complete, cb, NULL) == 0) + goto failed; + + adapter.index = index; + return; + } + + return; + +failed: + cb(-EIO, NULL); +} + +static void read_version_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_read_version *rp = param; + uint8_t mgmt_version, mgmt_revision; + bt_bluetooth_ready cb = user_data; + + DBG(""); + + if (status) { + error("Failed to read version information: %s (0x%02x)", + mgmt_errstr(status), status); + goto failed; + } + + if (length < sizeof(*rp)) { + error("Wrong size response"); + goto failed; + } + + mgmt_version = rp->version; + mgmt_revision = btohs(rp->revision); + + info("Bluetooth management interface %u.%u initialized", + mgmt_version, mgmt_revision); + + if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 3)) { + error("Version 1.3 or later of management interface required"); + goto failed; + } + + mgmt_register(mgmt_if, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, + mgmt_index_added_event, cb, NULL); + mgmt_register(mgmt_if, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, + mgmt_index_removed_event, NULL, NULL); + + if (mgmt_send(mgmt_if, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, + NULL, read_index_list_complete, cb, NULL) > 0) + return; + + error("Failed to read controller index list"); + +failed: + cb(-EIO, NULL); +} + +bool bt_bluetooth_start(int index, bt_bluetooth_ready cb) +{ + DBG("index %d", index); + + mgmt_if = mgmt_new_default(); + if (!mgmt_if) { + error("Failed to access management interface"); + return false; + } + + if (mgmt_send(mgmt_if, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL, + read_version_complete, cb, NULL) == 0) { + error("Error sending READ_VERSION mgmt command"); + + mgmt_unref(mgmt_if); + mgmt_if = NULL; + + return false; + } + + if (index >= 0) + option_index = index; + + return true; +} + +static void shutdown_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + bt_bluetooth_stopped cb = user_data; + + if (status != MGMT_STATUS_SUCCESS) + error("Clean controller shutdown failed"); + + cb(); +} + +bool bt_bluetooth_stop(bt_bluetooth_stopped cb) +{ + struct mgmt_mode cp; + + if (adapter.index == MGMT_INDEX_NONE) + return false; + + info("Switching controller off"); + + memset(&cp, 0, sizeof(cp)); + + return mgmt_send(mgmt_if, MGMT_OP_SET_POWERED, adapter.index, + sizeof(cp), &cp, shutdown_complete, (void *)cb, + NULL) > 0; +} + +void bt_bluetooth_cleanup(void) +{ + g_free(adapter.name); + adapter.name = NULL; + + mgmt_unref(mgmt_if); + mgmt_if = NULL; +} + +static bool set_discoverable(uint8_t mode, uint16_t timeout) +{ + struct mgmt_cp_set_discoverable cp; + + memset(&cp, 0, sizeof(cp)); + cp.val = mode; + cp.timeout = htobs(timeout); + + DBG("mode %u timeout %u", mode, timeout); + + if (mgmt_send(mgmt_if, MGMT_OP_SET_DISCOVERABLE, adapter.index, + sizeof(cp), &cp, set_mode_complete, NULL, NULL) > 0) + return true; + + error("Failed to set mode discoverable"); + + return false; +} + +static void get_address(void) +{ + uint8_t buf[BASELEN_PROP_CHANGED + sizeof(bdaddr_t)]; + struct hal_ev_adapter_props_changed *ev = (void *) buf; + + ev->num_props = 1; + ev->status = HAL_STATUS_SUCCESS; + + ev->props[0].type = HAL_PROP_ADAPTER_ADDR; + 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); +} + +static bool get_name(void) +{ + if (!adapter.name) + return false; + + adapter_name_changed((uint8_t *) adapter.name); + + return true; +} + + +static bool get_class(void) +{ + DBG(""); + + adapter_class_changed(); + + return true; +} + +static bool get_type(void) +{ + DBG("Not implemented"); + + /* TODO: Add implementation */ + + return false; +} + +static bool get_service(void) +{ + DBG("Not implemented"); + + /* TODO: Add implementation */ + + return false; +} + +static bool get_scan_mode(void) +{ + DBG(""); + + scan_mode_changed(); + + return true; +} + +static bool get_devices(void) +{ + DBG("Not implemented"); + + /* TODO: Add implementation */ + + return false; +} + +static bool get_discoverable_timeout(void) +{ + struct hal_ev_adapter_props_changed *ev; + uint8_t buf[BASELEN_PROP_CHANGED + sizeof(uint32_t)]; + + memset(buf, 0, sizeof(buf)); + ev = (void *) buf; + + ev->num_props = 1; + ev->status = HAL_STATUS_SUCCESS; + + ev->props[0].type = HAL_PROP_ADAPTER_DISC_TIMEOUT; + ev->props[0].len = sizeof(uint32_t); + memcpy(&ev->props[0].val, &adapter.discoverable_timeout, + sizeof(uint32_t)); + + ipc_send(notification_sk, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev, -1); + + return true; +} + +static bool get_property(void *buf, uint16_t len) +{ + struct hal_cmd_get_adapter_prop *cmd = buf; + + switch (cmd->type) { + case HAL_PROP_ADAPTER_ADDR: + get_address(); + return true; + case HAL_PROP_ADAPTER_NAME: + return get_name(); + case HAL_PROP_ADAPTER_UUIDS: + return get_uuids(); + case HAL_PROP_ADAPTER_CLASS: + return get_class(); + case HAL_PROP_ADAPTER_TYPE: + return get_type(); + case HAL_PROP_ADAPTER_SERVICE_REC: + return get_service(); + case HAL_PROP_ADAPTER_SCAN_MODE: + return get_scan_mode(); + case HAL_PROP_ADAPTER_BONDED_DEVICES: + return get_devices(); + case HAL_PROP_ADAPTER_DISC_TIMEOUT: + return get_discoverable_timeout(); + default: + return false; + } +} + +static void get_properties(void) +{ + get_address(); + get_name(); + get_uuids(); + get_class(); + get_type(); + get_service(); + get_scan_mode(); + get_devices(); + get_discoverable_timeout(); +} + +static bool start_discovery(void) +{ + struct mgmt_cp_start_discovery cp; + uint8_t type = 1 << BDADDR_BREDR; + + if (adapter.current_settings & type) + cp.type = type; + else + cp.type = 0; + + DBG("type=0x%x", type); + + if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) > 0) + return true; + + error("Failed to start discovery"); + return false; +} + +static bool stop_discovery(void) +{ + struct mgmt_cp_stop_discovery cp; + uint8_t type = 1 << BDADDR_BREDR; + + if (adapter.current_settings & type) + cp.type = type; + else + cp.type = 0; + + DBG("type=0x%x", type); + + if (mgmt_send(mgmt_if, MGMT_OP_STOP_DISCOVERY, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) > 0) + return true; + + error("Failed to start discovery"); + return false; +} + +static uint8_t set_scan_mode(void *buf, uint16_t len) +{ + uint8_t *mode = buf; + bool conn, disc, cur_conn, cur_disc; + + cur_conn = adapter.current_settings & MGMT_SETTING_CONNECTABLE; + cur_disc = adapter.current_settings & MGMT_SETTING_DISCOVERABLE; + + DBG("connectable %u discoverable %d mode %u", cur_conn, cur_disc, + *mode); + + switch (*mode) { + case HAL_ADAPTER_SCAN_MODE_NONE: + if (!cur_conn && !cur_disc) + goto done; + + conn = false; + disc = false; + break; + case HAL_ADAPTER_SCAN_MODE_CONN: + if (cur_conn && !cur_disc) + goto done; + + conn = true; + disc = false; + break; + case HAL_ADAPTER_SCAN_MODE_CONN_DISC: + if (cur_conn && cur_disc) + goto done; + + conn = true; + disc = true; + break; + default: + return HAL_STATUS_FAILED; + } + + if (cur_conn != conn) { + if (!set_mode(MGMT_OP_SET_CONNECTABLE, conn ? 0x01 : 0x00)) + return HAL_STATUS_FAILED; + } + + if (cur_disc != disc) { + if (!set_discoverable(disc ? 0x01 : 0x00, 0)) + return HAL_STATUS_FAILED; + } + + return HAL_STATUS_SUCCESS; + +done: + /* Android expects property changed callback */ + scan_mode_changed(); + + return HAL_STATUS_DONE; +} + +static uint8_t set_property(void *buf, uint16_t len) +{ + struct hal_cmd_set_adapter_prop *cmd = buf; + + switch (cmd->type) { + case HAL_PROP_ADAPTER_SCAN_MODE: + return set_scan_mode(cmd->val, cmd->len); + case HAL_PROP_ADAPTER_NAME: + return set_adapter_name(cmd->val, cmd->len); + case HAL_PROP_ADAPTER_DISC_TIMEOUT: + return set_discoverable_timeout(cmd->val); + default: + DBG("Unhandled property type 0x%x", cmd->type); + return HAL_STATUS_FAILED; + } +} + +static void pair_device_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_pair_device *rp = param; + + DBG("status %u", status); + + /* On success bond state change will be send when new link key event + * is received */ + if (status == MGMT_STATUS_SUCCESS) + return; + + set_device_bond_state(&rp->addr.bdaddr, status_mgmt2hal(status), + HAL_BOND_STATE_NONE); +} + +static bool create_bond(void *buf, uint16_t len) +{ + struct hal_cmd_create_bond *cmd = buf; + struct mgmt_cp_pair_device cp; + + cp.io_cap = DEFAULT_IO_CAPABILITY; + cp.addr.type = BDADDR_BREDR; + 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; + + set_device_bond_state(&cp.addr.bdaddr, HAL_STATUS_SUCCESS, + HAL_BOND_STATE_BONDING); + + return true; +} + +static bool cancel_bond(void *buf, uint16_t len) +{ + struct hal_cmd_cancel_bond *cmd = buf; + struct mgmt_addr_info cp; + + 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; +} + +static void unpair_device_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_unpair_device *rp = param; + + DBG("status %u", status); + + if (status != MGMT_STATUS_SUCCESS) + return; + + set_device_bond_state(&rp->addr.bdaddr, HAL_STATUS_SUCCESS, + HAL_BOND_STATE_NONE); +} + +static bool remove_bond(void *buf, uint16_t len) +{ + struct hal_cmd_remove_bond *cmd = buf; + struct mgmt_cp_unpair_device cp; + + 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, + sizeof(cp), &cp, unpair_device_complete, + NULL, NULL) > 0; +} + +static uint8_t pin_reply(void *buf, uint16_t len) +{ + struct hal_cmd_pin_reply *cmd = buf; + bdaddr_t bdaddr; + char addr[18]; + + android2bdaddr(cmd->bdaddr, &bdaddr); + ba2str(&bdaddr, addr); + + 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) { + struct mgmt_cp_pin_code_reply rp; + + memset(&rp, 0, sizeof(rp)); + + bacpy(&rp.addr.bdaddr, &bdaddr); + rp.addr.type = BDADDR_BREDR; + rp.pin_len = cmd->pin_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; + } else { + struct mgmt_cp_pin_code_neg_reply rp; + + bacpy(&rp.addr.bdaddr, &bdaddr); + rp.addr.type = BDADDR_BREDR; + + if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_NEG_REPLY, + adapter.index, sizeof(rp), &rp, + NULL, NULL, NULL) == 0) + return HAL_STATUS_FAILED; + } + + return HAL_STATUS_SUCCESS; +} + +static uint8_t user_confirm_reply(const bdaddr_t *bdaddr, bool accept) +{ + struct mgmt_addr_info cp; + uint16_t opcode; + + if (accept) + opcode = MGMT_OP_USER_CONFIRM_REPLY; + else + opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY; + + bacpy(&cp.bdaddr, bdaddr); + cp.type = BDADDR_BREDR; + + if (mgmt_reply(mgmt_if, opcode, adapter.index, sizeof(cp), &cp, + NULL, NULL, NULL) > 0) + return HAL_STATUS_SUCCESS; + + return HAL_STATUS_FAILED; +} + +static uint8_t user_passkey_reply(const bdaddr_t *bdaddr, bool accept, + uint32_t passkey) +{ + unsigned int id; + + if (accept) { + struct mgmt_cp_user_passkey_reply cp; + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, bdaddr); + cp.addr.type = BDADDR_BREDR; + cp.passkey = htobl(passkey); + + id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_REPLY, + adapter.index, sizeof(cp), &cp, + NULL, NULL, NULL); + } else { + struct mgmt_cp_user_passkey_neg_reply cp; + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, bdaddr); + cp.addr.type = BDADDR_BREDR; + + id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_NEG_REPLY, + adapter.index, sizeof(cp), &cp, + NULL, NULL, NULL); + } + + if (id == 0) + return HAL_STATUS_FAILED; + + return HAL_STATUS_SUCCESS; +} + +static uint8_t ssp_reply(void *buf, uint16_t len) +{ + struct hal_cmd_ssp_reply *cmd = buf; + uint8_t status; + bdaddr_t bdaddr; + char addr[18]; + + /* TODO should parameters sanity be verified here? */ + + android2bdaddr(cmd->bdaddr, &bdaddr); + ba2str(&bdaddr, addr); + + DBG("%s variant %u accept %u", addr, cmd->ssp_variant, cmd->accept); + + switch (cmd->ssp_variant) { + case HAL_SSP_VARIANT_CONFIRM: + case HAL_SSP_VARIANT_CONSENT: + status = user_confirm_reply(&bdaddr, cmd->accept); + break; + case HAL_SSP_VARIANT_ENTRY: + status = user_passkey_reply(&bdaddr, cmd->accept, + cmd->passkey); + break; + case HAL_SSP_VARIANT_NOTIF: + status = HAL_STATUS_SUCCESS; + break; + default: + status = HAL_STATUS_INVALID; + break; + } + + return status; +} + +static uint8_t get_remote_services(void *buf, uint16_t len) +{ + struct hal_cmd_get_remote_services *cmd = buf; + bdaddr_t addr; + + android2bdaddr(&cmd->bdaddr, &addr); + + return browse_remote_sdp(&addr); +} + +void bt_bluetooth_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len) +{ + uint8_t status = HAL_STATUS_FAILED; + + switch (opcode) { + case HAL_OP_ENABLE: + /* 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 (!set_mode(MGMT_OP_SET_POWERED, 0x01)) + goto error; + + break; + case HAL_OP_DISABLE: + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { + status = HAL_STATUS_DONE; + goto error; + } + + if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) + goto error; + + break; + case HAL_OP_GET_ADAPTER_PROPS: + get_properties(); + + break; + case HAL_OP_GET_ADAPTER_PROP: + if (!get_property(buf, len)) + goto error; + + break; + case HAL_OP_SET_ADAPTER_PROP: + status = set_property(buf, len); + if (status != HAL_STATUS_SUCCESS && status != HAL_STATUS_DONE) + goto error; + + break; + case HAL_OP_CREATE_BOND: + if (!create_bond(buf, len)) + goto error; + + break; + case HAL_OP_CANCEL_BOND: + if (!cancel_bond(buf, len)) + goto error; + + break; + case HAL_OP_REMOVE_BOND: + if (!remove_bond(buf, len)) + goto error; + + break; + case HAL_OP_PIN_REPLY: + status = pin_reply(buf, len); + if (status != HAL_STATUS_SUCCESS) + goto error; + + 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; + } + + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { + status = HAL_STATUS_NOT_READY; + goto error; + } + + if (!start_discovery()) + goto error; + + 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; + } + + if (!stop_discovery()) + goto error; + + 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(sk, HAL_SERVICE_ID_BLUETOOTH, opcode, 0, NULL, -1); + return; + +error: + error("Error handling command 0x%02x status %u", opcode, status); + + ipc_send_rsp(sk, HAL_SERVICE_ID_BLUETOOTH, status); +} + +bool bt_bluetooth_register(int sk) +{ + DBG(""); + + notification_sk = sk; + + return true; +} + +void bt_bluetooth_unregister(void) +{ + DBG(""); + + notification_sk = -1; +} diff --git a/android/bluetooth.h b/android/bluetooth.h new file mode 100644 index 0000000..44b8e9e --- /dev/null +++ b/android/bluetooth.h @@ -0,0 +1,38 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr); +bool bt_bluetooth_start(int index, bt_bluetooth_ready cb); + +typedef void (*bt_bluetooth_stopped)(void); +bool bt_bluetooth_stop(bt_bluetooth_stopped cb); + +void bt_bluetooth_cleanup(void); + +void bt_bluetooth_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len); + +bool bt_bluetooth_register(int sk); +void bt_bluetooth_unregister(void); + +int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint); +void bt_adapter_remove_record(uint32_t handle); diff --git a/android/client/haltest.c b/android/client/haltest.c new file mode 100644 index 0000000..7154d27 --- /dev/null +++ b/android/client/haltest.c @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "if-main.h" +#include "terminal.h" +#include "pollhandler.h" +#include "history.h" + +const struct interface *interfaces[] = { + &bluetooth_if, + &av_if, +#if PLATFORM_SDK_VERSION > 17 + &gatt_if, + &gatt_client_if, + &gatt_server_if, +#endif + &hf_if, + &hh_if, + &pan_if, + &sock_if, + NULL +}; + +static struct method commands[]; + +struct method *get_method(struct method *methods, const char *name) +{ + while (strcmp(methods->name, "") != 0) { + if (strcmp(methods->name, name) == 0) + return methods; + methods++; + } + + return NULL; +} + +/* function returns interface of given name or NULL if not found */ +const struct interface *get_interface(const char *name) +{ + int i; + + for (i = 0; interfaces[i] != NULL; ++i) { + if (strcmp(interfaces[i]->name, name) == 0) + break; + } + + return interfaces[i]; +} + +int haltest_error(const char *format, ...) +{ + va_list args; + int ret; + va_start(args, format); + ret = terminal_vprint(format, args); + va_end(args); + return ret; +} + +int haltest_info(const char *format, ...) +{ + va_list args; + int ret; + va_start(args, format); + ret = terminal_vprint(format, args); + va_end(args); + return ret; +} + +int haltest_warn(const char *format, ...) +{ + va_list args; + int ret; + va_start(args, format); + ret = terminal_vprint(format, args); + va_end(args); + return ret; +} + +static void help_print_interface(const struct interface *i) +{ + struct method *m; + + for (m = i->methods; strcmp(m->name, "") != 0; m++) + haltest_info("%s %s %s\n", i->name, m->name, + (m->help ? m->help : "")); +} + +/* Help completion */ +static void help_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 2) + *enum_func = interface_name; +} + +/* Help execution */ +static void help_p(int argc, const char **argv) +{ + const struct method *m = commands; + const struct interface **ip = interfaces; + const struct interface *i; + + if (argc == 1) { + terminal_print("haltest allows to call Android HAL methods.\n"); + terminal_print("\nAvailable commands:\n"); + while (0 != strcmp(m->name, "")) { + terminal_print("\t%s %s\n", m->name, + (m->help ? m->help : "")); + m++; + } + + terminal_print("\nAvailable interfaces to use:\n"); + while (NULL != *ip) { + terminal_print("\t%s\n", (*ip)->name); + ip++; + } + + terminal_print("\nTo get help on methods for each interface type:\n"); + terminal_print("\n\thelp \n"); + terminal_print("\nBasic scenario:\n\tbluetooth init\n"); + terminal_print("\tbluetooth enable\n\tbluetooth start_discovery\n"); + terminal_print("\tbluetooth get_profile_interface handsfree\n"); + terminal_print("\thandsfree init\n\n"); + return; + } + + i = get_interface(argv[1]); + if (i == NULL) { + haltest_error("No such interface\n"); + return; + } + + help_print_interface(i); +} + +/* quit/exit execution */ +static void quit_p(int argc, const char **argv) +{ + exit(0); +} + +static int fd_stack[10]; +static int fd_stack_pointer = 0; + +static void stdin_handler(struct pollfd *pollfd); + +static void process_file(const char *name) +{ + int fd = open(name, O_RDONLY); + + if (fd < 0) { + haltest_error("Can't open file: %s for reading\n", name); + return; + } + + if (fd_stack_pointer >= 10) { + haltest_error("To many open files\n"); + close(fd); + return; + } + + fd_stack[fd_stack_pointer++] = fd; + poll_unregister_fd(fd_stack[fd_stack_pointer - 2], stdin_handler); + poll_register_fd(fd_stack[fd_stack_pointer - 1], POLLIN, stdin_handler); +} + +static void source_p(int argc, const char **argv) +{ + if (argc < 2) { + haltest_error("No file specified"); + return; + } + + process_file(argv[1]); +} + +/* Commands available without interface */ +static struct method commands[] = { + STD_METHODCH(help, "[]"), + STD_METHOD(quit), + METHOD("exit", quit_p, NULL, NULL), + STD_METHODH(source, ""), + END_METHOD +}; + +/* Gets comman by name */ +struct method *get_command(const char *name) +{ + return get_method(commands, name); +} + +/* Function to enumerate interface names */ +const char *interface_name(void *v, int i) +{ + return interfaces[i] ? interfaces[i]->name : NULL; +} + +/* Function to enumerate command and interface names */ +const char *command_name(void *v, int i) +{ + int cmd_cnt = NELEM(commands); + + if (i >= cmd_cnt) + return interface_name(v, i - cmd_cnt); + else + return commands[i].name; +} + +/* + * This function changes input parameter line_buffer so it has + * null termination after each token (due to strtok) + * Output argv is filled with pointers to arguments + * returns number of tokens parsed - argc + */ +static int command_line_to_argv(char *line_buffer, char *argv[], int argv_size) +{ + static const char *token_breaks = "\r\n\t "; + char *token; + int argc = 0; + + token = strtok(line_buffer, token_breaks); + while (token != NULL && argc < (int) argv_size) { + argv[argc++] = token; + token = strtok(NULL, token_breaks); + } + + return argc; +} + +static void process_line(char *line_buffer) +{ + char *argv[10]; + int argc; + int i = 0; + struct method *m; + + argc = command_line_to_argv(line_buffer, argv, 10); + if (argc < 1) + return; + + while (interfaces[i] != NULL) { + if (strcmp(interfaces[i]->name, argv[0])) { + i++; + continue; + } + + if (argc < 2 || strcmp(argv[1], "?") == 0) { + help_print_interface(interfaces[i]); + return; + } + + m = get_method(interfaces[i]->methods, argv[1]); + if (m != NULL) { + m->func(argc, (const char **) argv); + return; + } + + haltest_error("No function %s found\n", argv[1]); + return; + } + /* No interface, try commands */ + m = get_command(argv[0]); + if (m == NULL) + haltest_error("No such command %s\n", argv[0]); + else + m->func(argc, (const char **) argv); +} + +/* called when there is something on stdin */ +static void stdin_handler(struct pollfd *pollfd) +{ + char buf[10]; + + if (pollfd->revents & POLLIN) { + int count = read(fd_stack[fd_stack_pointer - 1], buf, 10); + + if (count > 0) { + int i; + + for (i = 0; i < count; ++i) + terminal_process_char(buf[i], process_line); + return; + } + } + + if (fd_stack_pointer > 1) + poll_register_fd(fd_stack[fd_stack_pointer - 2], POLLIN, + stdin_handler); + if (fd_stack_pointer > 0) { + poll_unregister_fd(fd_stack[--fd_stack_pointer], stdin_handler); + + if (fd_stack[fd_stack_pointer]) + close(fd_stack[fd_stack_pointer]); + } +} + +static void usage(void) +{ + printf("haltest Android Bluetooth HAL testing tool\n" + "Usage:\n"); + printf("\thaltest [options]\n"); + printf("options:\n" + "\t-n, --no-init Don't call init for interfaces\n" + "\t --version Print version\n" + "\t-h, --help Show help options\n"); +} + +enum { + PRINT_VERSION = 1000 +}; + +int version = 1; +int revision = 0; + +static void print_version(void) +{ + printf("haltest version %d.%d\n", version, revision); +} + +static const struct option main_options[] = { + { "no-init", no_argument, NULL, 'n' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, PRINT_VERSION }, + { NULL } +}; + +static bool no_init = false; + +static void parse_command_line(int argc, char *argv[]) +{ + for (;;) { + int opt; + + opt = getopt_long(argc, argv, "nh", main_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'n': + no_init = true; + break; + case 'h': + usage(); + exit(0); + case PRINT_VERSION: + print_version(); + exit(0); + default: + putchar('\n'); + exit(-1); + break; + } + } +} + +static void init(void) +{ + static const char * const inames[] = { + BT_PROFILE_HANDSFREE_ID, + BT_PROFILE_ADVANCED_AUDIO_ID, + BT_PROFILE_HEALTH_ID, + BT_PROFILE_HIDHOST_ID, + BT_PROFILE_PAN_ID, +#if PLATFORM_SDK_VERSION > 17 + BT_PROFILE_GATT_ID, +#endif + BT_PROFILE_SOCKETS_ID + }; + const struct method *m; + const char *argv[4]; + char init_line[] = "bluetooth init"; + uint32_t i; + + process_line(init_line); + m = get_interface_method("bluetooth", "get_profile_interface"); + + for (i = 0; i < NELEM(inames); ++i) { + argv[2] = inames[i]; + m->func(3, argv); + } + + /* Init what is available to init */ + for (i = 1; i < NELEM(interfaces) - 1; ++i) { + m = get_interface_method(interfaces[i]->name, "init"); + if (m != NULL) + m->func(2, argv); + } +} + +int main(int argc, char **argv) +{ + struct stat rcstat; + + parse_command_line(argc, argv); + + terminal_setup(); + + if (!no_init) + init(); + + history_restore(".haltest_history"); + + fd_stack[fd_stack_pointer++] = 0; + /* Register command line handler */ + poll_register_fd(0, POLLIN, stdin_handler); + + if (stat(".haltestrc", &rcstat) == 0 && (rcstat.st_mode & S_IFREG) != 0) + process_file(".haltestrc"); + + poll_dispatch_loop(); + + return 0; +} diff --git a/android/client/history.c b/android/client/history.c new file mode 100644 index 0000000..ee285da --- /dev/null +++ b/android/client/history.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "history.h" + +/* + * Very simple history storage for easy usage of tool + */ + +#define HISTORY_DEPTH 20 +#define LINE_SIZE 100 +static char lines[HISTORY_DEPTH][LINE_SIZE]; +static int last_line = 0; +static int history_size = 0; + +/* TODO: Storing history not implemented yet */ +void history_store(const char *filename) +{ +} + +/* Restoring history from file */ +void history_restore(const char *filename) +{ + char line[1000]; + FILE *f = fopen(filename, "rt"); + + if (f == NULL) + return; + + for (;;) { + if (fgets(line, 1000, f) != NULL) { + int l = strlen(line); + + while (l > 0 && isspace(line[--l])) + line[l] = 0; + + if (l > 0) + history_add_line(line); + } else + break; + } + + fclose(f); +} + +/* Add new line to history buffer */ +void history_add_line(const char *line) +{ + if (line == NULL || strlen(line) == 0) + return; + + if (strcmp(line, lines[last_line]) == 0) + return; + + last_line = (last_line + 1) % HISTORY_DEPTH; + strncpy(&lines[last_line][0], line, LINE_SIZE - 1); + if (history_size < HISTORY_DEPTH) + history_size++; +} + +/* + * Get n-th line from history + * 0 - means latest + * -1 - means oldest + * return -1 if there is no such line + */ +int history_get_line(int n, char *buf, int buf_size) +{ + if (n == -1) + n = history_size - 1; + + if (n >= history_size || buf_size == 0 || n < 0) + return -1; + + strncpy(buf, + &lines[(HISTORY_DEPTH + last_line - n) % HISTORY_DEPTH][0], + buf_size - 1); + buf[buf_size - 1] = 0; + + return n; +} diff --git a/android/client/history.h b/android/client/history.h new file mode 100644 index 0000000..26085b5 --- /dev/null +++ b/android/client/history.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +void history_store(const char *filename); +void history_restore(const char *filename); +void history_add_line(const char *line); +int history_get_line(int n, char *buf, int buf_size); diff --git a/android/client/hwmodule.c b/android/client/hwmodule.c new file mode 100644 index 0000000..80e7475 --- /dev/null +++ b/android/client/hwmodule.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#include "hardware/hardware.h" + +int hw_get_module(const char *id, const struct hw_module_t **module) +{ + extern struct hw_module_t HAL_MODULE_INFO_SYM; + + *module = &HAL_MODULE_INFO_SYM; + + return 0; +} diff --git a/android/client/if-av.c b/android/client/if-av.c new file mode 100644 index 0000000..0470e0d --- /dev/null +++ b/android/client/if-av.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "if-main.h" +#include "../hal-utils.h" + +const btav_interface_t *if_av = NULL; + +SINTMAP(btav_connection_state_t, -1, "(unknown)") + DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTED), + DELEMENT(BTAV_CONNECTION_STATE_CONNECTING), + DELEMENT(BTAV_CONNECTION_STATE_CONNECTED), + DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTING), +ENDMAP + +SINTMAP(btav_audio_state_t, -1, "(unknown)") + DELEMENT(BTAV_AUDIO_STATE_REMOTE_SUSPEND), + DELEMENT(BTAV_AUDIO_STATE_STOPPED), + DELEMENT(BTAV_AUDIO_STATE_STARTED), +ENDMAP + +static char last_addr[MAX_ADDR_STR_LEN]; + +static void connection_state(btav_connection_state_t state, + bt_bdaddr_t *bd_addr) +{ + haltest_info("%s: connection_state=%s remote_bd_addr=%s\n", __func__, + btav_connection_state_t2str(state), + bt_bdaddr_t2str(bd_addr, last_addr)); +} + +static void audio_state(btav_audio_state_t state, bt_bdaddr_t *bd_addr) +{ + haltest_info("%s: audio_state=%s remote_bd_addr=%s\n", __func__, + btav_audio_state_t2str(state), + bt_bdaddr_t2str(bd_addr, last_addr)); +} + +static btav_callbacks_t av_cbacks = { + .size = sizeof(av_cbacks), + .connection_state_cb = connection_state, + .audio_state_cb = audio_state +}; + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_av); + + EXEC(if_av->init, &av_cbacks); +} + +/* connect */ + +static void connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = NULL; + *enum_func = enum_devices; + } +} + +static void connect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_av->connect, &addr); +} + +/* disconnect */ + +static void disconnect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = last_addr; + *enum_func = enum_one_string; + } +} + +static void disconnect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_av->disconnect, &addr); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_av); + + EXECV(if_av->cleanup); + if_av = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHODCH(connect, ""), + STD_METHODCH(disconnect, ""), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface av_if = { + .name = "av", + .methods = methods +}; diff --git a/android/client/if-bt.c b/android/client/if-bt.c new file mode 100644 index 0000000..0cd43db --- /dev/null +++ b/android/client/if-bt.c @@ -0,0 +1,829 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "if-main.h" +#include "terminal.h" +#include "../hal-utils.h" + +const bt_interface_t *if_bluetooth; + +#define VERIFY_PROP_TYPE_ARG(n, typ) \ + do { \ + if (n < argc) \ + typ = str2btpropertytype(argv[n]); \ + else { \ + haltest_error("No property type specified\n"); \ + return;\ + } \ + } while (0) + +static bt_scan_mode_t str2btscanmode(const char *str) +{ + bt_scan_mode_t v = str2bt_scan_mode_t(str); + + if ((int) v != -1) + return v; + + haltest_warn("WARN: %s cannot convert %s\n", __func__, str); + return (bt_scan_mode_t) atoi(str); +} + +static bt_ssp_variant_t str2btsspvariant(const char *str) +{ + bt_ssp_variant_t v = str2bt_ssp_variant_t(str); + + if ((int) v != -1) + return v; + + haltest_warn("WARN: %s cannot convert %s\n", __func__, str); + return (bt_ssp_variant_t) atoi(str); +} + +static bt_property_type_t str2btpropertytype(const char *str) +{ + bt_property_type_t v = str2bt_property_type_t(str); + + if ((int) v != -1) + return v; + + haltest_warn("WARN: %s cannot convert %s\n", __func__, str); + return (bt_property_type_t) atoi(str); +} + +static void dump_properties(int num_properties, bt_property_t *properties) +{ + int i; + + for (i = 0; i < num_properties; i++) { + /* + * properities sometimes come unaligned hence memcp to + * aligned buffer + */ + bt_property_t prop; + memcpy(&prop, properties + i, sizeof(prop)); + + haltest_info("prop: %s\n", btproperty2str(&prop)); + } +} + +/* + * Cache for remote devices, stored in sorted array + */ +static bt_bdaddr_t *remote_devices = NULL; +static int remote_devices_cnt = 0; +static int remote_devices_capacity = 0; + +/* Adds address to remote device set so it can be used in tab completion */ +void add_remote_device(const bt_bdaddr_t *addr) +{ + int i; + + if (remote_devices == NULL) { + remote_devices = malloc(4 * sizeof(bt_bdaddr_t)); + remote_devices_cnt = 0; + if (remote_devices == NULL) { + remote_devices_capacity = 0; + return; + } + + remote_devices_capacity = 4; + } + + /* Array is sorted, search for right place */ + for (i = 0; i < remote_devices_cnt; ++i) { + int res = memcmp(&remote_devices[i], addr, sizeof(*addr)); + + if (res == 0) + return; /* Already added */ + else if (res > 0) + break; + } + + /* Realloc space if needed */ + if (remote_devices_cnt >= remote_devices_capacity) { + remote_devices_capacity *= 2; + remote_devices = realloc(remote_devices, sizeof(bt_bdaddr_t) * + remote_devices_capacity); + if (remote_devices == NULL) { + remote_devices_capacity = 0; + remote_devices_cnt = 0; + return; + } + } + + if (i < remote_devices_cnt) + memmove(remote_devices + i + 1, remote_devices + i, + (remote_devices_cnt - i) * sizeof(bt_bdaddr_t)); + remote_devices[i] = *addr; + remote_devices_cnt++; +} + +const char *enum_devices(void *v, int i) +{ + static char buf[MAX_ADDR_STR_LEN]; + + if (i >= remote_devices_cnt) + return NULL; + + bt_bdaddr_t2str(&remote_devices[i], buf); + return buf; +} + +static void add_remote_device_from_props(int num_properties, + const bt_property_t *properties) +{ + int i; + + for (i = 0; i < num_properties; i++) { + /* + * properities sometimes come unaligned hence memcp to + * aligned buffer + */ + bt_property_t property; + + memcpy(&property, properties + i, sizeof(property)); + if (property.type == BT_PROPERTY_BDADDR) + add_remote_device((bt_bdaddr_t *) property.val); + } +} + +static void adapter_state_changed_cb(bt_state_t state) +{ + haltest_info("%s: state=%s\n", __func__, bt_state_t2str(state)); +} + +static void adapter_properties_cb(bt_status_t status, int num_properties, + bt_property_t *properties) +{ + haltest_info("%s: status=%s num_properties=%d\n", __func__, + bt_status_t2str(status), num_properties); + + dump_properties(num_properties, properties); +} + +static void remote_device_properties_cb(bt_status_t status, + bt_bdaddr_t *bd_addr, + int num_properties, + bt_property_t *properties) +{ + haltest_info("%s: status=%s bd_addr=%s num_properties=%d\n", __func__, + bt_status_t2str(status), bdaddr2str(bd_addr), + num_properties); + + add_remote_device(bd_addr); + + dump_properties(num_properties, properties); +} + +static void device_found_cb(int num_properties, bt_property_t *properties) +{ + haltest_info("%s: num_properties=%d\n", __func__, num_properties); + + add_remote_device_from_props(num_properties, properties); + + dump_properties(num_properties, properties); +} + +static void discovery_state_changed_cb(bt_discovery_state_t state) +{ + haltest_info("%s: state=%s\n", __func__, + bt_discovery_state_t2str(state)); +} + +/* + * Buffer for remote addres that came from one of bind request. + * It's stored for command completion. + */ +static char last_remote_addr[MAX_ADDR_STR_LEN]; +static bt_ssp_variant_t last_ssp_variant = (bt_ssp_variant_t) -1; + +static bt_bdaddr_t pin_request_addr; +static void pin_request_answer(char *reply) +{ + bt_pin_code_t pin; + int accept = 0; + int pin_len = strlen(reply); + + if (pin_len > 0) { + accept = 1; + if (pin_len > 16) + pin_len = 16; + memcpy(&pin.pin, reply, pin_len); + } + + EXEC(if_bluetooth->pin_reply, &pin_request_addr, accept, pin_len, &pin); +} + +static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, + uint32_t cod) +{ + /* Store for command completion */ + bt_bdaddr_t2str(remote_bd_addr, last_remote_addr); + pin_request_addr = *remote_bd_addr; + + haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x\n", __func__, + last_remote_addr, bd_name->name, cod); + terminal_prompt_for("Enter pin: ", pin_request_answer); +} + +/* Variables to store information from ssp_request_cb used for ssp_reply */ +static bt_bdaddr_t ssp_request_addr; +static bt_ssp_variant_t ssp_request_variant; +static uint32_t ssp_request_pask_key; + +/* Called when user hit enter on prompt for confirmation */ +static void ssp_request_yes_no_answer(char *reply) +{ + int accept = *reply == 0 || *reply == 'y' || *reply == 'Y'; + + if_bluetooth->ssp_reply(&ssp_request_addr, ssp_request_variant, accept, + ssp_request_pask_key); +} + +static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, + uint32_t cod, bt_ssp_variant_t pairing_variant, + uint32_t pass_key) +{ + static char prompt[50]; + + /* Store for command completion */ + bt_bdaddr_t2str(remote_bd_addr, last_remote_addr); + last_ssp_variant = pairing_variant; + + haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x pairing_variant=%s pass_key=%d\n", + __func__, last_remote_addr, bd_name->name, cod, + bt_ssp_variant_t2str(pairing_variant), pass_key); + + if (pairing_variant == BT_SSP_VARIANT_PASSKEY_CONFIRMATION) { + sprintf(prompt, "Does other device show %d [Y/n] ?", pass_key); + + ssp_request_addr = *remote_bd_addr; + ssp_request_variant = pairing_variant; + ssp_request_pask_key = pass_key; + + terminal_prompt_for(prompt, ssp_request_yes_no_answer); + } +} + +static void bond_state_changed_cb(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, + bt_bond_state_t state) +{ + haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__, + bt_status_t2str(status), bdaddr2str(remote_bd_addr), + bt_bond_state_t2str(state)); +} + +static void acl_state_changed_cb(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, + bt_acl_state_t state) +{ + haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__, + bt_status_t2str(status), bdaddr2str(remote_bd_addr), + bt_acl_state_t2str(state)); +} + +static void thread_evt_cb(bt_cb_thread_evt evt) +{ + haltest_info("%s: evt=%s\n", __func__, bt_cb_thread_evt2str(evt)); +} + +static void dut_mode_recv_cb(uint16_t opcode, uint8_t *buf, uint8_t len) +{ + haltest_info("%s\n", __func__); +} + +#if PLATFORM_SDK_VERSION > 17 +static void le_test_mode_cb(bt_status_t status, uint16_t num_packets) +{ + haltest_info("%s %s %d\n", __func__, bt_state_t2str(status), + num_packets); +} +#endif + +static bt_callbacks_t bt_callbacks = { + .size = sizeof(bt_callbacks), + .adapter_state_changed_cb = adapter_state_changed_cb, + .adapter_properties_cb = adapter_properties_cb, + .remote_device_properties_cb = remote_device_properties_cb, + .device_found_cb = device_found_cb, + .discovery_state_changed_cb = discovery_state_changed_cb, + .pin_request_cb = pin_request_cb, + .ssp_request_cb = ssp_request_cb, + .bond_state_changed_cb = bond_state_changed_cb, + .acl_state_changed_cb = acl_state_changed_cb, + .thread_evt_cb = thread_evt_cb, + .dut_mode_recv_cb = dut_mode_recv_cb, +#if PLATFORM_SDK_VERSION > 17 + .le_test_mode_cb = le_test_mode_cb +#endif +}; + +static void init_p(int argc, const char **argv) +{ + int err; + const hw_module_t *module; + hw_device_t *device; + + err = hw_get_module(BT_HARDWARE_MODULE_ID, &module); + if (err) { + haltest_error("he_get_module returned %d\n", err); + return; + } + + err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device); + if (err) { + haltest_error("module->methods->open returned %d\n", err); + return; + } + + if_bluetooth = + ((bluetooth_device_t *) device)->get_bluetooth_interface(); + if (!if_bluetooth) { + haltest_error("get_bluetooth_interface returned NULL\n"); + return; + } + + EXEC(if_bluetooth->init, &bt_callbacks); +} + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXECV(if_bluetooth->cleanup); + + if_bluetooth = NULL; +} + +static void enable_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXEC(if_bluetooth->enable); +} + +static void disable_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXEC(if_bluetooth->disable); +} + +static void get_adapter_properties_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXEC(if_bluetooth->get_adapter_properties); +} + +static void get_adapter_property_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(bt_property_type_t); + *enum_func = enum_defines; + } +} + +static void get_adapter_property_p(int argc, const char **argv) +{ + int type; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_PROP_TYPE_ARG(2, type); + + EXEC(if_bluetooth->get_adapter_property, type); +} + +static const char * const names[] = { + "BT_PROPERTY_BDNAME", + "BT_PROPERTY_ADAPTER_SCAN_MODE", + "BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT", + NULL +}; + +static void set_adapter_property_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = (void *) names; + *enum_func = enum_strings; + } else if (argc == 4) { + if (0 == strcmp(argv[2], "BT_PROPERTY_ADAPTER_SCAN_MODE")) { + *user = TYPE_ENUM(bt_scan_mode_t); + *enum_func = enum_defines; + } + } +} + +static void set_adapter_property_p(int argc, const char **argv) +{ + bt_property_t property; + bt_scan_mode_t mode; + int timeout; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_PROP_TYPE_ARG(2, property.type); + + if (argc <= 3) { + haltest_error("No property value specified\n"); + return; + } + switch (property.type) { + case BT_PROPERTY_BDNAME: + property.len = strlen(argv[3]) + 1; + property.val = (char *) argv[3]; + break; + + case BT_PROPERTY_ADAPTER_SCAN_MODE: + mode = str2btscanmode(argv[3]); + property.len = sizeof(bt_scan_mode_t); + property.val = &mode; + break; + + case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT: + timeout = atoi(argv[3]); + property.val = &timeout; + property.len = sizeof(timeout); + break; + + default: + haltest_error("Invalid property %s\n", argv[3]); + return; + } + + EXEC(if_bluetooth->set_adapter_property, &property); +} + +/* + * This function is to be used for completion methods that need only address + */ +static void complete_addr_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = NULL; + *enum_func = enum_devices; + } +} + +/* Just addres to complete, use complete_addr_c */ +#define get_remote_device_properties_c complete_addr_c + +static void get_remote_device_properties_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_bluetooth->get_remote_device_properties, &addr); +} + +static void get_remote_device_property_c(int argc, const char **argv, + enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = NULL; + *enum_func = enum_devices; + } else if (argc == 4) { + *user = TYPE_ENUM(bt_property_type_t); + *enum_func = enum_defines; + } +} + +static void get_remote_device_property_p(int argc, const char **argv) +{ + bt_property_type_t type; + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + VERIFY_PROP_TYPE_ARG(3, type); + + EXEC(if_bluetooth->get_remote_device_property, &addr, type); +} + +/* + * Same completion as for get_remote_device_property_c can be used for + * set_remote_device_property_c. No need to create separate function. + */ +#define set_remote_device_property_c get_remote_device_property_c + +static void set_remote_device_property_p(int argc, const char **argv) +{ + bt_property_t property; + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + VERIFY_PROP_TYPE_ARG(3, property.type); + + switch (property.type) { + case BT_PROPERTY_REMOTE_FRIENDLY_NAME: + property.len = strlen(argv[4]); + property.val = (char *) argv[4]; + break; + default: + return; + } + + EXEC(if_bluetooth->set_remote_device_property, &addr, &property); +} + +/* + * For now uuid is not autocompleted. Use routine for complete_addr_c + */ +#define get_remote_service_record_c complete_addr_c + +static void get_remote_service_record_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bt_uuid_t uuid; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + if (argc <= 3) { + haltest_error("No uuid specified\n"); + return; + } + + str2bt_uuid_t(argv[3], &uuid); + + EXEC(if_bluetooth->get_remote_service_record, &addr, &uuid); +} + +/* Just addres to complete, use complete_addr_c */ +#define get_remote_services_c complete_addr_c + +static void get_remote_services_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_bluetooth->get_remote_services, &addr); +} + +static void start_discovery_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXEC(if_bluetooth->start_discovery); +} + +static void cancel_discovery_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXEC(if_bluetooth->cancel_discovery); +} + +/* Just addres to complete, use complete_addr_c */ +#define create_bond_c complete_addr_c + +static void create_bond_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_bluetooth->create_bond, &addr); +} + +/* Just addres to complete, use complete_addr_c */ +#define remove_bond_c complete_addr_c + +static void remove_bond_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_bluetooth->remove_bond, &addr); +} + +/* Just addres to complete, use complete_addr_c */ +#define cancel_bond_c complete_addr_c + +static void cancel_bond_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_bluetooth->cancel_bond, &addr); +} + +static void pin_reply_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + static const char *const completions[] = { last_remote_addr, NULL }; + + if (argc == 3) { + *user = (void *) completions; + *enum_func = enum_strings; + } +} + +static void pin_reply_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bt_pin_code_t pin; + int pin_len = 0; + int accept = 0; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + if (argc > 3) { + accept = 1; + pin_len = strlen(argv[3]); + memcpy(pin.pin, argv[3], pin_len); + } + + EXEC(if_bluetooth->pin_reply, &addr, accept, pin_len, &pin); +} + +static void ssp_reply_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = last_remote_addr; + *enum_func = enum_one_string; + } else if (argc == 5) { + *user = "1"; + *enum_func = enum_one_string; + } else if (argc == 4) { + if (-1 != (int) last_ssp_variant) { + *user = (void *) bt_ssp_variant_t2str(last_ssp_variant); + *enum_func = enum_one_string; + } else { + *user = TYPE_ENUM(bt_ssp_variant_t); + *enum_func = enum_defines; + } + } +} + +static void ssp_reply_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bt_ssp_variant_t var; + int accept; + int passkey; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + if (argc < 4) { + haltest_error("No ssp variant specified\n"); + return; + } + + var = str2btsspvariant(argv[3]); + if (argc < 5) { + haltest_error("No accept value specified\n"); + return; + } + + accept = atoi(argv[4]); + passkey = 0; + + if (accept && var == BT_SSP_VARIANT_PASSKEY_ENTRY && argc >= 5) + passkey = atoi(argv[4]); + + EXEC(if_bluetooth->ssp_reply, &addr, var, accept, passkey); +} + +static void get_profile_interface_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + static const char *const profile_ids[] = { + BT_PROFILE_HANDSFREE_ID, + BT_PROFILE_ADVANCED_AUDIO_ID, + BT_PROFILE_HEALTH_ID, + BT_PROFILE_SOCKETS_ID, + BT_PROFILE_HIDHOST_ID, + BT_PROFILE_PAN_ID, +#if PLATFORM_SDK_VERSION > 17 + BT_PROFILE_GATT_ID, + BT_PROFILE_AV_RC_ID, +#endif + NULL + }; + + if (argc == 3) { + *user = (void *) profile_ids; + *enum_func = enum_strings; + } +} + +static void get_profile_interface_p(int argc, const char **argv) +{ + const char *id; + const void **pif = NULL; + const void *dummy = NULL; + + RETURN_IF_NULL(if_bluetooth); + if (argc <= 2) { + haltest_error("No interface specified\n"); + return; + } + + id = argv[2]; + + if (strcmp(BT_PROFILE_HANDSFREE_ID, id) == 0) + pif = (const void **) &if_hf; + else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_ID, id) == 0) + pif = (const void **) &if_av; + else if (strcmp(BT_PROFILE_HEALTH_ID, id) == 0) + pif = &dummy; /* TODO: change when if_hl is there */ + else if (strcmp(BT_PROFILE_SOCKETS_ID, id) == 0) + pif = (const void **) &if_sock; + else if (strcmp(BT_PROFILE_HIDHOST_ID, id) == 0) + pif = (const void **) &if_hh; + else if (strcmp(BT_PROFILE_PAN_ID, id) == 0) + pif = (const void **) &if_pan; +#if PLATFORM_SDK_VERSION > 17 + else if (strcmp(BT_PROFILE_AV_RC_ID, id) == 0) + pif = &dummy; /* TODO: change when if_rc is there */ + else if (strcmp(BT_PROFILE_GATT_ID, id) == 0) + pif = (const void **) &if_gatt; +#endif + else + haltest_error("%s is not correct for get_profile_interface\n", + id); + + if (pif != NULL) { + *pif = if_bluetooth->get_profile_interface(id); + haltest_info("get_profile_interface(%s) : %p\n", id, *pif); + } +} + +static void dut_mode_configure_p(int argc, const char **argv) +{ + uint8_t mode; + + RETURN_IF_NULL(if_bluetooth); + + if (argc <= 2) { + haltest_error("No dut mode specified\n"); + return; + } + + mode = strtol(argv[2], NULL, 0); + + EXEC(if_bluetooth->dut_mode_configure, mode); +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHOD(cleanup), + STD_METHOD(enable), + STD_METHOD(disable), + STD_METHOD(get_adapter_properties), + STD_METHODCH(get_adapter_property, ""), + STD_METHODCH(set_adapter_property, " "), + STD_METHODCH(get_remote_device_properties, ""), + STD_METHODCH(get_remote_device_property, " "), + STD_METHODCH(set_remote_device_property, + " "), + STD_METHODCH(get_remote_service_record, " "), + STD_METHODCH(get_remote_services, ""), + STD_METHOD(start_discovery), + STD_METHOD(cancel_discovery), + STD_METHODCH(create_bond, ""), + STD_METHODCH(remove_bond, ""), + STD_METHODCH(cancel_bond, ""), + STD_METHODCH(pin_reply, "
[]"), + STD_METHODCH(ssp_reply, "
1|0 []"), + STD_METHODCH(get_profile_interface, ""), + STD_METHODH(dut_mode_configure, ""), + END_METHOD +}; + +const struct interface bluetooth_if = { + .name = "bluetooth", + .methods = methods +}; diff --git a/android/client/if-gatt.c b/android/client/if-gatt.c new file mode 100644 index 0000000..bb53952 --- /dev/null +++ b/android/client/if-gatt.c @@ -0,0 +1,1758 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "../hal-utils.h" +#include "if-main.h" + +const btgatt_interface_t *if_gatt = NULL; + +/* In version 19 some callback were changed. + * btgatt_char_id_t -> btgatt_gatt_id_t + * bt_uuid_t -> btgatt_gatt_id_t + */ +#if PLATFORM_SDK_VERSION > 18 +#define str2btgatt_descr_id_t str2btgatt_gatt_id_t +#define btgatt_descr_id_t2str btgatt_gatt_id_t2str +#define btgatt_descr_id_t btgatt_gatt_id_t +#else +#define btgatt_descr_id_t2str gatt_uuid_t2str +#define str2btgatt_descr_id_t(a, b) gatt_str2bt_uuid_t(a, -1, b) +#define btgatt_gatt_id_t btgatt_char_id_t +#define btgatt_descr_id_t bt_uuid_t +#endif + +#define MAX_CHAR_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11) +#define MAX_SRVC_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11 + 1 + 11) +/* How man characters print from binary objects (arbitrary) */ +#define MAX_HEX_VAL_STR_LEN 100 +#define MAX_NOTIFY_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \ + + MAX_ADDR_STR_LEN + MAX_HEX_VAL_STR_LEN + 60) +#define MAX_READ_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \ + + MAX_UUID_STR_LEN + MAX_HEX_VAL_STR_LEN + 80) + +#define VERIFY_INT_ARG(n, v, err) \ + do { \ + if (n < argc) \ + v = atoi(argv[n]); \ + else { \ + haltest_error(err); \ + return;\ + } \ + } while (0) + +#define VERIFY_HEX_ARG(n, v, err) \ + do { \ + if (n < argc) \ + v = strtol(argv[n], NULL, 16); \ + else { \ + haltest_error(err); \ + return;\ + } \ + } while (0) + +/* Helper macros to verify arguments of methods */ +#define VERIFY_CLIENT_IF(n, v) VERIFY_INT_ARG(n, v, "No client_if specified\n") +#define VERIFY_SERVER_IF(n, v) VERIFY_INT_ARG(n, v, "No server_if specified\n") +#define VERIFY_CONN_ID(n, v) VERIFY_INT_ARG(n, v, "No conn_if specified\n") +#define VERIFY_HANDLE(n, v) VERIFY_HEX_ARG(n, v, "No "#v" specified\n") +#define VERIFY_SERVICE_HANDLE(n, v) VERIFY_HANDLE(n, v) + +#define VERIFY_UUID(n, v) \ + do { \ + if (n < argc) \ + gatt_str2bt_uuid_t(argv[n], -1, v); \ + else { \ + haltest_error("No uuid specified\n"); \ + return;\ + } \ + } while (0) + +#define VERIFY_SRVC_ID(n, v) \ + do { \ + if (n < argc) \ + str2btgatt_srvc_id_t(argv[n], v); \ + else { \ + haltest_error("No srvc_id specified\n"); \ + return;\ + } \ + } while (0) + +#define VERIFY_CHAR_ID(n, v) \ + do { \ + if (n < argc) \ + str2btgatt_gatt_id_t(argv[n], v); \ + else { \ + haltest_error("No char_id specified\n"); \ + return;\ + } \ + } while (0) + +#define VERIFY_DESCR_ID(n, v) \ + do { \ + if (n < argc) \ + str2btgatt_descr_id_t(argv[n], v); \ + else { \ + haltest_error("No descr_id specified\n"); \ + return;\ + } \ + } while (0) + +/* Gatt uses little endian uuid */ +static const char GATT_BASE_UUID[] = { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * converts gatt uuid to string + * buf should be at least 39 bytes + * + * This function formats 16, 32 and 128 bits uuid + * + * returns string representation of uuid + */ +static char *gatt_uuid_t2str(const bt_uuid_t *uuid, char *buf) +{ + int shift = 0; + int i = 16; + int limit = 0; + int j = 0; + + /* for bluetooth uuid only 32 bits */ + if (0 == memcmp(&uuid->uu, &GATT_BASE_UUID, + sizeof(bt_uuid_t) - 4)) { + limit = 12; + /* make it 16 bits */ + if (uuid->uu[15] == 0 && uuid->uu[14] == 0) + i = 14; + } + + while (i-- > limit) { + if (i == 11 || i == 9 || i == 7 || i == 5) { + buf[j * 2 + shift] = '-'; + shift++; + } + + sprintf(buf + j * 2 + shift, "%02x", uuid->uu[i]); + ++j; + } + + return buf; +} + +/* + * Tries to convert hex string of given size into out buffer. + * Output buffer is little endian. + */ +static void scan_field(const char *str, int len, uint8_t *out, int out_size) +{ + int i; + + memset(out, 0, out_size); + if (out_size * 2 > len + 1) + out_size = (len + 1) / 2; + + for (i = 0; i < out_size && len > 0; ++i) { + len -= 2; + if (len >= 0) + sscanf(str + len, "%02hhx", &out[i]); + else + sscanf(str, "%1hhx", &out[i]); + } +} + +/* Like strchr but with upper limit instead of 0 terminated string */ +static const char *strchrlimit(const char *p, const char *e, int c) +{ + while (p < e && *p != (char) c) + ++p; + + return p < e ? p : NULL; +} + +/* + * converts string to uuid + * it accepts uuid in following forms: + * 123 + * 0000123 + * 0000123-0014-1234-0000-000056789abc + * 0000123001412340000000056789abc + * 123-14-1234-0-56789abc + */ +static void gatt_str2bt_uuid_t(const char *str, int len, bt_uuid_t *uuid) +{ + int dash_cnt = 0; + int dashes[6] = {-1}; /* indexes of '-' or \0 */ + static uint8_t filed_offset[] = { 16, 12, 10, 8, 6, 0 }; + const char *p = str; + const char *e; + int i; + + e = str + ((len >= 0) ? len : (int) strlen(str)); + + while (p != NULL && dash_cnt < 5) { + const char *f = strchrlimit(p, e, '-'); + + if (f != NULL) + dashes[++dash_cnt] = f++ - str; + p = f; + } + + /* get index of \0 to dashes table */ + if (dash_cnt < 5) + dashes[++dash_cnt] = e - str; + + memcpy(uuid, GATT_BASE_UUID, sizeof(bt_uuid_t)); + + /* whole uuid in one string without dashes */ + if (dash_cnt == 1 && dashes[1] > 8) { + if (dashes[1] > 32) + dashes[1] = 32; + scan_field(str, dashes[1], + &uuid->uu[16 - (dashes[1] + 1) / 2], + (dashes[1] + 1) / 2); + } else { + for (i = 0; i < dash_cnt; ++i) { + scan_field(str + dashes[i] + 1, + dashes[i + 1] - dashes[i] - 1, + &uuid->uu[filed_offset[i + 1]], + filed_offset[i] - filed_offset[i + 1]); + } + } +} + +/* char_id formating function */ +static char *btgatt_gatt_id_t2str(const btgatt_gatt_id_t *char_id, char *buf) +{ + char uuid_buf[MAX_UUID_STR_LEN]; + + sprintf(buf, "{%s,%d}", gatt_uuid_t2str(&char_id->uuid, uuid_buf), + char_id->inst_id); + return buf; +} + +/* Parse btgatt_gatt_id_t */ +static void str2btgatt_gatt_id_t(const char *buf, btgatt_gatt_id_t *char_id) +{ + const char *e; + + memcpy(&char_id->uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t)); + char_id->inst_id = 0; + + if (*buf == '{') + buf++; + e = strpbrk(buf, " ,}"); + if (e == NULL) + e = buf + strlen(buf); + + gatt_str2bt_uuid_t(buf, e - buf, &char_id->uuid); + if (*e == ',') { + buf = e + 1; + e = strpbrk(buf, " ,}"); + if (e == NULL) + e = buf + strlen(buf); + if (buf < e) + char_id->inst_id = atoi(buf); + } +} + +/* service_id formating function */ +static char *btgatt_srvc_id_t2str(const btgatt_srvc_id_t *srvc_id, char *buf) +{ + char uuid_buf[MAX_UUID_STR_LEN]; + + sprintf(buf, "{%s,%d,%d}", gatt_uuid_t2str(&srvc_id->id.uuid, uuid_buf), + srvc_id->id.inst_id, srvc_id->is_primary); + return buf; +} + +/* Parse btgatt_srvc_id_t */ +static void str2btgatt_srvc_id_t(const char *buf, btgatt_srvc_id_t *srvc_id) +{ + const char *e; + + memcpy(&srvc_id->id.uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t)); + srvc_id->id.inst_id = 0; + srvc_id->is_primary = 1; + + if (*buf == '{') + buf++; + e = strpbrk(buf, " ,}"); + if (e == NULL) + e = buf + strlen(buf); + + gatt_str2bt_uuid_t(buf, e - buf, &srvc_id->id.uuid); + if (*e == ',') { + buf = e + 1; + e = strpbrk(buf, " ,}"); + if (e == NULL) + e = buf + strlen(buf); + if (buf < e) + srvc_id->id.inst_id = atoi(buf); + } + + if (*e == ',') { + buf = e + 1; + e = strpbrk(buf, " ,}"); + if (e == NULL) + e = buf + strlen(buf); + if (buf < e) + srvc_id->is_primary = atoi(buf); + } +} + +/* Converts array of uint8_t to string representation */ +static char *array2str(const uint8_t *v, int size, char *buf, int out_size) +{ + int limit = size; + int i; + + if (out_size > 0) { + *buf = '\0'; + if (size >= 2 * out_size) + limit = (out_size - 2) / 2; + + for (i = 0; i < limit; ++i) + sprintf(buf + 2 * i, "%02x", v[i]); + + /* output buffer not enough to hold whole field fill with ...*/ + if (limit < size) + sprintf(buf + 2 * i, "..."); + } + + return buf; +} + +/* Converts btgatt_notify_params_t to string */ +static char *btgatt_notify_params_t2str(const btgatt_notify_params_t *data, + char *buf) +{ + char addr[MAX_ADDR_STR_LEN]; + char srvc_id[MAX_SRVC_ID_STR_LEN]; + char char_id[MAX_CHAR_ID_STR_LEN]; + char value[MAX_HEX_VAL_STR_LEN]; + + sprintf(buf, "{bda=%s, srvc_id=%s, char_id=%s, val=%s, is_notify=%u}", + bt_bdaddr_t2str(&data->bda, addr), + btgatt_srvc_id_t2str(&data->srvc_id, srvc_id), + btgatt_gatt_id_t2str(&data->char_id, char_id), + array2str(data->value, data->len, value, sizeof(value)), + data->is_notify); + return buf; +} + +static char *btgatt_unformatted_value_t2str(const btgatt_unformatted_value_t *v, + char *buf, int size) +{ + return array2str(v->value, v->len, buf, size); +} + +static char *btgatt_read_params_t2str(const btgatt_read_params_t *data, + char *buf) +{ + char srvc_id[MAX_SRVC_ID_STR_LEN]; + char char_id[MAX_CHAR_ID_STR_LEN]; + char descr_id[MAX_UUID_STR_LEN]; + char value[MAX_HEX_VAL_STR_LEN]; + + sprintf(buf, "{srvc_id=%s, char_id=%s, descr_id=%s, val=%s value_type=%d, status=%d}", + btgatt_srvc_id_t2str(&data->srvc_id, srvc_id), + btgatt_gatt_id_t2str(&data->char_id, char_id), + btgatt_descr_id_t2str(&data->descr_id, descr_id), + btgatt_unformatted_value_t2str(&data->value, value, 100), + data->value_type, data->status); + return buf; +} + +/* BT-GATT Client callbacks. */ + +/* Cache client_if and conn_id for tab completion */ +static char client_if_str[20]; +static char conn_id_str[20]; +/* Cache address for tab completion */ +static char last_addr[MAX_ADDR_STR_LEN]; + +/* Callback invoked in response to register_client */ +static void gattc_register_client_cb(int status, int client_if, + bt_uuid_t *app_uuid) +{ + char buf[MAX_UUID_STR_LEN]; + + snprintf(client_if_str, sizeof(client_if_str), "%d", client_if); + + haltest_info("%s: status=%d client_if=%d app_uuid=%s\n", __func__, + status, client_if, + gatt_uuid_t2str(app_uuid, buf)); +} + +/* Callback for scan results */ +static void gattc_scan_result_cb(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data) +{ + char buf[MAX_ADDR_STR_LEN]; + + haltest_info("%s: bda=%s rssi=%d adv_data=%p\n", __func__, + bt_bdaddr_t2str(bda, buf), rssi, adv_data); +} + +/* GATT open callback invoked in response to open */ +static void gattc_connect_cb(int conn_id, int status, int client_if, + bt_bdaddr_t *bda) +{ + haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n", + __func__, conn_id, status, client_if, + bt_bdaddr_t2str(bda, last_addr)); +} + +/* Callback invoked in response to close */ +static void gattc_disconnect_cb(int conn_id, int status, int client_if, + bt_bdaddr_t *bda) +{ + char buf[MAX_ADDR_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n", + __func__, conn_id, status, client_if, + bt_bdaddr_t2str(bda, buf)); +} + +/* + * Invoked in response to search_service when the GATT service search + * has been completed. + */ +static void gattc_search_complete_cb(int conn_id, int status) +{ + haltest_info("%s: conn_id=%d status=%s\n", __func__, conn_id, status); +} + +/* Reports GATT services on a remote device */ +static void gattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id) +{ + char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + + haltest_info("%s: conn_id=%d srvc_id=%s\n", __func__, conn_id, + btgatt_srvc_id_t2str(srvc_id, srvc_id_buf)); +} + +/* GATT characteristic enumeration result callback */ +static void gattc_get_characteristic_cb(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + int char_prop) +{ + char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + char char_id_buf[MAX_CHAR_ID_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, char_prop=%x\n", + __func__, conn_id, status, + btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), + btgatt_gatt_id_t2str(char_id, char_id_buf), char_prop); + + /* enumerate next characteristic */ + if (status == 0) + EXEC(if_gatt->client->get_characteristic, conn_id, srvc_id, + char_id); +} + +/* GATT descriptor enumeration result callback */ +static void gattc_get_descriptor_cb(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_descr_id_t *descr_id) +{ + char buf[MAX_UUID_STR_LEN]; + char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + char char_id_buf[MAX_CHAR_ID_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, descr_id=%s\n", + __func__, conn_id, status, + btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), + btgatt_gatt_id_t2str(char_id, char_id_buf), + btgatt_descr_id_t2str(descr_id, buf)); + + if (status == 0) + EXEC(if_gatt->client->get_descriptor, conn_id, srvc_id, char_id, + descr_id); +} + +/* GATT included service enumeration result callback */ +static void gattc_get_included_service_cb(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, + btgatt_srvc_id_t *incl_srvc_id) +{ + char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + char incl_srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d srvc_id=%s incl_srvc_id=%s)\n", + __func__, conn_id, status, + btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), + btgatt_srvc_id_t2str(incl_srvc_id, incl_srvc_id_buf)); + + if (status == 0) + EXEC(if_gatt->client->get_included_service, conn_id, srvc_id, + incl_srvc_id); +} + +/* Callback invoked in response to [de]register_for_notification */ +static void gattc_register_for_notification_cb(int conn_id, int registered, + int status, + btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id) +{ + char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + char char_id_buf[MAX_CHAR_ID_STR_LEN]; + + haltest_info("%s: conn_id=%d registered=%d status=%d srvc_id=%s char_id=%s\n", + __func__, conn_id, registered, status, + btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), + btgatt_gatt_id_t2str(char_id, char_id_buf)); +} + +/* + * Remote device notification callback, invoked when a remote device sends + * a notification or indication that a client has registered for. + */ +static void gattc_notify_cb(int conn_id, btgatt_notify_params_t *p_data) +{ + char buf[MAX_NOTIFY_PARAMS_STR_LEN]; + + haltest_info("%s: conn_id=%d data=%s\n", __func__, conn_id, + btgatt_notify_params_t2str(p_data, buf)); +} + +/* Reports result of a GATT read operation */ +static void gattc_read_characteristic_cb(int conn_id, int status, + btgatt_read_params_t *p_data) +{ + char buf[MAX_READ_PARAMS_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id, + status, btgatt_read_params_t2str(p_data, buf)); +} + +/* GATT write characteristic operation callback */ +static void gattc_write_characteristic_cb(int conn_id, int status, + btgatt_write_params_t *p_data) +{ + haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); +} + +/* GATT execute prepared write callback */ +static void gattc_execute_write_cb(int conn_id, int status) +{ + haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); +} + +/* Callback invoked in response to read_descriptor */ +static void gattc_read_descriptor_cb(int conn_id, int status, + btgatt_read_params_t *p_data) +{ + char buf[MAX_READ_PARAMS_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id, + status, btgatt_read_params_t2str(p_data, buf)); +} + +/* Callback invoked in response to write_descriptor */ +static void gattc_write_descriptor_cb(int conn_id, int status, + btgatt_write_params_t *p_data) +{ + haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); +} + +/* Callback triggered in response to read_remote_rssi */ +static void gattc_read_remote_rssi_cb(int client_if, bt_bdaddr_t *bda, int rssi, + int status) +{ + char buf[MAX_ADDR_STR_LEN]; + + haltest_info("%s: client_if=%d bda=%s rssi=%d satus=%d\n", __func__, + client_if, bt_bdaddr_t2str(bda, buf), rssi, status); +} + +static const btgatt_client_callbacks_t btgatt_client_callbacks = { + .register_client_cb = gattc_register_client_cb, + .scan_result_cb = gattc_scan_result_cb, + .open_cb = gattc_connect_cb, + .close_cb = gattc_disconnect_cb, + .search_complete_cb = gattc_search_complete_cb, + .search_result_cb = gattc_search_result_cb, + .get_characteristic_cb = gattc_get_characteristic_cb, + .get_descriptor_cb = gattc_get_descriptor_cb, + .get_included_service_cb = gattc_get_included_service_cb, + .register_for_notification_cb = gattc_register_for_notification_cb, + .notify_cb = gattc_notify_cb, + .read_characteristic_cb = gattc_read_characteristic_cb, + .write_characteristic_cb = gattc_write_characteristic_cb, + .read_descriptor_cb = gattc_read_descriptor_cb, + .write_descriptor_cb = gattc_write_descriptor_cb, + .execute_write_cb = gattc_execute_write_cb, + .read_remote_rssi_cb = gattc_read_remote_rssi_cb +}; + +/* BT-GATT Server callbacks */ + +/* Cache server_if and conn_id for tab completion */ +static char server_if_str[20]; + +/* Callback invoked in response to register_server */ +static void gatts_register_server_cb(int status, int server_if, + bt_uuid_t *app_uuid) +{ + char buf[MAX_UUID_STR_LEN]; + + haltest_info("%s: status=%d server_if=%d app_uuid=%s\n", __func__, + status, server_if, gatt_uuid_t2str(app_uuid, buf)); +} + +/* + * Callback indicating that a remote device has connected + * or been disconnected + */ +static void gatts_connection_cb(int conn_id, int server_if, int connected, + bt_bdaddr_t *bda) +{ + haltest_info("%s: conn_id=%d server_if=%d connected=%d bda=%s\n", + __func__, conn_id, server_if, connected, + bt_bdaddr_t2str(bda, last_addr)); + snprintf(conn_id_str, sizeof(conn_id_str), "%d", conn_id); +} + +/* Callback invoked in response to create_service */ +static void gatts_service_added_cb(int status, int server_if, + btgatt_srvc_id_t *srvc_id, int srvc_handle) +{ + char buf[MAX_SRVC_ID_STR_LEN]; + + snprintf(server_if_str, sizeof(server_if_str), "%d", server_if); + + haltest_info("%s: status=%d server_if=%d srvc_id=%s handle=%x\n", + __func__, status, server_if, + btgatt_srvc_id_t2str(srvc_id, buf), srvc_handle); +} + +/* Callback indicating that an included service has been added to a service */ +static void gatts_included_service_added_cb(int status, int server_if, + int srvc_handle, + int incl_srvc_handle) +{ + haltest_info("%s: status=%d server_if=%d srvc_handle=%x inc_srvc_handle=%x\n", + __func__, status, server_if, + srvc_handle, incl_srvc_handle); +} + +/* Callback invoked when a characteristic has been added to a service */ +static void gatts_characteristic_added_cb(int status, int server_if, + bt_uuid_t *uuid, + int srvc_handle, + int char_handle) +{ + char buf[MAX_SRVC_ID_STR_LEN]; + + haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=%x char_handle=%x\n", + __func__, status, server_if, gatt_uuid_t2str(uuid, buf), + srvc_handle, char_handle); +} + +/* Callback invoked when a descriptor has been added to a characteristic */ +static void gatts_descriptor_added_cb(int status, int server_if, + bt_uuid_t *uuid, int srvc_handle, + int descr_handle) +{ + char buf[MAX_SRVC_ID_STR_LEN]; + + haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=%x descr_handle=%x\n", + __func__, status, server_if, gatt_uuid_t2str(uuid, buf), + srvc_handle, descr_handle); +} + +/* Callback invoked in response to start_service */ +static void gatts_service_started_cb(int status, int server_if, int srvc_handle) +{ + haltest_info("%s: status=%d server_if=%d srvc_handle=%x\n", + __func__, status, server_if, srvc_handle); +} + +/* Callback invoked in response to stop_service */ +static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle) +{ + haltest_info("%s: status=%d server_if=%d srvc_handle=%x\n", + __func__, status, server_if, srvc_handle); +} + +/* Callback triggered when a service has been deleted */ +static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle) +{ + haltest_info("%s: status=%d server_if=%d srvc_handle=%x\n", + __func__, status, server_if, srvc_handle); +} + +/* + * Callback invoked when a remote device has requested to read a characteristic + * or descriptor. The application must respond by calling send_response + */ +static void gatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, + int attr_handle, int offset, + bool is_long) +{ + char buf[MAX_ADDR_STR_LEN]; + + haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=%x offset=%d is_long=%d\n", + __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf), + attr_handle, offset, is_long); +} + +/* + * Callback invoked when a remote device has requested to write to a + * characteristic or descriptor. + */ +static void gatts_request_write_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, + int attr_handle, int offset, int length, + bool need_rsp, bool is_prep, + uint8_t *value) +{ + char buf[MAX_ADDR_STR_LEN]; + char valbuf[100]; + + haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=%x offset=%d length=%d need_rsp=%d is_prep=%d value=%s\n", + __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf), + attr_handle, offset, length, need_rsp, is_prep, + array2str(value, length, valbuf, sizeof(valbuf))); +} + +/* Callback invoked when a previously prepared write is to be executed */ +static void gatts_request_exec_write_cb(int conn_id, int trans_id, + bt_bdaddr_t *bda, int exec_write) +{ + char buf[MAX_ADDR_STR_LEN]; + + haltest_info("%s: conn_id=%d trans_id=%d bda=%s exec_write=%d\n", + __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf), + exec_write); +} + +/* + * Callback triggered in response to send_response if the remote device + * sends a confirmation. + */ +static void gatts_response_confirmation_cb(int status, int handle) +{ + haltest_info("%s: status=%d handle=%x\n", __func__, status, handle); +} + +static const btgatt_server_callbacks_t btgatt_server_callbacks = { + .register_server_cb = gatts_register_server_cb, + .connection_cb = gatts_connection_cb, + .service_added_cb = gatts_service_added_cb, + .included_service_added_cb = gatts_included_service_added_cb, + .characteristic_added_cb = gatts_characteristic_added_cb, + .descriptor_added_cb = gatts_descriptor_added_cb, + .service_started_cb = gatts_service_started_cb, + .service_stopped_cb = gatts_service_stopped_cb, + .service_deleted_cb = gatts_service_deleted_cb, + .request_read_cb = gatts_request_read_cb, + .request_write_cb = gatts_request_write_cb, + .request_exec_write_cb = gatts_request_exec_write_cb, + .response_confirmation_cb = gatts_response_confirmation_cb +}; + +static const btgatt_callbacks_t gatt_cbacks = { + .size = sizeof(gatt_cbacks), + .client = &btgatt_client_callbacks, + .server = &btgatt_server_callbacks +}; + +/* gatt client methods */ + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_gatt); + + EXEC(if_gatt->init, &gatt_cbacks); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_gatt); + + EXECV(if_gatt->cleanup); + + if_gatt = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface gatt_if = { + .name = "gatt", + .methods = methods +}; + +/* register_client */ + +static void register_client_p(int argc, const char **argv) +{ + bt_uuid_t uuid; + + RETURN_IF_NULL(if_gatt); + + /* uuid */ + if (argc <= 2) + gatt_str2bt_uuid_t("babe4bed", -1, &uuid); + else + gatt_str2bt_uuid_t(argv[2], -1, &uuid); + + EXEC(if_gatt->client->register_client, &uuid); +} + +/* unregister_client */ + +static void unregister_client_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } +} + +static void unregister_client_p(int argc, const char **argv) +{ + int client_if; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + + EXEC(if_gatt->client->unregister_client, client_if); +} + +/* scan */ + +/* Same completion as unregister for now, start stop is not auto completed */ +#define scan_c unregister_client_c + +static void scan_p(int argc, const char **argv) +{ + int client_if; + int start = 1; + + RETURN_IF_NULL(if_gatt); + + VERIFY_CLIENT_IF(2, client_if); + + /* start */ + if (argc >= 3) + start = atoi(argv[3]); + + EXEC(if_gatt->client->scan, client_if, start); +} + +/* connect */ + +static void connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = NULL; + *enum_func = enum_devices; + } +} + +static void connect_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + int is_direct = 1; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + + /* is_direct */ + if (argc > 4) + is_direct = atoi(argv[4]); + + EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct); +} + +/* disconnect */ + +static void disconnect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = last_addr; + *enum_func = enum_one_string; + } else if (argc == 5) { + *user = conn_id_str; + *enum_func = enum_one_string; + } +} + +static void disconnect_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + int conn_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + VERIFY_CONN_ID(4, conn_id); + + EXEC(if_gatt->client->disconnect, client_if, &bd_addr, conn_id); +} + +/* refresh */ + +static void refresh_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *enum_func = enum_devices; + } +} + +static void refresh_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + + EXEC(if_gatt->client->refresh, client_if, &bd_addr); +} + +/* search_service */ + +static void search_service_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = conn_id_str; + *enum_func = enum_one_string; + } +} + +static void search_service_p(int argc, const char **argv) +{ + int conn_id; + bt_uuid_t filter_uuid; + + RETURN_IF_NULL(if_gatt); + + VERIFY_CONN_ID(2, conn_id); + + /* uuid */ + if (argc <= 3) + memset(&filter_uuid, 0, sizeof(bt_uuid_t)); + else + gatt_str2bt_uuid_t(argv[3], -1, &filter_uuid); + + EXEC(if_gatt->client->search_service, conn_id, &filter_uuid); +} + +/* get_included_service */ + +static void get_included_service_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = conn_id_str; + *enum_func = enum_one_string; + } +} + +static void get_included_service_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + + EXEC(if_gatt->client->get_included_service, conn_id, &srvc_id, NULL); +} + +/* get_characteristic */ + +/* Same completion as get_included_service_c */ +#define get_characteristic_c get_included_service_c + +static void get_characteristic_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + + EXEC(if_gatt->client->get_characteristic, conn_id, &srvc_id, NULL); +} + +/* get_descriptor */ + +/* Same completion as get_included_service_c */ +#define get_descriptor_c get_included_service_c + +static void get_descriptor_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + VERIFY_CHAR_ID(4, &char_id); + + EXEC(if_gatt->client->get_descriptor, conn_id, &srvc_id, &char_id, + NULL); +} + +/* read_characteristic */ + +/* Same completion as get_included_service_c */ +#define read_characteristic_c get_included_service_c + +static void read_characteristic_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + int auth_req = 0; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + VERIFY_CHAR_ID(4, &char_id); + + /* auth_req */ + if (argc > 5) + auth_req = atoi(argv[5]); + + EXEC(if_gatt->client->read_characteristic, conn_id, &srvc_id, &char_id, + auth_req); +} + +/* write_characteristic */ + +static void write_characteristic_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + /* + * This should be from tGATT_WRITE_TYPE but it's burried + * inside bluedroid guts + */ + static const char *wrtypes[] = { "1", "2", "3", NULL }; + + if (argc == 3) { + *user = conn_id_str; + *enum_func = enum_one_string; + } else if (argc == 6) { + *user = wrtypes; + *enum_func = enum_strings; + } +} + +static void write_characteristic_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + int write_type; + int len; + int auth_req = 0; + uint8_t value[100]; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + VERIFY_CHAR_ID(4, &char_id); + + /* write type */ + if (argc <= 5) { + haltest_error("No write type specified\n"); + return; + } + write_type = atoi(argv[5]); + + /* value */ + if (argc <= 6) { + haltest_error("No value specified\n"); + return; + } + + /* len in chars */ + len = strlen(argv[6]); + scan_field(argv[6], len, value, sizeof(value)); + /* len in bytes converted from ascii chars */ + len = (len + 1) / 2; + + /* auth_req */ + if (argc > 7) + auth_req = atoi(argv[7]); + + EXEC(if_gatt->client->write_characteristic, conn_id, &srvc_id, &char_id, + write_type, len, auth_req, (char *) value); +} + +/* read_descriptor */ + +/* Same completion as get_included_service_c */ +#define read_descriptor_c get_included_service_c + +static void read_descriptor_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + btgatt_descr_id_t descr_id; + int auth_req = 0; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + VERIFY_CHAR_ID(4, &char_id); + VERIFY_DESCR_ID(5, &descr_id); + + /* auth_req */ + if (argc > 6) + auth_req = atoi(argv[6]); + + EXEC(if_gatt->client->read_descriptor, conn_id, &srvc_id, &char_id, + &descr_id, auth_req); +} + +/* write_descriptor */ + +static void write_descriptor_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + /* + * This should be from tGATT_WRITE_TYPE but it's burried + * inside bluedroid guts + */ + static const char *wrtypes[] = { "1", "2", "3", NULL }; + + if (argc == 3) { + *user = conn_id_str; + *enum_func = enum_one_string; + } else if (argc == 7) { + *user = wrtypes; + *enum_func = enum_strings; + } +} + +static void write_descriptor_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + btgatt_descr_id_t descr_id; + int write_type; + int len; + int auth_req = 0; + uint8_t value[200] = {0}; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + VERIFY_CHAR_ID(4, &char_id); + VERIFY_DESCR_ID(5, &descr_id); + + /* write type */ + if (argc <= 6) { + haltest_error("No write type specified\n"); + return; + } + write_type = atoi(argv[6]); + + /* value */ + if (argc <= 7) { + haltest_error("No value specified\n"); + return; + } + + /* len in chars */ + len = strlen(argv[7]); + scan_field(argv[7], len, value, sizeof(value)); + /* len in bytes converted from ascii chars */ + len = (len + 1) / 2; + + /* auth_req */ + if (argc > 8) + auth_req = atoi(argv[8]); + + EXEC(if_gatt->client->write_descriptor, conn_id, &srvc_id, &char_id, + &descr_id, write_type, len, auth_req, (char *) value); +} + +/* execute_write */ + +/* Same completion as search_service */ +#define execute_write_c search_service_c + +static void execute_write_p(int argc, const char **argv) +{ + int conn_id; + int execute; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + + /* execute */ + if (argc <= 3) { + haltest_error("No execute specified\n"); + return; + } + execute = atoi(argv[3]); + + EXEC(if_gatt->client->execute_write, conn_id, execute); +} + +/* register_for_notification */ + +static void register_for_notification_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = last_addr; + *enum_func = enum_one_string; + } +} + +static void register_for_notification_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + VERIFY_SRVC_ID(4, &srvc_id); + VERIFY_CHAR_ID(5, &char_id); + + EXEC(if_gatt->client->register_for_notification, client_if, &bd_addr, + &srvc_id, &char_id); +} + +/* deregister_for_notification */ + +/* Same completion as search_service */ +#define deregister_for_notification_c register_for_notification_c + +static void deregister_for_notification_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + VERIFY_SRVC_ID(4, &srvc_id); + VERIFY_CHAR_ID(5, &char_id); + + EXEC(if_gatt->client->deregister_for_notification, client_if, &bd_addr, + &srvc_id, &char_id); +} + +/* read_remote_rssi */ + +static void read_remote_rssi_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *enum_func = enum_devices; + } +} + +static void read_remote_rssi_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + + EXEC(if_gatt->client->read_remote_rssi, client_if, &bd_addr); +} + +/* get_device_type */ + +static void get_device_type_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) + *enum_func = enum_devices; +} + +static void get_device_type_p(int argc, const char **argv) +{ + bt_bdaddr_t bd_addr; + int dev_type; + + RETURN_IF_NULL(if_gatt); + VERIFY_ADDR_ARG(2, &bd_addr); + + dev_type = if_gatt->client->get_device_type(&bd_addr); + haltest_info("%s: %d\n", "get_device_type", dev_type); +} + +/* test_command */ + +static void test_command_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 4) + *enum_func = enum_devices; +} + +static void test_command_p(int argc, const char **argv) +{ + int command; + int i; + bt_bdaddr_t bd_addr; + bt_uuid_t uuid; + btgatt_test_params_t params = { + .bda1 = &bd_addr, + .uuid1 = &uuid + }; + uint16_t *u = ¶ms.u1; + + RETURN_IF_NULL(if_gatt); + + /* command */ + if (argc <= 2) { + haltest_error("No command specified\n"); + return; + } + command = atoi(argv[2]); + + VERIFY_ADDR_ARG(3, &bd_addr); + VERIFY_UUID(4, &uuid); + + for (i = 5; i < argc; i++) + *u++ = atoi(argv[i]); + + EXEC(if_gatt->client->test_command, command, ¶ms); +} + +static struct method client_methods[] = { + STD_METHODH(register_client, "[]"), + STD_METHODCH(unregister_client, ""), + STD_METHODCH(scan, " [1|0]"), + STD_METHODCH(connect, " []"), + STD_METHODCH(disconnect, " "), + STD_METHODCH(refresh, " "), + STD_METHODCH(search_service, " []"), + STD_METHODCH(get_included_service, " "), + STD_METHODCH(get_characteristic, " "), + STD_METHODCH(get_descriptor, " "), + STD_METHODCH(read_characteristic, + " []"), + STD_METHODCH(write_characteristic, + " []"), + STD_METHODCH(read_descriptor, + " []"), + STD_METHODCH(write_descriptor, + " []"), + STD_METHODCH(execute_write, " "), + STD_METHODCH(register_for_notification, + " "), + STD_METHODCH(deregister_for_notification, + " "), + STD_METHODCH(read_remote_rssi, " "), + STD_METHODCH(get_device_type, ""), + STD_METHODCH(test_command, + " [u1] [u2] [u3] [u4] [u5]"), + END_METHOD +}; + +const struct interface gatt_client_if = { + .name = "gattc", + .methods = client_methods +}; + +/* gatt server methods */ + +/* register_server */ + +static void gatts_register_server_p(int argc, const char *argv[]) +{ + bt_uuid_t uuid; + + RETURN_IF_NULL(if_gatt); + + /* uuid */ + if (argc <= 2) + gatt_str2bt_uuid_t("bed4babe", -1, &uuid); + else + gatt_str2bt_uuid_t(argv[2], -1, &uuid); + + EXEC(if_gatt->server->register_server, &uuid); +} + +/* unregister_server */ + +static void gatts_unregister_server_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = server_if_str; + *enum_func = enum_one_string; + } +} + +static void gatts_unregister_server_p(int argc, const char *argv[]) +{ + int server_if; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + + EXEC(if_gatt->server->unregister_server, server_if); +} + +/* connect */ + +static void gatts_connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = server_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = NULL; + *enum_func = enum_devices; + } +} + +static void gatts_connect_p(int argc, const char *argv[]) +{ + int server_if; + bt_bdaddr_t bd_addr; + int is_direct = 1; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_ADDR_ARG(3, &bd_addr); + + /* is_direct */ + if (argc > 4) + is_direct = atoi(argv[4]); + + EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct); +} + +/* disconnect */ + +static void gatts_disconnect_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = server_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = last_addr; + *enum_func = enum_one_string; + } else if (argc == 5) { + *user = conn_id_str; + *enum_func = enum_one_string; + } +} + +static void gatts_disconnect_p(int argc, const char *argv[]) +{ + int server_if; + bt_bdaddr_t bd_addr; + int conn_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_ADDR_ARG(3, &bd_addr); + VERIFY_CONN_ID(4, conn_id); + + EXEC(if_gatt->server->disconnect, server_if, &bd_addr, conn_id); +} + +/* add_service */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_add_service_c gatts_unregister_server_c + +static void gatts_add_service_p(int argc, const char *argv[]) +{ + int server_if; + btgatt_srvc_id_t srvc_id; + int num_handles; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SRVC_ID(3, &srvc_id); + + /* num handles */ + if (argc <= 4) { + haltest_error("No num_handles specified\n"); + return; + } + num_handles = atoi(argv[4]); + + EXEC(if_gatt->server->add_service, server_if, &srvc_id, num_handles); +} + +/* add_included_service */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_add_included_service_c gatts_unregister_server_c + +static void gatts_add_included_service_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + int included_handle; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + VERIFY_HANDLE(4, included_handle); + + EXEC(if_gatt->server->add_included_service, server_if, service_handle, + included_handle); +} + +/* add_characteristic */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_add_characteristic_c gatts_unregister_server_c + +static void gatts_add_characteristic_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + int properties; + int permissions; + bt_uuid_t uuid; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + VERIFY_UUID(4, &uuid); + + /* properties */ + if (argc <= 5) { + haltest_error("No properties specified\n"); + return; + } + properties = atoi(argv[5]); + + /* permissions */ + if (argc <= 6) { + haltest_error("No permissions specified\n"); + return; + } + permissions = atoi(argv[6]); + + EXEC(if_gatt->server->add_characteristic, server_if, service_handle, + &uuid, properties, permissions); +} + +/* add_descriptor */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_add_descriptor_c gatts_unregister_server_c + +static void gatts_add_descriptor_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + int permissions; + bt_uuid_t uuid; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + VERIFY_UUID(4, &uuid); + + /* permissions */ + if (argc <= 5) { + haltest_error("No permissions specified\n"); + return; + } + permissions = atoi(argv[5]); + + EXEC(if_gatt->server->add_descriptor, server_if, service_handle, &uuid, + permissions); +} + +/* start_service */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_start_service_c gatts_unregister_server_c + +static void gatts_start_service_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + int transport; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + + /* transport */ + if (argc <= 4) { + haltest_error("No transport specified\n"); + return; + } + transport = atoi(argv[4]); + + EXEC(if_gatt->server->start_service, server_if, service_handle, + transport); +} + +/* stop_service */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_stop_service_c gatts_unregister_server_c + +static void gatts_stop_service_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + + EXEC(if_gatt->server->stop_service, server_if, service_handle); +} + +/* delete_service */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_delete_service_c gatts_unregister_server_c + +static void gatts_delete_service_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + + EXEC(if_gatt->server->delete_service, server_if, service_handle); +} + +/* send_indication */ + +static void gatts_send_indication_p(int argc, const char *argv[]) +{ + int server_if; + int attr_handle; + int conn_id; + int confirm; + char data[200]; + int len = 0; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_HANDLE(3, attr_handle); + VERIFY_CONN_ID(4, conn_id); + + /* confirm */ + if (argc <= 5) { + haltest_error("No transport specified\n"); + return; + } + confirm = atoi(argv[5]); + + if (argc > 6) { + len = strlen(argv[6]); + scan_field(argv[6], len, (uint8_t *) data, sizeof(data)); + } + + EXEC(if_gatt->server->send_indication, server_if, attr_handle, conn_id, + len, confirm, data); +} + +/* send_response */ + +static void gatts_send_response_p(int argc, const char *argv[]) +{ + haltest_warn("%s is not implemented yet\n", __func__); +} + +#define GATTS_METHODH(n, h) METHOD(#n, gatts_##n##_p, NULL, h) +#define GATTS_METHODCH(n, h) METHOD(#n, gatts_##n##_p, gatts_##n##_c, h) + +static struct method server_methods[] = { + GATTS_METHODH(register_server, "[]"), + GATTS_METHODCH(unregister_server, ""), + GATTS_METHODCH(connect, " []"), + GATTS_METHODCH(disconnect, " "), + GATTS_METHODCH(add_service, " "), + GATTS_METHODCH(add_included_service, + " "), + GATTS_METHODCH(add_characteristic, + " "), + GATTS_METHODCH(add_descriptor, " "), + GATTS_METHODCH(start_service, + " "), + GATTS_METHODCH(stop_service, " "), + GATTS_METHODCH(delete_service, " "), + GATTS_METHODH(send_indication, + " []"), + GATTS_METHODH(send_response, " "), + END_METHOD +}; + +const struct interface gatt_server_if = { + .name = "gatts", + .methods = server_methods +}; diff --git a/android/client/if-hf.c b/android/client/if-hf.c new file mode 100644 index 0000000..d0e7a66 --- /dev/null +++ b/android/client/if-hf.c @@ -0,0 +1,785 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "if-main.h" +#include "../hal-utils.h" + +const bthf_interface_t *if_hf = NULL; + +SINTMAP(bthf_at_response_t, -1, "(unknown)") + DELEMENT(BTHF_AT_RESPONSE_ERROR), + DELEMENT(BTHF_AT_RESPONSE_OK), +ENDMAP + +SINTMAP(bthf_connection_state_t, -1, "(unknown)") + DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTED), + DELEMENT(BTHF_CONNECTION_STATE_CONNECTING), + DELEMENT(BTHF_CONNECTION_STATE_CONNECTED), + DELEMENT(BTHF_CONNECTION_STATE_SLC_CONNECTED), + DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTING), +ENDMAP + +SINTMAP(bthf_audio_state_t, -1, "(unknown)") + DELEMENT(BTHF_AUDIO_STATE_DISCONNECTED), + DELEMENT(BTHF_AUDIO_STATE_CONNECTING), + DELEMENT(BTHF_AUDIO_STATE_CONNECTED), + DELEMENT(BTHF_AUDIO_STATE_DISCONNECTING), +ENDMAP + +SINTMAP(bthf_vr_state_t, -1, "(unknown)") + DELEMENT(BTHF_VR_STATE_STOPPED), + DELEMENT(BTHF_VR_STATE_STARTED), +ENDMAP + +SINTMAP(bthf_volume_type_t, -1, "(unknown)") + DELEMENT(BTHF_VOLUME_TYPE_SPK), + DELEMENT(BTHF_VOLUME_TYPE_MIC), +ENDMAP + +SINTMAP(bthf_nrec_t, -1, "(unknown)") + DELEMENT(BTHF_NREC_STOP), + DELEMENT(BTHF_NREC_START), +ENDMAP + +SINTMAP(bthf_chld_type_t, -1, "(unknown)") + DELEMENT(BTHF_CHLD_TYPE_RELEASEHELD), + DELEMENT(BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD), + DELEMENT(BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD), + DELEMENT(BTHF_CHLD_TYPE_ADDHELDTOCONF), +ENDMAP + +/* Network Status */ +SINTMAP(bthf_network_state_t, -1, "(unknown)") + DELEMENT(BTHF_NETWORK_STATE_NOT_AVAILABLE), + DELEMENT(BTHF_NETWORK_STATE_AVAILABLE), +ENDMAP + +/* Service type */ +SINTMAP(bthf_service_type_t, -1, "(unknown)") + DELEMENT(BTHF_SERVICE_TYPE_HOME), + DELEMENT(BTHF_SERVICE_TYPE_ROAMING), +ENDMAP + +SINTMAP(bthf_call_state_t, -1, "(unknown)") + DELEMENT(BTHF_CALL_STATE_ACTIVE), + DELEMENT(BTHF_CALL_STATE_HELD), + DELEMENT(BTHF_CALL_STATE_DIALING), + DELEMENT(BTHF_CALL_STATE_ALERTING), + DELEMENT(BTHF_CALL_STATE_INCOMING), + DELEMENT(BTHF_CALL_STATE_WAITING), + DELEMENT(BTHF_CALL_STATE_IDLE), +ENDMAP + +SINTMAP(bthf_call_direction_t, -1, "(unknown)") + DELEMENT(BTHF_CALL_DIRECTION_OUTGOING), + DELEMENT(BTHF_CALL_DIRECTION_INCOMING), +ENDMAP + +SINTMAP(bthf_call_mode_t, -1, "(unknown)") + DELEMENT(BTHF_CALL_TYPE_VOICE), + DELEMENT(BTHF_CALL_TYPE_DATA), + DELEMENT(BTHF_CALL_TYPE_FAX), +ENDMAP + +SINTMAP(bthf_call_mpty_type_t, -1, "(unknown)") + DELEMENT(BTHF_CALL_MPTY_TYPE_SINGLE), + DELEMENT(BTHF_CALL_MPTY_TYPE_MULTI), +ENDMAP + +SINTMAP(bthf_call_addrtype_t, -1, "(unknown)") + DELEMENT(BTHF_CALL_ADDRTYPE_UNKNOWN), + DELEMENT(BTHF_CALL_ADDRTYPE_INTERNATIONAL), +ENDMAP + +/* Callbacks */ + +static char last_addr[MAX_ADDR_STR_LEN]; + +/* + * Callback for connection state change. + * state will have one of the values from BtHfConnectionState + */ +static void connection_state_cb(bthf_connection_state_t state, + bt_bdaddr_t *bd_addr) +{ + haltest_info("%s: state=%s bd_addr=%s\n", __func__, + bthf_connection_state_t2str(state), + bt_bdaddr_t2str(bd_addr, last_addr)); +} + +/* + * Callback for audio connection state change. + * state will have one of the values from BtHfAudioState + */ +static void audio_state_cb(bthf_audio_state_t state, bt_bdaddr_t *bd_addr) +{ + haltest_info("%s: state=%s bd_addr=%s\n", __func__, + bthf_audio_state_t2str(state), + bt_bdaddr_t2str(bd_addr, last_addr)); +} + +/* + * Callback for VR connection state change. + * state will have one of the values from BtHfVRState + */ +static void vr_cmd_cb(bthf_vr_state_t state) +{ + haltest_info("%s: state=%s\n", __func__, bthf_vr_state_t2str(state)); +} + +/* Callback for answer incoming call (ATA) */ +static void answer_call_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* Callback for disconnect call (AT+CHUP) */ +static void hangup_call_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* + * Callback for disconnect call (AT+CHUP) + * type will denote Speaker/Mic gain (BtHfVolumeControl). + */ +static void volume_cmd_cb(bthf_volume_type_t type, int volume) +{ + haltest_info("%s: type=%s volume=%d\n", __func__, + bthf_volume_type_t2str(type), volume); +} + +/* + * Callback for dialing an outgoing call + * If number is NULL, redial + */ +static void dial_call_cmd_cb(char *number) +{ + haltest_info("%s: number=%s\n", __func__, number); +} + +/* + * Callback for sending DTMF tones + * tone contains the dtmf character to be sent + */ +static void dtmf_cmd_cb(char tone) +{ + haltest_info("%s: tone=%d\n", __func__, tone); +} + +/* + * Callback for enabling/disabling noise reduction/echo cancellation + * value will be 1 to enable, 0 to disable + */ +static void nrec_cmd_cb(bthf_nrec_t nrec) +{ + haltest_info("%s: nrec=%s\n", __func__, bthf_nrec_t2str(nrec)); +} + +/* + * Callback for call hold handling (AT+CHLD) + * value will contain the call hold command (0, 1, 2, 3) + */ +static void chld_cmd_cb(bthf_chld_type_t chld) +{ + haltest_info("%s: chld=%s\n", __func__, bthf_chld_type_t2str(chld)); +} + +/* Callback for CNUM (subscriber number) */ +static void cnum_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* Callback for indicators (CIND) */ +static void cind_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* Callback for operator selection (COPS) */ +static void cops_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* Callback for call list (AT+CLCC) */ +static void clcc_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* + * Callback for unknown AT command recd from HF + * at_string will contain the unparsed AT string + */ +static void unknown_at_cmd_cb(char *at_string) +{ + haltest_info("%s: at_string=%s\n", __func__, at_string); +} + +/* Callback for keypressed (HSP) event. */ +static void key_pressed_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +static bthf_callbacks_t hf_cbacks = { + + .size = sizeof(hf_cbacks), + .connection_state_cb = connection_state_cb, + .audio_state_cb = audio_state_cb, + .vr_cmd_cb = vr_cmd_cb, + .answer_call_cmd_cb = answer_call_cmd_cb, + .hangup_call_cmd_cb = hangup_call_cmd_cb, + .volume_cmd_cb = volume_cmd_cb, + .dial_call_cmd_cb = dial_call_cmd_cb, + .dtmf_cmd_cb = dtmf_cmd_cb, + .nrec_cmd_cb = nrec_cmd_cb, + .chld_cmd_cb = chld_cmd_cb, + .cnum_cmd_cb = cnum_cmd_cb, + .cind_cmd_cb = cind_cmd_cb, + .cops_cmd_cb = cops_cmd_cb, + .clcc_cmd_cb = clcc_cmd_cb, + .unknown_at_cmd_cb = unknown_at_cmd_cb, + .key_pressed_cmd_cb = key_pressed_cmd_cb, +}; + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + EXEC(if_hf->init, &hf_cbacks); +} + +/* connect */ + +static void connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = NULL; + *enum_func = enum_devices; + } +} + +static void connect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hf); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hf->connect, &addr); +} + +/* disconnect */ + +/* + * This completion function will be used for several methods + * returning recently connected address + */ +static void connected_addr_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = last_addr; + *enum_func = enum_one_string; + } +} + +/* Map completion to connected_addr_c */ +#define disconnect_c connected_addr_c + +static void disconnect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hf); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hf->disconnect, &addr); +} + +/* create an audio connection */ + +/* Map completion to connected_addr_c */ +#define connect_audio_c connected_addr_c + +static void connect_audio_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hf); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hf->connect_audio, &addr); +} + +/* close the audio connection */ + +/* Map completion to connected_addr_c */ +#define disconnect_audio_c connected_addr_c + +static void disconnect_audio_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hf); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hf->disconnect_audio, &addr); +} + +/* start voice recognition */ + +static void start_voice_recognition_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + EXEC(if_hf->start_voice_recognition); +} + +/* stop voice recognition */ + +static void stop_voice_recognition_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + EXEC(if_hf->stop_voice_recognition); +} + +/* volume control */ + +static void volume_control_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(bthf_volume_type_t); + *enum_func = enum_defines; + } +} + +static void volume_control_p(int argc, const char **argv) +{ + bthf_volume_type_t type; + int volume; + + RETURN_IF_NULL(if_hf); + + /* volume type */ + if (argc <= 2) { + haltest_error("No volume type specified\n"); + return; + } + type = str2bthf_volume_type_t(argv[2]); + + /* volume */ + if (argc <= 3) { + haltest_error("No volume specified\n"); + return; + } + volume = atoi(argv[3]); + + EXEC(if_hf->volume_control, type, volume); +} + +/* Combined device status change notification */ + +static void device_status_notification_c(int argc, const char **argv, + enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(bthf_network_state_t); + *enum_func = enum_defines; + } else if (argc == 4) { + *user = TYPE_ENUM(bthf_service_type_t); + *enum_func = enum_defines; + } +} + +static void device_status_notification_p(int argc, const char **argv) +{ + bthf_network_state_t ntk_state; + bthf_service_type_t svc_type; + int signal; + int batt_chg; + + RETURN_IF_NULL(if_hf); + + /* network state */ + if (argc <= 2) { + haltest_error("No network state specified\n"); + return; + } + ntk_state = str2bthf_network_state_t(argv[2]); + + /* service type */ + if (argc <= 3) { + haltest_error("No service type specified\n"); + return; + } + svc_type = str2bthf_service_type_t(argv[3]); + + /* signal */ + if (argc <= 4) { + haltest_error("No signal specified\n"); + return; + } + signal = atoi(argv[4]); + + /* batt_chg */ + if (argc <= 5) { + haltest_error("No batt_chg specified\n"); + return; + } + batt_chg = atoi(argv[5]); + + EXEC(if_hf->device_status_notification, ntk_state, svc_type, signal, + batt_chg); +} + +/* Response for COPS command */ + +static void cops_response_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + /* response */ + if (argc <= 2) { + haltest_error("No cops specified\n"); + return; + } + + EXEC(if_hf->cops_response, argv[2]); +} + +/* Response for CIND command */ + +static void cind_response_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 6) { + *user = TYPE_ENUM(bthf_call_state_t); + *enum_func = enum_defines; + } +} + +static void cind_response_p(int argc, const char **argv) +{ + int svc; + int num_active; + int num_held; + bthf_call_state_t call_setup_state; + int signal; + int roam; + int batt_chg; + + RETURN_IF_NULL(if_hf); + + /* svc */ + if (argc <= 2) { + haltest_error("No service specified\n"); + return; + } + svc = atoi(argv[2]); + + /* num active */ + if (argc <= 3) { + haltest_error("No num active specified\n"); + return; + } + num_active = atoi(argv[3]); + + /* num held */ + if (argc <= 4) { + haltest_error("No num held specified\n"); + return; + } + num_held = atoi(argv[4]); + + /* call setup state */ + if (argc <= 5) { + haltest_error("No call setup state specified\n"); + return; + } + call_setup_state = str2bthf_call_state_t(argv[5]); + + /* signal */ + if (argc <= 6) { + haltest_error("No signal specified\n"); + return; + } + signal = atoi(argv[6]); + + /* roam */ + if (argc <= 7) { + haltest_error("No roam specified\n"); + return; + } + roam = atoi(argv[7]); + + /* batt_chg */ + if (argc <= 8) { + haltest_error("No batt_chg specified\n"); + return; + } + batt_chg = atoi(argv[8]); + + EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state, + signal, roam, batt_chg); +} + +/* Pre-formatted AT response, typically in response to unknown AT cmd */ + +static void formatted_at_response_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + /* response */ + if (argc <= 2) { + haltest_error("No response specified\n"); + return; + } + + EXEC(if_hf->formatted_at_response, argv[2]); +} + +/* at_response */ + +static void at_response_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(bthf_at_response_t); + *enum_func = enum_defines; + } +} + +static void at_response_p(int argc, const char **argv) +{ + bthf_at_response_t response_code; + int error_code = 0; + + RETURN_IF_NULL(if_hf); + + /* response type */ + if (argc <= 2) { + haltest_error("No response specified\n"); + return; + } + response_code = str2bthf_at_response_t(argv[2]); + + /* error code */ + if (argc >= 3) + error_code = atoi(argv[3]); + + EXEC(if_hf->at_response, response_code, error_code); +} + +/* response for CLCC command */ + +static void clcc_response_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 4) { + *user = TYPE_ENUM(bthf_call_direction_t); + *enum_func = enum_defines; + } else if (argc == 5) { + *user = TYPE_ENUM(bthf_call_state_t); + *enum_func = enum_defines; + } else if (argc == 6) { + *user = TYPE_ENUM(bthf_call_mode_t); + *enum_func = enum_defines; + } else if (argc == 7) { + *user = TYPE_ENUM(bthf_call_mpty_type_t); + *enum_func = enum_defines; + } else if (argc == 9) { + *user = TYPE_ENUM(bthf_call_addrtype_t); + *enum_func = enum_defines; + } +} + +static void clcc_response_p(int argc, const char **argv) +{ + int index; + bthf_call_direction_t dir; + bthf_call_state_t state; + bthf_call_mode_t mode; + bthf_call_mpty_type_t mpty; + const char *number; + bthf_call_addrtype_t type; + + RETURN_IF_NULL(if_hf); + + /* index */ + if (argc <= 2) { + haltest_error("No index specified\n"); + return; + } + index = atoi(argv[2]); + + /* direction */ + if (argc <= 3) { + haltest_error("No direction specified\n"); + return; + } + dir = str2bthf_call_direction_t(argv[3]); + + /* call state */ + if (argc <= 4) { + haltest_error("No call state specified\n"); + return; + } + state = str2bthf_call_state_t(argv[4]); + + /* call mode */ + if (argc <= 5) { + haltest_error("No mode specified\n"); + return; + } + mode = str2bthf_call_mode_t(argv[5]); + + /* call mpty type */ + if (argc <= 6) { + haltest_error("No mpty type specified\n"); + return; + } + mpty = str2bthf_call_mpty_type_t(argv[6]); + + /* number */ + if (argc <= 7) { + haltest_error("No number specified\n"); + return; + } + number = argv[7]; + + /* call mpty type */ + if (argc <= 8) { + haltest_error("No address type specified\n"); + return; + } + type = str2bthf_call_addrtype_t(argv[8]); + + EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number, + type); +} + +/* phone state change */ + +static void phone_state_change_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 5) { + *user = TYPE_ENUM(bthf_call_state_t); + *enum_func = enum_defines; + } else if (argc == 7) { + *user = TYPE_ENUM(bthf_call_addrtype_t); + *enum_func = enum_defines; + } +} + +static void phone_state_change_p(int argc, const char **argv) +{ + int num_active; + int num_held; + bthf_call_state_t call_setup_state; + const char *number; + bthf_call_addrtype_t type; + + RETURN_IF_NULL(if_hf); + + /* num_active */ + if (argc <= 2) { + haltest_error("No num_active specified\n"); + return; + } + num_active = atoi(argv[2]); + + /* num_held */ + if (argc <= 3) { + haltest_error("No num_held specified\n"); + return; + } + num_held = atoi(argv[3]); + + /* setup state */ + if (argc <= 4) { + haltest_error("No call setup state specified\n"); + return; + } + call_setup_state = str2bthf_call_state_t(argv[4]); + + /* number */ + if (argc <= 5) { + haltest_error("No number specified\n"); + return; + } + number = argv[5]; + + /* call mpty type */ + if (argc <= 6) { + haltest_error("No address type specified\n"); + return; + } + type = str2bthf_call_addrtype_t(argv[6]); + + EXEC(if_hf->phone_state_change, num_active, num_held, call_setup_state, + number, type); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + EXECV(if_hf->cleanup); + if_hf = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHODCH(connect, ""), + STD_METHODCH(disconnect, ""), + STD_METHODCH(connect_audio, ""), + STD_METHODCH(disconnect_audio, ""), + STD_METHOD(start_voice_recognition), + STD_METHOD(stop_voice_recognition), + STD_METHODCH(volume_control, " "), + STD_METHODCH(device_status_notification, + " "), + STD_METHODH(cops_response, ""), + STD_METHODCH(cind_response, + " "), + STD_METHODH(formatted_at_response, ""), + STD_METHODCH(at_response, " []"), + STD_METHODCH(clcc_response, + " "), + STD_METHODCH(phone_state_change, + " "), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface hf_if = { + .name = "handsfree", + .methods = methods +}; diff --git a/android/client/if-hh.c b/android/client/if-hh.c new file mode 100644 index 0000000..b8ebc8e --- /dev/null +++ b/android/client/if-hh.c @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include + +#include "if-main.h" +#include "pollhandler.h" +#include "../hal-utils.h" + +const bthh_interface_t *if_hh = NULL; + +SINTMAP(bthh_protocol_mode_t, -1, "(unknown)") + DELEMENT(BTHH_REPORT_MODE), + DELEMENT(BTHH_BOOT_MODE), + DELEMENT(BTHH_UNSUPPORTED_MODE), +ENDMAP + +SINTMAP(bthh_report_type_t, -1, "(unknown)") + DELEMENT(BTHH_INPUT_REPORT), + DELEMENT(BTHH_OUTPUT_REPORT), + DELEMENT(BTHH_FEATURE_REPORT), +ENDMAP + +SINTMAP(bthh_connection_state_t, -1, "(unknown)") + DELEMENT(BTHH_CONN_STATE_CONNECTED), + DELEMENT(BTHH_CONN_STATE_CONNECTING), + DELEMENT(BTHH_CONN_STATE_DISCONNECTED), + DELEMENT(BTHH_CONN_STATE_DISCONNECTING), + DELEMENT(BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST), + DELEMENT(BTHH_CONN_STATE_FAILED_KBD_FROM_HOST), + DELEMENT(BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES), + DELEMENT(BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER), + DELEMENT(BTHH_CONN_STATE_FAILED_GENERIC), + DELEMENT(BTHH_CONN_STATE_UNKNOWN), +ENDMAP + +SINTMAP(bthh_status_t, -1, "(unknown)") + DELEMENT(BTHH_OK), + DELEMENT(BTHH_HS_HID_NOT_READY), + DELEMENT(BTHH_HS_INVALID_RPT_ID), + DELEMENT(BTHH_HS_TRANS_NOT_SPT), + DELEMENT(BTHH_HS_INVALID_PARAM), + DELEMENT(BTHH_HS_ERROR), + DELEMENT(BTHH_ERR), + DELEMENT(BTHH_ERR_SDP), + DELEMENT(BTHH_ERR_PROTO), + DELEMENT(BTHH_ERR_DB_FULL), + DELEMENT(BTHH_ERR_TOD_UNSPT), + DELEMENT(BTHH_ERR_NO_RES), + DELEMENT(BTHH_ERR_AUTH_FAILED), + DELEMENT(BTHH_ERR_HDL), +ENDMAP + +static char connected_device_addr[MAX_ADDR_STR_LEN]; +/* + * Callback for connection state change. + * state will have one of the values from bthh_connection_state_t + */ +static void connection_state_cb(bt_bdaddr_t *bd_addr, + bthh_connection_state_t state) +{ + char addr[MAX_ADDR_STR_LEN]; + + haltest_info("%s: bd_addr=%s connection_state=%s\n", __func__, + bt_bdaddr_t2str(bd_addr, addr), + bthh_connection_state_t2str(state)); + if (state == BTHH_CONN_STATE_CONNECTED) + strcpy(connected_device_addr, addr); +} + +/* + * Callback for virtual unplug api. + * the status of the virtual unplug + */ +static void virtual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status) +{ + char addr[MAX_ADDR_STR_LEN]; + + haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__, + bt_bdaddr_t2str(bd_addr, addr), + bthh_status_t2str(hh_status)); +} + +/* + * Callback for get hid info + * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id, + * version, ctry_code, len + */ +static void hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info) +{ + char addr[MAX_ADDR_STR_LEN]; + + /* TODO: bluedroid does not seem to ever call this callback */ + haltest_info("%s: bd_addr=%s\n", __func__, + bt_bdaddr_t2str(bd_addr, addr)); +} + +/* + * Callback for get/set protocol api. + * the protocol mode is one of the value from bthh_protocol_mode_t + */ +static void protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, + bthh_protocol_mode_t mode) +{ + char addr[MAX_ADDR_STR_LEN]; + + haltest_info("%s: bd_addr=%s hh_status=%s mode=%s\n", __func__, + bt_bdaddr_t2str(bd_addr, addr), + bthh_status_t2str(hh_status), + bthh_protocol_mode_t2str(mode)); +} + +/* + * Callback for get/set_idle_time api. + */ +static void idle_time_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, + int idle_rate) +{ + char addr[MAX_ADDR_STR_LEN]; + + haltest_info("%s: bd_addr=%s hh_status=%s idle_rate=%d\n", __func__, + bt_bdaddr_t2str(bd_addr, addr), + bthh_status_t2str(hh_status), idle_rate); +} + + +/* + * Callback for get report api. + * if status is ok rpt_data contains the report data + */ +static void get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, + uint8_t *rpt_data, int rpt_size) +{ + char addr[MAX_ADDR_STR_LEN]; + + /* TODO: print actual report */ + haltest_info("%s: bd_addr=%s hh_status=%s rpt_size=%d\n", __func__, + bt_bdaddr_t2str(bd_addr, addr), + bthh_status_t2str(hh_status), rpt_size); +} + +static bthh_callbacks_t bthh_callbacks = { + .size = sizeof(bthh_callbacks), + .connection_state_cb = connection_state_cb, + .hid_info_cb = hid_info_cb, + .protocol_mode_cb = protocol_mode_cb, + .idle_time_cb = idle_time_cb, + .get_report_cb = get_report_cb, + .virtual_unplug_cb = virtual_unplug_cb +}; + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hh); + + EXEC(if_hh->init, &bthh_callbacks); +} + +/* connect */ + +static void connect_c(int argc, const const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = (void *) connected_device_addr; + *enum_func = enum_one_string; + } +} + +static void connect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hh->connect, &addr); +} + +/* disconnect */ + +/* Same completion as connect_c */ +#define disconnect_c connect_c + +static void disconnect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hh->disconnect, &addr); +} + +/* virtual_unplug */ + +/* Same completion as connect_c */ +#define virtual_unplug_c connect_c + +static void virtual_unplug_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hh->virtual_unplug, &addr); +} + +/* set_info */ + +static void set_info_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bthh_hid_info_t hid_info; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + /* TODO: set_info does not seem to be called anywhere */ + + EXEC(if_hh->set_info, &addr, hid_info); +} + +/* get_protocol */ + +static void get_protocol_c(int argc, const const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = connected_device_addr; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = TYPE_ENUM(bthh_protocol_mode_t); + *enum_func = enum_defines; + } +} + +static void get_protocol_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bthh_protocol_mode_t protocolMode; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + if (argc < 4) { + haltest_error("No protocol mode specified\n"); + return; + } + protocolMode = str2bthh_protocol_mode_t(argv[3]); + + EXEC(if_hh->get_protocol, &addr, protocolMode); +} + +/* set_protocol */ + +/* Same completion as get_protocol_c */ +#define set_protocol_c get_protocol_c + +static void set_protocol_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bthh_protocol_mode_t protocolMode; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + if (argc < 4) { + haltest_error("No protocol mode specified\n"); + return; + } + protocolMode = str2bthh_protocol_mode_t(argv[3]); + + EXEC(if_hh->set_protocol, &addr, protocolMode); +} + +/* get_report */ + +static void get_report_c(int argc, const const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = connected_device_addr; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = TYPE_ENUM(bthh_report_type_t); + *enum_func = enum_defines; + } +} + +static void get_report_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bthh_report_type_t reportType; + uint8_t reportId; + int bufferSize; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + if (argc < 4) { + haltest_error("No report type specified\n"); + return; + } + reportType = str2bthh_report_type_t(argv[3]); + + if (argc < 5) { + haltest_error("No reportId specified\n"); + return; + } + reportId = (uint8_t) atoi(argv[4]); + + if (argc < 6) { + haltest_error("No bufferSize specified\n"); + return; + } + bufferSize = atoi(argv[5]); + + EXEC(if_hh->get_report, &addr, reportType, reportId, bufferSize); +} + +/* set_report */ + +static void set_report_c(int argc, const const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = connected_device_addr; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = TYPE_ENUM(bthh_report_type_t); + *enum_func = enum_defines; + } +} + +static void set_report_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bthh_report_type_t reportType; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + if (argc <= 3) { + haltest_error("No report type specified\n"); + return; + } + reportType = str2bthh_report_type_t(argv[3]); + + if (argc <= 4) { + haltest_error("No report specified\n"); + return; + } + + EXEC(if_hh->set_report, &addr, reportType, (char *) argv[4]); +} + +/* send_data */ + +static void send_data_c(int argc, const const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = connected_device_addr; + *enum_func = enum_one_string; + } +} + +static void send_data_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + if (argc <= 3) { + haltest_error("No data to send specified\n"); + return; + } + + EXEC(if_hh->send_data, &addr, (char *) argv[3]); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hh); + + EXECV(if_hh->cleanup); +} + +/* Methods available in bthh_interface_t */ +static struct method methods[] = { + STD_METHOD(init), + STD_METHODCH(connect, ""), + STD_METHODCH(disconnect, ""), + STD_METHODCH(virtual_unplug, ""), + STD_METHOD(set_info), + STD_METHODCH(get_protocol, " "), + STD_METHODCH(set_protocol, " "), + STD_METHODCH(get_report, " "), + STD_METHODCH(set_report, " "), + STD_METHODCH(send_data, " "), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface hh_if = { + .name = "hidhost", + .methods = methods +}; diff --git a/android/client/if-main.h b/android/client/if-main.h new file mode 100644 index 0000000..a83f48b --- /dev/null +++ b/android/client/if-main.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if PLATFORM_SDK_VERSION > 17 +#include +#include +#include +#include +#include +#endif + +/* Interfaces from hal that can be populated during application lifetime */ +extern const bt_interface_t *if_bluetooth; +extern const btav_interface_t *if_av; +extern const bthf_interface_t *if_hf; +extern const bthh_interface_t *if_hh; +extern const btpan_interface_t *if_pan; +extern const btsock_interface_t *if_sock; +#if PLATFORM_SDK_VERSION > 17 +extern const btgatt_interface_t *if_gatt; +extern const btgatt_server_interface_t *if_gatt_server; +extern const btgatt_client_interface_t *if_gatt_client; +#endif + +/* + * Structure defines top level interfaces that can be used in test tool + * this will contain values as: bluetooth, av, gatt, socket, pan... + */ +struct interface { + const char *name; /* interface name */ + struct method *methods; /* methods available for this interface */ +}; + +extern const struct interface bluetooth_if; +extern const struct interface av_if; +#if PLATFORM_SDK_VERSION > 17 +extern const struct interface gatt_if; +extern const struct interface gatt_client_if; +extern const struct interface gatt_server_if; +#endif +extern const struct interface pan_if; +extern const struct interface sock_if; +extern const struct interface hf_if; +extern const struct interface hh_if; + +/* Interfaces that will show up in tool (first part of command line) */ +extern const struct interface *interfaces[]; + +#define METHOD(name, func, comp, help) {name, func, comp, help} +#define STD_METHOD(m) {#m, m##_p, NULL, NULL} +#define STD_METHODC(m) {#m, m##_p, m##_c, NULL} +#define STD_METHODH(m, h) {#m, m##_p, NULL, h} +#define STD_METHODCH(m, h) {#m, m##_p, m##_c, h} +#define END_METHOD {"", NULL, NULL, NULL} + +/* + * Function to parse argument for function, argv[0] and argv[1] are already + * parsed before this function is called and contain interface and method name + * up to argc - 1 arguments are finished and should be used to decide which + * function enumeration function to return + */ +typedef void (*parse_and_call)(int argc, const char **argv); + +/* + * This is prototype of function that will return string for given number. + * Purpose is to enumerate string for auto completion. + * Function of this type will always be called in loop. + * First time function is called i = 0, then if function returns non-NULL + * it will be called again till for some value of i it will return NULL + */ +typedef const char *(*enum_func)(void *user, int i); + +/* + * This is prototype of function that when given argc, argv will + * fill enum_func with pointer to function that will enumerate + * parameters for argc argument, user will be passed to enum_func. + */ +typedef void (*tab_complete)(int argc, const char **argv, enum_func *enum_func, + void **user); + +/* + * For each method there is name and two functions to parse command line + * and call proper hal function on. + */ +struct method { + const char *name; + parse_and_call func; + tab_complete complete; + const char *help; +}; + +int haltest_error(const char *format, ...); +int haltest_info(const char *format, ...); +int haltest_warn(const char *format, ...); + +/* + * Enumerator for discovered devices, to be used as tab completion enum_func + */ +const char *enum_devices(void *v, int i); +const char *interface_name(void *v, int i); +const char *command_name(void *v, int i); +void add_remote_device(const bt_bdaddr_t *addr); + +const struct interface *get_interface(const char *name); +struct method *get_method(struct method *methods, const char *name); +struct method *get_command(const char *name); +const struct method *get_interface_method(const char *iname, + const char *mname); + +#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) + +/* Helper macro for executing function on interface and printing BT_STATUS */ +#define EXEC(f, ...) \ + { \ + int err = f(__VA_ARGS__); \ + haltest_info("%s: %s\n", #f, bt_status_t2str(err)); \ + } + +/* Helper macro for executing void function on interface */ +#define EXECV(f, ...) \ + { \ + (void) f(__VA_ARGS__); \ + haltest_info("%s: void\n", #f); \ + } + +#define RETURN_IF_NULL(x) \ + do { if (!x) { haltest_error("%s is NULL\n", #x); return; } } while (0) + +#define VERIFY_ADDR_ARG(n, adr) \ + do { \ + if (n < argc) {\ + str2bt_bdaddr_t(argv[n], adr); \ + } else { \ + haltest_error("No address specified\n");\ + return;\ + } \ + } while (0) diff --git a/android/client/if-pan.c b/android/client/if-pan.c new file mode 100644 index 0000000..a11f2a3 --- /dev/null +++ b/android/client/if-pan.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "if-main.h" +#include "../hal-utils.h" + +const btpan_interface_t *if_pan = NULL; + +typedef int btpan_role_t; + +SINTMAP(btpan_role_t, -1, "(unknown)") + DELEMENT(BTPAN_ROLE_NONE), + DELEMENT(BTPAN_ROLE_PANNAP), + DELEMENT(BTPAN_ROLE_PANU), +ENDMAP + +SINTMAP(btpan_connection_state_t, -1, "(unknown)") + DELEMENT(BTPAN_STATE_CONNECTED), + DELEMENT(BTPAN_STATE_CONNECTING), + DELEMENT(BTPAN_STATE_DISCONNECTED), + DELEMENT(BTPAN_STATE_DISCONNECTING), +ENDMAP + +SINTMAP(btpan_control_state_t, -1, "(unknown)") + DELEMENT(BTPAN_STATE_ENABLED), + DELEMENT(BTPAN_STATE_DISABLED), +ENDMAP + +static void control_state_cb(btpan_control_state_t state, bt_status_t error, + int local_role, const char *ifname) +{ + haltest_info("%s: state=%s error=%s local_role=%s ifname=%s\n", + __func__, btpan_control_state_t2str(state), + bt_status_t2str(error), btpan_role_t2str(local_role), + ifname); +} + +static char last_used_addr[MAX_ADDR_STR_LEN]; + +static void connection_state_cb(btpan_connection_state_t state, + bt_status_t error, const bt_bdaddr_t *bd_addr, + int local_role, int remote_role) +{ + haltest_info("%s: state=%s error=%s bd_addr=%s local_role=%s remote_role=%s\n", + __func__, btpan_connection_state_t2str(state), + bt_status_t2str(error), + bt_bdaddr_t2str(bd_addr, last_used_addr), + btpan_role_t2str(local_role), + btpan_role_t2str(remote_role)); +} + +static btpan_callbacks_t pan_cbacks = { + .size = sizeof(pan_cbacks), + .control_state_cb = control_state_cb, + .connection_state_cb = connection_state_cb +}; + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_pan); + + EXEC(if_pan->init, &pan_cbacks); +} + +/* enable */ + +static void enable_c(int argc, const const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(btpan_role_t); + *enum_func = enum_defines; + } +} + +static void enable_p(int argc, const char **argv) +{ + int local_role; + + RETURN_IF_NULL(if_pan); + + /* local role */ + if (argc < 3) { + haltest_error("No local mode specified\n"); + return; + } + local_role = str2btpan_role_t(argv[2]); + if (local_role == -1) + local_role = atoi(argv[2]); + + EXEC(if_pan->enable, local_role); +} + +/* get_local_role */ + +static void get_local_role_p(int argc, const char **argv) +{ + int local_role; + + RETURN_IF_NULL(if_pan); + + local_role = if_pan->get_local_role(); + haltest_info("local_role: %s\n", btpan_role_t2str(local_role)); +} + +/* connect */ + +static void connect_c(int argc, const const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = NULL; + *enum_func = enum_devices; + } else if (argc == 4 || argc == 5) { + *user = TYPE_ENUM(btpan_role_t); + *enum_func = enum_defines; + } +} + +static void connect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + int local_role; + int remote_role; + + RETURN_IF_NULL(if_pan); + VERIFY_ADDR_ARG(2, &addr); + + /* local role */ + if (argc < 4) { + haltest_error("No local mode specified\n"); + return; + } + local_role = str2btpan_role_t(argv[3]); + if (local_role == -1) + local_role = atoi(argv[3]); + + /* remote role */ + if (argc < 5) { + haltest_error("No remote mode specified\n"); + return; + } + remote_role = str2btpan_role_t(argv[4]); + if (remote_role == -1) + remote_role = atoi(argv[4]); + + EXEC(if_pan->connect, &addr, local_role, remote_role); +} + +/* disconnect */ + +static void disconnect_c(int argc, const const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = last_used_addr; + *enum_func = enum_one_string; + } +} + +static void disconnect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_pan); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_pan->disconnect, &addr); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_pan); + + EXECV(if_pan->cleanup); + if_pan = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHODCH(connect, " "), + STD_METHODCH(enable, ""), + STD_METHOD(get_local_role), + STD_METHODCH(disconnect, ""), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface pan_if = { + .name = "pan", + .methods = methods +}; diff --git a/android/client/if-sock.c b/android/client/if-sock.c new file mode 100644 index 0000000..2cd06e8 --- /dev/null +++ b/android/client/if-sock.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "if-main.h" +#include "pollhandler.h" +#include "../hal-utils.h" + +const btsock_interface_t *if_sock = NULL; + +SINTMAP(btsock_type_t, -1, "(unknown)") + DELEMENT(BTSOCK_RFCOMM), + DELEMENT(BTSOCK_SCO), + DELEMENT(BTSOCK_L2CAP), +ENDMAP + +#define MAX_LISTEN_FD 15 +static int listen_fd[MAX_LISTEN_FD]; +static int listen_fd_count; + +/* + * This function reads data from file descriptor and + * prints it to the user + */ +static void receive_from_client(struct pollfd *pollfd) +{ + char buf[16]; + /* Buffer for lines: + * 41 42 43 20 20 00 31 32 00 07 04 00 00 00 00 00 ABC .12..... + */ + char outbuf[sizeof(buf) * 4 + 2]; + int i; + int ret; + + if (pollfd->revents & POLLHUP) { + haltest_error("Disconnected fd=%d\n", pollfd->fd); + poll_unregister_fd(pollfd->fd, receive_from_client); + } else if (pollfd->revents & POLLIN) { + haltest_info("receiving from client fd=%d\n", pollfd->fd); + + do { + memset(outbuf, ' ', sizeof(outbuf)); + outbuf[sizeof(outbuf) - 1] = 0; + ret = recv(pollfd->fd, buf, sizeof(buf), MSG_DONTWAIT); + + for (i = 0; i < ret; ++i) + sprintf(outbuf + i * 3, "%02X ", + (unsigned) buf[i]); + outbuf[i * 3] = ' '; + for (i = 0; i < ret; ++i) + sprintf(outbuf + 48 + i, "%c", + (isprint(buf[i]) ? buf[i] : '.')); + if (ret > 0) + haltest_info("%s\n", outbuf); + } while (ret > 0); + } else { + /* For now disconnect on all other events */ + haltest_error("Poll event %x\n", pollfd->revents); + poll_unregister_fd(pollfd->fd, receive_from_client); + } +} + +/* + * This function read from fd socket information about + * connected socket + */ +static void receive_sock_connect_signal(struct pollfd *pollfd) +{ + sock_connect_signal_t cs; + char addr_str[MAX_ADDR_STR_LEN]; + + if (pollfd->revents & POLLIN) { + int ret; + + poll_unregister_fd(pollfd->fd, receive_sock_connect_signal); + ret = read(pollfd->fd, &cs, sizeof(cs)); + if (ret != sizeof(cs)) { + haltest_info("Read on connect return %d\n", ret); + return; + } + + haltest_info("Connection to %s channel %d status=%d\n", + bt_bdaddr_t2str(&cs.bd_addr, addr_str), + cs.channel, cs.status); + + if (cs.status == 0) + poll_register_fd(pollfd->fd, POLLIN, + receive_from_client); + } + + if (pollfd->revents & POLLHUP) { + haltest_error("Disconnected fd=%d revents=0x%X\n", pollfd->fd, + pollfd->revents); + poll_unregister_fd(pollfd->fd, receive_sock_connect_signal); + } +} + +/* + * This function read from fd socket information about + * incoming connection and starts monitoring new connection + * on file descriptor read from fd. + */ +static void read_accepted(int fd) +{ + int ret; + struct msghdr msg; + struct iovec iv; + char cmsgbuf[CMSG_SPACE(1)]; + struct cmsghdr *cmsgptr; + sock_connect_signal_t cs; + int accepted_fd = -1; + char addr_str[MAX_ADDR_STR_LEN]; + + memset(&msg, 0, sizeof(msg)); + memset(&iv, 0, sizeof(iv)); + + iv.iov_base = &cs; + iv.iov_len = sizeof(cs); + + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + do { + ret = recvmsg(fd, &msg, MSG_NOSIGNAL); + } while (ret < 0 && errno == EINTR); + + if (ret < 16 || + (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) + haltest_error("Failed to accept connection\n"); + + for (cmsgptr = CMSG_FIRSTHDR(&msg); + cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { + int *descs; + int count; + + if (cmsgptr->cmsg_level != SOL_SOCKET || + cmsgptr->cmsg_type != SCM_RIGHTS) + continue; + + descs = (int *) CMSG_DATA(cmsgptr); + count = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int)); + + if (count != 1) + haltest_error("Failed to accept descriptors count=%d\n", + count); + + accepted_fd = descs[0]; + break; + } + + haltest_info("Incoming connection from %s channel %d status=%d fd=%d\n", + bt_bdaddr_t2str(&cs.bd_addr, addr_str), + cs.channel, cs.status, accepted_fd); + poll_register_fd(accepted_fd, POLLIN, receive_from_client); +} + +/* handles incoming connections on socket */ +static void client_connected(struct pollfd *pollfd) +{ + haltest_info("client connected %x\n", pollfd->revents); + + if (pollfd->revents & POLLHUP) + poll_unregister_fd(pollfd->fd, client_connected); + else if (pollfd->revents & POLLIN) + read_accepted(pollfd->fd); +} + +/** listen */ + +static void listen_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(btsock_type_t); + *enum_func = enum_defines; + } +} + +static void listen_p(int argc, const char **argv) +{ + btsock_type_t type; + const char *service_name; + bt_uuid_t service_uuid; + int channel; + int sock_fd; + int flags; + + RETURN_IF_NULL(if_sock); + + /* Socket type */ + if (argc < 3) { + haltest_error("No socket type specified\n"); + return; + } + type = str2btsock_type_t(argv[2]); + if ((int) type == -1) + type = atoi(argv[2]); + + /* service name */ + if (argc < 4) { + haltest_error("No service name specified\n"); + return; + } + service_name = argv[3]; + + /* uuid */ + if (argc < 5) { + haltest_error("No uuid specified\n"); + return; + } + str2bt_uuid_t(argv[4], &service_uuid); + + /* channel */ + channel = argc > 5 ? atoi(argv[5]) : 0; + + /* flags */ + flags = argc > 6 ? atoi(argv[6]) : 0; + + if (listen_fd_count >= MAX_LISTEN_FD) { + haltest_error("Max (%d) listening sockets exceeded\n", + listen_fd_count); + return; + } + EXEC(if_sock->listen, type, service_name, + &service_uuid.uu[0], channel, &sock_fd, flags); + if (sock_fd > 0) { + int channel = 0; + int ret = read(sock_fd, &channel, 4); + if (ret != 4) + haltest_info("Read channel failed\n"); + haltest_info("Channel returned from first read %d\n", channel); + listen_fd[listen_fd_count++] = sock_fd; + poll_register_fd(sock_fd, POLLIN, client_connected); + } +} + +/** connect */ + +static void connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *enum_func = enum_devices; + } else if (argc == 4) { + *user = TYPE_ENUM(btsock_type_t); + *enum_func = enum_defines; + } +} + +static void connect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + btsock_type_t type; + bt_uuid_t uuid; + int channel; + int sock_fd; + int flags; + + /* Address */ + if (argc <= 2) { + haltest_error("No address specified\n"); + return; + } + str2bt_bdaddr_t(argv[2], &addr); + + /* Socket type */ + if (argc <= 3) { + haltest_error("No socket type specified\n"); + return; + } + type = str2btsock_type_t(argv[3]); + if ((int) type == -1) + type = atoi(argv[3]); + + /* uuid */ + if (argc <= 4) { + haltest_error("No uuid specified\n"); + return; + } + str2bt_uuid_t(argv[4], &uuid); + + /* channel */ + if (argc <= 5) { + haltest_error("No channel specified\n"); + return; + } + channel = atoi(argv[5]); + + /* flags */ + flags = argc <= 6 ? 0 : atoi(argv[6]); + + RETURN_IF_NULL(if_sock); + + EXEC(if_sock->connect, &addr, type, &uuid.uu[0], channel, &sock_fd, + flags); + if (sock_fd > 0) { + int channel = 0; + int ret = read(sock_fd, &channel, 4); + + if (ret != 4) + haltest_info("Read channel failed\n"); + haltest_info("Channel returned from first read %d\n", channel); + listen_fd[listen_fd_count++] = sock_fd; + poll_register_fd(sock_fd, POLLIN, receive_sock_connect_signal); + } +} + +/* Methods available in btsock_interface_t */ +static struct method methods[] = { + STD_METHODCH(listen, + " [] []"), + STD_METHODCH(connect, + " []"), + END_METHOD +}; + +const struct interface sock_if = { + .name = "socket", + .methods = methods +}; diff --git a/android/client/pollhandler.c b/android/client/pollhandler.c new file mode 100644 index 0000000..6160921 --- /dev/null +++ b/android/client/pollhandler.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "pollhandler.h" + +/* + * Code that allows to poll multiply file descriptors for events + * File descriptors can be added and removed at runtime + * + * Call poll_register_fd function first to add file descriptors to monitor + * Then call poll_dispatch_loop that will poll all registered file descriptors + * as long as they are not unregistered. + * + * When event happen on given fd appropriate user supplied handler is called + */ + +/* Maximum number of files to monitor */ +#define MAX_OPEN_FD 10 + +/* Storage for pollfd structures for monitored file descriptors */ +static struct pollfd fds[MAX_OPEN_FD]; +static poll_handler fds_handler[MAX_OPEN_FD]; +/* Number of registered file descriptors */ +static int fds_count = 0; + +/* + * Function polls file descriptor in loop and calls appropriate handler + * on event. Function returns when there is no more file descriptor to + * monitor + */ +void poll_dispatch_loop(void) +{ + while (fds_count > 0) { + int i; + int cur_fds_count = fds_count; + int ready = poll(fds, fds_count, 1000); + + for (i = 0; i < fds_count && ready > 0; ++i) { + if (fds[i].revents == 0) + continue; + + fds_handler[i](fds + i); + ready--; + /* + * If handler was remove from table + * just skip the rest and poll again + * This is due to reordering of tables in + * register/unregister functions + */ + if (cur_fds_count != fds_count) + break; + } + } +} + +/* + * Registers file descriptor to be monitored for events (see man poll(2)) + * for events. + * + * return non negative value on success + * -EMFILE when there are to much descriptors + */ +int poll_register_fd(int fd, short events, poll_handler ph) +{ + if (fds_count >= MAX_OPEN_FD) + return -EMFILE; + + fds_handler[fds_count] = ph; + fds[fds_count].fd = fd; + fds[fds_count].events = events; + fds_count++; + + return fds_count; +} + +/* + * Unregisters file descriptor + * Both fd and ph must match previously registered data + * + * return 0 if unregister succeeded + * -EBADF if arguments do not match any register handler + */ +int poll_unregister_fd(int fd, poll_handler ph) +{ + int i; + + for (i = 0; i < fds_count; ++i) { + if (fds_handler[i] == ph && fds[i].fd == fd) { + fds_count--; + if (i < fds_count) { + fds[i].fd = fds[fds_count].fd; + fds[i].events = fds[fds_count].events; + fds_handler[i] = fds_handler[fds_count]; + } + return 0; + } + } + return -EBADF; +} diff --git a/android/client/pollhandler.h b/android/client/pollhandler.h new file mode 100644 index 0000000..e2f22df --- /dev/null +++ b/android/client/pollhandler.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +/* Function to be called when there are event for some descriptor */ +typedef void (*poll_handler)(struct pollfd *pollfd); + +int poll_register_fd(int fd, short events, poll_handler ph); +int poll_unregister_fd(int fd, poll_handler ph); + +void poll_dispatch_loop(void); diff --git a/android/client/tabcompletion.c b/android/client/tabcompletion.c new file mode 100644 index 0000000..f965620 --- /dev/null +++ b/android/client/tabcompletion.c @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include "if-main.h" +#include "terminal.h" + +/* how many times tab was hit */ +static int tab_hit_count; + +typedef struct split_arg { + struct split_arg *next; /* next argument in buffer */ + const char *origin; /* pointer to original argument */ + char ntcopy[1]; /* null terminated copy of argument */ +} split_arg_t; + +/* function returns method of given name or NULL if not found */ +const struct method *get_interface_method(const char *iname, + const char *mname) +{ + const struct interface *iface = get_interface(iname); + + if (iface == NULL) + return NULL; + + return get_method(iface->methods, mname); +} + +/* prints matching elements */ +static void print_matches(enum_func f, void *user, const char *prefix, int len) +{ + int i; + const char *enum_name; + + putchar('\n'); + for (i = 0; NULL != (enum_name = f(user, i)); ++i) { + if (strncmp(enum_name, prefix, len) == 0) + printf("%s\t", enum_name); + } + putchar('\n'); + terminal_draw_command_line(); +} + +/* + * This function splits command line into linked list of arguments. + * line_buffer - pointer to input command line + * size - size of command line to parse + * buf - output buffer to keep split arguments list + * buf_size_in_bytes - size of buf + */ +static int split_command(const char *line_buffer, int size, split_arg_t *buf, + int buf_size_in_bytes) +{ + split_arg_t *prev = NULL; + split_arg_t *arg = buf; + int argc = 0; + const char *p = line_buffer; + const char *e = p + (size > 0 ? size : (int) strlen(p)); + int len; + + do { + while (p < e && isspace(*p)) + p++; + arg->origin = p; + arg->next = NULL; + while (p < e && !isspace(*p)) + p++; + len = p - arg->origin; + if (&arg->ntcopy[0] + len + 1 > + (const char *) buf + buf_size_in_bytes) + break; + strncpy(arg->ntcopy, arg->origin, len); + arg->ntcopy[len] = 0; + if (prev != NULL) + prev->next = arg; + prev = arg; + arg += (2 * sizeof(*arg) + len) / sizeof(*arg); + argc++; + } while (p < e); + + return argc; +} + +/* Function to enumerate method names */ +static const char *methods_name(void *v, int i) +{ + const struct interface *iface = v; + + return iface->methods[i].name[0] ? iface->methods[i].name : NULL; +} + +struct command_completion_args; +typedef void (*short_help)(struct command_completion_args *args); + +struct command_completion_args { + const split_arg_t *arg; /* list of arguments */ + const char *typed; /* last typed element */ + enum_func func; /* enumerating function */ + void *user; /* argument to enumerating function */ + short_help help; /* help function */ + void *user_help; /* additional data (used by short_help) */ +}; + +/* + * complete command line + */ +static void tab_completion(struct command_completion_args *args) +{ + const char *name = args->typed; + const int len = strlen(name); + int i; + int j; + char prefix[128] = {0}; + int prefix_len = 0; + int count = 0; + const char *enum_name; + + for (i = 0; NULL != (enum_name = args->func(args->user, i)); ++i) { + /* prefix does not match */ + if (strncmp(enum_name, name, len) != 0) + continue; + + /* prefix matches first time */ + if (count++ == 0) { + strcpy(prefix, enum_name); + prefix_len = strlen(prefix); + continue; + } + + /* + * Prefix matches next time + * reduce prefix to common part + */ + for (j = 0; prefix[j] != 0 + && prefix[j] == enum_name[j];) + ++j; + prefix_len = j; + prefix[j] = 0; + } + + if (count == 0) { + /* no matches */ + if (args->help != NULL) + args->help(args); + tab_hit_count = 0; + return; + } + + /* len == prefix_len => nothing new was added */ + if (len == prefix_len) { + if (count != 1) { + if (tab_hit_count == 1) { + putchar('\a'); + } else if (tab_hit_count == 2 || + args->help == NULL) { + print_matches(args->func, + args->user, name, len); + } else { + args->help(args); + tab_hit_count = 1; + } + } else if (count == 1) { + /* nothing to add, exact match add space */ + terminal_insert_into_command_line(" "); + } + } else { + /* new chars can be added from some interface name(s) */ + if (count == 1) { + /* exact match, add space */ + prefix[prefix_len++] = ' '; + prefix[prefix_len] = '\0'; + } + + terminal_insert_into_command_line(prefix + len); + tab_hit_count = 0; + } +} + +/* interface completion */ +static void command_completion(split_arg_t *arg) +{ + struct command_completion_args args = { + .arg = arg, + .typed = arg->ntcopy, + .func = command_name + }; + + tab_completion(&args); +} + +/* method completion */ +static void method_completion(const struct interface *iface, split_arg_t *arg) +{ + struct command_completion_args args = { + .arg = arg, + .typed = arg->next->ntcopy, + .func = methods_name, + .user = (void *) iface + }; + + if (iface == NULL) + return; + + tab_completion(&args); +} + +static const char *bold = "\x1b[1m"; +static const char *normal = "\x1b[0m"; + +static bool find_nth_argument(const char *str, int n, const char **s, + const char **e) +{ + const char *p = str; + int argc = 0; + *e = NULL; + + while (p != NULL && *p != 0) { + + while (isspace(*p)) + ++p; + + if (n == argc) + *s = p; + + if (*p == '[') { + p = strchr(p, ']'); + if (p != NULL) + *e = ++p; + } else if (*p == '<') { + p = strchr(p, '>'); + if (p != NULL) + *e = ++p; + } else { + *e = strchr(p, ' '); + if (*e == NULL) + *e = p + strlen(p); + p = *e; + } + + if (n == argc) + break; + + argc++; + *e = NULL; + } + return *e != NULL; +} + +/* prints short help on method for interface */ +static void method_help(struct command_completion_args *args) +{ + int argc; + const split_arg_t *arg = args->arg; + const char *sb = NULL; + const char *eb = NULL; + const char *arg1 = ""; + int arg1_size = 0; /* size of method field (for methods > 0) */ + + if (args->user_help == NULL) + return; + + for (argc = 0; arg != NULL; argc++) + arg = arg->next; + + /* Check if this is method from interface */ + if (get_command(args->arg->ntcopy) == NULL) { + /* if so help is missing interface and method name */ + arg1 = args->arg->next->ntcopy; + arg1_size = strlen(arg1) + 1; + } + + find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2), + &sb, &eb); + + if (eb != NULL) + haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy, + arg1_size, arg1, + sb - (const char *) args->user_help, + args->user_help, + bold, eb - sb, sb, normal, eb); + else + haltest_info("%s %-*s%s\n", args->arg->ntcopy, + arg1_size, arg1, args->user_help); +} + +/* So we have empty enumeration */ +static const char *return_null(void *user, int i) +{ + return NULL; +} + +/* + * parameter completion function + * argc - number of elements in arg list + * arg - list of arguments + * method - method to get completion from (can be NULL) + */ +static void param_completion(int argc, const split_arg_t *arg, + const struct method *method, int hlpix) +{ + int i; + const char *argv[argc]; + const split_arg_t *tmp = arg; + struct command_completion_args args = { + .arg = arg, + .func = return_null + }; + + /* prepare standard argv from arg */ + for (i = 0; i < argc; ++i) { + argv[i] = tmp->ntcopy; + tmp = tmp->next; + } + + if (method != NULL && method->complete != NULL) { + /* ask method for completion function */ + method->complete(argc, argv, &args.func, &args.user); + } + + /* If method provided enumeration function call try to complete */ + if (args.func != NULL) { + args.typed = argv[argc - 1]; + args.help = method_help; + args.user_help = method ? (void *) method->help : NULL; + + tab_completion(&args); + } +} + +/* + * This method gets called when user tapped tab key. + * line - points to command line + * len - size of line that should be used for completions. This should be + * cursor position during tab hit. + */ +void process_tab(const char *line, int len) +{ + int argc; + static split_arg_t buf[(LINE_BUF_MAX * 2) / sizeof(split_arg_t)]; + const struct method *method; + + argc = split_command(line, len, buf, sizeof(buf)); + tab_hit_count++; + + if (argc == 0) + return; + + if (argc == 1) { + command_completion(buf); + return; + } + + method = get_command(buf[0].ntcopy); + if (method != NULL) { + param_completion(argc, buf, method, 1); + } else if (argc == 2) { + method_completion(get_interface(buf[0].ntcopy), buf); + } else { + /* Find method for pair */ + method = get_interface_method(buf[0].ntcopy, + buf[0].next->ntcopy); + param_completion(argc, buf, method, 2); + } +} diff --git a/android/client/terminal.c b/android/client/terminal.c new file mode 100644 index 0000000..f7b56de --- /dev/null +++ b/android/client/terminal.c @@ -0,0 +1,824 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "terminal.h" +#include "history.h" + +/* + * Character sequences recognized by code in this file + * Leading ESC 0x1B is not included + */ +#define SEQ_INSERT "[2~" +#define SEQ_DELETE "[3~" +#define SEQ_HOME "OH" +#define SEQ_END "OF" +#define SEQ_PGUP "[5~" +#define SEQ_PGDOWN "[6~" +#define SEQ_LEFT "[D" +#define SEQ_RIGHT "[C" +#define SEQ_UP "[A" +#define SEQ_DOWN "[B" +#define SEQ_STAB "[Z" +#define SEQ_M_n "n" +#define SEQ_M_p "p" +#define SEQ_CLEFT "[1;5D" +#define SEQ_CRIGHT "[1;5C" +#define SEQ_CUP "[1;5A" +#define SEQ_CDOWN "[1;5B" +#define SEQ_SLEFT "[1;2D" +#define SEQ_SRIGHT "[1;2C" +#define SEQ_SUP "[1;2A" +#define SEQ_SDOWN "[1;2B" +#define SEQ_MLEFT "[1;3D" +#define SEQ_MRIGHT "[1;3C" +#define SEQ_MUP "[1;3A" +#define SEQ_MDOWN "[1;3B" + +#define KEY_SEQUENCE(k) { KEY_##k, SEQ_##k } +struct ansii_sequence { + int code; + const char *sequence; +}; + +/* Table connects single int key codes with character sequences */ +static const struct ansii_sequence ansii_sequnces[] = { + KEY_SEQUENCE(INSERT), + KEY_SEQUENCE(DELETE), + KEY_SEQUENCE(HOME), + KEY_SEQUENCE(END), + KEY_SEQUENCE(PGUP), + KEY_SEQUENCE(PGDOWN), + KEY_SEQUENCE(LEFT), + KEY_SEQUENCE(RIGHT), + KEY_SEQUENCE(UP), + KEY_SEQUENCE(DOWN), + KEY_SEQUENCE(CLEFT), + KEY_SEQUENCE(CRIGHT), + KEY_SEQUENCE(CUP), + KEY_SEQUENCE(CDOWN), + KEY_SEQUENCE(SLEFT), + KEY_SEQUENCE(SRIGHT), + KEY_SEQUENCE(SUP), + KEY_SEQUENCE(SDOWN), + KEY_SEQUENCE(MLEFT), + KEY_SEQUENCE(MRIGHT), + KEY_SEQUENCE(MUP), + KEY_SEQUENCE(MDOWN), + KEY_SEQUENCE(STAB), + KEY_SEQUENCE(M_p), + KEY_SEQUENCE(M_n), + { 0, NULL } +}; + +#define KEY_SEQUNCE_NOT_FINISHED -1 +#define KEY_C_C 3 +#define KEY_C_D 4 +#define KEY_C_L 12 + +#define isseqence(c) ((c) == 0x1B) + +/* + * Number of characters that consist of ANSI sequence + * Should not be less then longest string in ansi_sequences + */ +#define MAX_ASCII_SEQUENCE 10 + +static char current_sequence[MAX_ASCII_SEQUENCE]; +static int current_sequence_len = -1; + +/* single line typed by user goes here */ +static char line_buf[LINE_BUF_MAX]; +/* index of cursor in input line */ +static int line_buf_ix = 0; +/* current length of input line */ +static int line_len = 0; + +/* line index used for fetching lines from history */ +static int line_index = 0; + +static char prompt_buf[10] = "> "; +static const char *const noprompt = ""; +static const char *current_prompt = prompt_buf; +static const char *prompt = prompt_buf; +/* + * Moves cursor to right or left + * + * n - positive - moves cursor right + * n - negative - moves cursor left + */ +static void terminal_move_cursor(int n) +{ + if (n < 0) { + for (; n < 0; n++) + putchar('\b'); + } else if (n > 0) { + printf("%*s", n, line_buf + line_buf_ix); + } +} + +/* Draw command line */ +void terminal_draw_command_line(void) +{ + /* + * this needs to be checked here since line_buf is not cleared + * before parsing event though line_len and line_buf_ix are + */ + if (line_len > 0) + printf("%s%s", prompt, line_buf); + else + printf("%s", prompt); + + /* move cursor to it's place */ + terminal_move_cursor(line_buf_ix - line_len); +} + +/* inserts string into command line at cursor position */ +void terminal_insert_into_command_line(const char *p) +{ + int len = strlen(p); + + if (line_len == line_buf_ix) { + strcat(line_buf, p); + printf("%s", p); + line_len = line_len + len; + line_buf_ix = line_len; + } else { + memmove(line_buf + line_buf_ix + len, + line_buf + line_buf_ix, line_len - line_buf_ix + 1); + memmove(line_buf + line_buf_ix, p, len); + printf("%s", line_buf + line_buf_ix); + line_buf_ix += len; + line_len += len; + terminal_move_cursor(line_buf_ix - line_len); + } +} + +/* Prints string and redraws command line */ +int terminal_print(const char *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + + ret = terminal_vprint(format, args); + + va_end(args); + return ret; +} + +/* Prints string and redraws command line */ +int terminal_vprint(const char *format, va_list args) +{ + int ret; + + printf("\r%*s\r", (int) line_len + 1, " "); + + ret = vprintf(format, args); + + terminal_draw_command_line(); + + fflush(stdout); + + return ret; +} + +/* + * Call this when text in line_buf was changed + * and line needs to be redrawn + */ +static void terminal_line_replaced(void) +{ + int len = strlen(line_buf); + + /* line is shorter that previous */ + if (len < line_len) { + /* if new line is shorter move cursor to end of new end */ + while (line_buf_ix > len) { + putchar('\b'); + line_buf_ix--; + } + + /* If cursor was not at the end, move it to the end */ + if (line_buf_ix < line_len) + printf("%.*s", line_len - line_buf_ix, + line_buf + line_buf_ix); + /* over write end of previous line */ + while (line_len >= len++) + putchar(' '); + } + + /* draw new line */ + printf("\r%s%s", prompt, line_buf); + /* set up indexes to new line */ + line_len = strlen(line_buf); + line_buf_ix = line_len; + fflush(stdout); +} + +static void terminal_clear_line(void) +{ + line_buf[0] = '\0'; + terminal_line_replaced(); +} + +static void terminal_clear_screen(void) +{ + line_buf[0] = '\0'; + line_buf_ix = 0; + line_len = 0; + + printf("\x1b[2J\x1b[1;1H%s", prompt); +} + +static void terminal_delete_char(void) +{ + /* delete character under cursor if not at the very end */ + if (line_buf_ix >= line_len) + return; + /* + * Prepare buffer with one character missing + * trailing 0 is moved + */ + line_len--; + memmove(line_buf + line_buf_ix, line_buf + line_buf_ix + 1, + line_len - line_buf_ix + 1); + /* print rest of line from current cursor position */ + printf("%s \b", line_buf + line_buf_ix); + /* move back cursor */ + terminal_move_cursor(line_buf_ix - line_len); +} + +/* + * Function tries to replace current line with specified line in history + * new_line_index - new line to show, -1 to show oldest + */ +static void terminal_get_line_from_history(int new_line_index) +{ + new_line_index = history_get_line(new_line_index, + line_buf, LINE_BUF_MAX); + + if (new_line_index >= 0) { + terminal_line_replaced(); + line_index = new_line_index; + } +} + +/* + * Function searches history back or forward for command line that starts + * with characters up to cursor position + * + * back - true - searches backward + * back - false - searches forward (more recent commands) + */ +static void terminal_match_hitory(bool back) +{ + char buf[line_buf_ix + 1]; + int line; + int matching_line = -1; + int dir = back ? 1 : -1; + + line = line_index + dir; + while (matching_line == -1 && line >= 0) { + int new_line_index; + + new_line_index = history_get_line(line, buf, line_buf_ix + 1); + if (new_line_index < 0) + break; + + if (0 == strncmp(line_buf, buf, line_buf_ix)) + matching_line = line; + line += dir; + } + + if (matching_line >= 0) { + int pos = line_buf_ix; + terminal_get_line_from_history(matching_line); + /* move back to cursor position to original place */ + line_buf_ix = pos; + terminal_move_cursor(pos - line_len); + } +} + +/* + * Converts terminal character sequences to single value representing + * keyboard keys + */ +static int terminal_convert_sequence(int c) +{ + int i; + + /* Not in sequence yet? */ + if (current_sequence_len == -1) { + /* Is ansi sequence detected by 0x1B ? */ + if (isseqence(c)) { + current_sequence_len++; + return KEY_SEQUNCE_NOT_FINISHED; + } + + return c; + } + + /* Inside sequence */ + current_sequence[current_sequence_len++] = c; + current_sequence[current_sequence_len] = '\0'; + for (i = 0; ansii_sequnces[i].code; ++i) { + /* Matches so far? */ + if (0 != strncmp(current_sequence, ansii_sequnces[i].sequence, + current_sequence_len)) + continue; + + /* Matches as a whole? */ + if (ansii_sequnces[i].sequence[current_sequence_len] == 0) { + current_sequence_len = -1; + return ansii_sequnces[i].code; + } + + /* partial match (not whole sequence yet) */ + return KEY_SEQUNCE_NOT_FINISHED; + } + + terminal_print("ansi char 0x%X %c\n", c); + /* + * Sequence does not match + * mark that no in sequence any more, return char + */ + current_sequence_len = -1; + return c; +} + +typedef void (*terminal_action)(int c, line_callback process_line); + +#define TERMINAL_ACTION(n) \ + static void n(int c, void (*process_line)(char *line)) + +TERMINAL_ACTION(terminal_action_null) +{ +} + +/* Mapping between keys and function */ +typedef struct { + int key; + terminal_action func; +} KeyAction; + +int action_keys[] = { + KEY_SEQUNCE_NOT_FINISHED, + KEY_LEFT, + KEY_RIGHT, + KEY_HOME, + KEY_END, + KEY_DELETE, + KEY_CLEFT, + KEY_CRIGHT, + KEY_SUP, + KEY_SDOWN, + KEY_UP, + KEY_DOWN, + KEY_BACKSPACE, + KEY_INSERT, + KEY_PGUP, + KEY_PGDOWN, + KEY_CUP, + KEY_CDOWN, + KEY_SLEFT, + KEY_SRIGHT, + KEY_MLEFT, + KEY_MRIGHT, + KEY_MUP, + KEY_MDOWN, + KEY_STAB, + KEY_M_n, + KEY_M_p, + KEY_C_C, + KEY_C_D, + KEY_C_L, + '\t', + '\r', + '\n', +}; + +#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) + +/* + * current_actions holds all recognizable kes and actions for them + * additional element (index 0) is used for default action + */ +static KeyAction current_actions[NELEM(action_keys) + 1]; + +/* KeyAction comparator by key, for qsort and bsearch */ +static int KeyActionKeyCompare(const void *a, const void *b) +{ + return ((const KeyAction *) a)->key - ((const KeyAction *) b)->key; +} + +/* Find action by key, NULL if no action for this key */ +static KeyAction *terminal_get_action(int key) +{ + KeyAction a = { .key = key }; + + return bsearch(&a, current_actions + 1, NELEM(action_keys), sizeof(a), + KeyActionKeyCompare); +} + +/* Sets new set of actions to use */ +static void terminal_set_actions(const KeyAction *actions) +{ + int i; + + /* Make map with empty function for every key */ + for (i = 0; i < NELEM(action_keys); ++i) { + /* + * + 1 due to 0 index reserved for default action that is + * called for non mapped key + */ + current_actions[i + 1].key = action_keys[i]; + current_actions[i + 1].func = terminal_action_null; + } + + /* Sort action from 1 (index 0 - default action) */ + qsort(current_actions + 1, NELEM(action_keys), sizeof(KeyAction), + KeyActionKeyCompare); + /* Set default action (first in array) */ + current_actions[0] = *actions++; + + /* Copy rest of actions into their places */ + for (; actions->key; ++actions) { + KeyAction *place = terminal_get_action(actions->key); + + if (place) + place->func = actions->func; + } +} + +TERMINAL_ACTION(terminal_action_left) +{ + /* if not at the beginning move to previous character */ + if (line_buf_ix <= 0) + return; + line_buf_ix--; + terminal_move_cursor(-1); +} + +TERMINAL_ACTION(terminal_action_right) +{ + /* + * If not at the end, just print current character + * and modify position + */ + if (line_buf_ix < line_len) + putchar(line_buf[line_buf_ix++]); +} + +TERMINAL_ACTION(terminal_action_home) +{ + /* move to beginning of line and update position */ + printf("\r%s", prompt); + line_buf_ix = 0; +} + +TERMINAL_ACTION(terminal_action_end) +{ + /* if not at the end of line */ + if (line_buf_ix < line_len) { + /* print everything from cursor */ + printf("%s", line_buf + line_buf_ix); + /* just modify current position */ + line_buf_ix = line_len; + } +} + +TERMINAL_ACTION(terminal_action_del) +{ + terminal_delete_char(); +} + +TERMINAL_ACTION(terminal_action_word_left) +{ + int old_pos; + /* + * Move by word left + * + * Are we at the beginning of line? + */ + if (line_buf_ix <= 0) + return; + + old_pos = line_buf_ix; + line_buf_ix--; + /* skip spaces left */ + while (line_buf_ix && isspace(line_buf[line_buf_ix])) + line_buf_ix--; + + /* skip all non spaces to the left */ + while (line_buf_ix > 0 && + !isspace(line_buf[line_buf_ix - 1])) + line_buf_ix--; + + /* move cursor to new position */ + terminal_move_cursor(line_buf_ix - old_pos); +} + +TERMINAL_ACTION(terminal_action_word_right) +{ + int old_pos; + /* + * Move by word right + * + * are we at the end of line? + */ + if (line_buf_ix >= line_len) + return; + + old_pos = line_buf_ix; + /* skip all spaces */ + while (line_buf_ix < line_len && isspace(line_buf[line_buf_ix])) + line_buf_ix++; + + /* skip all non spaces */ + while (line_buf_ix < line_len && !isspace(line_buf[line_buf_ix])) + line_buf_ix++; + /* + * Move cursor to right by printing text + * between old cursor and new + */ + if (line_buf_ix > old_pos) + printf("%.*s", (int) (line_buf_ix - old_pos), + line_buf + old_pos); +} + +TERMINAL_ACTION(terminal_action_history_begin) +{ + terminal_get_line_from_history(-1); +} + +TERMINAL_ACTION(terminal_action_history_end) +{ + if (line_index > 0) + terminal_get_line_from_history(0); +} + +TERMINAL_ACTION(terminal_action_history_up) +{ + terminal_get_line_from_history(line_index + 1); +} + +TERMINAL_ACTION(terminal_action_history_down) +{ + if (line_index > 0) + terminal_get_line_from_history(line_index - 1); +} + +TERMINAL_ACTION(terminal_action_tab) +{ + /* tab processing */ + process_tab(line_buf, line_buf_ix); +} + + +TERMINAL_ACTION(terminal_action_backspace) +{ + if (line_buf_ix <= 0) + return; + + if (line_buf_ix == line_len) { + printf("\b \b"); + line_len = --line_buf_ix; + line_buf[line_len] = 0; + } else { + putchar('\b'); + line_buf_ix--; + line_len--; + memmove(line_buf + line_buf_ix, + line_buf + line_buf_ix + 1, + line_len - line_buf_ix + 1); + printf("%s \b", line_buf + line_buf_ix); + terminal_move_cursor(line_buf_ix - line_len); + } +} + +TERMINAL_ACTION(terminal_action_find_history_forward) +{ + /* Search history forward */ + terminal_match_hitory(false); +} + +TERMINAL_ACTION(terminal_action_find_history_backward) +{ + /* Search history forward */ + terminal_match_hitory(true); +} + +TERMINAL_ACTION(terminal_action_ctrl_c) +{ + terminal_clear_line(); +} + +TERMINAL_ACTION(terminal_action_ctrl_d) +{ + if (line_len > 0) { + terminal_delete_char(); + } else { + puts(""); + exit(0); + } +} + +TERMINAL_ACTION(terminal_action_clear_screen) +{ + terminal_clear_screen(); +} + +TERMINAL_ACTION(terminal_action_enter) +{ + /* + * On new line add line to history + * forget history position + */ + history_add_line(line_buf); + line_len = 0; + line_buf_ix = 0; + line_index = -1; + /* print new line */ + putchar(c); + prompt = noprompt; + process_line(line_buf); + /* clear current line */ + line_buf[0] = '\0'; + prompt = current_prompt; + printf("%s", prompt); +} + +TERMINAL_ACTION(terminal_action_default) +{ + char str[2] = { c, 0 }; + + if (!isprint(c)) + /* + * TODO: remove this print once all meaningful sequences + * are identified + */ + printf("char-0x%02x\n", c); + else if (line_buf_ix < LINE_BUF_MAX - 1) + terminal_insert_into_command_line(str); +} + +/* Callback to call when user hit enter during prompt for */ +static line_callback prompt_callback; + +static KeyAction normal_actions[] = { + { 0, terminal_action_default }, + { KEY_LEFT, terminal_action_left }, + { KEY_RIGHT, terminal_action_right }, + { KEY_HOME, terminal_action_home }, + { KEY_END, terminal_action_end }, + { KEY_DELETE, terminal_action_del }, + { KEY_CLEFT, terminal_action_word_left }, + { KEY_CRIGHT, terminal_action_word_right }, + { KEY_SUP, terminal_action_history_begin }, + { KEY_SDOWN, terminal_action_history_end }, + { KEY_UP, terminal_action_history_up }, + { KEY_DOWN, terminal_action_history_down }, + { '\t', terminal_action_tab }, + { KEY_BACKSPACE, terminal_action_backspace }, + { KEY_M_n, terminal_action_find_history_forward }, + { KEY_M_p, terminal_action_find_history_backward }, + { KEY_C_C, terminal_action_ctrl_c }, + { KEY_C_D, terminal_action_ctrl_d }, + { KEY_C_L, terminal_action_clear_screen }, + { '\r', terminal_action_enter }, + { '\n', terminal_action_enter }, + { 0, NULL }, +}; + +TERMINAL_ACTION(terminal_action_answer) +{ + putchar(c); + + terminal_set_actions(normal_actions); + /* Restore default prompt */ + current_prompt = prompt_buf; + + /* No prompt for prints */ + prompt = noprompt; + line_buf_ix = 0; + line_len = 0; + /* Call user function with what was typed */ + prompt_callback(line_buf); + + line_buf[0] = 0; + /* promot_callback could change current_prompt */ + prompt = current_prompt; + + printf("%s", prompt); +} + +TERMINAL_ACTION(terminal_action_prompt_ctrl_c) +{ + printf("^C\n"); + line_buf_ix = 0; + line_len = 0; + line_buf[0] = 0; + + current_prompt = prompt_buf; + prompt = current_prompt; + terminal_set_actions(normal_actions); + + printf("%s", prompt); +} + +static KeyAction prompt_actions[] = { + { 0, terminal_action_default }, + { KEY_LEFT, terminal_action_left }, + { KEY_RIGHT, terminal_action_right }, + { KEY_HOME, terminal_action_home }, + { KEY_END, terminal_action_end }, + { KEY_DELETE, terminal_action_del }, + { KEY_CLEFT, terminal_action_word_left }, + { KEY_CRIGHT, terminal_action_word_right }, + { KEY_BACKSPACE, terminal_action_backspace }, + { KEY_C_C, terminal_action_prompt_ctrl_c }, + { KEY_C_D, terminal_action_ctrl_d }, + { '\r', terminal_action_answer }, + { '\n', terminal_action_answer }, + { 0, NULL }, +}; + +void terminal_process_char(int c, line_callback process_line) +{ + KeyAction *a; + + c = terminal_convert_sequence(c); + + /* Get action for this key */ + a = terminal_get_action(c); + + /* No action found, get default one */ + if (a == NULL) + a = ¤t_actions[0]; + + a->func(c, process_line); + fflush(stdout); +} + +void terminal_prompt_for(const char *s, line_callback process_line) +{ + current_prompt = s; + if (prompt != noprompt) { + prompt = s; + terminal_clear_line(); + } + prompt_callback = process_line; + terminal_set_actions(prompt_actions); +} + +static struct termios origianl_tios; + +static void terminal_cleanup(void) +{ + tcsetattr(0, TCSANOW, &origianl_tios); +} + +void terminal_setup(void) +{ + struct termios tios; + + terminal_set_actions(normal_actions); + + tcgetattr(0, &origianl_tios); + tios = origianl_tios; + + /* + * Turn off echo since all editing is done by hand, + * Ctrl-c handled internally + */ + tios.c_lflag &= ~(ICANON | ECHO | BRKINT | IGNBRK); + tcsetattr(0, TCSANOW, &tios); + + /* Restore terminal at exit */ + atexit(terminal_cleanup); + + printf("%s", prompt); + fflush(stdout); +} diff --git a/android/client/terminal.h b/android/client/terminal.h new file mode 100644 index 0000000..0e63936 --- /dev/null +++ b/android/client/terminal.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +/* size of supported line */ +#define LINE_BUF_MAX 1024 + +enum key_codes { + KEY_BACKSPACE = 0x7F, + KEY_INSERT = 1000, /* arbitrary value */ + KEY_DELETE, + KEY_HOME, + KEY_END, + KEY_PGUP, + KEY_PGDOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_UP, + KEY_DOWN, + KEY_CLEFT, + KEY_CRIGHT, + KEY_CUP, + KEY_CDOWN, + KEY_SLEFT, + KEY_SRIGHT, + KEY_SUP, + KEY_SDOWN, + KEY_MLEFT, + KEY_MRIGHT, + KEY_MUP, + KEY_MDOWN, + KEY_STAB, + KEY_M_p, + KEY_M_n +}; + +typedef void (*line_callback)(char *); + +void terminal_setup(void); +int terminal_print(const char *format, ...); +int terminal_vprint(const char *format, va_list args); +void terminal_process_char(int c, line_callback process_line); +void terminal_insert_into_command_line(const char *p); +void terminal_draw_command_line(void); +void terminal_prompt_for(const char *s, line_callback process_line); + +void process_tab(const char *line, int len); diff --git a/android/cutils/properties.h b/android/cutils/properties.h new file mode 100644 index 0000000..7951585 --- /dev/null +++ b/android/cutils/properties.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +/* property_set: returns 0 on success, < 0 on failure +*/ +static inline int property_set(const char *key, const char *value) +{ + static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; + + struct sockaddr_un addr; + char msg[256]; + int fd, len; + + fd = socket(PF_LOCAL, SOCK_DGRAM, 0); + if (fd < 0) + return -1; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); + + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(fd); + return 0; + } + + len = snprintf(msg, sizeof(msg), "%s=%s", key, value); + + if (send(fd, msg, len + 1, 0) < 0) { + close(fd); + return -1; + } + + close(fd); + + return 0; +} diff --git a/android/hal-a2dp.c b/android/hal-a2dp.c new file mode 100644 index 0000000..e9fadb7 --- /dev/null +++ b/android/hal-a2dp.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "hal-ipc.h" + +static const btav_callbacks_t *cbs = NULL; + +static bool interface_ready(void) +{ + return cbs != NULL; +} + +static void handle_conn_state(void *buf) +{ + struct hal_ev_a2dp_conn_state *ev = buf; + + if (cbs->connection_state_cb) + cbs->connection_state_cb(ev->state, + (bt_bdaddr_t *) (ev->bdaddr)); +} + +static void handle_audio_state(void *buf) +{ + struct hal_ev_a2dp_audio_state *ev = buf; + + if (cbs->audio_state_cb) + 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; + } +} + +static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_a2dp_connect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t disconnect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_a2dp_disconnect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t init(btav_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + + DBG(""); + + cbs = callbacks; + + cmd.service_id = HAL_SERVICE_ID_A2DP; + + return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static void cleanup() +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbs = NULL; + + cmd.service_id = HAL_SERVICE_ID_A2DP; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static btav_interface_t iface = { + .size = sizeof(iface), + .init = init, + .connect = a2dp_connect, + .disconnect = disconnect, + .cleanup = cleanup +}; + +btav_interface_t *bt_get_a2dp_interface() +{ + return &iface; +} diff --git a/android/hal-bluetooth.c b/android/hal-bluetooth.c new file mode 100644 index 0000000..078d537 --- /dev/null +++ b/android/hal-bluetooth.c @@ -0,0 +1,754 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "hal-ipc.h" +#include "hal-utils.h" + +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)); \ +} while (0) + +static void handle_adapter_state_changed(void *buf) +{ + struct hal_ev_adapter_state_changed *ev = buf; + + DBG("state: %s", bt_state_t2str(ev->state)); + + if (bt_hal_cbacks->adapter_state_changed_cb) + bt_hal_cbacks->adapter_state_changed_cb(ev->state); +} + +static void adapter_props_to_hal(bt_property_t *send_props, + struct hal_property *hal_prop, + uint8_t num_props, void *buff_end) +{ + void *p = hal_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"); + exit(EXIT_FAILURE); + } + + send_props[i].type = hal_prop->type; + + switch (hal_prop->type) { + case HAL_PROP_ADAPTER_TYPE: + create_enum_prop(send_props[i], hal_prop, + bt_device_type_t); + break; + case HAL_PROP_ADAPTER_SCAN_MODE: + create_enum_prop(send_props[i], hal_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; + break; + } + + DBG("prop[%d]: %s", i, btproperty2str(&send_props[i])); + } +} + +static void adapter_hal_props_cleanup(bt_property_t *props, uint8_t num) +{ + uint8_t i; + + 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; + } + } +} + +static void device_props_to_hal(bt_property_t *send_props, + struct hal_property *hal_prop, + uint8_t num_props, void *buff_end) +{ + void *p = hal_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"); + exit(EXIT_FAILURE); + } + + send_props[i].type = hal_prop->type; + + switch (hal_prop->type) { + case HAL_PROP_DEVICE_TYPE: + create_enum_prop(send_props[i], hal_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; + break; + } + + p += sizeof(*hal_prop) + hal_prop->len; + hal_prop = p; + + 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; + + for (i = 0; i < num; i++) { + switch (props[i].type) { + case HAL_PROP_DEVICE_TYPE: + free(props[i].val); + break; + default: + break; + } + } +} + +static void handle_adapter_props_changed(void *buf, uint16_t len) +{ + struct hal_ev_adapter_props_changed *ev = buf; + bt_property_t props[ev->num_props]; + + DBG(""); + + if (!bt_hal_cbacks->adapter_properties_cb) + return; + + adapter_props_to_hal(props, ev->props, ev->num_props, buf + 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) +{ + struct hal_ev_bond_state_changed *ev = buf; + bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; + + DBG("state %u", ev->state); + + if (bt_hal_cbacks->bond_state_changed_cb) + bt_hal_cbacks->bond_state_changed_cb(ev->status, addr, + ev->state); +} + +static void handle_pin_request(void *buf) +{ + struct hal_ev_pin_request *ev = buf; + /* Those are declared as packed, so it's safe to assign pointers */ + bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; + bt_bdname_t *name = (bt_bdname_t *) ev->name; + + DBG(""); + + if (bt_hal_cbacks->pin_request_cb) + bt_hal_cbacks->pin_request_cb(addr, name, ev->class_of_dev); +} + +static void handle_ssp_request(void *buf) +{ + struct hal_ev_ssp_request *ev = buf; + /* Those are declared as packed, so it's safe to assign pointers */ + bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; + bt_bdname_t *name = (bt_bdname_t *) ev->name; + + DBG(""); + + if (bt_hal_cbacks->ssp_request_cb) + bt_hal_cbacks->ssp_request_cb(addr, name, ev->class_of_dev, + ev->pairing_variant, + ev->passkey); +} + +void bt_thread_associate(void) +{ + if (bt_hal_cbacks->thread_evt_cb) + bt_hal_cbacks->thread_evt_cb(ASSOCIATE_JVM); +} + +void bt_thread_disassociate(void) +{ + if (bt_hal_cbacks->thread_evt_cb) + bt_hal_cbacks->thread_evt_cb(DISASSOCIATE_JVM); +} + +static bool interface_ready(void) +{ + return bt_hal_cbacks != NULL; +} + +static void handle_discovery_state_changed(void *buf) +{ + struct hal_ev_discovery_state_changed *ev = buf; + + DBG(""); + + if (bt_hal_cbacks->discovery_state_changed_cb) + bt_hal_cbacks->discovery_state_changed_cb(ev->state); +} + +static void handle_device_found(void *buf, uint16_t len) +{ + struct hal_ev_device_found *ev = buf; + bt_property_t props[ev->num_props]; + + DBG(""); + + if (!bt_hal_cbacks->device_found_cb) + return; + + device_props_to_hal(props, ev->props, ev->num_props, buf + 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) +{ + struct hal_ev_remote_device_props *ev = buf; + bt_property_t props[ev->num_props]; + + DBG(""); + + if (!bt_hal_cbacks->remote_device_properties_cb) + return; + + device_props_to_hal(props, ev->props, ev->num_props, buf + 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) +{ + struct hal_ev_acl_state_changed *ev = buf; + bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; + + DBG("state %u", ev->state); + + if (bt_hal_cbacks->acl_state_changed_cb) + bt_hal_cbacks->acl_state_changed_cb(ev->status, addr, + ev->state); +} + +/* will be called from notification thread context */ +void bt_notify_adapter(uint8_t opcode, void *buf, uint16_t len) +{ + if (!interface_ready()) + return; + + DBG("opcode 0x%x", opcode); + + 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; + } +} + +static int init(bt_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int status; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_SUCCESS; + + bt_hal_cbacks = callbacks; + + if (!hal_ipc_init()) { + bt_hal_cbacks = NULL; + return BT_STATUS_FAIL; + } + + cmd.service_id = HAL_SERVICE_ID_BLUETOOTH; + + status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + if (status != BT_STATUS_SUCCESS) { + error("Failed to register 'bluetooth' service"); + goto fail; + } + + cmd.service_id = HAL_SERVICE_ID_SOCK; + + status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + if (status != BT_STATUS_SUCCESS) { + error("Failed to register 'socket' service"); + goto fail; + } + + return status; + +fail: + hal_ipc_cleanup(); + bt_hal_cbacks = NULL; + return status; +} + +static int enable(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, 0, NULL, 0, + NULL, NULL); +} + +static int disable(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, 0, NULL, 0, + NULL, NULL); +} + +static void cleanup(void) +{ + DBG(""); + + if (!interface_ready()) + return; + + hal_ipc_cleanup(); + + bt_hal_cbacks = NULL; +} + +static int get_adapter_properties(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS, + 0, NULL, 0, NULL, NULL); +} + +static int get_adapter_property(bt_property_type_t type) +{ + struct hal_cmd_get_adapter_prop cmd; + + DBG("prop: %s (%d)", bt_property_type_t2str(type), 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; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static int set_adapter_property(const bt_property_t *property) +{ + char buf[sizeof(struct hal_cmd_set_adapter_prop) + property->len]; + struct hal_cmd_set_adapter_prop *cmd = (void *) buf; + + DBG("prop: %s", btproperty2str(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); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, + sizeof(buf), cmd, 0, NULL, NULL); +} + +static int get_remote_device_properties(bt_bdaddr_t *remote_addr) +{ + DBG("bdaddr: %s", bdaddr2str(remote_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return BT_STATUS_UNSUPPORTED; +} + +static int get_remote_device_property(bt_bdaddr_t *remote_addr, + bt_property_type_t type) +{ + 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; +} + +static int set_remote_device_property(bt_bdaddr_t *remote_addr, + const bt_property_t *property) +{ + DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr), + btproperty2str(property)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return BT_STATUS_UNSUPPORTED; +} + +static int get_remote_service_record(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid) +{ + DBG("bdaddr: %s", bdaddr2str(remote_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return BT_STATUS_UNSUPPORTED; +} + +static int get_remote_services(bt_bdaddr_t *remote_addr) +{ + struct hal_cmd_get_remote_services cmd; + + DBG("bdaddr: %s", bdaddr2str(remote_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICES, sizeof(cmd), &cmd, 0, + NULL, NULL); +} + +static int start_discovery(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_START_DISCOVERY, 0, NULL, 0, + NULL, NULL); +} + +static int cancel_discovery(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CANCEL_DISCOVERY, 0, NULL, 0, + NULL, NULL); +} + +static int create_bond(const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_create_bond cmd; + + DBG("bdaddr: %s", bdaddr2str(bd_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static int cancel_bond(const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_cancel_bond cmd; + + DBG("bdaddr: %s", bdaddr2str(bd_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static int remove_bond(const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_remove_bond cmd; + + DBG("bdaddr: %s", bdaddr2str(bd_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static int pin_reply(const bt_bdaddr_t *bd_addr, uint8_t accept, + uint8_t pin_len, bt_pin_code_t *pin_code) +{ + struct hal_cmd_pin_reply cmd; + + DBG("bdaddr: %s", bdaddr2str(bd_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + cmd.accept = accept; + cmd.pin_len = pin_len; + memcpy(cmd.pin_code, pin_code, sizeof(cmd.pin_code)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static int ssp_reply(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant, + uint8_t accept, uint32_t passkey) +{ + struct hal_cmd_ssp_reply cmd; + + DBG("bdaddr: %s", bdaddr2str(bd_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + /* type match IPC type */ + cmd.ssp_variant = variant; + cmd.accept = accept; + cmd.passkey = passkey; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static const void *get_profile_interface(const char *profile_id) +{ + DBG("%s: %s", __func__, profile_id); + + if (!interface_ready()) + return NULL; + + if (!strcmp(profile_id, BT_PROFILE_SOCKETS_ID)) + return bt_get_sock_interface(); + + if (!strcmp(profile_id, BT_PROFILE_HIDHOST_ID)) + return bt_get_hidhost_interface(); + + if (!strcmp(profile_id, BT_PROFILE_PAN_ID)) + return bt_get_pan_interface(); + + if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID)) + return bt_get_a2dp_interface(); + + return NULL; +} + +static int dut_mode_configure(uint8_t enable) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return BT_STATUS_UNSUPPORTED; +} + +static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t len) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return BT_STATUS_UNSUPPORTED; +} + +static const bt_interface_t bluetooth_if = { + .size = sizeof(bt_interface_t), + .init = init, + .enable = enable, + .disable = disable, + .cleanup = cleanup, + .get_adapter_properties = get_adapter_properties, + .get_adapter_property = get_adapter_property, + .set_adapter_property = set_adapter_property, + .get_remote_device_properties = get_remote_device_properties, + .get_remote_device_property = get_remote_device_property, + .set_remote_device_property = set_remote_device_property, + .get_remote_service_record = get_remote_service_record, + .get_remote_services = get_remote_services, + .start_discovery = start_discovery, + .cancel_discovery = cancel_discovery, + .create_bond = create_bond, + .remove_bond = remove_bond, + .cancel_bond = cancel_bond, + .pin_reply = pin_reply, + .ssp_reply = ssp_reply, + .get_profile_interface = get_profile_interface, + .dut_mode_configure = dut_mode_configure, + .dut_mode_send = dut_mode_send +}; + +static const bt_interface_t *get_bluetooth_interface(void) +{ + DBG(""); + + return &bluetooth_if; +} + +static int close_bluetooth(struct hw_device_t *device) +{ + DBG(""); + + cleanup(); + + return 0; +} + +static int open_bluetooth(const struct hw_module_t *module, char const *name, + struct hw_device_t **device) +{ + bluetooth_device_t *dev = malloc(sizeof(bluetooth_device_t)); + + DBG(""); + + memset(dev, 0, sizeof(bluetooth_device_t)); + dev->common.tag = HARDWARE_DEVICE_TAG; + dev->common.version = 0; + dev->common.module = (struct hw_module_t *) module; + dev->common.close = close_bluetooth; + dev->get_bluetooth_interface = get_bluetooth_interface; + + *device = (struct hw_device_t *) dev; + + return 0; +} + +static struct hw_module_methods_t bluetooth_module_methods = { + .open = open_bluetooth, +}; + +struct hw_module_t HAL_MODULE_INFO_SYM = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = BT_HARDWARE_MODULE_ID, + .name = "BlueZ Bluetooth stack", + .author = "Intel Corporation", + .methods = &bluetooth_module_methods +}; diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c new file mode 100644 index 0000000..2ce17a3 --- /dev/null +++ b/android/hal-hidhost.c @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "hal-ipc.h" + +static const bthh_callbacks_t *cbacks; + +static bool interface_ready(void) +{ + return cbacks != NULL; +} + +static void handle_conn_state(void *buf) +{ + struct hal_ev_hidhost_conn_state *ev = buf; + + if (cbacks->connection_state_cb) + cbacks->connection_state_cb((bt_bdaddr_t *) ev->bdaddr, + ev->state); +} + +static void handle_info(void *buf) +{ + struct hal_ev_hidhost_info *ev = buf; + bthh_hid_info_t info; + + info.attr_mask = ev->attr; + info.sub_class = ev->subclass; + info.app_id = ev->app_id; + info.vendor_id = ev->vendor; + info.product_id = ev->product; + info.version = ev->version; + info.ctry_code = ev->country; + info.dl_len = ev->descr_len; + memcpy(info.dsc_list, ev->descr, info.dl_len); + + if (cbacks->hid_info_cb) + cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info); +} + +static void handle_proto_mode(void *buf) +{ + struct hal_ev_hidhost_proto_mode *ev = buf; + + if (cbacks->protocol_mode_cb) + cbacks->protocol_mode_cb((bt_bdaddr_t *) ev->bdaddr, + ev->status, ev->mode); +} + +static void handle_get_report(void *buf) +{ + struct hal_ev_hidhost_get_report *ev = buf; + + 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) +{ + struct hal_ev_hidhost_virtual_unplug *ev = buf; + + if (cbacks->virtual_unplug_cb) + cbacks->virtual_unplug_cb((bt_bdaddr_t *) ev->bdaddr, + 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; + } +} + +static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_hidhost_connect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t disconnect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_hidhost_disconnect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t virtual_unplug(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_hidhost_virtual_unplug cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_VIRTUAL_UNPLUG, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info) +{ + struct hal_cmd_hidhost_set_info cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + cmd.attr = hid_info.attr_mask; + cmd.subclass = hid_info.sub_class; + cmd.app_id = hid_info.app_id; + cmd.vendor = hid_info.vendor_id; + cmd.product = hid_info.product_id; + cmd.country = hid_info.ctry_code; + cmd.descr_len = hid_info.dl_len; + memcpy(cmd.descr, hid_info.dsc_list, cmd.descr_len); + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t get_protocol(bt_bdaddr_t *bd_addr, + bthh_protocol_mode_t protocol_mode) +{ + struct hal_cmd_hidhost_get_protocol cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + switch (protocol_mode) { + case BTHH_REPORT_MODE: + cmd.mode = HAL_HIDHOST_REPORT_PROTOCOL; + break; + case BTHH_BOOT_MODE: + cmd.mode = HAL_HIDHOST_BOOT_PROTOCOL; + break; + default: + return BT_STATUS_PARM_INVALID; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_PROTOCOL, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t set_protocol(bt_bdaddr_t *bd_addr, + bthh_protocol_mode_t protocol_mode) +{ + struct hal_cmd_hidhost_set_protocol cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + switch (protocol_mode) { + case BTHH_REPORT_MODE: + cmd.mode = HAL_HIDHOST_REPORT_PROTOCOL; + break; + case BTHH_BOOT_MODE: + cmd.mode = HAL_HIDHOST_BOOT_PROTOCOL; + break; + default: + return BT_STATUS_PARM_INVALID; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_PROTOCOL, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t get_report(bt_bdaddr_t *bd_addr, + bthh_report_type_t report_type, + uint8_t report_id, + int buffer_size) +{ + struct hal_cmd_hidhost_get_report cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + cmd.id = report_id; + cmd.buf_size = buffer_size; + + switch (report_type) { + case BTHH_INPUT_REPORT: + cmd.type = HAL_HIDHOST_INPUT_REPORT; + break; + case BTHH_OUTPUT_REPORT: + cmd.type = HAL_HIDHOST_OUTPUT_REPORT; + break; + case BTHH_FEATURE_REPORT: + cmd.type = HAL_HIDHOST_FEATURE_REPORT; + break; + default: + return BT_STATUS_PARM_INVALID; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t set_report(bt_bdaddr_t *bd_addr, + bthh_report_type_t report_type, + char *report) +{ + uint8_t buf[BLUEZ_HAL_MTU]; + struct hal_cmd_hidhost_set_report *cmd = (void *) buf; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr || !report) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr)); + cmd->len = strlen(report); + memcpy(cmd->data, report, cmd->len); + + switch (report_type) { + case BTHH_INPUT_REPORT: + cmd->type = HAL_HIDHOST_INPUT_REPORT; + break; + case BTHH_OUTPUT_REPORT: + cmd->type = HAL_HIDHOST_OUTPUT_REPORT; + break; + case BTHH_FEATURE_REPORT: + cmd->type = HAL_HIDHOST_FEATURE_REPORT; + break; + default: + return BT_STATUS_PARM_INVALID; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, + sizeof(*cmd) + cmd->len, buf, 0, NULL, NULL); +} + +static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data) +{ + uint8_t buf[BLUEZ_HAL_MTU]; + struct hal_cmd_hidhost_send_data *cmd = (void *) buf; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr || !data) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr)); + cmd->len = strlen(data); + memcpy(cmd->data, data, cmd->len); + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, + sizeof(*cmd) + cmd->len, buf, 0, NULL, NULL); +} + +static bt_status_t init(bthh_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + + DBG(""); + + /* store reference to user callbacks */ + cbacks = callbacks; + + cmd.service_id = HAL_SERVICE_ID_HIDHOST; + + return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static void cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbacks = NULL; + + cmd.service_id = HAL_SERVICE_ID_HIDHOST; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bthh_interface_t hidhost_if = { + .size = sizeof(hidhost_if), + .init = init, + .connect = hidhost_connect, + .disconnect = disconnect, + .virtual_unplug = virtual_unplug, + .set_info = set_info, + .get_protocol = get_protocol, + .set_protocol = set_protocol, + .get_report = get_report, + .set_report = set_report, + .send_data = send_data, + .cleanup = cleanup +}; + +bthh_interface_t *bt_get_hidhost_interface(void) +{ + return &hidhost_if; +} diff --git a/android/hal-ipc-api.txt b/android/hal-ipc-api.txt index 9a8b770..57f4c13 100644 --- a/android/hal-ipc-api.txt +++ b/android/hal-ipc-api.txt @@ -117,7 +117,9 @@ Core Service (ID 0) Opcode 0x00 - Error response - Response parameters: Error (1 octet) + Response parameters: Status (1 octet) + + Valid status values: 0x01 Failed Opcode 0x01 - Register module command/response @@ -279,6 +281,11 @@ Commands and responses: Passkey (4 octets) Response parameters: + Valid SSP variant values: 0x00 = Passkey Confirmation + 0x01 = Passkey Entry + 0x02 = Consent (for Just Works) + 0x03 = Passkey Notification + In case of an error, the error response will be returned. Opcode 0x12 - DUT Mode Configure command/response @@ -312,6 +319,9 @@ Notifications: Notifications parameters: State (1 octect) + Valid state values: 0x00 = Off + 0x01 = On + Opcode 0x82 - Adapter Properties Changed notification Notification parameters: Status (1 octect) @@ -344,13 +354,13 @@ Notifications: Notification parameters: Remote address (6 octets) Remote name (249 octets) - Class of device (3 octets) + Class of device (4 octets) Opcode 0x87 - SSP Request notification Notification parameters: Remote address (6 octets) Remote name (249 octets) - Class of device (3 octets) + Class of device (4 octets) Pairing variant (1 octet) Passkey (4 octets) @@ -360,6 +370,10 @@ Notifications: Remote address (6 octets) Bond state (1 octet) + Valid bond state values: 0x00 = None + 0x01 = Bonding + 0x02 = Bonded + Opcode 0x89 - ACL State Changed notification Notification parameters: Status (1 octect) @@ -538,7 +552,9 @@ Commands and responses: Command parameters: Remote address (6 octets) Report type (1 octet) - ... + Report length (2 octets) + Report data (Report length) + Response parameters: Valid report types: 0x01 = Input @@ -550,7 +566,9 @@ Commands and responses: Opcode 0x09 - Send Data command/response Command parameters: Remote address (6 octets) - ... + Data length (2 octets) + Data (Data length) + Response parameters: In case of an error, the error response will be returned. @@ -560,6 +578,7 @@ Notifications: Opcode 0x81 - Connection State notification Notification parameters: Remote address (6 octets) + Connection State (1 octets) Valid connection states: 0x00 = Connected 0x01 = Connecting @@ -595,20 +614,14 @@ Notifications: 0x01 = Boot 0xff = Unsupported - Opcode 0x84 - Idle Time notification - - Notification parameters: Remote address (6 octets) - Status (1 octet) - Idle time (2 octets) - - Opcode 0x85 - Get Report notification + Opcode 0x84 - Get Report notification Notification parameters: Remote address (6 octets) Status (1 octet) Report length (2 octets) Report data (variable) - Opcode 0x86 - Virtual Unplug notification + Opcode 0x85 - Virtual Unplug notification Notification parameters: Remote address (6 octets) Status (1 octet) @@ -1013,7 +1026,7 @@ Notifications: Bluetooth Advanced Audio HAL (ID 6) =================================== -Android HAL name: "ad2p" (BT_PROFILE_ADVANCED_AUDIO_ID) +Android HAL name: "a2dp" (BT_PROFILE_ADVANCED_AUDIO_ID) Commands and responses: diff --git a/android/hal-ipc.c b/android/hal-ipc.c new file mode 100644 index 0000000..026e245 --- /dev/null +++ b/android/hal-ipc.c @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hal.h" +#include "hal-msg.h" +#include "hal-log.h" +#include "hal-ipc.h" + +#define CONNECT_TIMEOUT (5 * 1000) +#define SERVICE_NAME "bluetoothd" + +static int cmd_sk = -1; +static int notif_sk = -1; + +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) +{ + 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", + msg->service_id, msg->opcode); + break; + } +} + +static void *notification_handler(void *data) +{ + struct msghdr msg; + struct iovec iv; + 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; + + bt_thread_associate(); + + while (true) { + memset(&msg, 0, sizeof(msg)); + memset(buf, 0, sizeof(buf)); + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + + iv.iov_base = ev; + iv.iov_len = sizeof(buf); + + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + ret = recvmsg(notif_sk, &msg, 0); + if (ret < 0) { + error("Receiving notifications failed, aborting :%s", + strerror(errno)); + exit(EXIT_FAILURE); + } + + /* socket was shutdown */ + if (ret == 0) { + if (cmd_sk == -1) + break; + + error("Notification socket closed, aborting"); + 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 */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); + break; + } + } + + notification_dispatch(ev, fd); + } + + close(notif_sk); + notif_sk = -1; + + bt_thread_disassociate(); + + DBG("exit"); + + return NULL; +} + +static int accept_connection(int sk) +{ + int err; + struct pollfd pfd; + int new_sk; + + memset(&pfd, 0 , sizeof(pfd)); + pfd.fd = sk; + pfd.events = POLLIN; + + err = poll(&pfd, 1, CONNECT_TIMEOUT); + if (err < 0) { + err = errno; + error("Failed to poll: %d (%s)", err, strerror(err)); + return -1; + } + + if (err == 0) { + error("bluetoothd connect timeout"); + return -1; + } + + new_sk = accept(sk, NULL, NULL); + if (new_sk < 0) { + err = errno; + error("Failed to accept socket: %d (%s)", err, strerror(err)); + return -1; + } + + return new_sk; +} + +bool hal_ipc_init(void) +{ + struct sockaddr_un addr; + int sk; + int err; + + sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + err = errno; + error("Failed to create socket: %d (%s)", err, + strerror(err)); + return false; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = errno; + error("Failed to bind socket: %d (%s)", err, strerror(err)); + close(sk); + return false; + } + + if (listen(sk, 2) < 0) { + err = errno; + error("Failed to listen on socket: %d (%s)", err, + strerror(err)); + close(sk); + return false; + } + + /* Start Android Bluetooth daemon service */ + if (property_set("ctl.start", SERVICE_NAME) < 0) { + error("Failed to start service %s", SERVICE_NAME); + close(sk); + return false; + } + + cmd_sk = accept_connection(sk); + if (cmd_sk < 0) { + close(sk); + return false; + } + + notif_sk = accept_connection(sk); + if (notif_sk < 0) { + close(sk); + close(cmd_sk); + cmd_sk = -1; + return false; + } + + info("bluetoothd connected"); + + close(sk); + + err = pthread_create(¬if_th, NULL, notification_handler, NULL); + if (err < 0) { + notif_th = 0; + error("Failed to start notification thread: %d (%s)", -err, + strerror(-err)); + close(cmd_sk); + cmd_sk = -1; + close(notif_sk); + notif_sk = -1; + return false; + } + + return true; +} + +void hal_ipc_cleanup(void) +{ + close(cmd_sk); + cmd_sk = -1; + + shutdown(notif_sk, SHUT_RD); + + pthread_join(notif_th, NULL); + notif_th = 0; +} + +int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, + size_t *rsp_len, void *rsp, int *fd) +{ + ssize_t ret; + struct msghdr msg; + struct iovec iv[2]; + struct hal_hdr cmd; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct hal_status s; + size_t s_len = sizeof(s); + + if (cmd_sk < 0) { + error("Invalid cmd socket passed to hal_ipc_cmd, aborting"); + exit(EXIT_FAILURE); + } + + if (!rsp || !rsp_len) { + memset(&s, 0, s_len); + rsp_len = &s_len; + rsp = &s; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + cmd.service_id = service_id; + cmd.opcode = opcode; + cmd.len = len; + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = param; + iv[1].iov_len = len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + pthread_mutex_lock(&cmd_sk_mutex); + + ret = sendmsg(cmd_sk, &msg, 0); + if (ret < 0) { + error("Sending command failed, aborting :%s", strerror(errno)); + pthread_mutex_unlock(&cmd_sk_mutex); + exit(EXIT_FAILURE); + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = rsp; + iv[1].iov_len = *rsp_len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + if (fd) { + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + } + + ret = recvmsg(cmd_sk, &msg, 0); + if (ret < 0) { + error("Receiving command response failed, aborting :%s", + strerror(errno)); + pthread_mutex_unlock(&cmd_sk_mutex); + exit(EXIT_FAILURE); + } + + pthread_mutex_unlock(&cmd_sk_mutex); + + if (ret < (ssize_t) sizeof(cmd)) { + error("Too small response received(%zd bytes), aborting", ret); + exit(EXIT_FAILURE); + } + + if (cmd.service_id != service_id) { + error("Invalid service id (%u vs %u), aborting", + cmd.service_id, service_id); + exit(EXIT_FAILURE); + } + + if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) { + error("Malformed response received(%zd bytes), aborting", ret); + exit(EXIT_FAILURE); + } + + if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) { + error("Invalid opcode received (%u vs %u), aborting", + cmd.opcode, opcode); + exit(EXIT_FAILURE); + } + + if (cmd.opcode == HAL_OP_STATUS) { + struct hal_status *s = rsp; + return s->code; + } + + /* Receive auxiliary data in msg */ + if (fd) { + struct cmsghdr *cmsg; + + *fd = -1; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); + break; + } + } + } + + if (rsp_len) + *rsp_len = cmd.len; + + return BT_STATUS_SUCCESS; +} diff --git a/android/hal-ipc.h b/android/hal-ipc.h new file mode 100644 index 0000000..ea53e1c --- /dev/null +++ b/android/hal-ipc.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +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); diff --git a/android/hal-log.h b/android/hal-log.h new file mode 100644 index 0000000..9bd024d --- /dev/null +++ b/android/hal-log.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#define LOG_TAG "BlueZ" + +#ifdef __BIONIC__ +#include +#else +#include +#define LOG_INFO " I" +#define LOG_WARN " W" +#define LOG_ERROR " E" +#define LOG_DEBUG " D" +#define ALOG(pri, tag, fmt, arg...) printf(tag pri": " fmt"\n", ##arg) +#endif + +#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg) +#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg) +#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg) +#define DBG(fmt, arg...) ALOG(LOG_DEBUG, LOG_TAG, "%s:%s() "fmt, __FILE__, \ + __func__, ##arg) diff --git a/android/hal-msg.h b/android/hal-msg.h new file mode 100644 index 0000000..44fd5c8 --- /dev/null +++ b/android/hal-msg.h @@ -0,0 +1,548 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#define BLUEZ_HAL_MTU 1024 + +static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket"; + +struct hal_hdr { + uint8_t service_id; + uint8_t opcode; + uint16_t len; + uint8_t payload[0]; +} __attribute__((packed)); + +#define HAL_MINIMUM_EVENT 0x81 + +#define HAL_SERVICE_ID_CORE 0 +#define HAL_SERVICE_ID_BLUETOOTH 1 +#define HAL_SERVICE_ID_SOCK 2 +#define HAL_SERVICE_ID_HIDHOST 3 +#define HAL_SERVICE_ID_PAN 4 +#define HAL_SERVICE_ID_HANDSFREE 5 +#define HAL_SERVICE_ID_A2DP 6 +#define HAL_SERVICE_ID_HEALTH 7 +#define HAL_SERVICE_ID_AVRCP 8 +#define HAL_SERVICE_ID_GATT 9 + +#define HAL_SERVICE_ID_MAX HAL_SERVICE_ID_GATT + +/* Core Service */ + +#define HAL_STATUS_SUCCESS 0x00 +#define HAL_STATUS_FAILED 0x01 +#define HAL_STATUS_NOT_READY 0x02 +#define HAL_STATUS_NOMEM 0x03 +#define HAL_STATUS_BUSY 0x04 +#define HAL_STATUS_DONE 0x05 +#define HAL_STATUS_UNSUPPORTED 0x06 +#define HAL_STATUS_INVALID 0x07 +#define HAL_STATUS_UNHANDLED 0x08 +#define HAL_STATUS_AUTH_FAILURE 0x09 +#define HAL_STATUS_REMOTE_DEVICE_DOWN 0x0a + +#define HAL_OP_STATUS 0x00 +struct hal_status { + uint8_t code; +} __attribute__((packed)); + +#define HAL_OP_REGISTER_MODULE 0x01 +struct hal_cmd_register_module { + uint8_t service_id; +} __attribute__((packed)); + +#define HAL_OP_UNREGISTER_MODULE 0x02 +struct hal_cmd_unregister_module { + uint8_t service_id; +} __attribute__((packed)); + +/* Bluetooth Core HAL API */ + +#define HAL_OP_ENABLE 0x01 + +#define HAL_OP_DISABLE 0x02 + +#define HAL_OP_GET_ADAPTER_PROPS 0x03 + +#define HAL_OP_GET_ADAPTER_PROP 0x04 +struct hal_cmd_get_adapter_prop { + uint8_t type; +} __attribute__((packed)); + +#define HAL_MAX_NAME_LENGTH 249 + +#define HAL_PROP_ADAPTER_NAME 0x01 +#define HAL_PROP_ADAPTER_ADDR 0x02 +#define HAL_PROP_ADAPTER_UUIDS 0x03 +#define HAL_PROP_ADAPTER_CLASS 0x04 +#define HAL_PROP_ADAPTER_TYPE 0x05 +#define HAL_PROP_ADAPTER_SERVICE_REC 0x06 +#define HAL_PROP_ADAPTER_SCAN_MODE 0x07 +#define HAL_PROP_ADAPTER_BONDED_DEVICES 0x08 +#define HAL_PROP_ADAPTER_DISC_TIMEOUT 0x09 + +#define HAL_PROP_DEVICE_NAME 0x01 +#define HAL_PROP_DEVICE_ADDR 0x02 +#define HAL_PROP_DEVICE_UUIDS 0x03 +#define HAL_PROP_DEVICE_CLASS 0x04 +#define HAL_PROP_DEVICE_TYPE 0x05 +#define HAL_PROP_DEVICE_SERVICE_REC 0x06 +#define HAL_PROP_DEVICE_FRIENDLY_NAME 0x0a +#define HAL_PROP_DEVICE_RSSI 0x0b +#define HAL_PROP_DEVICE_VERSION_INFO 0x0c +#define HAL_PROP_DEVICE_TIMESTAMP 0xFF + +#define HAL_ADAPTER_SCAN_MODE_NONE 0x00 +#define HAL_ADAPTER_SCAN_MODE_CONN 0x01 +#define HAL_ADAPTER_SCAN_MODE_CONN_DISC 0x02 + +#define HAL_OP_SET_ADAPTER_PROP 0x05 +struct hal_cmd_set_adapter_prop { + uint8_t type; + uint16_t len; + uint8_t val[0]; +} __attribute__((packed)); + +#define HAL_OP_GET_REMOTE_DEVICE_PROPS 0x06 +struct hal_cmd_get_remote_device_props { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_GET_REMOTE_DEVICE_PROP 0x07 +struct hal_cmd_get_remote_device_prop { + uint8_t bdaddr[6]; + uint8_t type; +} __attribute__((packed)); + +#define HAL_OP_SET_REMOTE_DEVICE_PROP 0x08 +struct hal_cmd_set_remote_device_prop { + uint8_t bdaddr[6]; + uint8_t type; + uint16_t len; + uint8_t val[0]; +} __attribute__((packed)); + +#define HAL_OP_GET_REMOTE_SERVICE_REC 0x09 +struct hal_cmd_get_remote_service_rec { + uint8_t bdaddr[6]; + uint8_t uuid[16]; +} __attribute__((packed)); + +#define HAL_OP_GET_REMOTE_SERVICES 0x0a +struct hal_cmd_get_remote_services { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_START_DISCOVERY 0x0b + +#define HAL_OP_CANCEL_DISCOVERY 0x0c + +#define HAL_OP_CREATE_BOND 0x0d +struct hal_cmd_create_bond { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_REMOVE_BOND 0x0e +struct hal_cmd_remove_bond { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_CANCEL_BOND 0x0f +struct hal_cmd_cancel_bond { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_PIN_REPLY 0x10 +struct hal_cmd_pin_reply { + uint8_t bdaddr[6]; + uint8_t accept; + uint8_t pin_len; + uint8_t pin_code[16]; +} __attribute__((packed)); + +#define HAL_SSP_VARIANT_CONFIRM 0x00 +#define HAL_SSP_VARIANT_ENTRY 0x01 +#define HAL_SSP_VARIANT_CONSENT 0x02 +#define HAL_SSP_VARIANT_NOTIF 0x03 + +#define HAL_OP_SSP_REPLY 0x11 +struct hal_cmd_ssp_reply { + uint8_t bdaddr[6]; + uint8_t ssp_variant; + uint8_t accept; + uint32_t passkey; +} __attribute__((packed)); + +#define HAL_OP_DUT_MODE_CONF 0x12 +struct hal_cmd_dut_mode_conf { + uint8_t enable; +} __attribute__((packed)); + +#define HAL_OP_DUT_MODE_SEND 0x13 +struct hal_cmd_dut_mode_send { + uint16_t opcode; + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_OP_LE_TEST_MODE 0x14 +struct hal_cmd_le_test_mode { + uint16_t opcode; + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +/* Bluetooth Socket HAL api */ + +#define HAL_OP_SOCK_LISTEN 0x01 +struct hal_cmd_sock_listen { + uint8_t type; + uint8_t name[256]; + uint8_t uuid[16]; + uint16_t channel; + uint8_t flags; +} __attribute__((packed)); + +#define HAL_OP_SOCK_CONNECT 0x02 +struct hal_cmd_sock_connect { + uint8_t bdaddr[6]; + uint8_t type; + uint8_t uuid[16]; + uint16_t channel; + uint8_t flags; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_CONNECT 0x01 +struct hal_cmd_hidhost_connect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_DISCONNECT 0x02 +struct hal_cmd_hidhost_disconnect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_VIRTUAL_UNPLUG 0x03 +struct hal_cmd_hidhost_virtual_unplug { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_SET_INFO 0x04 +struct hal_cmd_hidhost_set_info { + uint8_t bdaddr[6]; + uint8_t attr; + uint8_t subclass; + uint8_t app_id; + uint16_t vendor; + uint16_t product; + uint16_t country; + uint16_t descr_len; + uint8_t descr[0]; +} __attribute__((packed)); + +#define HAL_HIDHOST_REPORT_PROTOCOL 0x00 +#define HAL_HIDHOST_BOOT_PROTOCOL 0x01 +#define HAL_HIDHOST_UNSUPPORTED_PROTOCOL 0xff + +#define HAL_OP_HIDHOST_GET_PROTOCOL 0x05 +struct hal_cmd_hidhost_get_protocol { + uint8_t bdaddr[6]; + uint8_t mode; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_SET_PROTOCOL 0x06 +struct hal_cmd_hidhost_set_protocol { + uint8_t bdaddr[6]; + uint8_t mode; +} __attribute__((packed)); + +#define HAL_HIDHOST_INPUT_REPORT 0x01 +#define HAL_HIDHOST_OUTPUT_REPORT 0x02 +#define HAL_HIDHOST_FEATURE_REPORT 0x03 + +#define HAL_OP_HIDHOST_GET_REPORT 0x07 +struct hal_cmd_hidhost_get_report { + uint8_t bdaddr[6]; + uint8_t type; + uint8_t id; + uint16_t buf_size; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_SET_REPORT 0x08 +struct hal_cmd_hidhost_set_report { + uint8_t bdaddr[6]; + uint8_t type; + uint16_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_SEND_DATA 0x09 +struct hal_cmd_hidhost_send_data { + uint8_t bdaddr[6]; + uint16_t len; + uint8_t data[0]; +} __attribute__((packed)); + +/* a2dp HAL API */ + +#define HAL_OP_A2DP_CONNECT 0x01 +struct hal_cmd_a2dp_connect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_A2DP_DISCONNECT 0x02 +struct hal_cmd_a2dp_disconnect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +/* PAN HAL API */ + +/* PAN Roles */ +#define HAL_PAN_ROLE_NONE 0x00 +#define HAL_PAN_ROLE_NAP 0x01 +#define HAL_PAN_ROLE_PANU 0x02 + +/* PAN Control states */ +#define HAL_PAN_CTRL_ENABLED 0x00 +#define HAL_PAN_CTRL_DISABLED 0x01 + +/* PAN Connection states */ +#define HAL_PAN_STATE_CONNECTED 0x00 +#define HAL_PAN_STATE_CONNECTING 0x01 +#define HAL_PAN_STATE_DISCONNECTED 0x02 +#define HAL_PAN_STATE_DISCONNECTING 0x03 + +/* PAN status values */ +#define HAL_PAN_STATUS_FAIL 0x01 +#define HAL_PAN_STATUS_NOT_READY 0x02 +#define HAL_PAN_STATUS_NO_MEMORY 0x03 +#define HAL_PAN_STATUS_BUSY 0x04 +#define HAL_PAN_STATUS_DONE 0x05 +#define HAL_PAN_STATUS_UNSUPORTED 0x06 +#define HAL_PAN_STATUS_INVAL 0x07 +#define HAL_PAN_STATUS_UNHANDLED 0x08 +#define HAL_PAN_STATUS_AUTH_FAILED 0x09 +#define HAL_PAN_STATUS_DEVICE_DOWN 0x0A + +#define HAL_OP_PAN_ENABLE 0x01 +struct hal_cmd_pan_enable { + uint8_t local_role; +} __attribute__((packed)); + +#define HAL_OP_PAN_GET_ROLE 0x02 +struct hal_rsp_pan_get_role { + uint8_t local_role; +} __attribute__((packed)); + +#define HAL_OP_PAN_CONNECT 0x03 +struct hal_cmd_pan_connect { + uint8_t bdaddr[6]; + uint8_t local_role; + uint8_t remote_role; +} __attribute__((packed)); + +#define HAL_OP_PAN_DISCONNECT 0x04 +struct hal_cmd_pan_disconnect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +/* Notifications and confirmations */ + + +#define HAL_POWER_OFF 0x00 +#define HAL_POWER_ON 0x01 + +#define HAL_EV_ADAPTER_STATE_CHANGED 0x81 +struct hal_ev_adapter_state_changed { + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_ADAPTER_PROPS_CHANGED 0x82 +struct hal_property { + uint8_t type; + uint16_t len; + uint8_t val[0]; +} __attribute__((packed)); +struct hal_ev_adapter_props_changed { + uint8_t status; + uint8_t num_props; + struct hal_property props[0]; +} __attribute__((packed)); + +#define HAL_EV_REMOTE_DEVICE_PROPS 0x83 +struct hal_ev_remote_device_props { + uint8_t status; + uint8_t bdaddr[6]; + uint8_t num_props; + struct hal_property props[0]; +} __attribute__((packed)); + +#define HAL_EV_DEVICE_FOUND 0x84 +struct hal_ev_device_found { + uint8_t num_props; + struct hal_property props[0]; +} __attribute__((packed)); + +#define HAL_DISCOVERY_STATE_STOPPED 0x00 +#define HAL_DISCOVERY_STATE_STARTED 0x01 + +#define HAL_EV_DISCOVERY_STATE_CHANGED 0x85 +struct hal_ev_discovery_state_changed { + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_PIN_REQUEST 0x86 +struct hal_ev_pin_request { + uint8_t bdaddr[6]; + uint8_t name[249]; + uint32_t class_of_dev; +} __attribute__((packed)); + +#define HAL_EV_SSP_REQUEST 0x87 +struct hal_ev_ssp_request { + uint8_t bdaddr[6]; + uint8_t name[249]; + uint32_t class_of_dev; + uint8_t pairing_variant; + uint32_t passkey; +} __attribute__((packed)); + +#define HAL_BOND_STATE_NONE 0 +#define HAL_BOND_STATE_BONDING 1 +#define HAL_BOND_STATE_BONDED 2 + +#define HAL_EV_BOND_STATE_CHANGED 0x88 +struct hal_ev_bond_state_changed { + uint8_t status; + uint8_t bdaddr[6]; + uint8_t state; +} __attribute__((packed)); + +#define HAL_ACL_STATE_CONNECTED 0x00 +#define HAL_ACL_STATE_DISCONNECTED 0x01 + +#define HAL_EV_ACL_STATE_CHANGED 0x89 +struct hal_ev_acl_state_changed { + uint8_t status; + uint8_t bdaddr[6]; + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_DUT_MODE_RECEIVE 0x8a +struct hal_ev_dut_mode_receive { + uint16_t opcode; + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_EV_LE_TEST_MODE 0x8b +struct hal_ev_le_test_mode { + uint8_t status; + uint16_t num_packets; +} __attribute__((packed)); + +#define HAL_HIDHOST_STATE_CONNECTED 0x00 +#define HAL_HIDHOST_STATE_CONNECTING 0x01 +#define HAL_HIDHOST_STATE_DISCONNECTED 0x02 +#define HAL_HIDHOST_STATE_DISCONNECTING 0x03 +#define HAL_HIDHOST_STATE_NO_HID 0x07 +#define HAL_HIDHOST_STATE_FAILED 0x08 +#define HAL_HIDHOST_STATE_UNKNOWN 0x09 + +#define HAL_EV_HIDHOST_CONN_STATE 0x81 +struct hal_ev_hidhost_conn_state { + uint8_t bdaddr[6]; + uint8_t state; +} __attribute__((packed)); + +#define HAL_HIDHOST_STATUS_OK 0x00 +#define HAL_HIDHOST_GENERAL_ERROR 0x06 + +#define HAL_EV_HIDHOST_INFO 0x82 +struct hal_ev_hidhost_info { + uint8_t bdaddr[6]; + uint8_t attr; + uint8_t subclass; + uint8_t app_id; + uint16_t vendor; + uint16_t product; + uint16_t version; + uint8_t country; + uint16_t descr_len; + uint8_t descr[884]; +} __attribute__((packed)); + +#define HAL_EV_HIDHOST_PROTO_MODE 0x83 +struct hal_ev_hidhost_proto_mode { + uint8_t bdaddr[6]; + uint8_t status; + uint8_t mode; +} __attribute__((packed)); + +#define HAL_EV_HIDHOST_GET_REPORT 0x84 +struct hal_ev_hidhost_get_report { + uint8_t bdaddr[6]; + uint8_t status; + uint16_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_EV_HIDHOST_VIRTUAL_UNPLUG 0x85 +struct hal_ev_hidhost_virtual_unplug { + uint8_t bdaddr[6]; + uint8_t status; +} __attribute__((packed)); + +#define HAL_EV_PAN_CTRL_STATE 0x81 +struct hal_ev_pan_ctrl_state { + uint8_t state; + uint8_t status; + uint8_t local_role; + uint8_t name[17]; +} __attribute__((packed)); + +#define HAL_EV_PAN_CONN_STATE 0x82 +struct hal_ev_pan_conn_state { + uint8_t state; + uint8_t status; + uint8_t bdaddr[6]; + uint8_t local_role; + uint8_t remote_role; +} __attribute__((packed)); + +#define HAL_A2DP_STATE_DISCONNECTED 0x00 +#define HAL_A2DP_STATE_CONNECTING 0x01 +#define HAL_A2DP_STATE_CONNECTED 0x02 +#define HAL_A2DP_STATE_DISCONNECTING 0x03 + +#define HAL_EV_A2DP_CONN_STATE 0x81 +struct hal_ev_a2dp_conn_state { + uint8_t state; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_EV_A2DP_AUDIO_STATE 0x82 +struct hal_ev_a2dp_audio_state { + uint8_t state; + uint8_t bdaddr[6]; +} __attribute__((packed)); diff --git a/android/hal-pan.c b/android/hal-pan.c new file mode 100644 index 0000000..2bc560e --- /dev/null +++ b/android/hal-pan.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "hal-ipc.h" + +static const btpan_callbacks_t *cbs = NULL; + +static bool interface_ready(void) +{ + return cbs != NULL; +} + +static void handle_conn_state(void *buf) +{ + struct hal_ev_pan_conn_state *ev = buf; + + if (cbs->connection_state_cb) + cbs->connection_state_cb(ev->state, ev->status, + (bt_bdaddr_t *) ev->bdaddr, + ev->local_role, ev->remote_role); +} + +static void handle_ctrl_state(void *buf) +{ + struct hal_ev_pan_ctrl_state *ev = buf; + + if (cbs->control_state_cb) + cbs->control_state_cb(ev->state, ev->status, + 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; + } +} + +static bt_status_t pan_enable(int local_role) +{ + struct hal_cmd_pan_enable cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.local_role = local_role; + + return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static int pan_get_local_role(void) +{ + struct hal_rsp_pan_get_role rsp; + size_t len = sizeof(rsp); + bt_status_t status; + + DBG(""); + + if (!interface_ready()) + return BTPAN_ROLE_NONE; + + status = hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, 0, NULL, + &len, &rsp, NULL); + if (status != BT_STATUS_SUCCESS) + return BTPAN_ROLE_NONE; + + return rsp.local_role; +} + +static bt_status_t pan_connect(const bt_bdaddr_t *bd_addr, int local_role, + int remote_role) +{ + struct hal_cmd_pan_connect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + cmd.local_role = local_role; + cmd.remote_role = remote_role; + + return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t pan_disconnect(const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_pan_disconnect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static bt_status_t pan_init(const btpan_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + + DBG(""); + + cbs = callbacks; + + cmd.service_id = HAL_SERVICE_ID_PAN; + + return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static void pan_cleanup() +{ + struct hal_cmd_register_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbs = NULL; + + cmd.service_id = HAL_SERVICE_ID_PAN; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, 0, NULL, NULL); +} + +static btpan_interface_t pan_if = { + .size = sizeof(pan_if), + .init = pan_init, + .enable = pan_enable, + .get_local_role = pan_get_local_role, + .connect = pan_connect, + .disconnect = pan_disconnect, + .cleanup = pan_cleanup +}; + +btpan_interface_t *bt_get_pan_interface() +{ + return &pan_if; +} diff --git a/android/hal-sock.c b/android/hal-sock.c new file mode 100644 index 0000000..b7bc88e --- /dev/null +++ b/android/hal-sock.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "hal-ipc.h" +#include "hal-log.h" +#include "hal-msg.h" +#include "hal-utils.h" +#include "hal.h" + +static bt_status_t sock_listen_rfcomm(const char *service_name, + const uint8_t *uuid, int chan, + int *sock, int flags) +{ + struct hal_cmd_sock_listen cmd; + + DBG(""); + + 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)); + + return hal_ipc_cmd(HAL_SERVICE_ID_SOCK, HAL_OP_SOCK_LISTEN, + sizeof(cmd), &cmd, NULL, NULL, sock); +} + +static bt_status_t sock_listen(btsock_type_t type, const char *service_name, + const uint8_t *uuid, int chan, + int *sock, int flags) +{ + if ((!uuid && chan <= 0) || !sock) { + error("Invalid params: uuid %s, chan %d, sock %p", + btuuid2str(uuid), chan, sock); + return BT_STATUS_PARM_INVALID; + } + + DBG("uuid %s chan %d sock %p type %d service_name %s", + btuuid2str(uuid), chan, sock, type, service_name); + + switch (type) { + case BTSOCK_RFCOMM: + return sock_listen_rfcomm(service_name, uuid, chan, sock, + flags); + default: + error("%s: Socket type %d not supported", __func__, type); + break; + } + + return BT_STATUS_UNSUPPORTED; +} + +static bt_status_t sock_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type, + const uint8_t *uuid, int chan, + int *sock, int flags) +{ + struct hal_cmd_sock_connect cmd; + + if ((!uuid && chan <= 0) || !bdaddr || !sock) { + error("Invalid params: bd_addr %p, uuid %s, chan %d, sock %p", + bdaddr, btuuid2str(uuid), chan, sock); + return BT_STATUS_PARM_INVALID; + } + + DBG("uuid %s chan %d sock %p type %d", btuuid2str(uuid), chan, sock, + type); + + if (type != BTSOCK_RFCOMM) { + error("Socket type %u not supported", type); + return BT_STATUS_UNSUPPORTED; + } + + cmd.flags = flags; + cmd.type = type; + cmd.channel = chan; + 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, + sizeof(cmd), &cmd, NULL, NULL, sock); +} + +static btsock_interface_t sock_if = { + sizeof(sock_if), + sock_listen, + sock_connect +}; + +btsock_interface_t *bt_get_sock_interface(void) +{ + return &sock_if; +} diff --git a/android/hal-utils.c b/android/hal-utils.c new file mode 100644 index 0000000..4f44d98 --- /dev/null +++ b/android/hal-utils.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "hal-utils.h" + +/* + * converts uuid to string + * buf should be at least 39 bytes + * + * returns string representation of uuid + */ +const char *bt_uuid_t2str(const uint8_t *uuid, char *buf) +{ + int shift = 0; + unsigned int i; + int is_bt; + + is_bt = !memcmp(&uuid[4], &BT_BASE_UUID[4], HAL_UUID_LEN - 4); + + for (i = 0; i < HAL_UUID_LEN; i++) { + if (i == 4 && is_bt) + break; + + if (i == 4 || i == 6 || i == 8 || i == 10) { + buf[i * 2 + shift] = '-'; + shift++; + } + sprintf(buf + i * 2 + shift, "%02x", uuid[i]); + } + + return buf; +} + +const char *btuuid2str(const uint8_t *uuid) +{ + static char buf[MAX_UUID_STR_LEN]; + + return bt_uuid_t2str(uuid, buf); +} + +INTMAP(bt_status_t, -1, "(unknown)") + DELEMENT(BT_STATUS_SUCCESS), + DELEMENT(BT_STATUS_FAIL), + DELEMENT(BT_STATUS_NOT_READY), + DELEMENT(BT_STATUS_NOMEM), + DELEMENT(BT_STATUS_BUSY), + DELEMENT(BT_STATUS_DONE), + DELEMENT(BT_STATUS_UNSUPPORTED), + DELEMENT(BT_STATUS_PARM_INVALID), + DELEMENT(BT_STATUS_UNHANDLED), + DELEMENT(BT_STATUS_AUTH_FAILURE), + DELEMENT(BT_STATUS_RMT_DEV_DOWN), +ENDMAP + +INTMAP(bt_state_t, -1, "(unknown)") + DELEMENT(BT_STATE_OFF), + DELEMENT(BT_STATE_ON), +ENDMAP + +INTMAP(bt_device_type_t, -1, "(unknown)") + DELEMENT(BT_DEVICE_DEVTYPE_BREDR), + DELEMENT(BT_DEVICE_DEVTYPE_BLE), + DELEMENT(BT_DEVICE_DEVTYPE_DUAL), +ENDMAP + +INTMAP(bt_scan_mode_t, -1, "(unknown)") + DELEMENT(BT_SCAN_MODE_NONE), + DELEMENT(BT_SCAN_MODE_CONNECTABLE), + DELEMENT(BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE), +ENDMAP + +INTMAP(bt_discovery_state_t, -1, "(unknown)") + DELEMENT(BT_DISCOVERY_STOPPED), + DELEMENT(BT_DISCOVERY_STARTED), +ENDMAP + +INTMAP(bt_acl_state_t, -1, "(unknown)") + DELEMENT(BT_ACL_STATE_CONNECTED), + DELEMENT(BT_ACL_STATE_DISCONNECTED), +ENDMAP + +INTMAP(bt_bond_state_t, -1, "(unknown)") + DELEMENT(BT_BOND_STATE_NONE), + DELEMENT(BT_BOND_STATE_BONDING), + DELEMENT(BT_BOND_STATE_BONDED), +ENDMAP + +INTMAP(bt_ssp_variant_t, -1, "(unknown)") + DELEMENT(BT_SSP_VARIANT_PASSKEY_CONFIRMATION), + DELEMENT(BT_SSP_VARIANT_PASSKEY_ENTRY), + DELEMENT(BT_SSP_VARIANT_CONSENT), + DELEMENT(BT_SSP_VARIANT_PASSKEY_NOTIFICATION), +ENDMAP + +INTMAP(bt_property_type_t, -1, "(unknown)") + DELEMENT(BT_PROPERTY_BDNAME), + DELEMENT(BT_PROPERTY_BDADDR), + DELEMENT(BT_PROPERTY_UUIDS), + DELEMENT(BT_PROPERTY_CLASS_OF_DEVICE), + DELEMENT(BT_PROPERTY_TYPE_OF_DEVICE), + DELEMENT(BT_PROPERTY_SERVICE_RECORD), + DELEMENT(BT_PROPERTY_ADAPTER_SCAN_MODE), + DELEMENT(BT_PROPERTY_ADAPTER_BONDED_DEVICES), + DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT), + DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME), + DELEMENT(BT_PROPERTY_REMOTE_RSSI), +#if PLATFORM_SDK_VERSION > 17 + DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO), +#endif + DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP), +ENDMAP + +INTMAP(bt_cb_thread_evt, -1, "(unknown)") + DELEMENT(ASSOCIATE_JVM), + DELEMENT(DISASSOCIATE_JVM), +ENDMAP + +/* Find first index of given value in table m */ +int int2str_findint(int v, const struct int2str m[]) +{ + int i; + + for (i = 0; m[i].str; ++i) { + if (m[i].val == v) + return i; + } + return -1; +} + +/* Find first index of given string in table m */ +int int2str_findstr(const char *str, const struct int2str m[]) +{ + int i; + + for (i = 0; m[i].str; ++i) { + if (strcmp(m[i].str, str) == 0) + return i; + } + return -1; +} + +/* + * convert bd_addr to string + * buf must be at least 18 char long + * + * returns buf + */ +const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf) +{ + const uint8_t *p = bd_addr->address; + + snprintf(buf, MAX_ADDR_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x", + p[0], p[1], p[2], p[3], p[4], p[5]); + + return buf; +} + +/* converts string to bt_bdaddr_t */ +void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr) +{ + uint8_t *p = bd_addr->address; + + sscanf(str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &p[0], &p[1], &p[2], &p[3], &p[4], &p[5]); +} + +/* converts string to uuid */ +void str2bt_uuid_t(const char *str, bt_uuid_t *uuid) +{ + int i = 0; + + memcpy(uuid, BT_BASE_UUID, sizeof(bt_uuid_t)); + + while (*str && i < (int) sizeof(bt_uuid_t)) { + while (*str == '-') + str++; + + if (sscanf(str, "%02hhx", &uuid->uu[i]) != 1) + break; + + i++; + str += 2; + } +} + +const char *enum_defines(void *v, int i) +{ + const struct int2str *m = v; + + return m[i].str != NULL ? m[i].str : NULL; +} + +const char *enum_strings(void *v, int i) +{ + const char **m = v; + + return m[i] != NULL ? m[i] : NULL; +} + +const char *enum_one_string(void *v, int i) +{ + const char *m = v; + + return (i == 0) && (m[0] != 0) ? m : NULL; +} + +const char *bdaddr2str(const bt_bdaddr_t *bd_addr) +{ + static char buf[MAX_ADDR_STR_LEN]; + + return bt_bdaddr_t2str(bd_addr, buf); +} + +const char *btproperty2str(const bt_property_t *property) +{ + static char buf[4096]; + char *p; + + p = buf + sprintf(buf, "type=%s len=%d val=", + bt_property_type_t2str(property->type), + property->len); + + switch (property->type) { + case BT_PROPERTY_BDNAME: + case BT_PROPERTY_REMOTE_FRIENDLY_NAME: + snprintf(p, property->len + 1, "%s", + ((bt_bdname_t *) property->val)->name); + break; + + case BT_PROPERTY_BDADDR: + sprintf(p, "%s", bdaddr2str((bt_bdaddr_t *) property->val)); + break; + + case BT_PROPERTY_CLASS_OF_DEVICE: + sprintf(p, "%06x", *((int *) property->val)); + break; + + case BT_PROPERTY_TYPE_OF_DEVICE: + sprintf(p, "%s", bt_device_type_t2str( + *((bt_device_type_t *) property->val))); + break; + + case BT_PROPERTY_REMOTE_RSSI: + sprintf(p, "%d", *((char *) property->val)); + break; + + case BT_PROPERTY_ADAPTER_SCAN_MODE: + sprintf(p, "%s", + bt_scan_mode_t2str(*((bt_scan_mode_t *) property->val))); + break; + + case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT: + sprintf(p, "%d", *((int *) property->val)); + break; + + case BT_PROPERTY_ADAPTER_BONDED_DEVICES: + { + int count = property->len / sizeof(bt_bdaddr_t); + char *ptr = property->val; + + strcat(p, "{"); + + while (count--) { + strcat(p, bdaddr2str((bt_bdaddr_t *) ptr)); + if (count) + strcat(p, ", "); + ptr += sizeof(bt_bdaddr_t); + } + + strcat(p, "}"); + + } + break; + + case BT_PROPERTY_UUIDS: + { + int count = property->len / sizeof(bt_uuid_t); + uint8_t *ptr = property->val; + + strcat(p, "{"); + + while (count--) { + strcat(p, btuuid2str(ptr)); + if (count) + strcat(p, ", "); + ptr += sizeof(bt_uuid_t); + } + + strcat(p, "}"); + + } + break; + + case BT_PROPERTY_SERVICE_RECORD: + { + bt_service_record_t *rec = property->val; + + sprintf(p, "{%s, %d, %s}", btuuid2str(rec->uuid.uu), + rec->channel, rec->name); + } + break; + + default: + sprintf(p, "%p", property->val); + } + + return buf; +} diff --git a/android/hal-utils.h b/android/hal-utils.h new file mode 100644 index 0000000..75de7e9 --- /dev/null +++ b/android/hal-utils.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#define MAX_UUID_STR_LEN 37 +#define HAL_UUID_LEN 16 +#define MAX_ADDR_STR_LEN 18 + +static const char BT_BASE_UUID[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb +}; + +const char *bt_uuid_t2str(const uint8_t *uuid, char *buf); +const char *btuuid2str(const uint8_t *uuid); +const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf); +void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr); +void str2bt_uuid_t(const char *str, bt_uuid_t *uuid); +const char *btproperty2str(const bt_property_t *property); +const char *bdaddr2str(const bt_bdaddr_t *bd_addr); + +/** + * Begin mapping section + * + * There are some mappings between integer values (enums) and strings + * to be presented to user. To make it easier to convert between those two + * set of macros is given. It is specially useful when we want to have + * strings that match constants from header files like: + * BT_STATUS_SUCCESS (0) and corresponding "BT_STATUS_SUCCESS" + * Example of usage: + * + * INTMAP(int, -1, "invalid") + * DELEMENT(BT_STATUS_SUCCESS) + * DELEMENT(BT_STATUS_FAIL) + * MELEMENT(123, "Some strange value") + * ENDMAP + * + * Just by doing this we have mapping table plus two functions: + * int str2int(const char *str); + * const char *int2str(int v); + * + * second argument to INTMAP specifies value to be returned from + * str2int function when there is not mapping for such number + * third argument specifies default value to be returned from int2str + * + * If same mapping is to be used in several source files put + * INTMAP in c file and DECINTMAP in h file. + * + * For mappings that are to be used in single file only + * use SINTMAP which will create the same but everything will be marked + * as static. + */ + +struct int2str { + int val; /* int value */ + const char *str; /* corresponding string */ +}; + +int int2str_findint(int v, const struct int2str m[]); +int int2str_findstr(const char *str, const struct int2str m[]); +const char *enum_defines(void *v, int i); +const char *enum_strings(void *v, int i); +const char *enum_one_string(void *v, int i); + +#define TYPE_ENUM(type) ((void *) &__##type##2str[0]) +#define DECINTMAP(type) \ +extern struct int2str __##type##2str[]; \ +const char *type##2##str(type v); \ +type str##2##type(const char *str); \ + +#define INTMAP(type, deft, defs) \ +const char *type##2##str(type v) \ +{ \ + int i = int2str_findint((int) v, __##type##2str); \ + return (i < 0) ? defs : __##type##2str[i].str; \ +} \ +type str##2##type(const char *str) \ +{ \ + int i = int2str_findstr(str, __##type##2str); \ + return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \ +} \ +struct int2str __##type##2str[] = { + +#define SINTMAP(type, deft, defs) \ +static struct int2str __##type##2str[]; \ +static inline const char *type##2##str(type v) \ +{ \ + int i = int2str_findint((int) v, __##type##2str); \ + return (i < 0) ? defs : __##type##2str[i].str; \ +} \ +static inline type str##2##type(const char *str) \ +{ \ + int i = int2str_findstr(str, __##type##2str); \ + return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \ +} \ +static struct int2str __##type##2str[] = { + +#define ENDMAP {0, NULL} }; + +/* use this to generate string from header file constant */ +#define MELEMENT(v, s) {v, s} +/* use this to have arbitrary mapping from int to string */ +#define DELEMENT(s) {s, #s} +/* End of mapping section */ + +DECINTMAP(bt_status_t); +DECINTMAP(bt_state_t); +DECINTMAP(bt_device_type_t); +DECINTMAP(bt_scan_mode_t); +DECINTMAP(bt_discovery_state_t); +DECINTMAP(bt_acl_state_t); +DECINTMAP(bt_bond_state_t); +DECINTMAP(bt_ssp_variant_t); +DECINTMAP(bt_property_type_t); +DECINTMAP(bt_cb_thread_evt); diff --git a/android/hal.h b/android/hal.h new file mode 100644 index 0000000..72090fe --- /dev/null +++ b/android/hal.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include + +btsock_interface_t *bt_get_sock_interface(void); +bthh_interface_t *bt_get_hidhost_interface(void); +btpan_interface_t *bt_get_pan_interface(void); +btav_interface_t *bt_get_a2dp_interface(void); + +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/hardware/bluetooth.h b/android/hardware/bluetooth.h new file mode 100644 index 0000000..c00a8f7 --- /dev/null +++ b/android/hardware/bluetooth.h @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BLUETOOTH_H +#define ANDROID_INCLUDE_BLUETOOTH_H + +#include +#include +#include + +#include + +__BEGIN_DECLS + +/** + * The Bluetooth Hardware Module ID + */ + +#define BT_HARDWARE_MODULE_ID "bluetooth" +#define BT_STACK_MODULE_ID "bluetooth" +#define BT_STACK_TEST_MODULE_ID "bluetooth_test" + + +/* Bluetooth profile interface IDs */ + +#define BT_PROFILE_HANDSFREE_ID "handsfree" +#define BT_PROFILE_ADVANCED_AUDIO_ID "a2dp" +#define BT_PROFILE_HEALTH_ID "health" +#define BT_PROFILE_SOCKETS_ID "socket" +#define BT_PROFILE_HIDHOST_ID "hidhost" +#define BT_PROFILE_PAN_ID "pan" + +#define BT_PROFILE_GATT_ID "gatt" +#define BT_PROFILE_AV_RC_ID "avrcp" + +/** Bluetooth Address */ +typedef struct { + uint8_t address[6]; +} __attribute__((packed))bt_bdaddr_t; + +/** Bluetooth Device Name */ +typedef struct { + uint8_t name[249]; +} __attribute__((packed))bt_bdname_t; + +/** Bluetooth Adapter Visibility Modes*/ +typedef enum { + BT_SCAN_MODE_NONE, + BT_SCAN_MODE_CONNECTABLE, + BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE +} bt_scan_mode_t; + +/** Bluetooth Adapter State */ +typedef enum { + BT_STATE_OFF, + BT_STATE_ON +} bt_state_t; + +/** Bluetooth Error Status */ +/** We need to build on this */ + +typedef enum { + BT_STATUS_SUCCESS, + BT_STATUS_FAIL, + BT_STATUS_NOT_READY, + BT_STATUS_NOMEM, + BT_STATUS_BUSY, + BT_STATUS_DONE, /* request already completed */ + BT_STATUS_UNSUPPORTED, + BT_STATUS_PARM_INVALID, + BT_STATUS_UNHANDLED, + BT_STATUS_AUTH_FAILURE, + BT_STATUS_RMT_DEV_DOWN + +} bt_status_t; + +/** Bluetooth PinKey Code */ +typedef struct { + uint8_t pin[16]; +} __attribute__((packed))bt_pin_code_t; + +/** Bluetooth Adapter Discovery state */ +typedef enum { + BT_DISCOVERY_STOPPED, + BT_DISCOVERY_STARTED +} bt_discovery_state_t; + +/** Bluetooth ACL connection state */ +typedef enum { + BT_ACL_STATE_CONNECTED, + BT_ACL_STATE_DISCONNECTED +} bt_acl_state_t; + +/** Bluetooth 128-bit UUID */ +typedef struct { + uint8_t uu[16]; +} bt_uuid_t; + +/** Bluetooth SDP service record */ +typedef struct +{ + bt_uuid_t uuid; + uint16_t channel; + char name[256]; // what's the maximum length +} bt_service_record_t; + + +/** Bluetooth Remote Version info */ +typedef struct +{ + int version; + int sub_ver; + int manufacturer; +} bt_remote_version_t; + +/* Bluetooth Adapter and Remote Device property types */ +typedef enum { + /* Properties common to both adapter and remote device */ + /** + * Description - Bluetooth Device Name + * Access mode - Adapter name can be GET/SET. Remote device can be GET + * Data type - bt_bdname_t + */ + BT_PROPERTY_BDNAME = 0x1, + /** + * Description - Bluetooth Device Address + * Access mode - Only GET. + * Data type - bt_bdaddr_t + */ + BT_PROPERTY_BDADDR, + /** + * Description - Bluetooth Service 128-bit UUIDs + * Access mode - Only GET. + * Data type - Array of bt_uuid_t (Array size inferred from property length). + */ + BT_PROPERTY_UUIDS, + /** + * Description - Bluetooth Class of Device as found in Assigned Numbers + * Access mode - Only GET. + * Data type - uint32_t. + */ + BT_PROPERTY_CLASS_OF_DEVICE, + /** + * Description - Device Type - BREDR, BLE or DUAL Mode + * Access mode - Only GET. + * Data type - bt_device_type_t + */ + BT_PROPERTY_TYPE_OF_DEVICE, + /** + * Description - Bluetooth Service Record + * Access mode - Only GET. + * Data type - bt_service_record_t + */ + BT_PROPERTY_SERVICE_RECORD, + + /* Properties unique to adapter */ + /** + * Description - Bluetooth Adapter scan mode + * Access mode - GET and SET + * Data type - bt_scan_mode_t. + */ + BT_PROPERTY_ADAPTER_SCAN_MODE, + /** + * Description - List of bonded devices + * Access mode - Only GET. + * Data type - Array of bt_bdaddr_t of the bonded remote devices + * (Array size inferred from property length). + */ + BT_PROPERTY_ADAPTER_BONDED_DEVICES, + /** + * Description - Bluetooth Adapter Discovery timeout (in seconds) + * Access mode - GET and SET + * Data type - uint32_t + */ + BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, + + /* Properties unique to remote device */ + /** + * Description - User defined friendly name of the remote device + * Access mode - GET and SET + * Data type - bt_bdname_t. + */ + BT_PROPERTY_REMOTE_FRIENDLY_NAME, + /** + * Description - RSSI value of the inquired remote device + * Access mode - Only GET. + * Data type - int32_t. + */ + BT_PROPERTY_REMOTE_RSSI, + /** + * Description - Remote version info + * Access mode - SET/GET. + * Data type - bt_remote_version_t. + */ + + BT_PROPERTY_REMOTE_VERSION_INFO, + + BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF, +} bt_property_type_t; + +/** Bluetooth Adapter Property data structure */ +typedef struct +{ + bt_property_type_t type; + int len; + void *val; +} bt_property_t; + +/** Bluetooth Device Type */ +typedef enum { + BT_DEVICE_DEVTYPE_BREDR = 0x1, + BT_DEVICE_DEVTYPE_BLE, + BT_DEVICE_DEVTYPE_DUAL +} bt_device_type_t; +/** Bluetooth Bond state */ +typedef enum { + BT_BOND_STATE_NONE, + BT_BOND_STATE_BONDING, + BT_BOND_STATE_BONDED +} bt_bond_state_t; + +/** Bluetooth SSP Bonding Variant */ +typedef enum { + BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + BT_SSP_VARIANT_PASSKEY_ENTRY, + BT_SSP_VARIANT_CONSENT, + BT_SSP_VARIANT_PASSKEY_NOTIFICATION +} bt_ssp_variant_t; + +#define BT_MAX_NUM_UUIDS 32 + +/** Bluetooth Interface callbacks */ + +/** Bluetooth Enable/Disable Callback. */ +typedef void (*adapter_state_changed_callback)(bt_state_t state); + +/** GET/SET Adapter Properties callback */ +/* TODO: For the GET/SET property APIs/callbacks, we may need a session + * identifier to associate the call with the callback. This would be needed + * whenever more than one simultaneous instance of the same adapter_type + * is get/set. + * + * If this is going to be handled in the Java framework, then we do not need + * to manage sessions here. + */ +typedef void (*adapter_properties_callback)(bt_status_t status, + int num_properties, + bt_property_t *properties); + +/** GET/SET Remote Device Properties callback */ +/** TODO: For remote device properties, do not see a need to get/set + * multiple properties - num_properties shall be 1 + */ +typedef void (*remote_device_properties_callback)(bt_status_t status, + bt_bdaddr_t *bd_addr, + int num_properties, + bt_property_t *properties); + +/** New device discovered callback */ +/** If EIR data is not present, then BD_NAME and RSSI shall be NULL and -1 + * respectively */ +typedef void (*device_found_callback)(int num_properties, + bt_property_t *properties); + +/** Discovery state changed callback */ +typedef void (*discovery_state_changed_callback)(bt_discovery_state_t state); + +/** Bluetooth Legacy PinKey Request callback */ +typedef void (*pin_request_callback)(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, uint32_t cod); + +/** Bluetooth SSP Request callback - Just Works & Numeric Comparison*/ +/** pass_key - Shall be 0 for BT_SSP_PAIRING_VARIANT_CONSENT & + * BT_SSP_PAIRING_PASSKEY_ENTRY */ +/* TODO: Passkey request callback shall not be needed for devices with display + * capability. We still need support this in the stack for completeness */ +typedef void (*ssp_request_callback)(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, + uint32_t cod, + bt_ssp_variant_t pairing_variant, + uint32_t pass_key); + +/** Bluetooth Bond state changed callback */ +/* Invoked in response to create_bond, cancel_bond or remove_bond */ +typedef void (*bond_state_changed_callback)(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, + bt_bond_state_t state); + +/** Bluetooth ACL connection state changed callback */ +typedef void (*acl_state_changed_callback)(bt_status_t status, bt_bdaddr_t *remote_bd_addr, + bt_acl_state_t state); + +typedef enum { + ASSOCIATE_JVM, + DISASSOCIATE_JVM +} bt_cb_thread_evt; + +/** Thread Associate/Disassociate JVM Callback */ +/* Callback that is invoked by the callback thread to allow upper layer to attach/detach to/from + * the JVM */ +typedef void (*callback_thread_event)(bt_cb_thread_evt evt); + +/** Bluetooth Test Mode Callback */ +/* Receive any HCI event from controller. Must be in DUT Mode for this callback to be received */ +typedef void (*dut_mode_recv_callback)(uint16_t opcode, uint8_t *buf, uint8_t len); + +/* LE Test mode callbacks +* This callback shall be invoked whenever the le_tx_test, le_rx_test or le_test_end is invoked +* The num_packets is valid only for le_test_end command */ +typedef void (*le_test_mode_callback)(bt_status_t status, uint16_t num_packets); +/** TODO: Add callbacks for Link Up/Down and other generic + * notifications/callbacks */ + +/** Bluetooth DM callback structure. */ +typedef struct { + /** set to sizeof(bt_callbacks_t) */ + size_t size; + adapter_state_changed_callback adapter_state_changed_cb; + adapter_properties_callback adapter_properties_cb; + remote_device_properties_callback remote_device_properties_cb; + device_found_callback device_found_cb; + discovery_state_changed_callback discovery_state_changed_cb; + pin_request_callback pin_request_cb; + ssp_request_callback ssp_request_cb; + bond_state_changed_callback bond_state_changed_cb; + acl_state_changed_callback acl_state_changed_cb; + callback_thread_event thread_evt_cb; + dut_mode_recv_callback dut_mode_recv_cb; + le_test_mode_callback le_test_mode_cb; +} bt_callbacks_t; + +/** NOTE: By default, no profiles are initialized at the time of init/enable. + * Whenever the application invokes the 'init' API of a profile, then one of + * the following shall occur: + * + * 1.) If Bluetooth is not enabled, then the Bluetooth core shall mark the + * profile as enabled. Subsequently, when the application invokes the + * Bluetooth 'enable', as part of the enable sequence the profile that were + * marked shall be enabled by calling appropriate stack APIs. The + * 'adapter_properties_cb' shall return the list of UUIDs of the + * enabled profiles. + * + * 2.) If Bluetooth is enabled, then the Bluetooth core shall invoke the stack + * profile API to initialize the profile and trigger a + * 'adapter_properties_cb' with the current list of UUIDs including the + * newly added profile's UUID. + * + * The reverse shall occur whenever the profile 'cleanup' APIs are invoked + */ + +/** Represents the standard Bluetooth DM interface. */ +typedef struct { + /** set to sizeof(bt_interface_t) */ + size_t size; + /** + * Opens the interface and provides the callback routines + * to the implemenation of this interface. + */ + int (*init)(bt_callbacks_t* callbacks ); + + /** Enable Bluetooth. */ + int (*enable)(void); + + /** Disable Bluetooth. */ + int (*disable)(void); + + /** Closes the interface. */ + void (*cleanup)(void); + + /** Get all Bluetooth Adapter properties at init */ + int (*get_adapter_properties)(void); + + /** Get Bluetooth Adapter property of 'type' */ + int (*get_adapter_property)(bt_property_type_t type); + + /** Set Bluetooth Adapter property of 'type' */ + /* Based on the type, val shall be one of + * bt_bdaddr_t or bt_bdname_t or bt_scanmode_t etc + */ + int (*set_adapter_property)(const bt_property_t *property); + + /** Get all Remote Device properties */ + int (*get_remote_device_properties)(bt_bdaddr_t *remote_addr); + + /** Get Remote Device property of 'type' */ + int (*get_remote_device_property)(bt_bdaddr_t *remote_addr, + bt_property_type_t type); + + /** Set Remote Device property of 'type' */ + int (*set_remote_device_property)(bt_bdaddr_t *remote_addr, + const bt_property_t *property); + + /** Get Remote Device's service record for the given UUID */ + int (*get_remote_service_record)(bt_bdaddr_t *remote_addr, + bt_uuid_t *uuid); + + /** Start SDP to get remote services */ + int (*get_remote_services)(bt_bdaddr_t *remote_addr); + + /** Start Discovery */ + int (*start_discovery)(void); + + /** Cancel Discovery */ + int (*cancel_discovery)(void); + + /** Create Bluetooth Bonding */ + int (*create_bond)(const bt_bdaddr_t *bd_addr); + + /** Remove Bond */ + int (*remove_bond)(const bt_bdaddr_t *bd_addr); + + /** Cancel Bond */ + int (*cancel_bond)(const bt_bdaddr_t *bd_addr); + + /** BT Legacy PinKey Reply */ + /** If accept==FALSE, then pin_len and pin_code shall be 0x0 */ + int (*pin_reply)(const bt_bdaddr_t *bd_addr, uint8_t accept, + uint8_t pin_len, bt_pin_code_t *pin_code); + + /** BT SSP Reply - Just Works, Numeric Comparison and Passkey + * passkey shall be zero for BT_SSP_VARIANT_PASSKEY_COMPARISON & + * BT_SSP_VARIANT_CONSENT + * For BT_SSP_VARIANT_PASSKEY_ENTRY, if accept==FALSE, then passkey + * shall be zero */ + int (*ssp_reply)(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant, + uint8_t accept, uint32_t passkey); + + /** Get Bluetooth profile interface */ + const void* (*get_profile_interface) (const char *profile_id); + + /** Bluetooth Test Mode APIs - Bluetooth must be enabled for these APIs */ + /* Configure DUT Mode - Use this mode to enter/exit DUT mode */ + int (*dut_mode_configure)(uint8_t enable); + + /* Send any test HCI (vendor-specific) command to the controller. Must be in DUT Mode */ + int (*dut_mode_send)(uint16_t opcode, uint8_t *buf, uint8_t len); + /** BLE Test Mode APIs */ + /* opcode MUST be one of: LE_Receiver_Test, LE_Transmitter_Test, LE_Test_End */ + int (*le_test_mode)(uint16_t opcode, uint8_t *buf, uint8_t len); + + /* enable or disable bluetooth HCI snoop log */ + int (*config_hci_snoop_log)(uint8_t enable); +} bt_interface_t; + +/** TODO: Need to add APIs for Service Discovery, Service authorization and + * connection management. Also need to add APIs for configuring + * properties of remote bonded devices such as name, UUID etc. */ + +typedef struct { + struct hw_device_t common; + const bt_interface_t* (*get_bluetooth_interface)(); +} bluetooth_device_t; + +typedef bluetooth_device_t bluetooth_module_t; +__END_DECLS + +#endif /* ANDROID_INCLUDE_BLUETOOTH_H */ diff --git a/android/hardware/bt_av.h b/android/hardware/bt_av.h new file mode 100644 index 0000000..2ec00c3 --- /dev/null +++ b/android/hardware/bt_av.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_AV_H +#define ANDROID_INCLUDE_BT_AV_H + +__BEGIN_DECLS + +/* Bluetooth AV connection states */ +typedef enum { + BTAV_CONNECTION_STATE_DISCONNECTED = 0, + BTAV_CONNECTION_STATE_CONNECTING, + BTAV_CONNECTION_STATE_CONNECTED, + BTAV_CONNECTION_STATE_DISCONNECTING +} btav_connection_state_t; + +/* Bluetooth AV datapath states */ +typedef enum { + BTAV_AUDIO_STATE_REMOTE_SUSPEND = 0, + BTAV_AUDIO_STATE_STOPPED, + BTAV_AUDIO_STATE_STARTED, +} btav_audio_state_t; + + +/** Callback for connection state change. + * state will have one of the values from btav_connection_state_t + */ +typedef void (* btav_connection_state_callback)(btav_connection_state_t state, + bt_bdaddr_t *bd_addr); + +/** Callback for audiopath state change. + * state will have one of the values from btav_audio_state_t + */ +typedef void (* btav_audio_state_callback)(btav_audio_state_t state, + bt_bdaddr_t *bd_addr); + +/** BT-AV callback structure. */ +typedef struct { + /** set to sizeof(btav_callbacks_t) */ + size_t size; + btav_connection_state_callback connection_state_cb; + btav_audio_state_callback audio_state_cb; +} btav_callbacks_t; + +/** + * NOTE: + * + * 1. AVRCP 1.0 shall be supported initially. AVRCP passthrough commands + * shall be handled internally via uinput + * + * 2. A2DP data path shall be handled via a socket pipe between the AudioFlinger + * android_audio_hw library and the Bluetooth stack. + * + */ +/** Represents the standard BT-AV interface. */ +typedef struct { + + /** set to sizeof(btav_interface_t) */ + size_t size; + /** + * Register the BtAv callbacks + */ + bt_status_t (*init)( btav_callbacks_t* callbacks ); + + /** connect to headset */ + bt_status_t (*connect)( bt_bdaddr_t *bd_addr ); + + /** dis-connect from headset */ + bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr ); + + /** Closes the interface. */ + void (*cleanup)( void ); +} btav_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_AV_H */ diff --git a/android/hardware/bt_gatt.h b/android/hardware/bt_gatt.h new file mode 100644 index 0000000..42e14c2 --- /dev/null +++ b/android/hardware/bt_gatt.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_INCLUDE_BT_GATT_H +#define ANDROID_INCLUDE_BT_GATT_H + +#include +#include "bt_gatt_client.h" +#include "bt_gatt_server.h" + +__BEGIN_DECLS + +/** BT-GATT callbacks */ +typedef struct { + /** Set to sizeof(btgatt_callbacks_t) */ + size_t size; + + /** GATT Client callbacks */ + const btgatt_client_callbacks_t* client; + + /** GATT Server callbacks */ + const btgatt_server_callbacks_t* server; +} btgatt_callbacks_t; + +/** Represents the standard Bluetooth GATT interface. */ +typedef struct { + /** Set to sizeof(btgatt_interface_t) */ + size_t size; + + /** + * Initializes the interface and provides callback routines + */ + bt_status_t (*init)( const btgatt_callbacks_t* callbacks ); + + /** Closes the interface */ + void (*cleanup)( void ); + + /** Pointer to the GATT client interface methods.*/ + const btgatt_client_interface_t* client; + + /** Pointer to the GATT server interface methods.*/ + const btgatt_server_interface_t* server; +} btgatt_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_GATT_H */ diff --git a/android/hardware/bt_gatt_client.h b/android/hardware/bt_gatt_client.h new file mode 100644 index 0000000..d6b0cb4 --- /dev/null +++ b/android/hardware/bt_gatt_client.h @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_INCLUDE_BT_GATT_CLIENT_H +#define ANDROID_INCLUDE_BT_GATT_CLIENT_H + +#include +#include "bt_gatt_types.h" + +__BEGIN_DECLS + +/** + * Buffer sizes for maximum attribute length and maximum read/write + * operation buffer size. + */ +#define BTGATT_MAX_ATTR_LEN 600 + +/** Buffer type for unformatted reads/writes */ +typedef struct +{ + uint8_t value[BTGATT_MAX_ATTR_LEN]; + uint16_t len; +} btgatt_unformatted_value_t; + +/** Parameters for GATT read operations */ +typedef struct +{ + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + btgatt_gatt_id_t descr_id; + btgatt_unformatted_value_t value; + uint16_t value_type; + uint8_t status; +} btgatt_read_params_t; + +/** Parameters for GATT write operations */ +typedef struct +{ + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + btgatt_gatt_id_t descr_id; + uint8_t status; +} btgatt_write_params_t; + +/** Attribute change notification parameters */ +typedef struct +{ + uint8_t value[BTGATT_MAX_ATTR_LEN]; + bt_bdaddr_t bda; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + uint16_t len; + uint8_t is_notify; +} btgatt_notify_params_t; + +typedef struct +{ + bt_bdaddr_t *bda1; + bt_uuid_t *uuid1; + uint16_t u1; + uint16_t u2; + uint16_t u3; + uint16_t u4; + uint16_t u5; +} btgatt_test_params_t; + +/** BT-GATT Client callback structure. */ + +/** Callback invoked in response to register_client */ +typedef void (*register_client_callback)(int status, int client_if, + bt_uuid_t *app_uuid); + +/** Callback for scan results */ +typedef void (*scan_result_callback)(bt_bdaddr_t* bda, int rssi, uint8_t* adv_data); + +/** GATT open callback invoked in response to open */ +typedef void (*connect_callback)(int conn_id, int status, int client_if, bt_bdaddr_t* bda); + +/** Callback invoked in response to close */ +typedef void (*disconnect_callback)(int conn_id, int status, + int client_if, bt_bdaddr_t* bda); + +/** + * Invoked in response to search_service when the GATT service search + * has been completed. + */ +typedef void (*search_complete_callback)(int conn_id, int status); + +/** Reports GATT services on a remote device */ +typedef void (*search_result_callback)( int conn_id, btgatt_srvc_id_t *srvc_id); + +/** GATT characteristic enumeration result callback */ +typedef void (*get_characteristic_callback)(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + int char_prop); + +/** GATT descriptor enumeration result callback */ +typedef void (*get_descriptor_callback)(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id); + +/** GATT included service enumeration result callback */ +typedef void (*get_included_service_callback)(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id); + +/** Callback invoked in response to [de]register_for_notification */ +typedef void (*register_for_notification_callback)(int conn_id, + int registered, int status, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id); + +/** + * Remote device notification callback, invoked when a remote device sends + * a notification or indication that a client has registered for. + */ +typedef void (*notify_callback)(int conn_id, btgatt_notify_params_t *p_data); + +/** Reports result of a GATT read operation */ +typedef void (*read_characteristic_callback)(int conn_id, int status, + btgatt_read_params_t *p_data); + +/** GATT write characteristic operation callback */ +typedef void (*write_characteristic_callback)(int conn_id, int status, + btgatt_write_params_t *p_data); + +/** GATT execute prepared write callback */ +typedef void (*execute_write_callback)(int conn_id, int status); + +/** Callback invoked in response to read_descriptor */ +typedef void (*read_descriptor_callback)(int conn_id, int status, + btgatt_read_params_t *p_data); + +/** Callback invoked in response to write_descriptor */ +typedef void (*write_descriptor_callback)(int conn_id, int status, + btgatt_write_params_t *p_data); + +/** Callback triggered in response to read_remote_rssi */ +typedef void (*read_remote_rssi_callback)(int client_if, bt_bdaddr_t* bda, + int rssi, int status); + +/** + * Callback indicationg the status of a listen() operation + */ +typedef void (*listen_callback)(int status, int server_if); + +typedef struct { + register_client_callback register_client_cb; + scan_result_callback scan_result_cb; + connect_callback open_cb; + disconnect_callback close_cb; + search_complete_callback search_complete_cb; + search_result_callback search_result_cb; + get_characteristic_callback get_characteristic_cb; + get_descriptor_callback get_descriptor_cb; + get_included_service_callback get_included_service_cb; + register_for_notification_callback register_for_notification_cb; + notify_callback notify_cb; + read_characteristic_callback read_characteristic_cb; + write_characteristic_callback write_characteristic_cb; + read_descriptor_callback read_descriptor_cb; + write_descriptor_callback write_descriptor_cb; + execute_write_callback execute_write_cb; + read_remote_rssi_callback read_remote_rssi_cb; + listen_callback listen_cb; +} btgatt_client_callbacks_t; + +/** Represents the standard BT-GATT client interface. */ + +typedef struct { + /** Registers a GATT client application with the stack */ + bt_status_t (*register_client)( bt_uuid_t *uuid ); + + /** Unregister a client application from the stack */ + bt_status_t (*unregister_client)(int client_if ); + + /** Start or stop LE device scanning */ + bt_status_t (*scan)( int client_if, bool start ); + + /** Create a connection to a remote LE or dual-mode device */ + bt_status_t (*connect)( int client_if, const bt_bdaddr_t *bd_addr, + bool is_direct ); + + /** Disconnect a remote device or cancel a pending connection */ + bt_status_t (*disconnect)( int client_if, const bt_bdaddr_t *bd_addr, + int conn_id); + + /** Start or stop advertisements to listen for incoming connections */ + bt_status_t (*listen)(int client_if, bool start); + + /** Clear the attribute cache for a given device */ + bt_status_t (*refresh)( int client_if, const bt_bdaddr_t *bd_addr ); + + /** + * Enumerate all GATT services on a connected device. + * Optionally, the results can be filtered for a given UUID. + */ + bt_status_t (*search_service)(int conn_id, bt_uuid_t *filter_uuid ); + + /** + * Enumerate included services for a given service. + * Set start_incl_srvc_id to NULL to get the first included service. + */ + bt_status_t (*get_included_service)( int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_srvc_id_t *start_incl_srvc_id); + + /** + * Enumerate characteristics for a given service. + * Set start_char_id to NULL to get the first characteristic. + */ + bt_status_t (*get_characteristic)( int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id); + + /** + * Enumerate descriptors for a given characteristic. + * Set start_descr_id to NULL to get the first descriptor. + */ + bt_status_t (*get_descriptor)( int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *start_descr_id); + + /** Read a characteristic on a remote device */ + bt_status_t (*read_characteristic)( int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + int auth_req ); + + /** Write a remote characteristic */ + bt_status_t (*write_characteristic)(int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + int write_type, int len, int auth_req, + char* p_value); + + /** Read the descriptor for a given characteristic */ + bt_status_t (*read_descriptor)(int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id, int auth_req); + + /** Write a remote descriptor for a given characteristic */ + bt_status_t (*write_descriptor)( int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id, int write_type, int len, + int auth_req, char* p_value); + + /** Execute a prepared write operation */ + bt_status_t (*execute_write)(int conn_id, int execute); + + /** + * Register to receive notifications or indications for a given + * characteristic + */ + bt_status_t (*register_for_notification)( int client_if, + const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id); + + /** Deregister a previous request for notifications/indications */ + bt_status_t (*deregister_for_notification)( int client_if, + const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id); + + /** Request RSSI for a given remote device */ + bt_status_t (*read_remote_rssi)( int client_if, const bt_bdaddr_t *bd_addr); + + /** Determine the type of the remote device (LE, BR/EDR, Dual-mode) */ + int (*get_device_type)( const bt_bdaddr_t *bd_addr ); + + /** Set the advertising data or scan response data */ + bt_status_t (*set_adv_data)(int server_if, bool set_scan_rsp, bool include_name, + bool include_txpower, int min_interval, int max_interval, int appearance, + uint16_t manufacturer_len, char* manufacturer_data); + + /** Test mode interface */ + bt_status_t (*test_command)( int command, btgatt_test_params_t* params); +} btgatt_client_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */ diff --git a/android/hardware/bt_gatt_server.h b/android/hardware/bt_gatt_server.h new file mode 100644 index 0000000..1a5a400 --- /dev/null +++ b/android/hardware/bt_gatt_server.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_INCLUDE_BT_GATT_SERVER_H +#define ANDROID_INCLUDE_BT_GATT_SERVER_H + +#include + +#include "bt_gatt_types.h" + +__BEGIN_DECLS + +/** GATT value type used in response to remote read requests */ +typedef struct +{ + uint8_t value[BTGATT_MAX_ATTR_LEN]; + uint16_t handle; + uint16_t offset; + uint16_t len; + uint8_t auth_req; +} btgatt_value_t; + +/** GATT remote read request response type */ +typedef union +{ + btgatt_value_t attr_value; + uint16_t handle; +} btgatt_response_t; + +/** BT-GATT Server callback structure. */ + +/** Callback invoked in response to register_server */ +typedef void (*register_server_callback)(int status, int server_if, + bt_uuid_t *app_uuid); + +/** Callback indicating that a remote device has connected or been disconnected */ +typedef void (*connection_callback)(int conn_id, int server_if, int connected, + bt_bdaddr_t *bda); + +/** Callback invoked in response to create_service */ +typedef void (*service_added_callback)(int status, int server_if, + btgatt_srvc_id_t *srvc_id, int srvc_handle); + +/** Callback indicating that an included service has been added to a service */ +typedef void (*included_service_added_callback)(int status, int server_if, + int srvc_handle, int incl_srvc_handle); + +/** Callback invoked when a characteristic has been added to a service */ +typedef void (*characteristic_added_callback)(int status, int server_if, + bt_uuid_t *uuid, int srvc_handle, int char_handle); + +/** Callback invoked when a descriptor has been added to a characteristic */ +typedef void (*descriptor_added_callback)(int status, int server_if, + bt_uuid_t *uuid, int srvc_handle, int descr_handle); + +/** Callback invoked in response to start_service */ +typedef void (*service_started_callback)(int status, int server_if, + int srvc_handle); + +/** Callback invoked in response to stop_service */ +typedef void (*service_stopped_callback)(int status, int server_if, + int srvc_handle); + +/** Callback triggered when a service has been deleted */ +typedef void (*service_deleted_callback)(int status, int server_if, + int srvc_handle); + +/** + * Callback invoked when a remote device has requested to read a characteristic + * or descriptor. The application must respond by calling send_response + */ +typedef void (*request_read_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda, + int attr_handle, int offset, bool is_long); + +/** + * Callback invoked when a remote device has requested to write to a + * characteristic or descriptor. + */ +typedef void (*request_write_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda, + int attr_handle, int offset, int length, + bool need_rsp, bool is_prep, uint8_t* value); + +/** Callback invoked when a previously prepared write is to be executed */ +typedef void (*request_exec_write_callback)(int conn_id, int trans_id, + bt_bdaddr_t *bda, int exec_write); + +/** + * Callback triggered in response to send_response if the remote device + * sends a confirmation. + */ +typedef void (*response_confirmation_callback)(int status, int handle); + +typedef struct { + register_server_callback register_server_cb; + connection_callback connection_cb; + service_added_callback service_added_cb; + included_service_added_callback included_service_added_cb; + characteristic_added_callback characteristic_added_cb; + descriptor_added_callback descriptor_added_cb; + service_started_callback service_started_cb; + service_stopped_callback service_stopped_cb; + service_deleted_callback service_deleted_cb; + request_read_callback request_read_cb; + request_write_callback request_write_cb; + request_exec_write_callback request_exec_write_cb; + response_confirmation_callback response_confirmation_cb; +} btgatt_server_callbacks_t; + +/** Represents the standard BT-GATT server interface. */ +typedef struct { + /** Registers a GATT server application with the stack */ + bt_status_t (*register_server)( bt_uuid_t *uuid ); + + /** Unregister a server application from the stack */ + bt_status_t (*unregister_server)(int server_if ); + + /** Create a connection to a remote peripheral */ + bt_status_t (*connect)(int server_if, const bt_bdaddr_t *bd_addr, bool is_direct ); + + /** Disconnect an established connection or cancel a pending one */ + bt_status_t (*disconnect)(int server_if, const bt_bdaddr_t *bd_addr, + int conn_id ); + + /** Create a new service */ + bt_status_t (*add_service)( int server_if, btgatt_srvc_id_t *srvc_id, int num_handles); + + /** Assign an included service to it's parent service */ + bt_status_t (*add_included_service)( int server_if, int service_handle, int included_handle); + + /** Add a characteristic to a service */ + bt_status_t (*add_characteristic)( int server_if, + int service_handle, bt_uuid_t *uuid, + int properties, int permissions); + + /** Add a descriptor to a given service */ + bt_status_t (*add_descriptor)(int server_if, int service_handle, + bt_uuid_t *uuid, int permissions); + + /** Starts a local service */ + bt_status_t (*start_service)(int server_if, int service_handle, + int transport); + + /** Stops a local service */ + bt_status_t (*stop_service)(int server_if, int service_handle); + + /** Delete a local service */ + bt_status_t (*delete_service)(int server_if, int service_handle); + + /** Send value indication to a remote device */ + bt_status_t (*send_indication)(int server_if, int attribute_handle, + int conn_id, int len, int confirm, + char* p_value); + + /** Send a response to a read/write operation */ + bt_status_t (*send_response)(int conn_id, int trans_id, + int status, btgatt_response_t *response); +} btgatt_server_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */ diff --git a/android/hardware/bt_gatt_types.h b/android/hardware/bt_gatt_types.h new file mode 100644 index 0000000..0ac217e --- /dev/null +++ b/android/hardware/bt_gatt_types.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_INCLUDE_BT_GATT_TYPES_H +#define ANDROID_INCLUDE_BT_GATT_TYPES_H + +#include +#include + +__BEGIN_DECLS + +/** + * GATT Service types + */ +#define BTGATT_SERVICE_TYPE_PRIMARY 0 +#define BTGATT_SERVICE_TYPE_SECONDARY 1 + +/** GATT ID adding instance id tracking to the UUID */ +typedef struct +{ + bt_uuid_t uuid; + uint8_t inst_id; +} btgatt_gatt_id_t; + +/** GATT Service ID also identifies the service type (primary/secondary) */ +typedef struct +{ + btgatt_gatt_id_t id; + uint8_t is_primary; +} btgatt_srvc_id_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_GATT_TYPES_H */ diff --git a/android/hardware/bt_hf.h b/android/hardware/bt_hf.h new file mode 100644 index 0000000..6135ac4 --- /dev/null +++ b/android/hardware/bt_hf.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_HF_H +#define ANDROID_INCLUDE_BT_HF_H + +__BEGIN_DECLS + +/* AT response code - OK/Error */ +typedef enum { + BTHF_AT_RESPONSE_ERROR = 0, + BTHF_AT_RESPONSE_OK +} bthf_at_response_t; + +typedef enum { + BTHF_CONNECTION_STATE_DISCONNECTED = 0, + BTHF_CONNECTION_STATE_CONNECTING, + BTHF_CONNECTION_STATE_CONNECTED, + BTHF_CONNECTION_STATE_SLC_CONNECTED, + BTHF_CONNECTION_STATE_DISCONNECTING +} bthf_connection_state_t; + +typedef enum { + BTHF_AUDIO_STATE_DISCONNECTED = 0, + BTHF_AUDIO_STATE_CONNECTING, + BTHF_AUDIO_STATE_CONNECTED, + BTHF_AUDIO_STATE_DISCONNECTING +} bthf_audio_state_t; + +typedef enum { + BTHF_VR_STATE_STOPPED = 0, + BTHF_VR_STATE_STARTED +} bthf_vr_state_t; + +typedef enum { + BTHF_VOLUME_TYPE_SPK = 0, + BTHF_VOLUME_TYPE_MIC +} bthf_volume_type_t; + +/* Noise Reduction and Echo Cancellation */ +typedef enum +{ + BTHF_NREC_STOP, + BTHF_NREC_START +} bthf_nrec_t; + +/* CHLD - Call held handling */ +typedef enum +{ + BTHF_CHLD_TYPE_RELEASEHELD, // Terminate all held or set UDUB("busy") to a waiting call + BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD, // Terminate all active calls and accepts a waiting/held call + BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD, // Hold all active calls and accepts a waiting/held call + BTHF_CHLD_TYPE_ADDHELDTOCONF, // Add all held calls to a conference +} bthf_chld_type_t; + +/** Callback for connection state change. + * state will have one of the values from BtHfConnectionState + */ +typedef void (* bthf_connection_state_callback)(bthf_connection_state_t state, bt_bdaddr_t *bd_addr); + +/** Callback for audio connection state change. + * state will have one of the values from BtHfAudioState + */ +typedef void (* bthf_audio_state_callback)(bthf_audio_state_t state, bt_bdaddr_t *bd_addr); + +/** Callback for VR connection state change. + * state will have one of the values from BtHfVRState + */ +typedef void (* bthf_vr_cmd_callback)(bthf_vr_state_t state); + +/** Callback for answer incoming call (ATA) + */ +typedef void (* bthf_answer_call_cmd_callback)(); + +/** Callback for disconnect call (AT+CHUP) + */ +typedef void (* bthf_hangup_call_cmd_callback)(); + +/** Callback for disconnect call (AT+CHUP) + * type will denote Speaker/Mic gain (BtHfVolumeControl). + */ +typedef void (* bthf_volume_cmd_callback)(bthf_volume_type_t type, int volume); + +/** Callback for dialing an outgoing call + * If number is NULL, redial + */ +typedef void (* bthf_dial_call_cmd_callback)(char *number); + +/** Callback for sending DTMF tones + * tone contains the dtmf character to be sent + */ +typedef void (* bthf_dtmf_cmd_callback)(char tone); + +/** Callback for enabling/disabling noise reduction/echo cancellation + * value will be 1 to enable, 0 to disable + */ +typedef void (* bthf_nrec_cmd_callback)(bthf_nrec_t nrec); + +/** Callback for call hold handling (AT+CHLD) + * value will contain the call hold command (0, 1, 2, 3) + */ +typedef void (* bthf_chld_cmd_callback)(bthf_chld_type_t chld); + +/** Callback for CNUM (subscriber number) + */ +typedef void (* bthf_cnum_cmd_callback)(); + +/** Callback for indicators (CIND) + */ +typedef void (* bthf_cind_cmd_callback)(); + +/** Callback for operator selection (COPS) + */ +typedef void (* bthf_cops_cmd_callback)(); + +/** Callback for call list (AT+CLCC) + */ +typedef void (* bthf_clcc_cmd_callback) (); + +/** Callback for unknown AT command recd from HF + * at_string will contain the unparsed AT string + */ +typedef void (* bthf_unknown_at_cmd_callback)(char *at_string); + +/** Callback for keypressed (HSP) event. + */ +typedef void (* bthf_key_pressed_cmd_callback)(); + +/** BT-HF callback structure. */ +typedef struct { + /** set to sizeof(BtHfCallbacks) */ + size_t size; + bthf_connection_state_callback connection_state_cb; + bthf_audio_state_callback audio_state_cb; + bthf_vr_cmd_callback vr_cmd_cb; + bthf_answer_call_cmd_callback answer_call_cmd_cb; + bthf_hangup_call_cmd_callback hangup_call_cmd_cb; + bthf_volume_cmd_callback volume_cmd_cb; + bthf_dial_call_cmd_callback dial_call_cmd_cb; + bthf_dtmf_cmd_callback dtmf_cmd_cb; + bthf_nrec_cmd_callback nrec_cmd_cb; + bthf_chld_cmd_callback chld_cmd_cb; + bthf_cnum_cmd_callback cnum_cmd_cb; + bthf_cind_cmd_callback cind_cmd_cb; + bthf_cops_cmd_callback cops_cmd_cb; + bthf_clcc_cmd_callback clcc_cmd_cb; + bthf_unknown_at_cmd_callback unknown_at_cmd_cb; + bthf_key_pressed_cmd_callback key_pressed_cmd_cb; +} bthf_callbacks_t; + +/** Network Status */ +typedef enum +{ + BTHF_NETWORK_STATE_NOT_AVAILABLE = 0, + BTHF_NETWORK_STATE_AVAILABLE +} bthf_network_state_t; + +/** Service type */ +typedef enum +{ + BTHF_SERVICE_TYPE_HOME = 0, + BTHF_SERVICE_TYPE_ROAMING +} bthf_service_type_t; + +typedef enum { + BTHF_CALL_STATE_ACTIVE = 0, + BTHF_CALL_STATE_HELD, + BTHF_CALL_STATE_DIALING, + BTHF_CALL_STATE_ALERTING, + BTHF_CALL_STATE_INCOMING, + BTHF_CALL_STATE_WAITING, + BTHF_CALL_STATE_IDLE +} bthf_call_state_t; + +typedef enum { + BTHF_CALL_DIRECTION_OUTGOING = 0, + BTHF_CALL_DIRECTION_INCOMING +} bthf_call_direction_t; + +typedef enum { + BTHF_CALL_TYPE_VOICE = 0, + BTHF_CALL_TYPE_DATA, + BTHF_CALL_TYPE_FAX +} bthf_call_mode_t; + +typedef enum { + BTHF_CALL_MPTY_TYPE_SINGLE = 0, + BTHF_CALL_MPTY_TYPE_MULTI +} bthf_call_mpty_type_t; + +typedef enum { + BTHF_CALL_ADDRTYPE_UNKNOWN = 0x81, + BTHF_CALL_ADDRTYPE_INTERNATIONAL = 0x91 +} bthf_call_addrtype_t; +/** Represents the standard BT-HF interface. */ +typedef struct { + + /** set to sizeof(BtHfInterface) */ + size_t size; + /** + * Register the BtHf callbacks + */ + bt_status_t (*init)( bthf_callbacks_t* callbacks ); + + /** connect to headset */ + bt_status_t (*connect)( bt_bdaddr_t *bd_addr ); + + /** dis-connect from headset */ + bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr ); + + /** create an audio connection */ + bt_status_t (*connect_audio)( bt_bdaddr_t *bd_addr ); + + /** close the audio connection */ + bt_status_t (*disconnect_audio)( bt_bdaddr_t *bd_addr ); + + /** start voice recognition */ + bt_status_t (*start_voice_recognition)(); + + /** stop voice recognition */ + bt_status_t (*stop_voice_recognition)(); + + /** volume control */ + bt_status_t (*volume_control) (bthf_volume_type_t type, int volume); + + /** Combined device status change notification */ + bt_status_t (*device_status_notification)(bthf_network_state_t ntk_state, bthf_service_type_t svc_type, int signal, + int batt_chg); + + /** Response for COPS command */ + bt_status_t (*cops_response)(const char *cops); + + /** Response for CIND command */ + bt_status_t (*cind_response)(int svc, int num_active, int num_held, bthf_call_state_t call_setup_state, + int signal, int roam, int batt_chg); + + /** Pre-formatted AT response, typically in response to unknown AT cmd */ + bt_status_t (*formatted_at_response)(const char *rsp); + + /** ok/error response + * ERROR (0) + * OK (1) + */ + bt_status_t (*at_response) (bthf_at_response_t response_code, int error_code); + + /** response for CLCC command + * Can be iteratively called for each call index + * Call index of 0 will be treated as NULL termination (Completes response) + */ + bt_status_t (*clcc_response) (int index, bthf_call_direction_t dir, + bthf_call_state_t state, bthf_call_mode_t mode, + bthf_call_mpty_type_t mpty, const char *number, + bthf_call_addrtype_t type); + + /** notify of a call state change + * Each update notifies + * 1. Number of active/held/ringing calls + * 2. call_state: This denotes the state change that triggered this msg + * This will take one of the values from BtHfCallState + * 3. number & type: valid only for incoming & waiting call + */ + bt_status_t (*phone_state_change) (int num_active, int num_held, bthf_call_state_t call_setup_state, + const char *number, bthf_call_addrtype_t type); + + /** Closes the interface. */ + void (*cleanup)( void ); +} bthf_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_HF_H */ diff --git a/android/hardware/bt_hh.h b/android/hardware/bt_hh.h new file mode 100644 index 0000000..09f547b --- /dev/null +++ b/android/hardware/bt_hh.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_HH_H +#define ANDROID_INCLUDE_BT_HH_H + +#include + +__BEGIN_DECLS + +#define BTHH_MAX_DSC_LEN 884 + +/* HH connection states */ +typedef enum +{ + BTHH_CONN_STATE_CONNECTED = 0, + BTHH_CONN_STATE_CONNECTING, + BTHH_CONN_STATE_DISCONNECTED, + BTHH_CONN_STATE_DISCONNECTING, + BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST, + BTHH_CONN_STATE_FAILED_KBD_FROM_HOST, + BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES, + BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER, + BTHH_CONN_STATE_FAILED_GENERIC, + BTHH_CONN_STATE_UNKNOWN +} bthh_connection_state_t; + +typedef enum +{ + BTHH_OK = 0, + BTHH_HS_HID_NOT_READY, /* handshake error : device not ready */ + BTHH_HS_INVALID_RPT_ID, /* handshake error : invalid report ID */ + BTHH_HS_TRANS_NOT_SPT, /* handshake error : transaction not spt */ + BTHH_HS_INVALID_PARAM, /* handshake error : invalid paremter */ + BTHH_HS_ERROR, /* handshake error : unspecified HS error */ + BTHH_ERR, /* general BTA HH error */ + BTHH_ERR_SDP, /* SDP error */ + BTHH_ERR_PROTO, /* SET_Protocol error, + only used in BTA_HH_OPEN_EVT callback */ + BTHH_ERR_DB_FULL, /* device database full error, used */ + BTHH_ERR_TOD_UNSPT, /* type of device not supported */ + BTHH_ERR_NO_RES, /* out of system resources */ + BTHH_ERR_AUTH_FAILED, /* authentication fail */ + BTHH_ERR_HDL +}bthh_status_t; + +/* Protocol modes */ +typedef enum { + BTHH_REPORT_MODE = 0x00, + BTHH_BOOT_MODE = 0x01, + BTHH_UNSUPPORTED_MODE = 0xff +}bthh_protocol_mode_t; + +/* Report types */ +typedef enum { + BTHH_INPUT_REPORT = 1, + BTHH_OUTPUT_REPORT, + BTHH_FEATURE_REPORT +}bthh_report_type_t; + +typedef struct +{ + int attr_mask; + uint8_t sub_class; + uint8_t app_id; + int vendor_id; + int product_id; + int version; + uint8_t ctry_code; + int dl_len; + uint8_t dsc_list[BTHH_MAX_DSC_LEN]; +} bthh_hid_info_t; + +/** Callback for connection state change. + * state will have one of the values from bthh_connection_state_t + */ +typedef void (* bthh_connection_state_callback)(bt_bdaddr_t *bd_addr, bthh_connection_state_t state); + +/** Callback for vitual unplug api. + * the status of the vitual unplug + */ +typedef void (* bthh_virtual_unplug_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status); + +/** Callback for get hid info + * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id, version, ctry_code, len + */ +typedef void (* bthh_hid_info_callback)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info); + +/** Callback for get/set protocal api. + * the protocol mode is one of the value from bthh_protocol_mode_t + */ +typedef void (* bthh_protocol_mode_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,bthh_protocol_mode_t mode); + +/** Callback for get/set_idle_time api. + */ +typedef void (* bthh_idle_time_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, int idle_rate); + + +/** Callback for get report api. + * if staus is ok rpt_data contains the report data + */ +typedef void (* bthh_get_report_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, uint8_t* rpt_data, int rpt_size); + + +/** BT-HH callback structure. */ +typedef struct { + /** set to sizeof(BtHfCallbacks) */ + size_t size; + bthh_connection_state_callback connection_state_cb; + bthh_hid_info_callback hid_info_cb; + bthh_protocol_mode_callback protocol_mode_cb; + bthh_idle_time_callback idle_time_cb; + bthh_get_report_callback get_report_cb; + bthh_virtual_unplug_callback virtual_unplug_cb; + +} bthh_callbacks_t; + + + +/** Represents the standard BT-HH interface. */ +typedef struct { + + /** set to sizeof(BtHhInterface) */ + size_t size; + + /** + * Register the BtHh callbacks + */ + bt_status_t (*init)( bthh_callbacks_t* callbacks ); + + /** connect to hid device */ + bt_status_t (*connect)( bt_bdaddr_t *bd_addr); + + /** dis-connect from hid device */ + bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr ); + + /** Virtual UnPlug (VUP) the specified HID device */ + bt_status_t (*virtual_unplug)(bt_bdaddr_t *bd_addr); + + /** Set the HID device descriptor for the specified HID device. */ + bt_status_t (*set_info)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info ); + + /** Get the HID proto mode. */ + bt_status_t (*get_protocol) (bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode); + + /** Set the HID proto mode. */ + bt_status_t (*set_protocol)(bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode); + + /** Send a GET_REPORT to HID device. */ + bt_status_t (*get_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, uint8_t reportId, int bufferSize); + + /** Send a SET_REPORT to HID device. */ + bt_status_t (*set_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, char* report); + + /** Send data to HID device. */ + bt_status_t (*send_data)(bt_bdaddr_t *bd_addr, char* data); + + /** Closes the interface. */ + void (*cleanup)( void ); + +} bthh_interface_t; +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_HH_H */ + + diff --git a/android/hardware/bt_hl.h b/android/hardware/bt_hl.h new file mode 100644 index 0000000..bd29e3a --- /dev/null +++ b/android/hardware/bt_hl.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_HL_H +#define ANDROID_INCLUDE_BT_HL_H + +__BEGIN_DECLS + +/* HL connection states */ + +typedef enum +{ + BTHL_MDEP_ROLE_SOURCE, + BTHL_MDEP_ROLE_SINK +} bthl_mdep_role_t; + +typedef enum { + BTHL_APP_REG_STATE_REG_SUCCESS, + BTHL_APP_REG_STATE_REG_FAILED, + BTHL_APP_REG_STATE_DEREG_SUCCESS, + BTHL_APP_REG_STATE_DEREG_FAILED +} bthl_app_reg_state_t; + +typedef enum +{ + BTHL_CHANNEL_TYPE_RELIABLE, + BTHL_CHANNEL_TYPE_STREAMING, + BTHL_CHANNEL_TYPE_ANY +} bthl_channel_type_t; + + +/* HL connection states */ +typedef enum { + BTHL_CONN_STATE_CONNECTING, + BTHL_CONN_STATE_CONNECTED, + BTHL_CONN_STATE_DISCONNECTING, + BTHL_CONN_STATE_DISCONNECTED, + BTHL_CONN_STATE_DESTROYED +} bthl_channel_state_t; + +typedef struct +{ + bthl_mdep_role_t mdep_role; + int data_type; + bthl_channel_type_t channel_type; + const char *mdep_description; /* MDEP description to be used in the SDP (optional); null terminated */ +} bthl_mdep_cfg_t; + +typedef struct +{ + const char *application_name; + const char *provider_name; /* provider name to be used in the SDP (optional); null terminated */ + const char *srv_name; /* service name to be used in the SDP (optional); null terminated*/ + const char *srv_desp; /* service description to be used in the SDP (optional); null terminated */ + int number_of_mdeps; + bthl_mdep_cfg_t *mdep_cfg; /* Dynamic array */ +} bthl_reg_param_t; + +/** Callback for application registration status. + * state will have one of the values from bthl_app_reg_state_t + */ +typedef void (* bthl_app_reg_state_callback)(int app_id, bthl_app_reg_state_t state); + +/** Callback for channel connection state change. + * state will have one of the values from + * bthl_connection_state_t and fd (file descriptor) + */ +typedef void (* bthl_channel_state_callback)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int channel_id, bthl_channel_state_t state, int fd); + +/** BT-HL callback structure. */ +typedef struct { + /** set to sizeof(bthl_callbacks_t) */ + size_t size; + bthl_app_reg_state_callback app_reg_state_cb; + bthl_channel_state_callback channel_state_cb; +} bthl_callbacks_t; + + +/** Represents the standard BT-HL interface. */ +typedef struct { + + /** set to sizeof(bthl_interface_t) */ + size_t size; + + /** + * Register the Bthl callbacks + */ + bt_status_t (*init)( bthl_callbacks_t* callbacks ); + + /** Register HL application */ + bt_status_t (*register_application) ( bthl_reg_param_t *p_reg_param, int *app_id); + + /** Unregister HL application */ + bt_status_t (*unregister_application) (int app_id); + + /** connect channel */ + bt_status_t (*connect_channel)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int *channel_id); + + /** destroy channel */ + bt_status_t (*destroy_channel)(int channel_id); + + /** Close the Bthl callback **/ + void (*cleanup)(void); + +} bthl_interface_t; +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_HL_H */ + + diff --git a/android/hardware/bt_pan.h b/android/hardware/bt_pan.h new file mode 100644 index 0000000..c8b36b4 --- /dev/null +++ b/android/hardware/bt_pan.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_PAN_H +#define ANDROID_INCLUDE_BT_PAN_H + +__BEGIN_DECLS + +#define BTPAN_ROLE_NONE 0 +#define BTPAN_ROLE_PANNAP 1 +#define BTPAN_ROLE_PANU 2 + +typedef enum { + BTPAN_STATE_CONNECTED = 0, + BTPAN_STATE_CONNECTING = 1, + BTPAN_STATE_DISCONNECTED = 2, + BTPAN_STATE_DISCONNECTING = 3 +} btpan_connection_state_t; + +typedef enum { + BTPAN_STATE_ENABLED = 0, + BTPAN_STATE_DISABLED = 1 +} btpan_control_state_t; + +/** +* Callback for pan connection state +*/ +typedef void (*btpan_connection_state_callback)(btpan_connection_state_t state, bt_status_t error, + const bt_bdaddr_t *bd_addr, int local_role, int remote_role); +typedef void (*btpan_control_state_callback)(btpan_control_state_t state, bt_status_t error, + int local_role, const char* ifname); + +typedef struct { + size_t size; + btpan_control_state_callback control_state_cb; + btpan_connection_state_callback connection_state_cb; +} btpan_callbacks_t; +typedef struct { + /** set to size of this struct*/ + size_t size; + /** + * Initialize the pan interface and register the btpan callbacks + */ + bt_status_t (*init)(const btpan_callbacks_t* callbacks); + /* + * enable the pan service by specified role. The result state of + * enabl will be returned by btpan_control_state_callback. when pan-nap is enabled, + * the state of connecting panu device will be notified by btpan_connection_state_callback + */ + bt_status_t (*enable)(int local_role); + /* + * get current pan local role + */ + int (*get_local_role)(void); + /** + * start bluetooth pan connection to the remote device by specified pan role. The result state will be + * returned by btpan_connection_state_callback + */ + bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, int local_role, int remote_role); + /** + * stop bluetooth pan connection. The result state will be returned by btpan_connection_state_callback + */ + bt_status_t (*disconnect)(const bt_bdaddr_t *bd_addr); + + /** + * Cleanup the pan interface + */ + void (*cleanup)(void); + +} btpan_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_PAN_H */ diff --git a/android/hardware/bt_rc.h b/android/hardware/bt_rc.h new file mode 100644 index 0000000..d455543 --- /dev/null +++ b/android/hardware/bt_rc.h @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_RC_H +#define ANDROID_INCLUDE_BT_RC_H + +__BEGIN_DECLS + +/* Macros */ +#define BTRC_MAX_ATTR_STR_LEN 255 +#define BTRC_UID_SIZE 8 +#define BTRC_MAX_APP_SETTINGS 8 +#define BTRC_MAX_FOLDER_DEPTH 4 +#define BTRC_MAX_APP_ATTR_SIZE 16 +#define BTRC_MAX_ELEM_ATTR_SIZE 7 + +typedef uint8_t btrc_uid_t[BTRC_UID_SIZE]; + +typedef enum { + BTRC_FEAT_NONE = 0x00, /* AVRCP 1.0 */ + BTRC_FEAT_METADATA = 0x01, /* AVRCP 1.3 */ + BTRC_FEAT_ABSOLUTE_VOLUME = 0x02, /* Supports TG role and volume sync */ + BTRC_FEAT_BROWSE = 0x04, /* AVRCP 1.4 and up, with Browsing support */ +} btrc_remote_features_t; + +typedef enum { + BTRC_PLAYSTATE_STOPPED = 0x00, /* Stopped */ + BTRC_PLAYSTATE_PLAYING = 0x01, /* Playing */ + BTRC_PLAYSTATE_PAUSED = 0x02, /* Paused */ + BTRC_PLAYSTATE_FWD_SEEK = 0x03, /* Fwd Seek*/ + BTRC_PLAYSTATE_REV_SEEK = 0x04, /* Rev Seek*/ + BTRC_PLAYSTATE_ERROR = 0xFF, /* Error */ +} btrc_play_status_t; + +typedef enum { + BTRC_EVT_PLAY_STATUS_CHANGED = 0x01, + BTRC_EVT_TRACK_CHANGE = 0x02, + BTRC_EVT_TRACK_REACHED_END = 0x03, + BTRC_EVT_TRACK_REACHED_START = 0x04, + BTRC_EVT_PLAY_POS_CHANGED = 0x05, + BTRC_EVT_APP_SETTINGS_CHANGED = 0x08, +} btrc_event_id_t; + +typedef enum { + BTRC_NOTIFICATION_TYPE_INTERIM = 0, + BTRC_NOTIFICATION_TYPE_CHANGED = 1, +} btrc_notification_type_t; + +typedef enum { + BTRC_PLAYER_ATTR_EQUALIZER = 0x01, + BTRC_PLAYER_ATTR_REPEAT = 0x02, + BTRC_PLAYER_ATTR_SHUFFLE = 0x03, + BTRC_PLAYER_ATTR_SCAN = 0x04, +} btrc_player_attr_t; + +typedef enum { + BTRC_MEDIA_ATTR_TITLE = 0x01, + BTRC_MEDIA_ATTR_ARTIST = 0x02, + BTRC_MEDIA_ATTR_ALBUM = 0x03, + BTRC_MEDIA_ATTR_TRACK_NUM = 0x04, + BTRC_MEDIA_ATTR_NUM_TRACKS = 0x05, + BTRC_MEDIA_ATTR_GENRE = 0x06, + BTRC_MEDIA_ATTR_PLAYING_TIME = 0x07, +} btrc_media_attr_t; + +typedef enum { + BTRC_PLAYER_VAL_OFF_REPEAT = 0x01, + BTRC_PLAYER_VAL_SINGLE_REPEAT = 0x02, + BTRC_PLAYER_VAL_ALL_REPEAT = 0x03, + BTRC_PLAYER_VAL_GROUP_REPEAT = 0x04 +} btrc_player_repeat_val_t; + +typedef enum { + BTRC_PLAYER_VAL_OFF_SHUFFLE = 0x01, + BTRC_PLAYER_VAL_ALL_SHUFFLE = 0x02, + BTRC_PLAYER_VAL_GROUP_SHUFFLE = 0x03 +} btrc_player_shuffle_val_t; + +typedef enum { + BTRC_STS_BAD_CMD = 0x00, /* Invalid command */ + BTRC_STS_BAD_PARAM = 0x01, /* Invalid parameter */ + BTRC_STS_NOT_FOUND = 0x02, /* Specified parameter is wrong or not found */ + BTRC_STS_INTERNAL_ERR = 0x03, /* Internal Error */ + BTRC_STS_NO_ERROR = 0x04 /* Operation Success */ +} btrc_status_t; + +typedef struct { + uint8_t num_attr; + uint8_t attr_ids[BTRC_MAX_APP_SETTINGS]; + uint8_t attr_values[BTRC_MAX_APP_SETTINGS]; +} btrc_player_settings_t; + +typedef union +{ + btrc_play_status_t play_status; + btrc_uid_t track; /* queue position in NowPlaying */ + uint32_t song_pos; + btrc_player_settings_t player_setting; +} btrc_register_notification_t; + +typedef struct { + uint8_t id; /* can be attr_id or value_id */ + uint8_t text[BTRC_MAX_ATTR_STR_LEN]; +} btrc_player_setting_text_t; + +typedef struct { + uint32_t attr_id; + uint8_t text[BTRC_MAX_ATTR_STR_LEN]; +} btrc_element_attr_val_t; + +/** Callback for the controller's supported feautres */ +typedef void (* btrc_remote_features_callback)(bt_bdaddr_t *bd_addr, + btrc_remote_features_t features); + +/** Callback for play status request */ +typedef void (* btrc_get_play_status_callback)(); + +/** Callback for list player application attributes (Shuffle, Repeat,...) */ +typedef void (* btrc_list_player_app_attr_callback)(); + +/** Callback for list player application attributes (Shuffle, Repeat,...) */ +typedef void (* btrc_list_player_app_values_callback)(btrc_player_attr_t attr_id); + +/** Callback for getting the current player application settings value +** num_attr: specifies the number of attribute ids contained in p_attrs +*/ +typedef void (* btrc_get_player_app_value_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs); + +/** Callback for getting the player application settings attributes' text +** num_attr: specifies the number of attribute ids contained in p_attrs +*/ +typedef void (* btrc_get_player_app_attrs_text_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs); + +/** Callback for getting the player application settings values' text +** num_attr: specifies the number of value ids contained in p_vals +*/ +typedef void (* btrc_get_player_app_values_text_callback) (uint8_t attr_id, uint8_t num_val, uint8_t *p_vals); + +/** Callback for setting the player application settings values */ +typedef void (* btrc_set_player_app_value_callback) (btrc_player_settings_t *p_vals); + +/** Callback to fetch the get element attributes of the current song +** num_attr: specifies the number of attributes requested in p_attrs +*/ +typedef void (* btrc_get_element_attr_callback) (uint8_t num_attr, btrc_media_attr_t *p_attrs); + +/** Callback for register notification (Play state change/track change/...) +** param: Is only valid if event_id is BTRC_EVT_PLAY_POS_CHANGED +*/ +typedef void (* btrc_register_notification_callback) (btrc_event_id_t event_id, uint32_t param); + +/* AVRCP 1.4 Enhancements */ +/** Callback for volume change on CT +** volume: Current volume setting on the CT (0-127) +*/ +typedef void (* btrc_volume_change_callback) (uint8_t volume, uint8_t ctype); + +/** Callback for passthrough commands */ +typedef void (* btrc_passthrough_cmd_callback) (int id, int key_state); + +/** BT-RC callback structure. */ +typedef struct { + /** set to sizeof(BtRcCallbacks) */ + size_t size; + btrc_remote_features_callback remote_features_cb; + btrc_get_play_status_callback get_play_status_cb; + btrc_list_player_app_attr_callback list_player_app_attr_cb; + btrc_list_player_app_values_callback list_player_app_values_cb; + btrc_get_player_app_value_callback get_player_app_value_cb; + btrc_get_player_app_attrs_text_callback get_player_app_attrs_text_cb; + btrc_get_player_app_values_text_callback get_player_app_values_text_cb; + btrc_set_player_app_value_callback set_player_app_value_cb; + btrc_get_element_attr_callback get_element_attr_cb; + btrc_register_notification_callback register_notification_cb; + btrc_volume_change_callback volume_change_cb; + btrc_passthrough_cmd_callback passthrough_cmd_cb; +} btrc_callbacks_t; + +/** Represents the standard BT-RC interface. */ +typedef struct { + + /** set to sizeof(BtRcInterface) */ + size_t size; + /** + * Register the BtRc callbacks + */ + bt_status_t (*init)( btrc_callbacks_t* callbacks ); + + /** Respose to GetPlayStatus request. Contains the current + ** 1. Play status + ** 2. Song duration/length + ** 3. Song position + */ + bt_status_t (*get_play_status_rsp)( btrc_play_status_t play_status, uint32_t song_len, uint32_t song_pos); + + /** Lists the support player application attributes (Shuffle/Repeat/...) + ** num_attr: Specifies the number of attributes contained in the pointer p_attrs + */ + bt_status_t (*list_player_app_attr_rsp)( int num_attr, btrc_player_attr_t *p_attrs); + + /** Lists the support player application attributes (Shuffle Off/On/Group) + ** num_val: Specifies the number of values contained in the pointer p_vals + */ + bt_status_t (*list_player_app_value_rsp)( int num_val, uint8_t *p_vals); + + /** Returns the current application attribute values for each of the specified attr_id */ + bt_status_t (*get_player_app_value_rsp)( btrc_player_settings_t *p_vals); + + /** Returns the application attributes text ("Shuffle"/"Repeat"/...) + ** num_attr: Specifies the number of attributes' text contained in the pointer p_attrs + */ + bt_status_t (*get_player_app_attr_text_rsp)( int num_attr, btrc_player_setting_text_t *p_attrs); + + /** Returns the application attributes text ("Shuffle"/"Repeat"/...) + ** num_attr: Specifies the number of attribute values' text contained in the pointer p_vals + */ + bt_status_t (*get_player_app_value_text_rsp)( int num_val, btrc_player_setting_text_t *p_vals); + + /** Returns the current songs' element attributes text ("Title"/"Album"/"Artist") + ** num_attr: Specifies the number of attributes' text contained in the pointer p_attrs + */ + bt_status_t (*get_element_attr_rsp)( uint8_t num_attr, btrc_element_attr_val_t *p_attrs); + + /** Response to set player attribute request ("Shuffle"/"Repeat") + ** rsp_status: Status of setting the player attributes for the current media player + */ + bt_status_t (*set_player_app_value_rsp)(btrc_status_t rsp_status); + + /* Response to the register notification request (Play state change/track change/...). + ** event_id: Refers to the event_id this notification change corresponds too + ** type: Response type - interim/changed + ** p_params: Based on the event_id, this parameter should be populated + */ + bt_status_t (*register_notification_rsp)(btrc_event_id_t event_id, + btrc_notification_type_t type, + btrc_register_notification_t *p_param); + + /* AVRCP 1.4 enhancements */ + + /**Send current volume setting to remote side. Support limited to SetAbsoluteVolume + ** This can be enhanced to support Relative Volume (AVRCP 1.0). + ** With RelateVolume, we will send VOLUME_UP/VOLUME_DOWN opposed to absolute volume level + ** volume: Should be in the range 0-127. bit7 is reseved and cannot be set + */ + bt_status_t (*set_volume)(uint8_t volume); + + /** Closes the interface. */ + void (*cleanup)( void ); +} btrc_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_RC_H */ diff --git a/android/hardware/bt_sock.h b/android/hardware/bt_sock.h new file mode 100644 index 0000000..a4aa046 --- /dev/null +++ b/android/hardware/bt_sock.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_SOCK_H +#define ANDROID_INCLUDE_BT_SOCK_H + +__BEGIN_DECLS + +#define BTSOCK_FLAG_ENCRYPT 1 +#define BTSOCK_FLAG_AUTH (1 << 1) + +typedef enum { + BTSOCK_RFCOMM = 1, + BTSOCK_SCO = 2, + BTSOCK_L2CAP = 3 +} btsock_type_t; + +/** Represents the standard BT SOCKET interface. */ +typedef struct { + short size; + bt_bdaddr_t bd_addr; + int channel; + int status; +} __attribute__((packed)) sock_connect_signal_t; + +typedef struct { + + /** set to size of this struct*/ + size_t size; + /** + * listen to a rfcomm uuid or channel. It returns the socket fd from which + * btsock_connect_signal can be read out when a remote device connected + */ + bt_status_t (*listen)(btsock_type_t type, const char* service_name, const uint8_t* service_uuid, int channel, int* sock_fd, int flags); + /* + * connect to a rfcomm uuid channel of remote device, It returns the socket fd from which + * the btsock_connect_signal and a new socket fd to be accepted can be read out when connected + */ + bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, btsock_type_t type, const uint8_t* uuid, int channel, int* sock_fd, int flags); + +} btsock_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_SOCK_H */ diff --git a/android/hardware/hardware.h b/android/hardware/hardware.h new file mode 100644 index 0000000..c7e8cc7 --- /dev/null +++ b/android/hardware/hardware.h @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_HARDWARE_HARDWARE_H +#define ANDROID_INCLUDE_HARDWARE_HARDWARE_H + +#include +#include + +__BEGIN_DECLS + +/* + * Value for the hw_module_t.tag field + */ + +#define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D)) + +#define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T') +#define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T') + +#define HARDWARE_MAKE_API_VERSION(maj,min) \ + ((((maj) & 0xff) << 8) | ((min) & 0xff)) + +#define HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) \ + ((((maj) & 0xff) << 24) | (((min) & 0xff) << 16) | ((hdr) & 0xffff)) +#define HARDWARE_API_VERSION_2_MAJ_MIN_MASK 0xffff0000 +#define HARDWARE_API_VERSION_2_HEADER_MASK 0x0000ffff + + +/* + * The current HAL API version. + * + * All module implementations must set the hw_module_t.hal_api_version field + * to this value when declaring the module with HAL_MODULE_INFO_SYM. + * + * Note that previous implementations have always set this field to 0. + * Therefore, libhardware HAL API will always consider versions 0.0 and 1.0 + * to be 100% binary compatible. + * + */ +#define HARDWARE_HAL_API_VERSION HARDWARE_MAKE_API_VERSION(1, 0) + +/* + * Helper macros for module implementors. + * + * The derived modules should provide convenience macros for supported + * versions so that implementations can explicitly specify module/device + * versions at definition time. + * + * Use this macro to set the hw_module_t.module_api_version field. + */ +#define HARDWARE_MODULE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min) +#define HARDWARE_MODULE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) + +/* + * Use this macro to set the hw_device_t.version field + */ +#define HARDWARE_DEVICE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min) +#define HARDWARE_DEVICE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) + +struct hw_module_t; +struct hw_module_methods_t; +struct hw_device_t; + +/** + * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM + * and the fields of this data structure must begin with hw_module_t + * followed by module specific information. + */ +typedef struct hw_module_t { + /** tag must be initialized to HARDWARE_MODULE_TAG */ + uint32_t tag; + + /** + * The API version of the implemented module. The module owner is + * responsible for updating the version when a module interface has + * changed. + * + * The derived modules such as gralloc and audio own and manage this field. + * The module user must interpret the version field to decide whether or + * not to inter-operate with the supplied module implementation. + * For example, SurfaceFlinger is responsible for making sure that + * it knows how to manage different versions of the gralloc-module API, + * and AudioFlinger must know how to do the same for audio-module API. + * + * The module API version should include a major and a minor component. + * For example, version 1.0 could be represented as 0x0100. This format + * implies that versions 0x0100-0x01ff are all API-compatible. + * + * In the future, libhardware will expose a hw_get_module_version() + * (or equivalent) function that will take minimum/maximum supported + * versions as arguments and would be able to reject modules with + * versions outside of the supplied range. + */ + uint16_t module_api_version; +#define version_major module_api_version + /** + * version_major/version_minor defines are supplied here for temporary + * source code compatibility. They will be removed in the next version. + * ALL clients must convert to the new version format. + */ + + /** + * The API version of the HAL module interface. This is meant to + * version the hw_module_t, hw_module_methods_t, and hw_device_t + * structures and definitions. + * + * The HAL interface owns this field. Module users/implementations + * must NOT rely on this value for version information. + * + * Presently, 0 is the only valid value. + */ + uint16_t hal_api_version; +#define version_minor hal_api_version + + /** Identifier of module */ + const char *id; + + /** Name of this module */ + const char *name; + + /** Author/owner/implementor of the module */ + const char *author; + + /** Modules methods */ + struct hw_module_methods_t* methods; + + /** module's dso */ + void* dso; + + /** padding to 128 bytes, reserved for future use */ + uint32_t reserved[32-7]; + +} hw_module_t; + +typedef struct hw_module_methods_t { + /** Open a specific device */ + int (*open)(const struct hw_module_t* module, const char* id, + struct hw_device_t** device); + +} hw_module_methods_t; + +/** + * Every device data structure must begin with hw_device_t + * followed by module specific public methods and attributes. + */ +typedef struct hw_device_t { + /** tag must be initialized to HARDWARE_DEVICE_TAG */ + uint32_t tag; + + /** + * Version of the module-specific device API. This value is used by + * the derived-module user to manage different device implementations. + * + * The module user is responsible for checking the module_api_version + * and device version fields to ensure that the user is capable of + * communicating with the specific module implementation. + * + * One module can support multiple devices with different versions. This + * can be useful when a device interface changes in an incompatible way + * but it is still necessary to support older implementations at the same + * time. One such example is the Camera 2.0 API. + * + * This field is interpreted by the module user and is ignored by the + * HAL interface itself. + */ + uint32_t version; + + /** reference to the module this device belongs to */ + struct hw_module_t* module; + + /** padding reserved for future use */ + uint32_t reserved[12]; + + /** Close this device */ + int (*close)(struct hw_device_t* device); + +} hw_device_t; + +/** + * Name of the hal_module_info + */ +#define HAL_MODULE_INFO_SYM HMI + +/** + * Name of the hal_module_info as a string + */ +#define HAL_MODULE_INFO_SYM_AS_STR "HMI" + +/** + * Get the module info associated with a module by id. + * + * @return: 0 == success, <0 == error and *module == NULL + */ +int hw_get_module(const char *id, const struct hw_module_t **module); + +/** + * Get the module info associated with a module instance by class 'class_id' + * and instance 'inst'. + * + * Some modules types necessitate multiple instances. For example audio supports + * multiple concurrent interfaces and thus 'audio' is the module class + * and 'primary' or 'a2dp' are module interfaces. This implies that the files + * providing these modules would be named audio.primary..so and + * audio.a2dp..so + * + * @return: 0 == success, <0 == error and *module == NULL + */ +int hw_get_module_by_class(const char *class_id, const char *inst, + const struct hw_module_t **module); + +__END_DECLS + +#endif /* ANDROID_INCLUDE_HARDWARE_HARDWARE_H */ diff --git a/android/hidhost.c b/android/hidhost.c new file mode 100644 index 0000000..f5a607c --- /dev/null +++ b/android/hidhost.c @@ -0,0 +1,1240 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include "btio/btio.h" +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "lib/uuid.h" +#include "src/shared/mgmt.h" +#include "src/sdp-client.h" +#include "src/glib-helper.h" +#include "profiles/input/uhid_copy.h" + +#include "log.h" +#include "hal-msg.h" +#include "ipc.h" +#include "hidhost.h" +#include "utils.h" + +#define L2CAP_PSM_HIDP_CTRL 0x11 +#define L2CAP_PSM_HIDP_INTR 0x13 +#define UHID_DEVICE_FILE "/dev/uhid" + +/* HID message types */ +#define HID_MSG_CONTROL 0x10 +#define HID_MSG_GET_REPORT 0x40 +#define HID_MSG_SET_REPORT 0x50 +#define HID_MSG_GET_PROTOCOL 0x60 +#define HID_MSG_SET_PROTOCOL 0x70 +#define HID_MSG_DATA 0xa0 + +/* HID data types */ +#define HID_DATA_TYPE_INPUT 0x01 +#define HID_DATA_TYPE_OUTPUT 0x02 +#define HID_DATA_TYPE_FEATURE 0x03 + +/* HID protocol header parameters */ +#define HID_PROTO_BOOT 0x00 +#define HID_PROTO_REPORT 0x01 + +/* HID GET REPORT Size Field */ +#define HID_GET_REPORT_SIZE_FIELD 0x08 + +/* HID Virtual Cable Unplug */ +#define HID_VIRTUAL_CABLE_UNPLUG 0x05 + +static bdaddr_t adapter_addr; + +static int notification_sk = -1; +static GIOChannel *ctrl_io = NULL; +static GIOChannel *intr_io = NULL; +static GSList *devices = NULL; + +struct hid_device { + bdaddr_t dst; + uint8_t state; + uint8_t subclass; + uint16_t vendor; + uint16_t product; + uint16_t version; + uint8_t country; + int rd_size; + void *rd_data; + uint8_t boot_dev; + GIOChannel *ctrl_io; + GIOChannel *intr_io; + guint ctrl_watch; + guint intr_watch; + int uhid_fd; + guint uhid_watch_id; + uint8_t last_hid_msg; +}; + +static int device_cmp(gconstpointer s, gconstpointer user_data) +{ + const struct hid_device *dev = s; + const bdaddr_t *dst = user_data; + + return bacmp(&dev->dst, dst); +} + +static void uhid_destroy(int fd) +{ + struct uhid_event ev; + + /* destroy uHID device */ + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_DESTROY; + + if (write(fd, &ev, sizeof(ev)) < 0) + error("Failed to destroy uHID device: %s (%d)", + strerror(errno), errno); + + close(fd); +} + +static void hid_device_free(struct hid_device *dev) +{ + if (dev->ctrl_watch > 0) + g_source_remove(dev->ctrl_watch); + + if (dev->intr_watch > 0) + g_source_remove(dev->intr_watch); + + if (dev->intr_io) + g_io_channel_unref(dev->intr_io); + + if (dev->ctrl_io) + g_io_channel_unref(dev->ctrl_io); + + if (dev->uhid_watch_id) { + g_source_remove(dev->uhid_watch_id); + dev->uhid_watch_id = 0; + } + + if (dev->uhid_fd > 0) + uhid_destroy(dev->uhid_fd); + + g_free(dev->rd_data); + + devices = g_slist_remove(devices, dev); + g_free(dev); +} + +static void handle_uhid_event(struct hid_device *dev, struct uhid_event *ev) +{ + int fd, i; + uint8_t *req = NULL; + uint8_t req_size = 0; + + if (!(dev->ctrl_io)) + return; + + req_size = 1 + (ev->u.output.size / 2); + req = g_try_malloc0(req_size); + if (!req) + return; + + req[0] = HID_MSG_SET_REPORT | ev->u.output.rtype; + for (i = 0; i < (req_size - 1); i++) + sscanf((char *) &(ev->u.output.data)[i * 2], + "%hhx", &req[1 + i]); + + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, req, req_size) < 0) + error("error writing set_report: %s (%d)", + strerror(errno), errno); + + g_free(req); +} + +static gboolean uhid_event_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct hid_device *dev = user_data; + struct uhid_event ev; + ssize_t bread; + int fd; + + DBG(""); + + if (cond & (G_IO_ERR | G_IO_NVAL)) + goto failed; + + fd = g_io_channel_unix_get_fd(io); + memset(&ev, 0, sizeof(ev)); + + bread = read(fd, &ev, sizeof(ev)); + if (bread < 0) { + DBG("read: %s (%d)", strerror(errno), errno); + goto failed; + } + + DBG("uHID event type %d received", ev.type); + + switch (ev.type) { + case UHID_START: + case UHID_STOP: + /* These are called to start and stop the underlying hardware. + * We open the channels before creating the device so the + * hardware is always ready. No need to handle these. + * The kernel never destroys a device itself! Only an explicit + * UHID_DESTROY request can remove a device. */ + + break; + case UHID_OPEN: + case UHID_CLOSE: + /* OPEN/CLOSE are sent whenever user-space opens any interface + * provided by the kernel HID device. Whenever the open-count + * is non-zero we must be ready for I/O. As long as it is zero, + * we can decide to drop all I/O and put the device + * asleep This is optional, though. */ + break; + case UHID_OUTPUT: + case UHID_FEATURE: + handle_uhid_event(dev, &ev); + break; + case UHID_OUTPUT_EV: + /* This is only sent by kernels prior to linux-3.11. It + * requires us to parse HID-descriptors in user-space to + * properly handle it. This is redundant as the kernel + * does it already. That's why newer kernels assemble + * the output-reports and send it to us via UHID_OUTPUT. */ + DBG("UHID_OUTPUT_EV unsupported"); + break; + default: + warn("unexpected uHID event"); + } + + return TRUE; + +failed: + dev->uhid_watch_id = 0; + return FALSE; +} + +static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data) +{ + struct hid_device *dev = data; + uint8_t buf[UHID_DATA_MAX]; + struct uhid_event ev; + int fd, bread; + + /* Wait uHID if not ready */ + if (dev->uhid_fd < 0) + return TRUE; + + fd = g_io_channel_unix_get_fd(chan); + bread = read(fd, buf, sizeof(buf)); + if (bread < 0) { + error("read: %s(%d)", strerror(errno), -errno); + return TRUE; + } + + /* Discard non-data packets */ + if (bread == 0 || buf[0] != (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) + return TRUE; + + /* send data to uHID device skipping HIDP header byte */ + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT; + ev.u.input.size = bread - 1; + memcpy(ev.u.input.data, &buf[1], ev.u.input.size); + + if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0) + DBG("uhid write: %s (%d)", strerror(errno), errno); + + return TRUE; +} + +static void bt_hid_notify_state(struct hid_device *dev, uint8_t state) +{ + struct hal_ev_hidhost_conn_state ev; + char address[18]; + + if (dev->state == state) + return; + + dev->state = state; + + ba2str(&dev->dst, address); + DBG("device %s state %u", address, 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); +} + +static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct hid_device *dev = data; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) + goto error; + + if (cond & G_IO_IN) + return intr_io_watch_cb(chan, data); + +error: + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + + /* Checking for ctrl_watch avoids a double g_io_channel_shutdown since + * it's likely that ctrl_watch_cb has been queued for dispatching in + * this mainloop iteration */ + if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->ctrl_watch) + g_io_channel_shutdown(chan, TRUE, NULL); + + /* Close control channel */ + if (dev->ctrl_io && !(cond & G_IO_NVAL)) + g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); + + hid_device_free(dev); + + return FALSE; +} + +static void bt_hid_notify_proto_mode(struct hid_device *dev, uint8_t *buf, + int len) +{ + struct hal_ev_hidhost_proto_mode ev; + char address[18]; + + ba2str(&dev->dst, address); + DBG("device %s", address); + + memset(&ev, 0, sizeof(ev)); + bdaddr2android(&dev->dst, ev.bdaddr); + + if (buf[0] == HID_MSG_DATA) { + ev.status = HAL_HIDHOST_STATUS_OK; + if (buf[1] == HID_PROTO_REPORT) + ev.mode = HAL_HIDHOST_REPORT_PROTOCOL; + else if (buf[1] == HID_PROTO_BOOT) + ev.mode = HAL_HIDHOST_BOOT_PROTOCOL; + else + ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL; + + } else { + ev.status = buf[0]; + ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL; + } + + ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_PROTO_MODE, sizeof(ev), &ev, -1); +} + +static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf, + int len) +{ + struct hal_ev_hidhost_get_report *ev; + int ev_len; + char address[18]; + + ba2str(&dev->dst, address); + DBG("device %s", address); + + ev_len = sizeof(*ev) + sizeof(struct hal_ev_hidhost_get_report) + 1; + + if (!((buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) || + (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_OUTPUT)) || + (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_FEATURE)))) { + ev = g_malloc0(ev_len); + ev->status = buf[0]; + bdaddr2android(&dev->dst, ev->bdaddr); + goto send; + } + + /* Report porotocol mode reply contains id after hdr, in boot + * protocol mode id doesn't exist */ + ev_len += (dev->boot_dev) ? (len - 1) : (len - 2); + ev = g_malloc0(ev_len); + ev->status = HAL_HIDHOST_STATUS_OK; + bdaddr2android(&dev->dst, ev->bdaddr); + + /* Report porotocol mode reply contains id after hdr, in boot + * protocol mode id doesn't exist */ + if (dev->boot_dev) { + ev->len = len - 1; + memcpy(ev->data, buf + 1, ev->len); + } else { + ev->len = len - 2; + memcpy(ev->data, buf + 2, ev->len); + } + +send: + ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_GET_REPORT, ev_len, ev, -1); + g_free(ev); +} + +static void bt_hid_notify_virtual_unplug(struct hid_device *dev, + uint8_t *buf, int len) +{ + struct hal_ev_hidhost_virtual_unplug ev; + char address[18]; + + ba2str(&dev->dst, address); + DBG("device %s", address); + bdaddr2android(&dev->dst, ev.bdaddr); + + ev.status = HAL_HIDHOST_GENERAL_ERROR; + + /* Wait either channels to HUP */ + if (dev->intr_io && dev->ctrl_io) { + g_io_channel_shutdown(dev->intr_io, TRUE, NULL); + g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); + ev.status = HAL_HIDHOST_STATUS_OK; + } + + ipc_send(notification_sk, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_VIRTUAL_UNPLUG, sizeof(ev), &ev, -1); + +} + +static gboolean ctrl_io_watch_cb(GIOChannel *chan, gpointer data) +{ + struct hid_device *dev = data; + int fd, bread; + uint8_t buf[UHID_DATA_MAX]; + + DBG(""); + + fd = g_io_channel_unix_get_fd(chan); + bread = read(fd, buf, sizeof(buf)); + if (bread < 0) { + error("read: %s(%d)", strerror(errno), -errno); + return TRUE; + } + + switch (dev->last_hid_msg) { + case HID_MSG_GET_PROTOCOL: + case HID_MSG_SET_PROTOCOL: + bt_hid_notify_proto_mode(dev, buf, bread); + break; + case HID_MSG_GET_REPORT: + bt_hid_notify_get_report(dev, buf, bread); + break; + } + + if (buf[0] == (HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG)) + bt_hid_notify_virtual_unplug(dev, buf, bread); + + /* reset msg type request */ + dev->last_hid_msg = 0; + + return TRUE; +} + +static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct hid_device *dev = data; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) + goto error; + + if (cond & G_IO_IN) + return ctrl_io_watch_cb(chan, data); + +error: + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + + /* Checking for intr_watch avoids a double g_io_channel_shutdown since + * it's likely that intr_watch_cb has been queued for dispatching in + * this mainloop iteration */ + if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->intr_watch) + g_io_channel_shutdown(chan, TRUE, NULL); + + if (dev->intr_io && !(cond & G_IO_NVAL)) + g_io_channel_shutdown(dev->intr_io, TRUE, NULL); + + hid_device_free(dev); + + return FALSE; +} + +static void bt_hid_set_info(struct hid_device *dev) +{ + struct hal_ev_hidhost_info ev; + + DBG(""); + + bdaddr2android(&dev->dst, ev.bdaddr); + ev.attr = 0; /* TODO: Check what is this field */ + ev.subclass = dev->subclass; + ev.app_id = 0; /* TODO: Check what is this field */ + ev.vendor = dev->vendor; + ev.product = dev->product; + ev.version = dev->version; + ev.country = dev->country; + ev.descr_len = dev->rd_size; + 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); +} + +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; + + dev->uhid_fd = open(UHID_DEVICE_FILE, O_RDWR | O_CLOEXEC); + if (dev->uhid_fd < 0) { + error("Failed to open uHID device: %s", strerror(errno)); + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_NO_HID); + return -errno; + } + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + strcpy((char *) ev.u.create.name, "bluez-input-device"); + ev.u.create.bus = BUS_BLUETOOTH; + ev.u.create.vendor = dev->vendor; + ev.u.create.product = dev->product; + ev.u.create.version = dev->version; + ev.u.create.country = dev->country; + ev.u.create.rd_size = dev->rd_size; + ev.u.create.rd_data = dev->rd_data; + + if (write(dev->uhid_fd, &ev, sizeof(ev)) < 0) { + 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; + } + + io = g_io_channel_unix_new(dev->uhid_fd); + g_io_channel_set_encoding(io, NULL, NULL); + dev->uhid_watch_id = g_io_add_watch(io, cond, uhid_event_cb, dev); + g_io_channel_unref(io); + + bt_hid_set_info(dev); + + return 0; +} + +static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, + gpointer user_data) +{ + struct hid_device *dev = user_data; + + DBG(""); + + if (conn_err) { + error("%s", conn_err->message); + goto failed; + } + + if (uhid_create(dev) < 0) + goto failed; + + dev->intr_watch = g_io_add_watch(dev->intr_io, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + intr_watch_cb, dev); + + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED); + + return; + +failed: + hid_device_free(dev); +} + +static void control_connect_cb(GIOChannel *chan, GError *conn_err, + gpointer user_data) +{ + struct hid_device *dev = user_data; + GError *err = NULL; + + DBG(""); + + if (conn_err) { + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + error("%s", conn_err->message); + goto failed; + } + + /* Connect to the HID interrupt channel */ + dev->intr_io = bt_io_connect(interrupt_connect_cb, dev, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (!dev->intr_io) { + error("%s", err->message); + g_error_free(err); + goto failed; + } + + dev->ctrl_watch = g_io_add_watch(dev->ctrl_io, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + ctrl_watch_cb, dev); + + return; + +failed: + hid_device_free(dev); +} + +static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) +{ + struct hid_device *dev = data; + sdp_list_t *list; + GError *gerr = NULL; + + 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_data_t *data; + + data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); + if (data) + dev->vendor = data->val.uint16; + + data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); + if (data) + dev->product = data->val.uint16; + + data = sdp_data_get(rec, SDP_ATTR_VERSION); + if (data) + dev->version = data->val.uint16; + + data = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE); + if (data) + dev->country = data->val.uint8; + + data = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS); + if (data) + dev->subclass = data->val.uint8; + + data = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE); + if (data) + dev->boot_dev = data->val.uint8; + + data = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST); + if (data) { + if (!SDP_IS_SEQ(data->dtd)) + goto fail; + + /* First HIDDescriptor */ + data = data->val.dataseq; + if (!SDP_IS_SEQ(data->dtd)) + goto fail; + + /* ClassDescriptorType */ + data = data->val.dataseq; + if (data->dtd != SDP_UINT8) + goto fail; + + /* ClassDescriptorData */ + data = data->next; + if (!data || !SDP_IS_TEXT_STR(data->dtd)) + goto fail; + + dev->rd_size = data->unitSize; + dev->rd_data = g_memdup(data->val.str, data->unitSize); + } + } + + if (dev->ctrl_io) { + if (uhid_create(dev) < 0) + goto fail; + return; + } + + dev->ctrl_io = bt_io_connect(control_connect_cb, dev, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + goto fail; + } + + return; + +fail: + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + hid_device_free(dev); +} + +static uint8_t bt_hid_connect(struct hal_cmd_hidhost_connect *cmd, + uint16_t len) +{ + struct hid_device *dev; + char addr[18]; + bdaddr_t dst; + GSList *l; + uuid_t uuid; + + 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; + + dev = g_new0(struct hid_device, 1); + bacpy(&dev->dst, &dst); + dev->uhid_fd = -1; + + ba2str(&dev->dst, addr); + DBG("connecting to %s", addr); + + bt_string2uuid(&uuid, HID_UUID); + if (bt_search_service(&adapter_addr, &dev->dst, &uuid, + hid_sdp_search_cb, dev, NULL) < 0) { + error("Failed to search sdp details"); + hid_device_free(dev); + return HAL_STATUS_FAILED; + } + + devices = g_slist_append(devices, dev); + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t bt_hid_disconnect(struct hal_cmd_hidhost_disconnect *cmd, + uint16_t len) +{ + struct hid_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; + + dev = l->data; + + /* Wait either channels to HUP */ + if (dev->intr_io) + g_io_channel_shutdown(dev->intr_io, TRUE, NULL); + + if (dev->ctrl_io) + g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); + + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t bt_hid_virtual_unplug(struct hal_cmd_hidhost_virtual_unplug *cmd, + uint16_t len) +{ + struct hid_device *dev; + GSList *l; + 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; + + dev = l->data; + + if (!(dev->ctrl_io)) + return HAL_STATUS_FAILED; + + hdr = HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG; + + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, &hdr, sizeof(hdr)) < 0) { + error("error writing virtual unplug command: %s (%d)", + strerror(errno), errno); + return HAL_STATUS_FAILED; + } + + /* Wait either channels to HUP */ + if (dev->intr_io) + g_io_channel_shutdown(dev->intr_io, TRUE, NULL); + + if (dev->ctrl_io) + g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); + + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t bt_hid_info(struct hal_cmd_hidhost_set_info *cmd, 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 + * done through the fd. There is no way to use this information + * once device is created with HID internals. */ + DBG("Not supported"); + + return HAL_STATUS_UNSUPPORTED; +} + +static uint8_t bt_hid_get_protocol(struct hal_cmd_hidhost_get_protocol *cmd, + uint16_t len) +{ + struct hid_device *dev; + GSList *l; + bdaddr_t dst; + int fd; + uint8_t hdr; + + 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; + + dev = l->data; + + if (dev->boot_dev) + return HAL_STATUS_UNSUPPORTED; + + hdr = HID_MSG_GET_PROTOCOL | cmd->mode; + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, &hdr, sizeof(hdr)) < 0) { + error("error writing device_get_protocol: %s (%d)", + strerror(errno), errno); + return HAL_STATUS_FAILED; + } + + dev->last_hid_msg = HID_MSG_GET_PROTOCOL; + return HAL_STATUS_SUCCESS; +} + +static uint8_t bt_hid_set_protocol(struct hal_cmd_hidhost_set_protocol *cmd, + uint16_t len) +{ + struct hid_device *dev; + GSList *l; + bdaddr_t dst; + int fd; + uint8_t hdr; + + 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; + + dev = l->data; + + if (dev->boot_dev) + return HAL_STATUS_UNSUPPORTED; + + hdr = HID_MSG_SET_PROTOCOL | cmd->mode; + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, &hdr, sizeof(hdr)) < 0) { + error("error writing device_set_protocol: %s (%d)", + strerror(errno), errno); + return HAL_STATUS_FAILED; + } + + dev->last_hid_msg = HID_MSG_SET_PROTOCOL; + return HAL_STATUS_SUCCESS; +} + +static uint8_t bt_hid_get_report(struct hal_cmd_hidhost_get_report *cmd, + uint16_t len) +{ + struct hid_device *dev; + GSList *l; + bdaddr_t dst; + int fd; + uint8_t *req; + uint8_t req_size; + + 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; + + dev = l->data; + req_size = (cmd->buf_size > 0) ? 4 : 2; + req = g_try_malloc0(req_size); + if (!req) + return HAL_STATUS_NOMEM; + + req[0] = HID_MSG_GET_REPORT | cmd->type; + req[1] = cmd->id; + + if (cmd->buf_size > 0) { + req[0] = req[0] | HID_GET_REPORT_SIZE_FIELD; + bt_put_le16(cmd->buf_size, &req[2]); + } + + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, req, req_size) < 0) { + error("error writing hid_get_report: %s (%d)", + strerror(errno), errno); + g_free(req); + return HAL_STATUS_FAILED; + } + + dev->last_hid_msg = HID_MSG_GET_REPORT; + g_free(req); + return HAL_STATUS_SUCCESS; +} + +static uint8_t bt_hid_set_report(struct hal_cmd_hidhost_set_report *cmd, + uint16_t len) +{ + struct hid_device *dev; + GSList *l; + bdaddr_t dst; + int i, fd; + uint8_t *req; + uint8_t req_size; + + 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; + + dev = l->data; + + if (!(dev->ctrl_io)) + return HAL_STATUS_FAILED; + + req_size = 1 + (cmd->len / 2); + req = g_try_malloc0(req_size); + if (!req) + return HAL_STATUS_NOMEM; + + req[0] = HID_MSG_SET_REPORT | cmd->type; + /* Report data coming to HAL is in ascii format, HAL sends + * data in hex to daemon, so convert to binary. */ + for (i = 0; i < (req_size - 1); i++) + sscanf((char *) &(cmd->data)[i * 2], "%hhx", &(req + 1)[i]); + + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, req, req_size) < 0) { + error("error writing hid_set_report: %s (%d)", + strerror(errno), errno); + g_free(req); + return HAL_STATUS_FAILED; + } + + dev->last_hid_msg = HID_MSG_SET_REPORT; + g_free(req); + return HAL_STATUS_SUCCESS; +} + +static uint8_t bt_hid_send_data(struct hal_cmd_hidhost_send_data *cmd, + uint16_t len) +{ + struct hid_device *dev; + GSList *l; + bdaddr_t dst; + int i, fd; + uint8_t *req; + uint8_t req_size; + + 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; + + dev = l->data; + + if (!(dev->intr_io)) + return HAL_STATUS_FAILED; + + req_size = 1 + (cmd->len / 2); + req = g_try_malloc0(req_size); + if (!req) + return HAL_STATUS_NOMEM; + + req[0] = HID_MSG_DATA | HID_DATA_TYPE_OUTPUT; + /* Report data coming to HAL is in ascii format, HAL sends + * data in hex to daemon, so convert to binary. */ + for (i = 0; i < (req_size - 1); i++) + sscanf((char *) &(cmd->data)[i * 2], "%hhx", &(req + 1)[i]); + + fd = g_io_channel_unix_get_fd(dev->intr_io); + + if (write(fd, req, req_size) < 0) { + error("error writing data to HID device: %s (%d)", + strerror(errno), errno); + g_free(req); + return HAL_STATUS_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; + } + + ipc_send_rsp(sk, HAL_SERVICE_ID_HIDHOST, status); +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + struct hid_device *dev; + bdaddr_t src, dst; + char address[18]; + uint16_t psm; + GError *gerr = NULL; + GSList *l; + uuid_t uuid; + + if (err) { + error("%s", err->message); + return; + } + + bt_io_get(chan, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_PSM, &psm, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_io_channel_shutdown(chan, TRUE, NULL); + g_error_free(gerr); + return; + } + + ba2str(&dst, address); + DBG("Incoming connection from %s on PSM %d", address, psm); + + switch (psm) { + case L2CAP_PSM_HIDP_CTRL: + l = g_slist_find_custom(devices, &dst, device_cmp); + if (l) + return; + + dev = g_new0(struct hid_device, 1); + bacpy(&dev->dst, &dst); + dev->ctrl_io = g_io_channel_ref(chan); + dev->uhid_fd = -1; + + bt_string2uuid(&uuid, HID_UUID); + if (bt_search_service(&src, &dev->dst, &uuid, + hid_sdp_search_cb, dev, NULL) < 0) { + error("failed to search sdp details"); + hid_device_free(dev); + return; + } + + devices = g_slist_append(devices, dev); + + dev->ctrl_watch = g_io_add_watch(dev->ctrl_io, + G_IO_HUP | G_IO_ERR | G_IO_NVAL, + ctrl_watch_cb, dev); + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING); + break; + + case L2CAP_PSM_HIDP_INTR: + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) + return; + + dev = l->data; + dev->intr_io = g_io_channel_ref(chan); + dev->intr_watch = g_io_add_watch(dev->intr_io, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + intr_watch_cb, dev); + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED); + break; + } +} + +bool bt_hid_register(int sk, const bdaddr_t *addr) +{ + GError *err = NULL; + + DBG(""); + + bacpy(&adapter_addr, addr); + + ctrl_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (!ctrl_io) { + error("Failed to listen on ctrl channel: %s", err->message); + g_error_free(err); + return false; + } + + intr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (!intr_io) { + error("Failed to listen on intr channel: %s", err->message); + g_io_channel_unref(ctrl_io); + g_error_free(err); + return false; + } + + notification_sk = sk; + + return true; +} + +void bt_hid_unregister(void) +{ + DBG(""); + + notification_sk = -1; + + if (ctrl_io) { + g_io_channel_shutdown(ctrl_io, TRUE, NULL); + g_io_channel_unref(ctrl_io); + ctrl_io = NULL; + } + + if (intr_io) { + g_io_channel_shutdown(intr_io, TRUE, NULL); + g_io_channel_unref(intr_io); + intr_io = NULL; + } +} diff --git a/android/hidhost.h b/android/hidhost.h new file mode 100644 index 0000000..688086a --- /dev/null +++ b/android/hidhost.h @@ -0,0 +1,27 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +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); +void bt_hid_unregister(void); diff --git a/android/ipc.c b/android/ipc.c new file mode 100644 index 0000000..729f157 --- /dev/null +++ b/android/ipc.c @@ -0,0 +1,90 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#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, + void *param, int fd) +{ + struct msghdr msg; + struct iovec iv[2]; + struct hal_hdr m; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + + memset(&msg, 0, sizeof(msg)); + memset(&m, 0, sizeof(m)); + + m.service_id = service_id; + m.opcode = opcode; + m.len = len; + + iv[0].iov_base = &m; + iv[0].iov_len = sizeof(m); + + iv[1].iov_base = param; + iv[1].iov_len = len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + if (fd >= 0) { + 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(int)); + + /* Initialize the payload */ + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + } + + if (sendmsg(sk, &msg, 0) < 0) { + error("IPC send failed, terminating :%s", strerror(errno)); + raise(SIGTERM); + } +} + +void ipc_send_rsp(int sk, uint8_t service_id, uint8_t status) +{ + struct hal_status s; + + s.code = status; + + ipc_send(sk, service_id, HAL_OP_STATUS, sizeof(s), &s, -1); +} diff --git a/android/ipc.h b/android/ipc.h new file mode 100644 index 0000000..cf0f3d6 --- /dev/null +++ b/android/ipc.h @@ -0,0 +1,26 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +void ipc_send(int sk, 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); diff --git a/android/main.c b/android/main.c index f75b0a8..a4f5e84 100644 --- a/android/main.c +++ b/android/main.c @@ -25,53 +25,514 @@ #include #endif +#include #include #include #include #include #include #include +#include +#include + +#include +#include +#include #include #include "log.h" +#include "src/sdpd.h" + +#include "lib/bluetooth.h" + +#include "bluetooth.h" +#include "socket.h" +#include "hidhost.h" +#include "hal-msg.h" +#include "ipc.h" +#include "a2dp.h" +#include "pan.h" + +/* TODO: Consider to remove PLATFORM_SDKVERSION check if requirement +* for minimal Android platform version increases. */ +#if defined(ANDROID) && PLATFORM_SDK_VERSION >= 18 +#include +#endif +#define STARTUP_GRACE_SECONDS 5 #define SHUTDOWN_GRACE_SECONDS 10 +static guint bluetooth_start_timeout = 0; + +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) +{ + struct hal_cmd_register_module *m = buf; + int sk = g_io_channel_unix_get_fd(hal_notif_io); + + if (m->service_id > HAL_SERVICE_ID_MAX || services[m->service_id]) + goto failed; + + switch (m->service_id) { + case HAL_SERVICE_ID_BLUETOOTH: + if (!bt_bluetooth_register(sk)) + goto failed; + + break; + case HAL_SERVICE_ID_SOCK: + if (!bt_socket_register(sk, &adapter_bdaddr)) + goto failed; + + break; + case HAL_SERVICE_ID_HIDHOST: + if (!bt_hid_register(sk, &adapter_bdaddr)) + goto failed; + + break; + case HAL_SERVICE_ID_A2DP: + if (!bt_a2dp_register(sk, &adapter_bdaddr)) + goto failed; + + break; + case HAL_SERVICE_ID_PAN: + if (!bt_pan_register(sk, &adapter_bdaddr)) + goto failed; + + break; + default: + DBG("service %u not supported", m->service_id); + 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); + + 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); +} + +static void service_unregister(void *buf, uint16_t len) +{ + struct hal_cmd_unregister_module *m = buf; + + if (m->service_id > HAL_SERVICE_ID_MAX || !services[m->service_id]) + goto failed; + + switch (m->service_id) { + 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; + default: + /* This would indicate bug in HAL, as unregister should not be + * called in init failed */ + DBG("service %u not supported", m->service_id); + 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); + + 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); +} + +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 void bluetooth_stopped(void) +{ + g_main_loop_quit(event_loop); +} + static gboolean quit_eventloop(gpointer user_data) { g_main_loop_quit(event_loop); + return FALSE; +} + +static void stop_bluetooth(void) +{ + static bool __stop = false; + + if (__stop) + return; + + __stop = true; + + if (!bt_bluetooth_stop(bluetooth_stopped)) { + g_main_loop_quit(event_loop); + return; + } + + 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 void sig_term(int sig) +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) { + error("Adapter initialization failed: %s", strerror(-err)); + exit(EXIT_FAILURE); + } + + bacpy(&adapter_bdaddr, addr); + + if (bluetooth_start_timeout > 0) { + g_source_remove(bluetooth_start_timeout); + bluetooth_start_timeout = 0; + } + + info("Adapter initialized"); + + hal_cmd_io = connect_hal(cmd_connect_cb); + if (!hal_cmd_io) { + error("Cannot connect to HAL, terminating"); + stop_bluetooth(); + } +} + +static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, + gpointer user_data) { static bool __terminated = false; + struct signalfd_siginfo si; + ssize_t result; + int fd; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) + return FALSE; + + fd = g_io_channel_unix_get_fd(channel); + + result = read(fd, &si, sizeof(si)); + if (result != sizeof(si)) + return FALSE; - if (!__terminated) { - g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, - quit_eventloop, NULL); + switch (si.ssi_signo) { + case SIGINT: + case SIGTERM: + if (!__terminated) { + info("Terminating"); + stop_bluetooth(); + } + + __terminated = true; + break; + } + + return TRUE; +} + +static guint setup_signalfd(void) +{ + GIOChannel *channel; + guint source; + sigset_t mask; + int fd; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { + perror("Failed to set signal mask"); + return 0; + } + + fd = signalfd(-1, &mask, 0); + if (fd < 0) { + perror("Failed to create signal descriptor"); + return 0; } - __terminated = true; + channel = g_io_channel_unix_new(fd); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + signal_handler, NULL); + + g_io_channel_unref(channel); + + return source; } static gboolean option_version = FALSE; +static gint option_index = -1; static GOptionEntry options[] = { { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, "Show version information and exit", NULL }, + { "index", 'i', 0, G_OPTION_ARG_INT, &option_index, + "Use specified controller", "INDEX"}, { NULL } }; +static void cleanup_hal_connection(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; + } + + if (hal_notif_io) { + g_io_channel_shutdown(hal_notif_io, TRUE, NULL); + g_io_channel_unref(hal_notif_io); + hal_notif_io = NULL; + } +} + +static bool set_capabilities(void) +{ +#if defined(ANDROID) + struct __user_cap_header_struct header; + struct __user_cap_data_struct cap; + + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + + cap.effective = cap.permitted = + CAP_TO_MASK(CAP_NET_ADMIN) | + CAP_TO_MASK(CAP_NET_BIND_SERVICE); + cap.inheritable = 0; + + /* TODO: Move to cap_set_proc once bionic support it */ + if (capset(&header, &cap) < 0) { + error("%s: capset(): %s", __func__, strerror(errno)); + return false; + } + + /* TODO: Move to cap_get_proc once bionic support it */ + if (capget(&header, &cap) < 0) { + error("%s: capget(): %s", __func__, strerror(errno)); + return false; + } + + DBG("Caps: eff: 0x%x, perm: 0x%x, inh: 0x%x", cap.effective, + cap.permitted, cap.inheritable); + +#endif + return true; +} + int main(int argc, char *argv[]) { GOptionContext *context; GError *err = NULL; - struct sigaction sa; + 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); @@ -94,19 +555,42 @@ int main(int argc, char *argv[]) } event_loop = g_main_loop_new(NULL, FALSE); + signal = setup_signalfd(); + if (!signal) + return EXIT_FAILURE; + + __btd_log_init("*", 0); + + if (!set_capabilities()) + return EXIT_FAILURE; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = sig_term; - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); + bluetooth_start_timeout = g_timeout_add_seconds(STARTUP_GRACE_SECONDS, + quit_eventloop, NULL); + if (bluetooth_start_timeout == 0) { + error("Failed to init startup timeout"); + return EXIT_FAILURE; + } + + if (!bt_bluetooth_start(option_index, adapter_ready)) + return EXIT_FAILURE; + + /* Use params: mtu = 0, flags = 0 */ + start_sdp_server(0, 0); DBG("Entering main loop"); g_main_loop_run(event_loop); + g_source_remove(signal); + + cleanup_hal_connection(); + stop_sdp_server(); + bt_bluetooth_cleanup(); g_main_loop_unref(event_loop); info("Exit"); + __btd_log_cleanup(); + return EXIT_SUCCESS; } diff --git a/android/pan.c b/android/pan.c new file mode 100644 index 0000000..46b3700 --- /dev/null +++ b/android/pan.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "log.h" +#include "pan.h" +#include "hal-msg.h" +#include "ipc.h" + +static int notification_sk = -1; + +static uint8_t bt_pan_enable(struct hal_cmd_pan_enable *cmd, uint16_t len) +{ + DBG("Not Implemented"); + + return HAL_STATUS_FAILED; +} + +static uint8_t bt_pan_get_role(void *cmd, uint16_t len) +{ + DBG("Not Implemented"); + + return HAL_STATUS_FAILED; +} + +static uint8_t bt_pan_connect(struct hal_cmd_pan_connect *cmd, uint16_t len) +{ + DBG("Not Implemented"); + + return HAL_STATUS_FAILED; +} + +static uint8_t bt_pan_disconnect(struct hal_cmd_pan_connect *cmd, uint16_t len) +{ + DBG("Not Implemented"); + + return HAL_STATUS_FAILED; +} + +void bt_pan_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len) +{ + uint8_t status = HAL_STATUS_FAILED; + + 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); + break; + case HAL_OP_PAN_CONNECT: + status = bt_pan_connect(buf, len); + break; + case HAL_OP_PAN_DISCONNECT: + status = bt_pan_disconnect(buf, len); + break; + default: + DBG("Unhandled command, opcode 0x%x", opcode); + break; + } + + ipc_send_rsp(sk, HAL_SERVICE_ID_PAN, status); +} + +bool bt_pan_register(int sk, const bdaddr_t *addr) +{ + DBG(""); + + notification_sk = sk; + + return true; +} + +void bt_pan_unregister(void) +{ + DBG(""); + + notification_sk = -1; +} diff --git a/android/pan.h b/android/pan.h new file mode 100644 index 0000000..2430378 --- /dev/null +++ b/android/pan.h @@ -0,0 +1,27 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +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); +void bt_pan_unregister(void); diff --git a/android/socket.c b/android/socket.c new file mode 100644 index 0000000..c283c5f --- /dev/null +++ b/android/socket.c @@ -0,0 +1,90 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "lib/bluetooth.h" +#include "log.h" +#include "hal-msg.h" +#include "hal-ipc.h" +#include "ipc.h" +#include "socket.h" + + +static int handle_listen(void *buf) +{ + DBG("Not implemented"); + + return -1; +} + +static int handle_connect(void *buf) +{ + DBG("Not implemented"); + + return -1; +} + +void bt_sock_handle_cmd(int sk, uint8_t opcode, void *buf, uint16_t len) +{ + int fd; + + switch (opcode) { + case HAL_OP_SOCK_LISTEN: + fd = handle_listen(buf); + if (fd < 0) + break; + + ipc_send(sk, HAL_SERVICE_ID_SOCK, opcode, 0, NULL, fd); + return; + case HAL_OP_SOCK_CONNECT: + fd = handle_connect(buf); + if (fd < 0) + break; + + ipc_send(sk, HAL_SERVICE_ID_SOCK, opcode, 0, NULL, fd); + return; + default: + DBG("Unhandled command, opcode 0x%x", opcode); + break; + } + + ipc_send_rsp(sk, HAL_SERVICE_ID_SOCK, HAL_STATUS_FAILED); +} + +bool bt_socket_register(int sk, const bdaddr_t *addr) +{ + DBG(""); + + return true; +} + +void bt_socket_unregister(void) +{ + DBG(""); +} diff --git a/android/socket.h b/android/socket.h new file mode 100644 index 0000000..7aa5574 --- /dev/null +++ b/android/socket.h @@ -0,0 +1,27 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +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_unregister(void); diff --git a/android/system-emulator.c b/android/system-emulator.c new file mode 100644 index 0000000..24f2741 --- /dev/null +++ b/android/system-emulator.c @@ -0,0 +1,173 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "monitor/mainloop.h" + +static char exec_dir[PATH_MAX + 1]; + +static pid_t daemon_pid = -1; + +static void ctl_start(void) +{ + char prg_name[PATH_MAX + 1]; + char *prg_argv[3]; + char *prg_envp[3]; + pid_t pid; + + snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); + + prg_argv[0] = "/usr/bin/valgrind"; + prg_argv[1] = prg_name; + prg_argv[2] = NULL; + + prg_envp[0] = "G_SLICE=always-malloc"; + prg_envp[1] = "G_DEBUG=gc-friendly"; + prg_envp[2] = NULL; + + printf("Starting %s\n", prg_name); + + pid = fork(); + if (pid < 0) { + perror("Failed to fork new process"); + return; + } + + if (pid == 0) { + execve(prg_argv[0], prg_argv, prg_envp); + exit(0); + } + + printf("New process %d created\n", pid); + + daemon_pid = pid; +} + +static void system_socket_callback(int fd, uint32_t events, void *user_data) +{ + char buf[4096]; + ssize_t len; + + if (events & (EPOLLERR | EPOLLHUP)) { + mainloop_remove_fd(fd); + return; + } + + len = read(fd, buf, sizeof(buf)); + if (len < 0) + return; + + printf("Received %s\n", buf); + + if (strcmp(buf, "ctl.start=bluetoothd")) + return; + + if (daemon_pid > 0) + return; + + ctl_start(); +} + +static void signal_callback(int signum, void *user_data) +{ + switch (signum) { + case SIGINT: + case SIGTERM: + mainloop_quit(); + break; + case SIGCHLD: + while (1) { + pid_t pid; + int status; + + pid = waitpid(WAIT_ANY, &status, WNOHANG); + if (pid < 0 || pid == 0) + break; + + printf("Process %d terminated with status=%d\n", + pid, status); + + if (pid == daemon_pid) + daemon_pid = -1; + } + break; + } +} + +int main(int argc, char *argv[]) +{ + static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; + + sigset_t mask; + struct sockaddr_un addr; + int fd; + + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGCHLD); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + printf("Android system emulator ver %s\n", VERSION); + + snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0])); + + fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + perror("Failed to create system socket"); + return EXIT_FAILURE; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to bind system socket"); + close(fd); + return EXIT_FAILURE; + } + + mainloop_add_fd(fd, EPOLLIN, system_socket_callback, NULL, NULL); + + return mainloop_run(); +} diff --git a/android/log.c b/android/utils.h similarity index 53% rename from android/log.c rename to android/utils.h index ce07b82..5b009bc 100644 --- a/android/log.c +++ b/android/utils.h @@ -21,62 +21,13 @@ * */ -#ifdef HAVE_CONFIG_H -#include -#endif -#include -#include -#include -#include -#include - -#include - -void info(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - - vfprintf(stdout, format, ap); - fprintf(stdout, "\n"); - - va_end(ap); -} - -void warn(const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - - vfprintf(stderr, format, ap); - fprintf(stderr, "\n"); - - va_end(ap); -} - -void error(const char *format, ...) +static inline void android2bdaddr(const void *buf, bdaddr_t *dst) { - va_list ap; - - va_start(ap, format); - - vfprintf(stderr, format, ap); - fprintf(stderr, "\n"); - - va_end(ap); + baswap(dst, buf); } -void btd_debug(const char *format, ...) +static inline void bdaddr2android(const bdaddr_t *src, void *buf) { - va_list ap; - - va_start(ap, format); - - vfprintf(stdout, format, ap); - fprintf(stdout, "\n"); - - va_end(ap); + baswap(buf, src); } diff --git a/configure b/configure index 2bb5b0f..76c63e7 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for bluez 5.10. +# Generated by GNU Autoconf 2.69 for bluez 5.11. # # # 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.10' -PACKAGE_STRING='bluez 5.10' +PACKAGE_VERSION='5.11' +PACKAGE_STRING='bluez 5.11' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1397,7 +1397,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.10 to adapt to many kinds of systems. +\`configure' configures bluez 5.11 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1467,7 +1467,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bluez 5.10:";; + short | recursive ) echo "Configuration of bluez 5.11:";; esac cat <<\_ACEOF @@ -1615,7 +1615,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bluez configure 5.10 +bluez configure 5.11 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1980,7 +1980,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.10, which was +It was created by bluez $as_me 5.11, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2835,7 +2835,7 @@ fi # Define the identity of the package. PACKAGE='bluez' - VERSION='5.10' + VERSION='5.11' cat >>confdefs.h <<_ACEOF @@ -14195,7 +14195,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.10, which was +This file was extended by bluez $as_me 5.11, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14261,7 +14261,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.10 +bluez config.status 5.11 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index b4d3998..949846e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT(bluez, 5.10) +AC_INIT(bluez, 5.11) AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules tar-pax no-dist-gzip dist-xz]) diff --git a/doc/obex-api.txt b/doc/obex-api.txt index c3f5243..1f22fea 100644 --- a/doc/obex-api.txt +++ b/doc/obex-api.txt @@ -801,7 +801,7 @@ Properties string Folder [readonly] boolean Deleted [writeonly] - Message read flag + Message deleted flag boolean Sent [readonly] diff --git a/emulator/bthost.c b/emulator/bthost.c index da56b5c..0c5836f 100644 --- a/emulator/bthost.c +++ b/emulator/bthost.c @@ -60,11 +60,19 @@ struct cmd_queue { struct cmd *tail; }; +struct cid_hook { + uint16_t cid; + bthost_cid_hook_func_t func; + void *user_data; + struct cid_hook *next; +}; + struct btconn { uint16_t handle; uint8_t addr_type; uint16_t next_cid; struct l2conn *l2conns; + struct cid_hook *cid_hooks; struct btconn *next; }; @@ -123,6 +131,13 @@ static void btconn_free(struct btconn *conn) l2conn_free(l2conn); } + while (conn->cid_hooks) { + struct cid_hook *hook = conn->cid_hooks; + + conn->cid_hooks = hook->next; + free(hook); + } + free(conn); } @@ -311,6 +326,42 @@ static uint8_t l2cap_sig_send(struct bthost *bthost, struct btconn *conn, return ident; } +void bthost_add_cid_hook(struct bthost *bthost, uint16_t handle, uint16_t cid, + bthost_cid_hook_func_t func, void *user_data) +{ + struct cid_hook *hook; + struct btconn *conn; + + conn = bthost_find_conn(bthost, handle); + if (!conn) + return; + + hook = malloc(sizeof(*hook)); + if (!hook) + return; + + memset(hook, 0, sizeof(*hook)); + + hook->cid = cid; + hook->func = func; + hook->user_data = user_data; + + hook->next = conn->cid_hooks; + conn->cid_hooks = hook; +} + +void bthost_send_cid(struct bthost *bthost, uint16_t handle, uint16_t cid, + const void *data, uint16_t len) +{ + struct btconn *conn; + + conn = bthost_find_conn(bthost, handle); + if (!conn) + return; + + send_acl(bthost, handle, cid, data, len); +} + bool bthost_l2cap_req(struct bthost *bthost, uint16_t handle, uint8_t code, const void *data, uint16_t len, bthost_l2cap_rsp_cb cb, void *user_data) @@ -973,11 +1024,24 @@ reject: &rej, sizeof(rej)); } +static struct cid_hook *find_cid_hook(struct btconn *conn, uint16_t cid) +{ + struct cid_hook *hook; + + for (hook = conn->cid_hooks; hook != NULL; hook = hook->next) { + if (hook->cid == cid) + return hook; + } + + return NULL; +} + static void process_acl(struct bthost *bthost, const void *data, uint16_t len) { const struct bt_hci_acl_hdr *acl_hdr = data; const struct bt_l2cap_hdr *l2_hdr = data + sizeof(*acl_hdr); uint16_t handle, cid, acl_len, l2_len; + struct cid_hook *hook; struct btconn *conn; const void *l2_data; @@ -1003,6 +1067,12 @@ static void process_acl(struct bthost *bthost, const void *data, uint16_t len) cid = le16_to_cpu(l2_hdr->cid); + hook = find_cid_hook(conn, cid); + if (hook) { + hook->func(l2_data, l2_len, hook->user_data); + return; + } + switch (cid) { case 0x0001: l2cap_sig(bthost, conn, l2_data, l2_len); diff --git a/emulator/bthost.h b/emulator/bthost.h index cd5bf1c..32b10f9 100644 --- a/emulator/bthost.h +++ b/emulator/bthost.h @@ -52,6 +52,15 @@ void bthost_set_connect_cb(struct bthost *bthost, bthost_new_conn_cb cb, void bthost_hci_connect(struct bthost *bthost, const uint8_t *bdaddr, uint8_t addr_type); +typedef void (*bthost_cid_hook_func_t)(const void *data, uint16_t len, + void *user_data); + +void bthost_add_cid_hook(struct bthost *bthost, uint16_t handle, uint16_t cid, + bthost_cid_hook_func_t func, void *user_data); + +void bthost_send_cid(struct bthost *bthost, uint16_t handle, uint16_t cid, + const void *data, uint16_t len); + typedef void (*bthost_l2cap_rsp_cb) (uint8_t code, const void *data, uint16_t len, void *user_data); diff --git a/gdbus/object.c b/gdbus/object.c index 268fed5..b248cbb 100644 --- a/gdbus/object.c +++ b/gdbus/object.c @@ -1006,6 +1006,8 @@ static gboolean process_changes(gpointer user_data) if (data->removed != NULL) emit_interfaces_removed(data); + data->process_id = 0; + return FALSE; } @@ -1019,6 +1021,7 @@ static void generic_unregister(DBusConnection *connection, void *user_data) if (data->process_id > 0) { g_source_remove(data->process_id); + data->process_id = 0; process_changes(data); } diff --git a/lib/bluetooth.c b/lib/bluetooth.c index 05fe2c3..5598ccf 100644 --- a/lib/bluetooth.c +++ b/lib/bluetooth.c @@ -757,6 +757,42 @@ const char *bt_compidtostr(int compid) return "VSN Technologies Inc."; case 248: return "AceUni Corp., Ltd."; + case 249: + return "StickNFind"; + case 250: + return "Crystal Code AB"; + case 251: + return "KOUKAAM a.s."; + case 252: + return "Delphi Corporation"; + case 253: + return "ValenceTech Limited"; + case 254: + return "Reserved"; + case 255: + return "Typo Products, LLC"; + case 256: + return "TomTom International BV"; + case 257: + return "Fugoo, Inc"; + case 258: + return "Keiser Corporation"; + case 259: + return "Bang & Olufsen A/S"; + case 260: + return "PLUS Locations Systems Pty Ltd"; + case 261: + return "Ubiquitous Computing Technology Corporation"; + case 262: + return "Innovative Yachtter Solutions"; + case 263: + return "William Demant Holding A/S"; + case 264: + return "Chicony Electronics Co., Ltd."; + case 265: + return "Atus BV"; + case 266: + return "Codegate Ltd."; case 65535: return "internal use"; default: diff --git a/monitor/bt.h b/monitor/bt.h index 2245ab3..2aec950 100644 --- a/monitor/bt.h +++ b/monitor/bt.h @@ -706,6 +706,75 @@ struct bt_hci_cmd_write_voice_setting { uint16_t setting; } __attribute__ ((packed)); +#define BT_HCI_CMD_READ_AUTO_FLUSH_TIMEOUT 0x0c27 +struct bt_hci_cmd_read_auto_flush_timeout { + uint16_t handle; +} __attribute__ ((packed)); +struct bt_hci_rsp_read_auto_flush_timeout { + uint8_t status; + uint16_t handle; + uint16_t timeout; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_WRITE_AUTO_FLUSH_TIMEOUT 0x0c28 +struct bt_hci_cmd_write_auto_flush_timeout { + uint16_t handle; + uint16_t timeout; +} __attribute__ ((packed)); +struct bt_hci_rsp_write_auto_flush_timeout { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_READ_NUM_BROADCAST_RETRANS 0x0c29 +struct bt_hci_rsp_read_num_broadcast_retrans { + uint8_t status; + uint8_t num_retrans; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_WRITE_NUM_BROADCAST_RETRANS 0x0c2a +struct bt_hci_cmd_write_num_broadcast_retrans { + uint8_t num_retrans; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_READ_HOLD_MODE_ACTIVITY 0x0c2b +struct bt_hci_rsp_read_hold_mode_activity { + uint8_t status; + uint8_t activity; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_WRITE_HOLD_MODE_ACTIVITY 0x0c2c +struct bt_hci_cmd_write_hold_mode_activity { + uint8_t activity; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_READ_TX_POWER 0x0c2d +struct bt_hci_cmd_read_tx_power { + uint16_t handle; + uint8_t type; +} __attribute__ ((packed)); +struct bt_hci_rsp_read_tx_power { + uint8_t status; + uint16_t handle; + int8_t level; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_READ_SYNC_FLOW_CONTROL 0x0c2e +struct bt_hci_rsp_read_sync_flow_control { + uint8_t status; + uint8_t enable; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_WRITE_SYNC_FLOW_CONTROL 0x0c2f +struct bt_hci_cmd_write_sync_flow_control { + uint8_t enable; +} __attribute__ ((packed)); + +#define BT_HCI_CMD_SET_HOST_FLOW_CONTROL 0x0c31 +struct bt_hci_cmd_set_host_flow_control { + uint8_t enable; +} __attribute__ ((packed)); + #define BT_HCI_CMD_HOST_BUFFER_SIZE 0x0c33 struct bt_hci_cmd_host_buffer_size { uint16_t acl_mtu; @@ -877,6 +946,16 @@ struct bt_hci_cmd_enhanced_flush { uint8_t type; } __attribute__ ((packed)); +#define BT_HCI_CMD_SEND_KEYPRESS_NOTIFY 0x0c60 +struct bt_hci_cmd_send_keypress_notify { + uint8_t bdaddr[6]; + uint8_t type; +} __attribute__ ((packed)); +struct bt_hci_rsp_send_keypress_notify { + uint8_t status; + uint8_t bdaddr[6]; +} __attribute__ ((packed)); + #define BT_HCI_CMD_SET_EVENT_MASK_PAGE2 0x0c63 struct bt_hci_cmd_set_event_mask_page2 { uint8_t mask[8]; @@ -1107,6 +1186,11 @@ struct bt_hci_rsp_write_remote_amp_assoc { #define BT_HCI_CMD_ENABLE_DUT_MODE 0x1803 +#define BT_HCI_CMD_WRITE_SSP_DEBUG_MODE 0x1804 +struct bt_hci_cmd_write_ssp_debug_mode { + uint8_t mode; +} __attribute__ ((packed)); + #define BT_HCI_CMD_LE_SET_EVENT_MASK 0x2001 struct bt_hci_cmd_le_set_event_mask { uint8_t mask[8]; diff --git a/monitor/display.c b/monitor/display.c index b8dce1f..af4171f 100644 --- a/monitor/display.c +++ b/monitor/display.c @@ -58,10 +58,10 @@ int num_columns(void) if (__builtin_expect(!!(cached_num_columns < 0), 0)) { struct winsize ws; - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) - return -1; - - if (ws.ws_col > 0) + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0 || + ws.ws_col == 0) + cached_num_columns = FALLBACK_TERMINAL_WIDTH; + else cached_num_columns = ws.ws_col; } diff --git a/monitor/display.h b/monitor/display.h index 6139cc2..885eb34 100644 --- a/monitor/display.h +++ b/monitor/display.h @@ -40,6 +40,8 @@ bool use_color(void); #define COLOR_ERROR "\x1B[1;31m" +#define FALLBACK_TERMINAL_WIDTH 80 + #define print_indent(indent, color1, prefix, title, color2, fmt, args...) \ do { \ printf("%*c%s%s%s%s" fmt "%s\n", (indent), ' ', \ diff --git a/monitor/main.c b/monitor/main.c index e72b240..aed9b03 100644 --- a/monitor/main.c +++ b/monitor/main.c @@ -70,6 +70,7 @@ static const struct option main_options[] = { { "time", no_argument, NULL, 't' }, { "date", no_argument, NULL, 'T' }, { "sco", no_argument, NULL, 'S' }, + { "todo", no_argument, NULL, '#' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } @@ -126,6 +127,9 @@ int main(int argc, char *argv[]) case 'S': filter_mask |= PACKET_FILTER_SHOW_SCO_DATA; break; + case '#': + packet_todo(); + return EXIT_SUCCESS; case 'v': printf("%s\n", VERSION); return EXIT_SUCCESS; diff --git a/monitor/packet.c b/monitor/packet.c index 55c9f0f..0efa60b 100644 --- a/monitor/packet.c +++ b/monitor/packet.c @@ -599,7 +599,7 @@ static const struct { const char *str; } major_class_av_table[] = { { 0x00, "Uncategorized, code for device not assigned" }, - { 0x01, "earable Headset Device" }, + { 0x01, "Wearable Headset Device" }, { 0x02, "Hands-free Device" }, { 0x04, "Microphone" }, { 0x05, "Loudspeaker" }, @@ -740,6 +740,96 @@ static void print_dev_class(const uint8_t *dev_class) " Unknown service class (0x%2.2x)", mask); } +static void print_num_broadcast_retrans(uint8_t num_retrans) +{ + print_field("Number of broadcast retransmissions: %u", num_retrans); +} + +static void print_hold_mode_activity(uint8_t activity) +{ + print_field("Activity: 0x%2.2x", activity); + + if (activity == 0x00) { + print_field(" Maintain current Power State"); + return; + } + + if (activity & 0x01) + print_field(" Suspend Page Scan"); + if (activity & 0x02) + print_field(" Suspend Inquiry Scan"); + if (activity & 0x04) + print_field(" Suspend Periodic Inquiries"); +} + +static void print_power_type(uint8_t type) +{ + const char *str; + + switch (type) { + case 0x00: + str = "Current Transmit Power Level"; + break; + case 0x01: + str = "Maximum Transmit Power Level"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Type: %s (0x%2.2x)", str, type); +} + +static void print_power_level(int8_t level) +{ + print_field("TX power: %d dBm", level); +} + +static void print_sync_flow_control(uint8_t enable) +{ + const char *str; + + switch (enable) { + case 0x00: + str = "Disabled"; + break; + case 0x01: + str = "Enabled"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Flow control: %s (0x%2.2x)", str, enable); +} + +static void print_host_flow_control(uint8_t enable) +{ + const char *str; + + switch (enable) { + case 0x00: + str = "Off"; + break; + case 0x01: + str = "ACL Data Packets"; + break; + case 0x02: + str = "Synchronous Data Packets"; + break; + case 0x03: + str = "ACL and Synchronous Data Packets"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Flow control: %s (0x%2.2x)", str, enable); +} + static void print_voice_setting(uint16_t setting) { print_field("Setting: 0x%4.4x", btohs(setting)); @@ -797,7 +887,23 @@ static void print_scan_enable(uint8_t scan_enable) static void print_link_policy(uint16_t link_policy) { - print_field("Link policy: 0x%4.4x", btohs(link_policy)); + uint16_t policy = btohs(link_policy); + + print_field("Link policy: 0x%4.4x", policy); + + if (policy == 0x0000) { + print_field(" Disable All Modes"); + return; + } + + if (policy & 0x0001) + print_field(" Enable Role Switch"); + if (policy & 0x0002) + print_field(" Enable Hold Mode"); + if (policy & 0x0004) + print_field(" Enable Sniff Mode"); + if (policy & 0x0008) + print_field(" Enabled Park State"); } static void print_air_mode(uint8_t mode) @@ -924,6 +1030,25 @@ static void print_simple_pairing_mode(uint8_t mode) print_field("Mode: %s (0x%2.2x)", str, mode); } +static void print_ssp_debug_mode(uint8_t mode) +{ + const char *str; + + switch (mode) { + case 0x00: + str = "Disabled"; + break; + case 0x01: + str = "Enabled"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Debug mode: %s (0x%2.2x)", str, mode); +} + static void print_pscan_rep_mode(uint8_t pscan_rep_mode) { const char *str; @@ -1589,6 +1714,14 @@ static void print_channel_map(const uint8_t *map) print_field("Channel map: 0x%s", str); } +static void print_flush_timeout(uint16_t timeout) +{ + if (timeout) + print_timeout(timeout); + else + print_field("Timeout: No Automatic Flush"); +} + void packet_print_version(const char *label, uint8_t version, const char *sublabel, uint16_t subversion) { @@ -2180,7 +2313,7 @@ static void print_manufacturer_apple(const void *data, uint8_t data_len) break; case 0x02: len = *((uint8_t *) (data + 1)); - if (len != 0x15) { + if (len != 0x15 || len != data_len - 2) { print_hex_field(" Data", data, data_len); break; } @@ -2221,6 +2354,39 @@ static void print_manufacturer_data(const void *data, uint8_t data_len) } } +static void print_device_id(const void *data, uint8_t data_len) +{ + uint16_t source; + const char *str; + + if (data_len < 8) + return; + + source = bt_get_le16(data); + + switch (source) { + case 0x0001: + str = "Bluetooth SIG assigned"; + break; + case 0x0002: + str = "USB Implementer's Forum assigned"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Device ID: %s (0x%4.4x)", str, source); + + if (source == 0x0001) + packet_print_company(" Vendor", bt_get_le16(data + 2)); + else + print_field(" Vendor: 0x%4.4x", bt_get_le16(data + 2)); + + print_field(" Product: 0x%4.4x", bt_get_le16(data + 4)); + print_field(" Version: 0x%4.4x", bt_get_le16(data + 6)); +} + static void print_uuid16_list(const char *label, const void *data, uint8_t data_len) { @@ -2422,15 +2588,7 @@ static void print_eir(const uint8_t *eir, uint8_t eir_len, bool le) if (le) print_hex_field("SMP TK", data, data_len); else if (data_len >= 8) - print_field("Device ID: " - "Source 0x%4.4x " - "Vendor 0x%4.4x " - "Product 0x%4.4x " - "Version 0x%4.4x", - bt_get_le16(&data[0]), - bt_get_le16(&data[2]), - bt_get_le16(&data[4]), - bt_get_le16(&data[6])); + print_device_id(data, data_len); break; case BT_EIR_SMP_OOB_FLAGS: @@ -3601,6 +3759,107 @@ static void write_voice_setting_cmd(const void *data, uint8_t size) print_voice_setting(cmd->setting); } +static void read_auto_flush_timeout_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_read_auto_flush_timeout *cmd = data; + + print_handle(cmd->handle); +} + +static void read_auto_flush_timeout_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_read_auto_flush_timeout *rsp = data; + + print_status(rsp->status); + print_handle(rsp->handle); + print_flush_timeout(rsp->timeout); +} + +static void write_auto_flush_timeout_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_write_auto_flush_timeout *cmd = data; + + print_handle(cmd->handle); + print_flush_timeout(cmd->timeout); +} + +static void write_auto_flush_timeout_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_write_auto_flush_timeout *rsp = data; + + print_status(rsp->status); + print_handle(rsp->handle); +} + +static void read_num_broadcast_retrans_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_read_num_broadcast_retrans *rsp = data; + + print_status(rsp->status); + print_num_broadcast_retrans(rsp->num_retrans); +} + +static void write_num_broadcast_retrans_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_write_num_broadcast_retrans *cmd = data; + + print_num_broadcast_retrans(cmd->num_retrans); +} + +static void read_hold_mode_activity_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_read_hold_mode_activity *rsp = data; + + print_status(rsp->status); + print_hold_mode_activity(rsp->activity); +} + +static void write_hold_mode_activity_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_write_hold_mode_activity *cmd = data; + + print_hold_mode_activity(cmd->activity); +} + +static void read_tx_power_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_read_tx_power *cmd = data; + + print_handle(cmd->handle); + print_power_type(cmd->type); +} + +static void read_tx_power_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_read_tx_power *rsp = data; + + print_status(rsp->status); + print_handle(rsp->handle); + print_power_level(rsp->level); +} + +static void read_sync_flow_control_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_read_sync_flow_control *rsp = data; + + print_status(rsp->status); + print_sync_flow_control(rsp->enable); +} + +static void write_sync_flow_control_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_write_sync_flow_control *cmd = data; + + print_sync_flow_control(cmd->enable); +} + +static void set_host_flow_control_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_set_host_flow_control *cmd = data; + + print_host_flow_control(cmd->enable); +} + static void host_buffer_size_cmd(const void *data, uint8_t size) { const struct bt_hci_cmd_host_buffer_size *cmd = data; @@ -3824,14 +4083,14 @@ static void read_inquiry_resp_tx_power_rsp(const void *data, uint8_t size) const struct bt_hci_rsp_read_inquiry_resp_tx_power *rsp = data; print_status(rsp->status); - print_field("TX power: %d dBm", rsp->level); + print_power_level(rsp->level); } static void write_inquiry_tx_power_cmd(const void *data, uint8_t size) { const struct bt_hci_cmd_write_inquiry_tx_power *cmd = data; - print_field("TX power: %d dBm", cmd->level); + print_power_level(cmd->level); } static void enhanced_flush_cmd(const void *data, uint8_t size) @@ -3853,6 +4112,45 @@ static void enhanced_flush_cmd(const void *data, uint8_t size) print_field("Type: %s (0x%2.2x)", str, cmd->type); } +static void send_keypress_notify_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_send_keypress_notify *cmd = data; + const char *str; + + print_bdaddr(cmd->bdaddr); + + switch (cmd->type) { + case 0x00: + str = "Passkey entry started"; + break; + case 0x01: + str = "Passkey digit entered"; + break; + case 0x02: + str = "Passkey digit erased"; + break; + case 0x03: + str = "Passkey cleared"; + break; + case 0x04: + str = "Passkey entry completed"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Type: %s (0x%2.2x)", str, cmd->type); +} + +static void send_keypress_notify_rsp(const void *data, uint8_t size) +{ + const struct bt_hci_rsp_send_keypress_notify *rsp = data; + + print_status(rsp->status); + print_bdaddr(rsp->bdaddr); +} + static void set_event_mask_page2_cmd(const void *data, uint8_t size) { const struct bt_hci_cmd_set_event_mask_page2 *cmd = data; @@ -4226,6 +4524,13 @@ static void write_remote_amp_assoc_rsp(const void *data, uint8_t size) print_phy_handle(rsp->phy_handle); } +static void write_ssp_debug_mode_cmd(const void *data, uint8_t size) +{ + const struct bt_hci_cmd_write_ssp_debug_mode *cmd = data; + + print_ssp_debug_mode(cmd->mode); +} + static void le_set_event_mask_cmd(const void *data, uint8_t size) { const struct bt_hci_cmd_le_set_event_mask *cmd = data; @@ -4334,6 +4639,7 @@ static void le_set_adv_parameters_cmd(const void *data, uint8_t size) case 0x03: str = "Allow Scan Request from White List Only, " "Allow Connect Request from White List Only"; + break; default: str = "Reserved"; break; @@ -4347,7 +4653,7 @@ static void le_read_adv_tx_power_rsp(const void *data, uint8_t size) const struct bt_hci_rsp_le_read_adv_tx_power *rsp = data; print_status(rsp->status); - print_field("TX power: %d dBm", rsp->level); + print_power_level(rsp->level); } static void le_set_adv_data_cmd(const void *data, uint8_t size) @@ -4922,16 +5228,36 @@ static const struct opcode_data opcode_table[] = { { 0x0c26, 75, "Write Voice Setting", write_voice_setting_cmd, 2, true, status_rsp, 1, true }, - { 0x0c27, 76, "Read Automatic Flush Timeout" }, - { 0x0c28, 77, "Write Automatic Flush Timeout" }, - { 0x0c29, 78, "Read Num Broadcast Retransmissions" }, - { 0x0c2a, 79, "Write Num Broadcast Retransmissions" }, - { 0x0c2b, 80, "Read Hold Mode Activity" }, - { 0x0c2c, 81, "Write Hold Mode Activity" }, - { 0x0c2d, 82, "Read Transmit Power Level" }, - { 0x0c2e, 83, "Read Sync Flow Control Enable" }, - { 0x0c2f, 84, "Write Sync Flow Control Enable" }, - { 0x0c31, 85, "Set Host Controller To Host Flow" }, + { 0x0c27, 76, "Read Automatic Flush Timeout", + read_auto_flush_timeout_cmd, 2, true, + read_auto_flush_timeout_rsp, 5, true }, + { 0x0c28, 77, "Write Automatic Flush Timeout", + write_auto_flush_timeout_cmd, 4, true, + write_auto_flush_timeout_rsp, 3, true }, + { 0x0c29, 78, "Read Num Broadcast Retransmissions", + null_cmd, 0, true, + read_num_broadcast_retrans_rsp, 2, true }, + { 0x0c2a, 79, "Write Num Broadcast Retransmissions", + write_num_broadcast_retrans_cmd, 1, true, + status_rsp, 1, true }, + { 0x0c2b, 80, "Read Hold Mode Activity", + null_cmd, 0, true, + read_hold_mode_activity_rsp, 2, true }, + { 0x0c2c, 81, "Write Hold Mode Activity", + write_hold_mode_activity_cmd, 1, true, + status_rsp, 1, true }, + { 0x0c2d, 82, "Read Transmit Power Level", + read_tx_power_cmd, 3, true, + read_tx_power_rsp, 4, true }, + { 0x0c2e, 83, "Read Sync Flow Control Enable", + null_cmd, 0, true, + read_sync_flow_control_rsp, 2, true }, + { 0x0c2f, 84, "Write Sync Flow Control Enable", + write_sync_flow_control_cmd, 1, true, + status_rsp, 1, true }, + { 0x0c31, 85, "Set Controller To Host Flow Control", + set_host_flow_control_cmd, 1, true, + status_rsp, 1, true }, { 0x0c33, 86, "Host Buffer Size", host_buffer_size_cmd, 7, true, status_rsp, 1, true }, @@ -5017,7 +5343,9 @@ static const struct opcode_data opcode_table[] = { { 0x0c5b, 147, "Write Default Erroneous Reporting" }, { 0x0c5f, 158, "Enhanced Flush", enhanced_flush_cmd, 3, true }, - { 0x0c60, 162, "Send Keypress Notification" }, + { 0x0c60, 162, "Send Keypress Notification", + send_keypress_notify_cmd, 7, true, + send_keypress_notify_rsp, 7, true }, { 0x0c61, 176, "Read Logical Link Accept Timeout" }, { 0x0c62, 177, "Write Logical Link Accept Timeout" }, { 0x0c63, 178, "Set Event Mask Page 2", @@ -5126,7 +5454,9 @@ static const struct opcode_data opcode_table[] = { { 0x1803, 130, "Enable Device Under Test Mode", null_cmd, 0, true, status_rsp, 1, true }, - { 0x1804, 157, "Write Simple Pairing Debug Mode" }, + { 0x1804, 157, "Write Simple Pairing Debug Mode", + write_ssp_debug_mode_cmd, 1, true, + status_rsp, 1, true }, { 0x1807, 189, "Enable AMP Receiver Reports" }, { 0x1808, 190, "AMP Test End" }, { 0x1809, 191, "AMP Test" }, @@ -6472,3 +6802,36 @@ void packet_hci_scodata(struct timeval *tv, uint16_t index, bool in, if (filter_mask & PACKET_FILTER_SHOW_SCO_DATA) packet_hexdump(data, size); } + +void packet_todo(void) +{ + int i; + + printf("HCI commands with missing decodings:\n"); + + for (i = 0; opcode_table[i].str; i++) { + if (opcode_table[i].bit < 0) + continue; + + if (opcode_table[i].cmd_func) + continue; + + printf("\t%s\n", opcode_table[i].str); + } + + printf("HCI events with missing decodings:\n"); + + for (i = 0; event_table[i].str; i++) { + if (event_table[i].func) + continue; + + printf("\t%s\n", event_table[i].str); + } + + for (i = 0; subevent_table[i].str; i++) { + if (subevent_table[i].func) + continue; + + printf("\t%s\n", subevent_table[i].str); + } +} diff --git a/monitor/packet.h b/monitor/packet.h index 07c1ec3..d44849c 100644 --- a/monitor/packet.h +++ b/monitor/packet.h @@ -67,3 +67,5 @@ void packet_hci_acldata(struct timeval *tv, uint16_t index, bool in, const void *data, uint16_t size); void packet_hci_scodata(struct timeval *tv, uint16_t index, bool in, const void *data, uint16_t size); + +void packet_todo(void); diff --git a/obexd/src/main.c b/obexd/src/main.c index 61a06b2..80645f8 100644 --- a/obexd/src/main.c +++ b/obexd/src/main.c @@ -50,8 +50,6 @@ #include "obexd.h" #include "server.h" -#define DEFAULT_ROOT_PATH "/tmp" - #define DEFAULT_CAP_FILE CONFIGDIR "/capability.xml" static GMainLoop *main_loop = NULL; @@ -167,7 +165,7 @@ static GOptionEntry options[] = { "Specify root folder location. Both absolute " "and relative can be used, but relative paths " "are assumed to be relative to user $HOME " - "folder", "PATH" }, + "folder. Default $XDG_CACHE_HOME", "PATH" }, { "root-setup", 'S', 0, G_OPTION_ARG_STRING, &option_root_setup, "Root folder setup script", "SCRIPT" }, { "symlinks", 'l', 0, G_OPTION_ARG_NONE, &option_symlinks, @@ -285,8 +283,11 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if (option_root == NULL) - option_root = g_strdup(DEFAULT_ROOT_PATH); + if (option_root == NULL) { + option_root = g_build_filename(g_get_user_cache_dir(), "obexd", + NULL); + g_mkdir_with_parents(option_root, 0700); + } if (option_root[0] != '/') { char *old_root = option_root, *home = getenv("HOME"); diff --git a/plugins/wiimote.c b/plugins/wiimote.c index beda307..6cc21ee 100644 --- a/plugins/wiimote.c +++ b/plugins/wiimote.c @@ -59,14 +59,16 @@ */ static uint16_t wii_ids[][2] = { - { 0x057e, 0x0306 }, - { 0x057e, 0x0330 }, + { 0x057e, 0x0306 }, /* 1st gen */ + { 0x054c, 0x0306 }, /* LEGO wiimote */ + { 0x057e, 0x0330 }, /* 2nd gen */ }; static const char *wii_names[] = { - "Nintendo RVL-CNT-01", - "Nintendo RVL-CNT-01-TR", - "Nintendo RVL-WBC-01", + "Nintendo RVL-CNT-01", /* 1st gen */ + "Nintendo RVL-CNT-01-TR", /* 2nd gen */ + "Nintendo RVL-CNT-01-UC", /* Wii U Pro Controller */ + "Nintendo RVL-WBC-01", /* Balance Board */ }; static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device, diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c index 8477b5d..864cb18 100644 --- a/profiles/audio/a2dp.c +++ b/profiles/audio/a2dp.c @@ -1187,17 +1187,8 @@ static struct a2dp_server *find_server(GSList *list, struct btd_adapter *a) static struct a2dp_server *a2dp_server_register(struct btd_adapter *adapter) { struct a2dp_server *server; - int av_err; server = g_new0(struct a2dp_server, 1); - - av_err = avdtp_init(adapter); - if (av_err < 0) { - DBG("AVDTP not registered"); - g_free(server); - return NULL; - } - server->adapter = btd_adapter_ref(adapter); servers = g_slist_append(servers, server); @@ -1217,8 +1208,6 @@ static void a2dp_unregister_sep(struct a2dp_sep *sep) static void a2dp_server_unregister(struct a2dp_server *server) { - avdtp_exit(server->adapter); - servers = g_slist_remove(servers, server); btd_adapter_unref(server->adapter); g_free(server); @@ -1872,10 +1861,22 @@ static void a2dp_sink_remove(struct btd_service *service) static int a2dp_source_connect(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); + struct btd_adapter *adapter = device_get_adapter(dev); + struct a2dp_server *server; const char *path = device_get_path(dev); DBG("path %s", path); + server = find_server(servers, adapter); + if (!server || !server->sink_enabled) { + DBG("Unexpected error: cannot find server"); + return -EPROTONOSUPPORT; + } + + /* Return protocol not available if no record/endpoint exists */ + if (server->sink_record_id == 0) + return -ENOPROTOOPT; + return source_connect(service); } @@ -1892,10 +1893,22 @@ static int a2dp_source_disconnect(struct btd_service *service) static int a2dp_sink_connect(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); + struct btd_adapter *adapter = device_get_adapter(dev); + struct a2dp_server *server; const char *path = device_get_path(dev); DBG("path %s", path); + server = find_server(servers, adapter); + if (!server || !server->source_enabled) { + DBG("Unexpected error: cannot find server"); + return -EPROTONOSUPPORT; + } + + /* Return protocol not available if no record/endpoint exists */ + if (server->source_record_id == 0) + return -ENOPROTOOPT; + return sink_connect(service); } diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c index dab8f1c..9378350 100644 --- a/profiles/audio/avdtp.c +++ b/profiles/audio/avdtp.c @@ -3683,6 +3683,45 @@ int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, &req, sizeof(req)); } +static GIOChannel *avdtp_server_socket(const bdaddr_t *src, gboolean master) +{ + GError *err = NULL; + GIOChannel *io; + + io = bt_io_listen(NULL, avdtp_confirm_cb, + NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, src, + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_MASTER, master, + BT_IO_OPT_INVALID); + if (!io) { + error("%s", err->message); + g_error_free(err); + } + + return io; +} + +static struct avdtp_server *avdtp_server_init(struct btd_adapter *adapter) +{ + struct avdtp_server *server; + + server = g_new0(struct avdtp_server, 1); + + server->io = avdtp_server_socket(adapter_get_address(adapter), TRUE); + if (!server->io) { + g_free(server); + return NULL; + } + + server->adapter = btd_adapter_ref(adapter); + + servers = g_slist_append(servers, server); + + return server; +} + struct avdtp_local_sep *avdtp_register_sep(struct btd_adapter *adapter, uint8_t type, uint8_t media_type, @@ -3696,8 +3735,11 @@ struct avdtp_local_sep *avdtp_register_sep(struct btd_adapter *adapter, struct avdtp_local_sep *sep; server = find_server(servers, adapter); - if (!server) - return NULL; + if (!server) { + server = avdtp_server_init(adapter); + if (!server) + return NULL; + } if (g_slist_length(server->seps) > MAX_SEID) return NULL; @@ -3722,6 +3764,18 @@ struct avdtp_local_sep *avdtp_register_sep(struct btd_adapter *adapter, return sep; } +static void avdtp_server_destroy(struct avdtp_server *server) +{ + g_slist_free_full(server->sessions, avdtp_free); + + servers = g_slist_remove(servers, server); + + g_io_channel_shutdown(server->io, TRUE, NULL); + g_io_channel_unref(server->io); + btd_adapter_unref(server->adapter); + g_free(server); +} + int avdtp_unregister_sep(struct avdtp_local_sep *sep) { struct avdtp_server *server; @@ -3740,27 +3794,12 @@ int avdtp_unregister_sep(struct avdtp_local_sep *sep) g_free(sep); - return 0; -} - -static GIOChannel *avdtp_server_socket(const bdaddr_t *src, gboolean master) -{ - GError *err = NULL; - GIOChannel *io; + if (server->seps) + return 0; - io = bt_io_listen(NULL, avdtp_confirm_cb, - NULL, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, src, - BT_IO_OPT_PSM, AVDTP_PSM, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_MASTER, master, - BT_IO_OPT_INVALID); - if (!io) { - error("%s", err->message); - g_error_free(err); - } + avdtp_server_destroy(server); - return io; + return 0; } const char *avdtp_strerror(struct avdtp_error *err) @@ -3823,43 +3862,6 @@ struct btd_device *avdtp_get_device(struct avdtp *session) return session->device; } -int avdtp_init(struct btd_adapter *adapter) -{ - struct avdtp_server *server; - - server = g_new0(struct avdtp_server, 1); - - server->io = avdtp_server_socket(adapter_get_address(adapter), TRUE); - if (!server->io) { - g_free(server); - return -1; - } - - server->adapter = btd_adapter_ref(adapter); - - servers = g_slist_append(servers, server); - - return 0; -} - -void avdtp_exit(struct btd_adapter *adapter) -{ - struct avdtp_server *server; - - server = find_server(servers, adapter); - if (!server) - return; - - g_slist_free_full(server->sessions, avdtp_free); - - servers = g_slist_remove(servers, server); - - g_io_channel_shutdown(server->io, TRUE, NULL); - g_io_channel_unref(server->io); - btd_adapter_unref(server->adapter); - g_free(server); -} - gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream) { return g_slist_find(session->streams, stream) ? TRUE : FALSE; diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h index 3bf7503..5606506 100644 --- a/profiles/audio/avdtp.h +++ b/profiles/audio/avdtp.h @@ -308,6 +308,3 @@ 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); - -int avdtp_init(struct btd_adapter *adapter); -void avdtp_exit(struct btd_adapter *adapter); diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c index 296067c..cd027c6 100644 --- a/profiles/audio/avrcp.c +++ b/profiles/audio/avrcp.c @@ -3363,9 +3363,6 @@ static void target_init(struct avrcp *session) if (service != NULL) btd_service_connecting_complete(service, 0); - if (target->version < 0x0103) - return; - player = g_slist_nth_data(server->players, 0); if (player != NULL) { target->player = player; diff --git a/src/adapter.c b/src/adapter.c index 40a9c4e..d904a56 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -99,7 +99,6 @@ static GSList *adapters = NULL; static struct mgmt *mgmt_master = NULL; -#define MGMT_VERSION(v, r) ((v << 16) + (r)) static uint8_t mgmt_version = 0; static uint8_t mgmt_revision = 0; @@ -285,7 +284,7 @@ static void set_dev_class_complete(uint8_t status, uint16_t length, } /* - * The parameters are idential and also the task that is + * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ @@ -487,7 +486,7 @@ static void set_mode_complete(uint8_t status, uint16_t length, } /* - * The parameters are idential and also the task that is + * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ @@ -631,7 +630,7 @@ static void set_local_name_complete(uint8_t status, uint16_t length, } /* - * The parameters are idential and also the task that is + * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ @@ -753,7 +752,7 @@ static void add_uuid_complete(uint8_t status, uint16_t length, } /* - * The parameters are idential and also the task that is + * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ @@ -805,7 +804,7 @@ static void remove_uuid_complete(uint8_t status, uint16_t length, } /* - * The parameters are idential and also the task that is + * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ @@ -856,7 +855,7 @@ static void clear_uuids_complete(uint8_t status, uint16_t length, } /* - * The parameters are idential and also the task that is + * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ @@ -1868,7 +1867,7 @@ static void property_set_mode_complete(uint8_t status, uint16_t length, g_dbus_pending_property_success(data->id); /* - * The parameters are idential and also the task that is + * The parameters are identical and also the task that is * required in both cases. So it is safe to just call the * event handling functions here. */ diff --git a/src/adapter.h b/src/adapter.h index 5d124e7..80c5f77 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -82,7 +82,6 @@ void adapter_shutdown(void); struct btd_adapter *adapter_find(const bdaddr_t *sba); struct btd_adapter *adapter_find_by_id(int id); -struct btd_adapter *adapter_get_default(void); void adapter_foreach(adapter_cb func, gpointer user_data); bool btd_adapter_get_pairable(struct btd_adapter *adapter); diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in index 2b73254..35e9457 100644 --- a/src/bluetooth.service.in +++ b/src/bluetooth.service.in @@ -9,7 +9,7 @@ ExecStart=@libexecdir@/bluetoothd NotifyAccess=main #WatchdogSec=10 #Restart=on-failure -CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE LimitNPROC=1 [Install] diff --git a/src/device.c b/src/device.c index 457ab64..82b66e6 100644 --- a/src/device.c +++ b/src/device.c @@ -31,8 +31,6 @@ #include #include #include -#include -#include #include #include #include @@ -64,7 +62,6 @@ #include "sdp-client.h" #include "attrib/gatt.h" #include "agent.h" -#include "sdp-xml.h" #include "storage.h" #include "attrib-server.h" @@ -148,7 +145,7 @@ typedef void (*attio_error_cb) (const GError *gerr, gpointer user_data); typedef void (*attio_success_cb) (gpointer user_data); struct att_callbacks { - attio_error_cb error; /* Callback for error */ + attio_error_cb err; /* Callback for error */ attio_success_cb success; /* Callback for success */ gpointer user_data; }; @@ -1159,8 +1156,15 @@ static void device_profile_connected(struct btd_device *dev, if (dev->pending == NULL) return; - if (!dev->connected && err == -EHOSTDOWN) - goto done; + if (!dev->connected) { + switch (-err) { + case EHOSTDOWN: /* page timeout */ + case EHOSTUNREACH: /* adapter not powered */ + case ECONNABORTED: /* adapter powered down */ + goto done; + } + } + pending = dev->pending->data; l = find_service_with_profile(dev->pending, profile); @@ -1300,6 +1304,9 @@ static DBusMessage *connect_profiles(struct btd_device *dev, DBusMessage *msg, if (dev->pending || dev->connect || dev->browse) return btd_error_in_progress(msg); + if (!btd_adapter_get_powered(dev->adapter)) + return btd_error_not_ready(msg); + device_set_temporary(dev, FALSE); if (!dev->svc_resolved) @@ -3243,8 +3250,8 @@ static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) if (gerr) { DBG("%s", gerr->message); - if (attcb->error) - attcb->error(gerr, user_data); + if (attcb->err) + attcb->err(gerr, user_data); err = -ECONNABORTED; goto done; @@ -3348,7 +3355,7 @@ int device_connect_le(struct btd_device *dev) DBG("Connection attempt to: %s", addr); attcb = g_new0(struct att_callbacks, 1); - attcb->error = att_error_cb; + attcb->err = att_error_cb; attcb->success = att_success_cb; attcb->user_data = dev; @@ -3438,7 +3445,7 @@ static int device_browse_primary(struct btd_device *device, DBusMessage *msg) } attcb = g_new0(struct att_callbacks, 1); - attcb->error = att_browse_error_cb; + attcb->err = att_browse_error_cb; attcb->success = att_browse_cb; attcb->user_data = device; @@ -3446,6 +3453,7 @@ static int device_browse_primary(struct btd_device *device, DBusMessage *msg) attcb, NULL, NULL, BT_IO_OPT_SOURCE_BDADDR, 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, BT_IO_OPT_CID, ATT_CID, diff --git a/src/sdp-client.c b/src/sdp-client.c index 2789db6..51f3048 100644 --- a/src/sdp-client.c +++ b/src/sdp-client.c @@ -196,30 +196,23 @@ static gboolean search_process_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) { struct search_context *ctxt = user_data; - int err = 0; if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { - err = EIO; - goto failed; - } - - if (sdp_process(ctxt->session) < 0) - goto failed; - - return TRUE; - -failed: - if (err) { sdp_close(ctxt->session); ctxt->session = NULL; if (ctxt->cb) - ctxt->cb(NULL, err, ctxt->user_data); + ctxt->cb(NULL, -EIO, ctxt->user_data); search_context_cleanup(ctxt); + return FALSE; } - return FALSE; + /* If sdp_process fails it calls search_completed_cb */ + if (sdp_process(ctxt->session) < 0) + return FALSE; + + return TRUE; } static gboolean connect_watch(GIOChannel *chan, GIOCondition cond, diff --git a/src/service.c b/src/service.c index 52a8291..7a480d6 100644 --- a/src/service.c +++ b/src/service.c @@ -198,18 +198,16 @@ int btd_service_connect(struct btd_service *service) return -EBUSY; } - change_state(service, BTD_SERVICE_STATE_CONNECTING, 0); - err = profile->connect(service); - if (err == 0) + if (err == 0) { + change_state(service, BTD_SERVICE_STATE_CONNECTING, 0); return 0; + } ba2str(device_get_address(service->device), addr); error("%s profile connect failed for %s: %s", profile->name, addr, strerror(-err)); - btd_service_connecting_complete(service, err); - return err; } diff --git a/src/shared/mgmt.h b/src/shared/mgmt.h index 60a2128..e23cc7d 100644 --- a/src/shared/mgmt.h +++ b/src/shared/mgmt.h @@ -24,6 +24,8 @@ #include #include +#define MGMT_VERSION(v, r) (((v) << 16) + (r)) + typedef void (*mgmt_destroy_func_t)(void *user_data); struct mgmt; diff --git a/tools/hid2hci.c b/tools/hid2hci.c index bb8a521..95b4abf 100644 --- a/tools/hid2hci.c +++ b/tools/hid2hci.c @@ -221,18 +221,21 @@ static int usb_switch_dell(int fd, enum mode mode) static int find_device(struct udev_device *udev_dev) { char path[PATH_MAX]; - const char *busnum, *devnum; + const char *busnum_str, *devnum_str; + int busnum, devnum; int fd; - busnum = udev_device_get_sysattr_value(udev_dev, "busnum"); - if (!busnum) + busnum_str = udev_device_get_sysattr_value(udev_dev, "busnum"); + if (!busnum_str) return -1; + busnum = strtol(busnum_str, NULL, 10); - devnum = udev_device_get_sysattr_value(udev_dev, "devnum"); - if (!devnum) + devnum_str = udev_device_get_sysattr_value(udev_dev, "devnum"); + if (!devnum_str) return -1; + devnum = strtol(devnum_str, NULL, 10); - snprintf(path, sizeof(path), "/dev/bus/usb/%s/%s", busnum, devnum); + snprintf(path, sizeof(path), "/dev/bus/usb/%03d/%03d", busnum, devnum); fd = open(path, O_RDWR, O_CLOEXEC); if (fd < 0) { diff --git a/tools/l2cap-tester.c b/tools/l2cap-tester.c index df8f1bf..acfd7c3 100644 --- a/tools/l2cap-tester.c +++ b/tools/l2cap-tester.c @@ -290,7 +290,8 @@ static const struct l2cap_server_data l2cap_server_nval_pdu_test1 = { }; static const uint8_t l2cap_nval_dc_req[] = { 0x12, 0x34, 0x56, 0x78 }; -static const uint8_t l2cap_nval_cid_rsp[] = { 0x02, 0x00 }; +static const uint8_t l2cap_nval_cid_rsp[] = { 0x02, 0x00, + 0x12, 0x34, 0x56, 0x78 }; static const struct l2cap_server_data l2cap_server_nval_cid_test1 = { .send_req_code = BT_L2CAP_PDU_DISCONN_REQ, @@ -302,7 +303,8 @@ static const struct l2cap_server_data l2cap_server_nval_cid_test1 = { }; static const uint8_t l2cap_nval_cfg_req[] = { 0x12, 0x34, 0x00, 0x00 }; -static const uint8_t l2cap_nval_cfg_rsp[] = { 0x02, 0x00 }; +static const uint8_t l2cap_nval_cfg_rsp[] = { 0x02, 0x00, + 0x12, 0x34, 0x00, 0x00 }; static const struct l2cap_server_data l2cap_server_nval_cid_test2 = { .send_req_code = BT_L2CAP_PDU_CONFIG_REQ, diff --git a/tools/l2test.c b/tools/l2test.c index 125532b..c5c8763 100644 --- a/tools/l2test.c +++ b/tools/l2test.c @@ -280,6 +280,8 @@ static int do_connect(char *svr) addr.l2_family = AF_BLUETOOTH; bacpy(&addr.l2_bdaddr, &bdaddr); addr.l2_bdaddr_type = bdaddr_type; + if (cid) + addr.l2_cid = htobs(cid); if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { syslog(LOG_ERR, "Can't bind socket: %s (%d)", diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c index 2ef72cf..e2fe73d 100644 --- a/tools/mgmt-tester.c +++ b/tools/mgmt-tester.c @@ -38,9 +38,8 @@ #include "src/shared/mgmt.h" #include "src/shared/hciemu.h" -static gboolean option_wait_powered = FALSE; - struct test_data { + tester_data_func_t test_setup; const void *test_data; uint8_t expected_version; uint16_t expected_manufacturer; @@ -299,6 +298,7 @@ static void test_condition_complete(struct test_data *data) if (!user) \ break; \ user->hciemu_type = HCIEMU_TYPE_BREDRLE; \ + user->test_setup = setup; \ user->test_data = data; \ user->expected_version = 0x06; \ user->expected_manufacturer = 0x003f; \ @@ -306,7 +306,7 @@ static void test_condition_complete(struct test_data *data) user->initial_settings = 0x00000080; \ user->unmet_conditions = 0; \ tester_add_full(name, data, \ - test_pre_setup, setup, func, NULL, \ + test_pre_setup, test_setup, func, NULL, \ test_post_teardown, 2, user, free); \ } while (0) @@ -317,6 +317,7 @@ static void test_condition_complete(struct test_data *data) if (!user) \ break; \ user->hciemu_type = HCIEMU_TYPE_BREDR; \ + user->test_setup = setup; \ user->test_data = data; \ user->expected_version = 0x05; \ user->expected_manufacturer = 0x003f; \ @@ -324,7 +325,7 @@ static void test_condition_complete(struct test_data *data) user->initial_settings = 0x00000080; \ user->unmet_conditions = 0; \ tester_add_full(name, data, \ - test_pre_setup, setup, func, NULL, \ + test_pre_setup, test_setup, func, NULL, \ test_post_teardown, 2, user, free); \ } while (0) @@ -335,6 +336,7 @@ static void test_condition_complete(struct test_data *data) if (!user) \ break; \ user->hciemu_type = HCIEMU_TYPE_LE; \ + user->test_setup = setup; \ user->test_data = data; \ user->expected_version = 0x06; \ user->expected_manufacturer = 0x003f; \ @@ -342,7 +344,7 @@ static void test_condition_complete(struct test_data *data) user->initial_settings = 0x00000200; \ user->unmet_conditions = 0; \ tester_add_full(name, data, \ - test_pre_setup, setup, func, NULL, \ + test_pre_setup, test_setup, func, NULL, \ test_post_teardown, 2, user, free); \ } while (0) @@ -352,6 +354,9 @@ static void controller_setup(const void *test_data) } struct generic_data { + const uint16_t *setup_settings; + bool setup_nobredr; + bool setup_limited_discov; uint16_t setup_expect_hci_command; const void *setup_expect_hci_param; uint8_t setup_expect_hci_len; @@ -480,11 +485,14 @@ static const struct generic_data set_powered_on_invalid_index_test = { .expect_status = MGMT_STATUS_INVALID_INDEX, }; +static const uint16_t settings_powered[] = { MGMT_OP_SET_POWERED, 0 }; + static const char set_powered_off_param[] = { 0x00 }; static const char set_powered_off_settings_param[] = { 0x80, 0x00, 0x00, 0x00 }; static const char set_powered_off_class_of_dev[] = { 0x00, 0x00, 0x00 }; static const struct generic_data set_powered_off_success_test = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_POWERED, .send_param = set_powered_off_param, .send_len = sizeof(set_powered_off_param), @@ -508,11 +516,13 @@ static const struct generic_data set_powered_off_class_test = { }; static const struct generic_data set_powered_off_invalid_param_test_1 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_POWERED, .expect_status = MGMT_STATUS_INVALID_PARAMS, }; static const struct generic_data set_powered_off_invalid_param_test_2 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_POWERED, .send_param = set_powered_invalid_param, .send_len = sizeof(set_powered_invalid_param), @@ -520,6 +530,7 @@ static const struct generic_data set_powered_off_invalid_param_test_2 = { }; static const struct generic_data set_powered_off_invalid_param_test_3 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_POWERED, .send_param = set_powered_garbage_param, .send_len = sizeof(set_powered_garbage_param), @@ -544,6 +555,7 @@ static const struct generic_data set_connectable_on_success_test_1 = { }; static const struct generic_data set_connectable_on_success_test_2 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_CONNECTABLE, .send_param = set_connectable_on_param, .send_len = sizeof(set_connectable_on_param), @@ -583,12 +595,75 @@ static const struct generic_data set_connectable_on_invalid_index_test = { .expect_status = MGMT_STATUS_INVALID_INDEX, }; +static uint16_t settings_powered_advertising[] = { MGMT_OP_SET_ADVERTISING, + MGMT_OP_SET_POWERED, 0 }; + +static const char set_connectable_le_settings_param_1[] = { 0x02, 0x02, 0x00, 0x00 }; +static const char set_connectable_le_settings_param_2[] = { 0x03, 0x02, 0x00, 0x00 }; +static const char set_connectable_le_settings_param_3[] = { 0x03, 0x06, 0x00, 0x00 }; + +static const struct generic_data set_connectable_on_le_test_1 = { + .send_opcode = MGMT_OP_SET_CONNECTABLE, + .send_param = set_connectable_on_param, + .send_len = sizeof(set_connectable_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_connectable_le_settings_param_1, + .expect_len = sizeof(set_connectable_le_settings_param_1), + .expect_settings_set = MGMT_SETTING_CONNECTABLE, +}; + +static const struct generic_data set_connectable_on_le_test_2 = { + .setup_settings = settings_powered, + .send_opcode = MGMT_OP_SET_CONNECTABLE, + .send_param = set_connectable_on_param, + .send_len = sizeof(set_connectable_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_connectable_le_settings_param_2, + .expect_len = sizeof(set_connectable_le_settings_param_2), + .expect_settings_set = MGMT_SETTING_CONNECTABLE, +}; + +static uint8_t set_connectable_on_adv_param[] = { + 0x00, 0x08, /* min_interval */ + 0x00, 0x08, /* max_interval */ + 0x00, /* type */ + 0x00, /* own_addr_type */ + 0x00, /* direct_addr_type */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* direct_addr */ + 0x07, /* channel_map */ + 0x00, /* filter_policy */ +}; + +static const struct generic_data set_connectable_on_le_test_3 = { + .setup_settings = settings_powered_advertising, + .send_opcode = MGMT_OP_SET_CONNECTABLE, + .send_param = set_connectable_on_param, + .send_len = sizeof(set_connectable_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_connectable_le_settings_param_3, + .expect_len = sizeof(set_connectable_le_settings_param_3), + .expect_settings_set = MGMT_SETTING_CONNECTABLE, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + .expect_hci_param = set_connectable_on_adv_param, + .expect_hci_len = sizeof(set_connectable_on_adv_param), +}; + +static const uint16_t settings_connectable[] = { MGMT_OP_SET_CONNECTABLE, 0 }; +static const uint16_t settings_powered_connectable[] = { + MGMT_OP_SET_CONNECTABLE, + MGMT_OP_SET_POWERED, 0 }; +static const uint16_t settings_powered_discoverable[] = { + MGMT_OP_SET_CONNECTABLE, + MGMT_OP_SET_DISCOVERABLE, + MGMT_OP_SET_POWERED, 0 }; + static const char set_connectable_off_param[] = { 0x00 }; static const char set_connectable_off_settings_1[] = { 0x80, 0x00, 0x00, 0x00 }; static const char set_connectable_off_settings_2[] = { 0x81, 0x00, 0x00, 0x00 }; static const char set_connectable_off_scan_enable_param[] = { 0x00 }; static const struct generic_data set_connectable_off_success_test_1 = { + .setup_settings = settings_connectable, .send_opcode = MGMT_OP_SET_CONNECTABLE, .send_param = set_connectable_off_param, .send_len = sizeof(set_connectable_off_param), @@ -599,6 +674,21 @@ static const struct generic_data set_connectable_off_success_test_1 = { }; static const struct generic_data set_connectable_off_success_test_2 = { + .setup_settings = settings_powered_connectable, + .send_opcode = MGMT_OP_SET_CONNECTABLE, + .send_param = set_connectable_off_param, + .send_len = sizeof(set_connectable_off_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_connectable_off_settings_2, + .expect_len = sizeof(set_connectable_off_settings_2), + .expect_settings_unset = MGMT_SETTING_CONNECTABLE, + .expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE, + .expect_hci_param = set_connectable_off_scan_enable_param, + .expect_hci_len = sizeof(set_connectable_off_scan_enable_param), +}; + +static const struct generic_data set_connectable_off_success_test_3 = { + .setup_settings = settings_powered_discoverable, .send_opcode = MGMT_OP_SET_CONNECTABLE, .send_param = set_connectable_off_param, .send_len = sizeof(set_connectable_off_param), @@ -611,10 +701,95 @@ static const struct generic_data set_connectable_off_success_test_2 = { .expect_hci_len = sizeof(set_connectable_off_scan_enable_param), }; +static const char set_connectable_off_le_settings_1[] = { 0x00, 0x02, 0x00, 0x00 }; +static const char set_connectable_off_le_settings_2[] = { 0x01, 0x06, 0x00, 0x00 }; + +static uint16_t settings_le_connectable[] = { MGMT_OP_SET_LE, + MGMT_OP_SET_CONNECTABLE, 0 }; + +static const struct generic_data set_connectable_off_le_test_1 = { + .setup_settings = settings_le_connectable, + .send_opcode = MGMT_OP_SET_CONNECTABLE, + .send_param = set_connectable_off_param, + .send_len = sizeof(set_connectable_off_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_connectable_off_le_settings_1, + .expect_len = sizeof(set_connectable_off_le_settings_1), + .expect_settings_unset = MGMT_SETTING_CONNECTABLE, +}; + +static uint16_t settings_powered_le_connectable_advertising[] = { + MGMT_OP_SET_LE, + MGMT_OP_SET_CONNECTABLE, + MGMT_OP_SET_ADVERTISING, + MGMT_OP_SET_POWERED, 0 }; + +static uint8_t set_connectable_off_adv_param[] = { + 0x00, 0x08, /* min_interval */ + 0x00, 0x08, /* max_interval */ + 0x03, /* type */ + 0x00, /* own_addr_type */ + 0x00, /* direct_addr_type */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* direct_addr */ + 0x07, /* channel_map */ + 0x00, /* filter_policy */ +}; + +static const struct generic_data set_connectable_off_le_test_2 = { + .setup_settings = settings_powered_le_connectable_advertising, + .send_opcode = MGMT_OP_SET_CONNECTABLE, + .send_param = set_connectable_off_param, + .send_len = sizeof(set_connectable_off_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_connectable_off_le_settings_2, + .expect_len = sizeof(set_connectable_off_le_settings_2), + .expect_settings_unset = MGMT_SETTING_CONNECTABLE, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + .expect_hci_param = set_connectable_off_adv_param, + .expect_hci_len = sizeof(set_connectable_off_adv_param), +}; + +static uint16_t settings_powered_le_discoverable_advertising[] = { + MGMT_OP_SET_LE, + MGMT_OP_SET_CONNECTABLE, + MGMT_OP_SET_ADVERTISING, + MGMT_OP_SET_POWERED, + MGMT_OP_SET_DISCOVERABLE, 0 }; + +static const struct generic_data set_connectable_off_le_test_3 = { + .setup_settings = settings_powered_le_discoverable_advertising, + .send_opcode = MGMT_OP_SET_CONNECTABLE, + .send_param = set_connectable_off_param, + .send_len = sizeof(set_connectable_off_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_connectable_off_le_settings_2, + .expect_len = sizeof(set_connectable_off_le_settings_2), + .expect_settings_unset = MGMT_SETTING_CONNECTABLE, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + .expect_hci_param = set_connectable_off_adv_param, + .expect_hci_len = sizeof(set_connectable_off_adv_param), +}; + +static const struct generic_data set_connectable_off_le_test_4 = { + .setup_settings = settings_powered_le_discoverable_advertising, + .setup_limited_discov = true, + .send_opcode = MGMT_OP_SET_CONNECTABLE, + .send_param = set_connectable_off_param, + .send_len = sizeof(set_connectable_off_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_connectable_off_le_settings_2, + .expect_len = sizeof(set_connectable_off_le_settings_2), + .expect_settings_unset = MGMT_SETTING_CONNECTABLE, + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS, + .expect_hci_param = set_connectable_off_adv_param, + .expect_hci_len = sizeof(set_connectable_off_adv_param), +}; + static const char set_fast_conn_on_param[] = { 0x01 }; static const char set_fast_conn_on_settings_1[] = { 0x87, 0x00, 0x00, 0x00 }; static const struct generic_data set_fast_conn_on_success_test_1 = { + .setup_settings = settings_powered_connectable, .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE, .send_param = set_fast_conn_on_param, .send_len = sizeof(set_fast_conn_on_param), @@ -624,6 +799,14 @@ static const struct generic_data set_fast_conn_on_success_test_1 = { .expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE, }; +static const struct generic_data set_fast_conn_on_not_supported_test_1 = { + .setup_settings = settings_powered_connectable, + .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE, + .send_param = set_fast_conn_on_param, + .send_len = sizeof(set_fast_conn_on_param), + .expect_status = MGMT_STATUS_NOT_SUPPORTED, +}; + static const char set_pairable_on_param[] = { 0x01 }; static const char set_pairable_invalid_param[] = { 0x02 }; static const char set_pairable_garbage_param[] = { 0x01, 0x00 }; @@ -712,7 +895,16 @@ static const struct generic_data set_discoverable_on_not_powered_test_1 = { .expect_status = MGMT_STATUS_NOT_POWERED, }; +static const struct generic_data set_discoverable_on_not_powered_test_2 = { + .setup_settings = settings_connectable, + .send_opcode = MGMT_OP_SET_DISCOVERABLE, + .send_param = set_discoverable_timeout_param, + .send_len = sizeof(set_discoverable_timeout_param), + .expect_status = MGMT_STATUS_NOT_POWERED, +}; + static const struct generic_data set_discoverable_on_rejected_test_1 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_DISCOVERABLE, .send_param = set_discoverable_on_param, .send_len = sizeof(set_discoverable_on_param), @@ -720,6 +912,7 @@ static const struct generic_data set_discoverable_on_rejected_test_1 = { }; static const struct generic_data set_discoverable_on_rejected_test_2 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_DISCOVERABLE, .send_param = set_discoverable_on_param, .send_len = sizeof(set_discoverable_on_param), @@ -727,6 +920,7 @@ static const struct generic_data set_discoverable_on_rejected_test_2 = { }; static const struct generic_data set_discoverable_on_rejected_test_3 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_DISCOVERABLE, .send_param = set_discoverable_timeout_param, .send_len = sizeof(set_discoverable_timeout_param), @@ -734,6 +928,7 @@ static const struct generic_data set_discoverable_on_rejected_test_3 = { }; static const struct generic_data set_discoverable_on_success_test_1 = { + .setup_settings = settings_connectable, .send_opcode = MGMT_OP_SET_DISCOVERABLE, .send_param = set_discoverable_on_param, .send_len = sizeof(set_discoverable_on_param), @@ -744,6 +939,7 @@ static const struct generic_data set_discoverable_on_success_test_1 = { }; static const struct generic_data set_discoverable_on_success_test_2 = { + .setup_settings = settings_powered_connectable, .send_opcode = MGMT_OP_SET_DISCOVERABLE, .send_param = set_discoverable_on_param, .send_len = sizeof(set_discoverable_on_param), @@ -756,7 +952,25 @@ static const struct generic_data set_discoverable_on_success_test_2 = { .expect_hci_len = sizeof(set_discoverable_on_scan_enable_param), }; +static uint8_t set_discov_on_le_param[] = { 0x0b, 0x06, 0x00, 0x00 }; +static uint8_t set_discov_adv_data[32] = { 0x06, 0x02, 0x01, 0x06, + 0x02, 0x0a, }; + +static const struct generic_data set_discov_on_le_success_1 = { + .setup_settings = settings_powered_le_connectable_advertising, + .send_opcode = MGMT_OP_SET_DISCOVERABLE, + .send_param = set_discoverable_on_param, + .send_len = sizeof(set_discoverable_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_discov_on_le_param, + .expect_len = sizeof(set_discov_on_le_param), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_discov_adv_data, + .expect_hci_len = sizeof(set_discov_adv_data), +}; + static const struct generic_data set_discoverable_off_success_test_1 = { + .setup_settings = settings_connectable, .send_opcode = MGMT_OP_SET_DISCOVERABLE, .send_param = set_discoverable_off_param, .send_len = sizeof(set_discoverable_off_param), @@ -766,6 +980,7 @@ static const struct generic_data set_discoverable_off_success_test_1 = { }; static const struct generic_data set_discoverable_off_success_test_2 = { + .setup_settings = settings_powered_discoverable, .send_opcode = MGMT_OP_SET_DISCOVERABLE, .send_param = set_discoverable_off_param, .send_len = sizeof(set_discoverable_off_param), @@ -777,6 +992,69 @@ static const struct generic_data set_discoverable_off_success_test_2 = { .expect_hci_len = sizeof(set_discoverable_off_scan_enable_param), }; +static const uint8_t set_limited_discov_on_param[] = { 0x02, 0x01, 0x00 }; + +static const struct generic_data set_limited_discov_on_success_1 = { + .setup_settings = settings_powered_connectable, + .send_opcode = MGMT_OP_SET_DISCOVERABLE, + .send_param = set_limited_discov_on_param, + .send_len = sizeof(set_limited_discov_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_discoverable_on_settings_param_2, + .expect_len = sizeof(set_discoverable_on_settings_param_2), + .expect_hci_command = BT_HCI_CMD_WRITE_SCAN_ENABLE, + .expect_hci_param = set_discoverable_on_scan_enable_param, + .expect_hci_len = sizeof(set_discoverable_on_scan_enable_param), +}; + +static uint8_t write_current_iac_lap_limited[] = { 0x01, 0x00, 0x8b, 0x9e }; + +static const struct generic_data set_limited_discov_on_success_2 = { + .setup_settings = settings_powered_connectable, + .send_opcode = MGMT_OP_SET_DISCOVERABLE, + .send_param = set_limited_discov_on_param, + .send_len = sizeof(set_limited_discov_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_discoverable_on_settings_param_2, + .expect_len = sizeof(set_discoverable_on_settings_param_2), + .expect_hci_command = BT_HCI_CMD_WRITE_CURRENT_IAC_LAP, + .expect_hci_param = write_current_iac_lap_limited, + .expect_hci_len = sizeof(write_current_iac_lap_limited), +}; + +static uint8_t write_cod_limited[] = { 0x00, 0x20, 0x00 }; + +static const struct generic_data set_limited_discov_on_success_3 = { + .setup_settings = settings_powered_connectable, + .send_opcode = MGMT_OP_SET_DISCOVERABLE, + .send_param = set_limited_discov_on_param, + .send_len = sizeof(set_limited_discov_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_discoverable_on_settings_param_2, + .expect_len = sizeof(set_discoverable_on_settings_param_2), + .expect_hci_command = BT_HCI_CMD_WRITE_CLASS_OF_DEV, + .expect_hci_param = write_cod_limited, + .expect_hci_len = sizeof(write_cod_limited), +}; + +static uint8_t set_limited_discov_adv_data[32] = { 0x06, 0x02, 0x01, 0x05, + 0x02, 0x0a, }; + +static const struct generic_data set_limited_discov_on_le_success_1 = { + .setup_settings = settings_powered_le_connectable_advertising, + .send_opcode = MGMT_OP_SET_DISCOVERABLE, + .send_param = set_limited_discov_on_param, + .send_len = sizeof(set_limited_discov_on_param), + .expect_status = MGMT_STATUS_SUCCESS, + .expect_param = set_discov_on_le_param, + .expect_len = sizeof(set_discov_on_le_param), + .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA, + .expect_hci_param = set_limited_discov_adv_data, + .expect_hci_len = sizeof(set_limited_discov_adv_data), +}; + +static uint16_t settings_link_sec[] = { MGMT_OP_SET_LINK_SECURITY, 0 }; + static const char set_link_sec_on_param[] = { 0x01 }; static const char set_link_sec_invalid_param[] = { 0x02 }; static const char set_link_sec_garbage_param[] = { 0x01, 0x00 }; @@ -795,6 +1073,7 @@ static const struct generic_data set_link_sec_on_success_test_1 = { }; static const struct generic_data set_link_sec_on_success_test_2 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_LINK_SECURITY, .send_param = set_link_sec_on_param, .send_len = sizeof(set_link_sec_on_param), @@ -808,6 +1087,7 @@ static const struct generic_data set_link_sec_on_success_test_2 = { }; static const struct generic_data set_link_sec_on_success_test_3 = { + .setup_settings = settings_link_sec, .send_opcode = MGMT_OP_SET_POWERED, .send_param = set_powered_on_param, .send_len = sizeof(set_powered_on_param), @@ -847,12 +1127,17 @@ static const struct generic_data set_link_sec_on_invalid_index_test = { .expect_status = MGMT_STATUS_INVALID_INDEX, }; +static const uint16_t settings_powered_link_sec[] = { + MGMT_OP_SET_LINK_SECURITY, + MGMT_OP_SET_POWERED, 0 }; + static const char set_link_sec_off_param[] = { 0x00 }; static const char set_link_sec_off_settings_1[] = { 0x80, 0x00, 0x00, 0x00 }; static const char set_link_sec_off_settings_2[] = { 0x81, 0x00, 0x00, 0x00 }; static const char set_link_sec_off_auth_enable_param[] = { 0x00 }; static const struct generic_data set_link_sec_off_success_test_1 = { + .setup_settings = settings_link_sec, .send_opcode = MGMT_OP_SET_LINK_SECURITY, .send_param = set_link_sec_off_param, .send_len = sizeof(set_link_sec_off_param), @@ -863,6 +1148,7 @@ static const struct generic_data set_link_sec_off_success_test_1 = { }; static const struct generic_data set_link_sec_off_success_test_2 = { + .setup_settings = settings_powered_link_sec, .send_opcode = MGMT_OP_SET_LINK_SECURITY, .send_param = set_link_sec_off_param, .send_len = sizeof(set_link_sec_off_param), @@ -875,6 +1161,8 @@ static const struct generic_data set_link_sec_off_success_test_2 = { .expect_hci_len = sizeof(set_link_sec_off_auth_enable_param), }; +static uint16_t settings_ssp[] = { MGMT_OP_SET_SSP, 0 }; + static const char set_ssp_on_param[] = { 0x01 }; static const char set_ssp_invalid_param[] = { 0x02 }; static const char set_ssp_garbage_param[] = { 0x01, 0x00 }; @@ -893,6 +1181,7 @@ static const struct generic_data set_ssp_on_success_test_1 = { }; static const struct generic_data set_ssp_on_success_test_2 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_SSP, .send_param = set_ssp_on_param, .send_len = sizeof(set_ssp_on_param), @@ -906,6 +1195,7 @@ static const struct generic_data set_ssp_on_success_test_2 = { }; static const struct generic_data set_ssp_on_success_test_3 = { + .setup_settings = settings_ssp, .send_opcode = MGMT_OP_SET_POWERED, .send_param = set_powered_on_param, .send_len = sizeof(set_powered_on_param), @@ -951,6 +1241,7 @@ static const char set_hs_garbage_param[] = { 0x01, 0x00 }; static const char set_hs_settings_param_1[] = { 0xc0, 0x01, 0x00, 0x00 }; static const struct generic_data set_hs_on_success_test = { + .setup_settings = settings_ssp, .send_opcode = MGMT_OP_SET_HS, .send_param = set_hs_on_param, .send_len = sizeof(set_hs_on_param), @@ -961,11 +1252,13 @@ static const struct generic_data set_hs_on_success_test = { }; static const struct generic_data set_hs_on_invalid_param_test_1 = { + .setup_settings = settings_ssp, .send_opcode = MGMT_OP_SET_HS, .expect_status = MGMT_STATUS_INVALID_PARAMS, }; static const struct generic_data set_hs_on_invalid_param_test_2 = { + .setup_settings = settings_ssp, .send_opcode = MGMT_OP_SET_HS, .send_param = set_hs_invalid_param, .send_len = sizeof(set_hs_invalid_param), @@ -973,6 +1266,7 @@ static const struct generic_data set_hs_on_invalid_param_test_2 = { }; static const struct generic_data set_hs_on_invalid_param_test_3 = { + .setup_settings = settings_ssp, .send_opcode = MGMT_OP_SET_HS, .send_param = set_hs_garbage_param, .send_len = sizeof(set_hs_garbage_param), @@ -980,6 +1274,7 @@ static const struct generic_data set_hs_on_invalid_param_test_3 = { }; static const struct generic_data set_hs_on_invalid_index_test = { + .setup_settings = settings_ssp, .send_index_none = true, .send_opcode = MGMT_OP_SET_HS, .send_param = set_hs_on_param, @@ -987,6 +1282,8 @@ static const struct generic_data set_hs_on_invalid_index_test = { .expect_status = MGMT_STATUS_INVALID_INDEX, }; +static uint16_t settings_le[] = { MGMT_OP_SET_LE, 0 }; + static const char set_le_on_param[] = { 0x01 }; static const char set_le_invalid_param[] = { 0x02 }; static const char set_le_garbage_param[] = { 0x01, 0x00 }; @@ -1005,6 +1302,7 @@ static const struct generic_data set_le_on_success_test_1 = { }; static const struct generic_data set_le_on_success_test_2 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_LE, .send_param = set_le_on_param, .send_len = sizeof(set_le_on_param), @@ -1018,6 +1316,7 @@ static const struct generic_data set_le_on_success_test_2 = { }; static const struct generic_data set_le_on_success_test_3 = { + .setup_settings = settings_le, .send_opcode = MGMT_OP_SET_POWERED, .send_param = set_powered_on_param, .send_len = sizeof(set_powered_on_param), @@ -1057,12 +1356,16 @@ static const struct generic_data set_le_on_invalid_index_test = { .expect_status = MGMT_STATUS_INVALID_INDEX, }; +static uint16_t settings_powered_le[] = { MGMT_OP_SET_LE, + MGMT_OP_SET_POWERED, 0 }; + static const char set_adv_on_param[] = { 0x01 }; static const char set_adv_settings_param_1[] = { 0x80, 0x06, 0x00, 0x00 }; static const char set_adv_settings_param_2[] = { 0x81, 0x06, 0x00, 0x00 }; static const char set_adv_on_set_adv_enable_param[] = { 0x01 }; static const struct generic_data set_adv_on_success_test_1 = { + .setup_settings = settings_le, .send_opcode = MGMT_OP_SET_ADVERTISING, .send_param = set_adv_on_param, .send_len = sizeof(set_adv_on_param), @@ -1073,6 +1376,7 @@ static const struct generic_data set_adv_on_success_test_1 = { }; static const struct generic_data set_adv_on_success_test_2 = { + .setup_settings = settings_powered_le, .send_opcode = MGMT_OP_SET_ADVERTISING, .send_param = set_adv_on_param, .send_len = sizeof(set_adv_on_param), @@ -1086,6 +1390,7 @@ static const struct generic_data set_adv_on_success_test_2 = { }; static const struct generic_data set_adv_on_rejected_test_1 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_ADVERTISING, .send_param = set_adv_on_param, .send_len = sizeof(set_adv_on_param), @@ -1100,6 +1405,7 @@ static const char set_bredr_settings_param_2[] = { 0x80, 0x02, 0x00, 0x00 }; static const char set_bredr_settings_param_3[] = { 0x81, 0x02, 0x00, 0x00 }; static const struct generic_data set_bredr_off_success_test_1 = { + .setup_settings = settings_le, .send_opcode = MGMT_OP_SET_BREDR, .send_param = set_bredr_off_param, .send_len = sizeof(set_bredr_off_param), @@ -1110,6 +1416,8 @@ static const struct generic_data set_bredr_off_success_test_1 = { }; static const struct generic_data set_bredr_on_success_test_1 = { + .setup_settings = settings_le, + .setup_nobredr = true, .send_opcode = MGMT_OP_SET_BREDR, .send_param = set_bredr_on_param, .send_len = sizeof(set_bredr_on_param), @@ -1120,6 +1428,8 @@ static const struct generic_data set_bredr_on_success_test_1 = { }; static const struct generic_data set_bredr_on_success_test_2 = { + .setup_settings = settings_powered_le, + .setup_nobredr = true, .send_opcode = MGMT_OP_SET_BREDR, .send_param = set_bredr_on_param, .send_len = sizeof(set_bredr_on_param), @@ -1137,6 +1447,7 @@ static const struct generic_data set_bredr_off_notsupp_test = { }; static const struct generic_data set_bredr_off_failure_test_1 = { + .setup_settings = settings_powered_le, .send_opcode = MGMT_OP_SET_BREDR, .send_param = set_bredr_off_param, .send_len = sizeof(set_bredr_off_param), @@ -1144,6 +1455,7 @@ static const struct generic_data set_bredr_off_failure_test_1 = { }; static const struct generic_data set_bredr_off_failure_test_2 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_BREDR, .send_param = set_bredr_off_param, .send_len = sizeof(set_bredr_off_param), @@ -1151,12 +1463,16 @@ static const struct generic_data set_bredr_off_failure_test_2 = { }; static const struct generic_data set_bredr_off_failure_test_3 = { + .setup_settings = settings_le, .send_opcode = MGMT_OP_SET_BREDR, .send_param = set_bredr_invalid_param, .send_len = sizeof(set_bredr_invalid_param), .expect_status = MGMT_STATUS_INVALID_PARAMS, }; +static uint16_t settings_powered_ssp[] = { MGMT_OP_SET_SSP, + MGMT_OP_SET_POWERED, 0 }; + static const char set_local_name_param[260] = { 'T', 'e', 's', 't', ' ', 'n', 'a', 'm', 'e' }; static const char write_local_name_hci[248] = { 'T', 'e', 's', 't', ' ', @@ -1178,6 +1494,7 @@ static const struct generic_data set_local_name_test_1 = { }; static const struct generic_data set_local_name_test_2 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_LOCAL_NAME, .send_param = set_local_name_param, .send_len = sizeof(set_local_name_param), @@ -1193,6 +1510,7 @@ static const struct generic_data set_local_name_test_2 = { }; static const struct generic_data set_local_name_test_3 = { + .setup_settings = settings_powered_ssp, .send_opcode = MGMT_OP_SET_LOCAL_NAME, .send_param = set_local_name_param, .send_len = sizeof(set_local_name_param), @@ -1223,6 +1541,7 @@ static const struct generic_data start_discovery_not_powered_test_1 = { }; static const struct generic_data start_discovery_invalid_param_test_1 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_START_DISCOVERY, .send_param = start_discovery_invalid_param, .send_len = sizeof(start_discovery_invalid_param), @@ -1230,6 +1549,7 @@ static const struct generic_data start_discovery_invalid_param_test_1 = { }; static const struct generic_data start_discovery_not_supported_test_1 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_START_DISCOVERY, .send_param = start_discovery_le_param, .send_len = sizeof(start_discovery_le_param), @@ -1237,6 +1557,7 @@ static const struct generic_data start_discovery_not_supported_test_1 = { }; static const struct generic_data start_discovery_valid_param_test_1 = { + .setup_settings = settings_powered_le, .send_opcode = MGMT_OP_START_DISCOVERY, .send_param = start_discovery_bredrle_param, .send_len = sizeof(start_discovery_bredrle_param), @@ -1252,6 +1573,7 @@ static const struct generic_data start_discovery_valid_param_test_1 = { }; static const struct generic_data start_discovery_valid_param_test_2 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_START_DISCOVERY, .send_param = start_discovery_le_param, .send_len = sizeof(start_discovery_le_param), @@ -1275,6 +1597,7 @@ static const char stop_discovery_bredr_discovering[] = { 0x01, 0x00 }; static const char stop_discovery_inq_param[] = { 0x33, 0x8b, 0x9e, 0x08, 0x00 }; static const struct generic_data stop_discovery_success_test_1 = { + .setup_settings = settings_powered_le, .send_opcode = MGMT_OP_STOP_DISCOVERY, .send_param = stop_discovery_bredrle_param, .send_len = sizeof(stop_discovery_bredrle_param), @@ -1290,6 +1613,7 @@ static const struct generic_data stop_discovery_success_test_1 = { }; static const struct generic_data stop_discovery_bredr_success_test_1 = { + .setup_settings = settings_powered_le, .setup_expect_hci_command = BT_HCI_CMD_INQUIRY, .setup_expect_hci_param = stop_discovery_inq_param, .setup_expect_hci_len = sizeof(stop_discovery_inq_param), @@ -1306,6 +1630,7 @@ static const struct generic_data stop_discovery_bredr_success_test_1 = { }; static const struct generic_data stop_discovery_rejected_test_1 = { + .setup_settings = settings_powered_le, .send_opcode = MGMT_OP_STOP_DISCOVERY, .send_param = stop_discovery_bredrle_param, .send_len = sizeof(stop_discovery_bredrle_param), @@ -1315,6 +1640,7 @@ static const struct generic_data stop_discovery_rejected_test_1 = { }; static const struct generic_data stop_discovery_invalid_param_test_1 = { + .setup_settings = settings_powered_le, .send_opcode = MGMT_OP_STOP_DISCOVERY, .send_param = stop_discovery_bredrle_invalid_param, .send_len = sizeof(stop_discovery_bredrle_invalid_param), @@ -1339,6 +1665,7 @@ static const struct generic_data set_dev_class_valid_param_test_1 = { }; static const struct generic_data set_dev_class_valid_param_test_2 = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_DEV_CLASS, .send_param = set_dev_class_valid_param, .send_len = sizeof(set_dev_class_valid_param), @@ -1525,6 +1852,7 @@ static const char write_eir_uuid_mix_hci[241] = { 0x00, 0x44, 0x33, 0x22, 0x11 }; static const struct generic_data add_uuid16_test_1 = { + .setup_settings = settings_powered_ssp, .send_opcode = MGMT_OP_ADD_UUID, .send_param = add_spp_uuid_param, .send_len = sizeof(add_spp_uuid_param), @@ -1561,6 +1889,7 @@ static const struct generic_data add_multi_uuid16_test_2 = { }; static const struct generic_data add_uuid32_test_1 = { + .setup_settings = settings_powered_ssp, .send_opcode = MGMT_OP_ADD_UUID, .send_param = add_uuid32_param_1, .send_len = sizeof(add_uuid32_param_1), @@ -1597,6 +1926,7 @@ static const struct generic_data add_uuid32_multi_test_2 = { }; static const struct generic_data add_uuid128_test_1 = { + .setup_settings = settings_powered_ssp, .send_opcode = MGMT_OP_ADD_UUID, .send_param = add_uuid128_param_1, .send_len = sizeof(add_uuid128_param_1), @@ -1891,6 +2221,7 @@ static const struct generic_data set_static_addr_success_test = { }; static const struct generic_data set_static_addr_failure_test = { + .setup_settings = settings_powered, .send_opcode = MGMT_OP_SET_STATIC_ADDRESS, .send_param = set_static_addr_valid_param, .send_len = sizeof(set_static_addr_valid_param), @@ -1906,11 +2237,6 @@ static const struct generic_data set_scan_params_success_test = { .expect_status = MGMT_STATUS_SUCCESS, }; -static void powered_delay(void *user_data) -{ - tester_setup_complete(); -} - static void setup_powered_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -1921,47 +2247,7 @@ static void setup_powered_callback(uint8_t status, uint16_t length, tester_print("Controller powered on"); - if (option_wait_powered) - tester_wait(1, powered_delay, NULL); - else - tester_setup_complete(); -} - -static void setup_powered_discoverable(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - unsigned char discov_param[] = { 0x01, 0x00, 0x00 }; - - tester_print("Enabling connectable, discoverable and powered"); - - mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index, - sizeof(param), param, - NULL, NULL, NULL); - - mgmt_send(data->mgmt, MGMT_OP_SET_DISCOVERABLE, data->mgmt_index, - sizeof(discov_param), discov_param, - NULL, NULL, NULL); - - mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, - sizeof(param), param, - setup_powered_callback, NULL, NULL); -} - -static void setup_powered_connectable(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Enabling connectable and powered"); - - mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index, - sizeof(param), param, - NULL, NULL, NULL); - - mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, - sizeof(param), param, - setup_powered_callback, NULL, NULL); + tester_setup_complete(); } static void setup_class(const void *test_data) @@ -1981,54 +2267,6 @@ static void setup_class(const void *test_data) setup_powered_callback, NULL, NULL); } -static void setup_ssp_powered(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Powering on controller (with SSP enabled)"); - - mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index, - sizeof(param), param, NULL, NULL, NULL); - - mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, - sizeof(param), param, - setup_powered_callback, NULL, NULL); -} - -static void setup_le_powered(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Powering on controller (with LE enabled)"); - - mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, - sizeof(param), param, NULL, NULL, NULL); - - mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, - sizeof(param), param, - setup_powered_callback, NULL, NULL); -} - -static void setup_le_nobr_powered(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char on[] = { 0x01 }; - unsigned char off[] = { 0x00 }; - - tester_print("Powering on controller (with LE enabled)"); - - mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, - sizeof(on), on, NULL, NULL, NULL); - mgmt_send(data->mgmt, MGMT_OP_SET_BREDR, data->mgmt_index, - sizeof(off), off, NULL, NULL, NULL); - - mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, - sizeof(on), on, - setup_powered_callback, NULL, NULL); -} - static void setup_discovery_callback(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -2071,19 +2309,11 @@ done: return false; } -static void setup_start_discovery_callback(uint8_t status, uint16_t length, - const void *param, void *user_data) +static void setup_start_discovery(const void *test_data) { struct test_data *data = tester_get_data(); const struct generic_data *test = data->test_data; - if (status != MGMT_STATUS_SUCCESS) { - tester_setup_failed(); - return; - } - - tester_print("Controller powered on"); - if (test->setup_expect_hci_command) { tester_print("Registering HCI command callback (setup)"); hciemu_add_hook(data->hciemu, HCIEMU_HOOK_PRE_EVT, @@ -2100,90 +2330,6 @@ static void setup_start_discovery_callback(uint8_t status, uint16_t length, sizeof(disc_param), disc_param, setup_discovery_callback, NULL, NULL); } - - if (option_wait_powered) - tester_wait(1, NULL, NULL); -} - -static void setup_start_discovery(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Powering on controller (with LE enabled)"); - - mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, - sizeof(param), param, NULL, NULL, NULL); - - mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, - sizeof(param), param, - setup_start_discovery_callback, NULL, NULL); -} - -static void setup_ssp_callback(uint8_t status, uint16_t length, - const void *param, void *user_data) -{ - if (status != MGMT_STATUS_SUCCESS) { - tester_setup_failed(); - return; - } - - tester_print("SSP enabled"); - - tester_setup_complete(); -} - -static void setup_ssp(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Enabling SSP"); - - mgmt_send(data->mgmt, MGMT_OP_SET_SSP, data->mgmt_index, - sizeof(param), param, setup_ssp_callback, - NULL, NULL); -} - -static void setup_le_callback(uint8_t status, uint16_t length, - const void *param, void *user_data) -{ - if (status != MGMT_STATUS_SUCCESS) { - tester_setup_failed(); - return; - } - - tester_print("Low Energy enabled"); - - tester_setup_complete(); -} - -static void setup_le(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Enabling Low Energy"); - - mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, - sizeof(param), param, setup_le_callback, - NULL, NULL); -} - -static void setup_le_nobr(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char on[] = { 0x01 }; - unsigned char off[] = { 0x00 }; - - tester_print("Enabling Low Energy"); - - mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index, - sizeof(on), on, NULL, - NULL, NULL); - mgmt_send(data->mgmt, MGMT_OP_SET_BREDR, data->mgmt_index, - sizeof(off), off, setup_le_callback, - NULL, NULL); } static void setup_multi_uuid32(const void *test_data) @@ -2370,96 +2516,69 @@ static void setup_uuid_mix(const void *test_data) setup_powered_callback, NULL, NULL); } -static void setup_powered(const void *test_data) +static void setup_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) { struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Powering on controller"); - mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, - sizeof(param), param, - setup_powered_callback, NULL, NULL); -} - -static void setup_connectable_callback(uint8_t status, uint16_t length, - const void *param, void *user_data) -{ if (status != MGMT_STATUS_SUCCESS) { tester_setup_failed(); return; } - tester_print("Controller connectable on"); - - tester_setup_complete(); -} - -static void setup_connectable(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Setting controller connectable"); + tester_print("Initial settings completed"); - mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index, - sizeof(param), param, - setup_connectable_callback, NULL, NULL); + if (data->test_setup) + data->test_setup(data); + else + tester_setup_complete(); } -static void setup_connectable_powered(const void *test_data) +static void test_setup(const void *test_data) { struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Setting controller powered and connectable"); - - mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index, - sizeof(param), param, NULL, NULL, NULL); - - mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, - sizeof(param), param, - setup_powered_callback, NULL, NULL); -} + const struct generic_data *test = data->test_data; + const uint16_t *cmd; -static void setup_link_sec_callback(uint8_t status, uint16_t length, - const void *param, void *user_data) -{ - if (status != MGMT_STATUS_SUCCESS) { - tester_setup_failed(); + if (!test || !test->setup_settings) { + if (data->test_setup) + data->test_setup(data); + else + tester_setup_complete(); return; } - tester_print("Link security enabled"); - - tester_setup_complete(); -} - -static void setup_link_sec(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Enabling link security"); - - mgmt_send(data->mgmt, MGMT_OP_SET_LINK_SECURITY, data->mgmt_index, - sizeof(param), param, setup_link_sec_callback, - NULL, NULL); -} - -static void setup_link_sec_powered(const void *test_data) -{ - struct test_data *data = tester_get_data(); - unsigned char param[] = { 0x01 }; - - tester_print("Enabling link security and powering on"); - - mgmt_send(data->mgmt, MGMT_OP_SET_LINK_SECURITY, data->mgmt_index, - sizeof(param), param, NULL, NULL, NULL); - - mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index, - sizeof(param), param, - setup_powered_callback, NULL, NULL); + for (cmd = test->setup_settings; *cmd; cmd++) { + unsigned char simple_param[] = { 0x01 }; + unsigned char discov_param[] = { 0x01, 0x00, 0x00 }; + unsigned char *param = simple_param; + size_t param_size = sizeof(simple_param); + mgmt_request_func_t func = NULL; + + /* If this is the last command (next one is 0) request + * for a callback. */ + if (!cmd[1]) + func = setup_complete; + + if (*cmd == MGMT_OP_SET_DISCOVERABLE) { + if (test->setup_limited_discov) { + discov_param[0] = 0x02; + discov_param[1] = 0x01; + } + param_size = sizeof(discov_param); + param = discov_param; + } + + mgmt_send(data->mgmt, *cmd, data->mgmt_index, + param_size, param, func, data, NULL); + + if (*cmd == MGMT_OP_SET_LE && test->setup_nobredr) { + unsigned char off[] = { 0x00 }; + mgmt_send(data->mgmt, MGMT_OP_SET_BREDR, + data->mgmt_index, sizeof(off), off, + NULL, data, NULL); + } + } } static void command_generic_new_settings(uint16_t index, uint16_t length, @@ -2639,39 +2758,15 @@ static void test_command_generic(const void *test_data) test_add_condition(data); } -static GOptionEntry options[] = { - { "wait-powered", 'P', 0, G_OPTION_ARG_NONE, &option_wait_powered, - "Add a delay after powering on" }, - { NULL }, -}; - int main(int argc, char *argv[]) { - GOptionContext *context; - GError *error = NULL; - - context = g_option_context_new(NULL); - g_option_context_add_main_entries(context, options, NULL); - g_option_context_set_ignore_unknown_options(context, TRUE); - - if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { - if (error != NULL) { - g_printerr("%s\n", error->message); - g_error_free(error); - } else - g_printerr("An unknown error occurred\n"); - exit(1); - } - - g_option_context_free(context); - tester_init(&argc, &argv); test_bredrle("Controller setup", NULL, NULL, controller_setup); test_bredr("Controller setup (BR/EDR-only)", NULL, NULL, controller_setup); - test_le("Controller setup (LE-only)", + test_le("Controller setup (LE)", NULL, NULL, controller_setup); test_bredrle("Invalid command", @@ -2724,26 +2819,26 @@ int main(int argc, char *argv[]) test_bredrle("Set powered off - Success", &set_powered_off_success_test, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set powered off - Class of Device", &set_powered_off_class_test, setup_class, test_command_generic); test_bredrle("Set powered off - Invalid parameters 1", &set_powered_off_invalid_param_test_1, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set powered off - Invalid parameters 2", &set_powered_off_invalid_param_test_2, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set powered off - Invalid parameters 3", &set_powered_off_invalid_param_test_3, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set connectable on - Success 1", &set_connectable_on_success_test_1, NULL, test_command_generic); test_bredrle("Set connectable on - Success 2", &set_connectable_on_success_test_2, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set connectable on - Invalid parameters 1", &set_connectable_on_invalid_param_test_1, NULL, test_command_generic); @@ -2757,16 +2852,45 @@ int main(int argc, char *argv[]) &set_connectable_on_invalid_index_test, NULL, test_command_generic); + test_le("Set connectable on (LE) - Success 1", + &set_connectable_on_le_test_1, + NULL, test_command_generic); + test_le("Set connectable on (LE) - Success 2", + &set_connectable_on_le_test_2, + NULL, test_command_generic); + test_le("Set connectable on (LE) - Success 3", + &set_connectable_on_le_test_3, + NULL, test_command_generic); + test_bredrle("Set connectable off - Success 1", - &set_connectable_off_success_test_1, - setup_connectable, test_command_generic); + &set_connectable_off_success_test_1, + NULL, test_command_generic); test_bredrle("Set connectable off - Success 2", - &set_connectable_off_success_test_2, - setup_connectable_powered, test_command_generic); + &set_connectable_off_success_test_2, + NULL, test_command_generic); + test_bredrle("Set connectable off - Success 3", + &set_connectable_off_success_test_3, + NULL, test_command_generic); + + test_le("Set connectable off (LE) - Success 1", + &set_connectable_off_le_test_1, + NULL, test_command_generic); + test_le("Set connectable off (LE) - Success 2", + &set_connectable_off_le_test_2, + NULL, test_command_generic); + test_le("Set connectable off (LE) - Success 3", + &set_connectable_off_le_test_3, + NULL, test_command_generic); + test_le("Set connectable off (LE) - Success 4", + &set_connectable_off_le_test_4, + NULL, test_command_generic); test_bredrle("Set fast connectable on - Success 1", - &set_fast_conn_on_success_test_1, - setup_powered_connectable, test_command_generic); + &set_fast_conn_on_success_test_1, + NULL, test_command_generic); + test_le("Set fast connectable on - Not Supported 1", + &set_fast_conn_on_not_supported_test_1, + NULL, test_command_generic); test_bredrle("Set pairable on - Success", &set_pairable_on_success_test, @@ -2799,44 +2923,55 @@ int main(int argc, char *argv[]) test_bredrle("Set discoverable on - Not powered 1", &set_discoverable_on_not_powered_test_1, NULL, test_command_generic); - test_bredrle("Set discoverable on - Not powered 1", - &set_discoverable_on_not_powered_test_1, - NULL, test_command_generic); test_bredrle("Set discoverable on - Not powered 2", - &set_discoverable_on_not_powered_test_1, - setup_connectable, test_command_generic); + &set_discoverable_on_not_powered_test_2, + NULL, test_command_generic); test_bredrle("Set discoverable on - Rejected 1", &set_discoverable_on_rejected_test_1, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set discoverable on - Rejected 2", &set_discoverable_on_rejected_test_2, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set discoverable on - Rejected 3", &set_discoverable_on_rejected_test_3, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set discoverable on - Success 1", &set_discoverable_on_success_test_1, - setup_connectable, test_command_generic); + NULL, test_command_generic); test_bredrle("Set discoverable on - Success 2", &set_discoverable_on_success_test_2, - setup_powered_connectable, test_command_generic); + NULL, test_command_generic); + test_le("Set discoverable on (LE) - Success 1", + &set_discov_on_le_success_1, + NULL, test_command_generic); test_bredrle("Set discoverable off - Success 1", &set_discoverable_off_success_test_1, - setup_connectable, test_command_generic); + NULL, test_command_generic); test_bredrle("Set discoverable off - Success 2", &set_discoverable_off_success_test_2, - setup_powered_discoverable, - test_command_generic); + NULL, test_command_generic); + test_bredrle("Set limited discoverable on - Success 1", + &set_limited_discov_on_success_1, + NULL, test_command_generic); + test_bredrle("Set limited discoverable on - Success 2", + &set_limited_discov_on_success_2, + NULL, test_command_generic); + test_bredrle("Set limited discoverable on - Success 3", + &set_limited_discov_on_success_3, + NULL, test_command_generic); + test_le("Set limited discoverable on (LE) - Success 1", + &set_limited_discov_on_le_success_1, + NULL, test_command_generic); test_bredrle("Set link security on - Success 1", &set_link_sec_on_success_test_1, NULL, test_command_generic); test_bredrle("Set link security on - Success 2", &set_link_sec_on_success_test_2, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set link security on - Success 3", &set_link_sec_on_success_test_3, - setup_link_sec, test_command_generic); + NULL, test_command_generic); test_bredrle("Set link security on - Invalid parameters 1", &set_link_sec_on_invalid_param_test_1, NULL, test_command_generic); @@ -2852,20 +2987,20 @@ int main(int argc, char *argv[]) test_bredrle("Set link security off - Success 1", &set_link_sec_off_success_test_1, - setup_link_sec, test_command_generic); + NULL, test_command_generic); test_bredrle("Set link security off - Success 2", &set_link_sec_off_success_test_2, - setup_link_sec_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set SSP on - Success 1", &set_ssp_on_success_test_1, NULL, test_command_generic); test_bredrle("Set SSP on - Success 2", &set_ssp_on_success_test_2, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set SSP on - Success 3", &set_ssp_on_success_test_3, - setup_ssp, test_command_generic); + NULL, test_command_generic); test_bredrle("Set SSP on - Invalid parameters 1", &set_ssp_on_invalid_param_test_1, NULL, test_command_generic); @@ -2881,29 +3016,29 @@ int main(int argc, char *argv[]) test_bredrle("Set High Speed on - Success", &set_hs_on_success_test, - setup_ssp, test_command_generic); + NULL, test_command_generic); test_bredrle("Set High Speed on - Invalid parameters 1", &set_hs_on_invalid_param_test_1, - setup_ssp, test_command_generic); + NULL, test_command_generic); test_bredrle("Set High Speed on - Invalid parameters 2", &set_hs_on_invalid_param_test_2, - setup_ssp, test_command_generic); + NULL, test_command_generic); test_bredrle("Set High Speed on - Invalid parameters 3", &set_hs_on_invalid_param_test_3, - setup_ssp, test_command_generic); + NULL, test_command_generic); test_bredrle("Set High Speed on - Invalid index", &set_hs_on_invalid_index_test, - setup_ssp, test_command_generic); + NULL, test_command_generic); test_bredrle("Set Low Energy on - Success 1", &set_le_on_success_test_1, NULL, test_command_generic); test_bredrle("Set Low Energy on - Success 2", &set_le_on_success_test_2, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set Low Energy on - Success 3", &set_le_on_success_test_3, - setup_le, test_command_generic); + NULL, test_command_generic); test_bredrle("Set Low Energy on - Invalid parameters 1", &set_le_on_invalid_param_test_1, NULL, test_command_generic); @@ -2919,23 +3054,23 @@ int main(int argc, char *argv[]) test_bredrle("Set Advertising on - Success 1", &set_adv_on_success_test_1, - setup_le, test_command_generic); + NULL, test_command_generic); test_bredrle("Set Advertising on - Success 2", &set_adv_on_success_test_2, - setup_le_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set Advertising on - Rejected 1", &set_adv_on_rejected_test_1, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set BR/EDR off - Success 1", &set_bredr_off_success_test_1, - setup_le, test_command_generic); + NULL, test_command_generic); test_bredrle("Set BR/EDR on - Success 1", &set_bredr_on_success_test_1, - setup_le_nobr, test_command_generic); + NULL, test_command_generic); test_bredrle("Set BR/EDR on - Success 2", &set_bredr_on_success_test_2, - setup_le_nobr_powered, test_command_generic); + NULL, test_command_generic); test_bredr("Set BR/EDR off - Not Supported 1", &set_bredr_off_notsupp_test, NULL, test_command_generic); @@ -2944,39 +3079,39 @@ int main(int argc, char *argv[]) NULL, test_command_generic); test_bredrle("Set BR/EDR off - Rejected 1", &set_bredr_off_failure_test_1, - setup_le_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set BR/EDR off - Rejected 2", &set_bredr_off_failure_test_2, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set BR/EDR off - Invalid Parameters 1", &set_bredr_off_failure_test_3, - setup_le, test_command_generic); + NULL, test_command_generic); test_bredr("Set Local Name - Success 1", &set_local_name_test_1, NULL, test_command_generic); test_bredr("Set Local Name - Success 2", &set_local_name_test_2, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredr("Set Local Name - Success 3", &set_local_name_test_3, - setup_ssp_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Start Discovery - Not powered 1", &start_discovery_not_powered_test_1, NULL, test_command_generic); test_bredrle("Start Discovery - Invalid parameters 1", &start_discovery_invalid_param_test_1, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Start Discovery - Not supported 1", &start_discovery_not_supported_test_1, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Start Discovery - Success 1", &start_discovery_valid_param_test_1, - setup_le_powered, test_command_generic); + NULL, test_command_generic); test_le("Start Discovery - Success 2", &start_discovery_valid_param_test_2, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Stop Discovery - Success 1", &stop_discovery_success_test_1, @@ -2986,7 +3121,7 @@ int main(int argc, char *argv[]) setup_start_discovery, test_command_generic); test_bredrle("Stop Discovery - Rejected 1", &stop_discovery_rejected_test_1, - setup_le_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Stop Discovery - Invalid parameters 1", &stop_discovery_invalid_param_test_1, setup_start_discovery, test_command_generic); @@ -2996,14 +3131,14 @@ int main(int argc, char *argv[]) NULL, test_command_generic); test_bredrle("Set Device Class - Success 2", &set_dev_class_valid_param_test_2, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set Device Class - Invalid parameters 1", &set_dev_class_invalid_param_test_1, NULL, test_command_generic); test_bredrle("Add UUID - UUID-16 1", &add_uuid16_test_1, - setup_ssp_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Add UUID - UUID-16 multiple 1", &add_multi_uuid16_test_1, setup_multi_uuid16, test_command_generic); @@ -3012,7 +3147,7 @@ int main(int argc, char *argv[]) setup_multi_uuid16_2, test_command_generic); test_bredrle("Add UUID - UUID-32 1", &add_uuid32_test_1, - setup_ssp_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Add UUID - UUID-32 multiple 1", &add_uuid32_multi_test_1, setup_multi_uuid32, test_command_generic); @@ -3021,7 +3156,7 @@ int main(int argc, char *argv[]) setup_multi_uuid32_2, test_command_generic); test_bredrle("Add UUID - UUID-128 1", &add_uuid128_test_1, - setup_ssp_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Add UUID - UUID-128 multiple 1", &add_uuid128_multi_test_1, setup_multi_uuid128, test_command_generic); @@ -3098,7 +3233,7 @@ int main(int argc, char *argv[]) NULL, test_command_generic); test_bredrle("Set Static Address - Failure", &set_static_addr_failure_test, - setup_powered, test_command_generic); + NULL, test_command_generic); test_bredrle("Set Scan Parameters - Success", &set_scan_params_success_test, diff --git a/tools/smp-tester.c b/tools/smp-tester.c new file mode 100644 index 0000000..46dc65a --- /dev/null +++ b/tools/smp-tester.c @@ -0,0 +1,538 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/mgmt.h" + +#include "monitor/bt.h" +#include "emulator/bthost.h" + +#include "src/shared/tester.h" +#include "src/shared/mgmt.h" +#include "src/shared/hciemu.h" + +#define SMP_CID 0x0006 + +struct test_data { + const void *test_data; + struct mgmt *mgmt; + uint16_t mgmt_index; + struct hciemu *hciemu; + enum hciemu_type hciemu_type; + unsigned int io_id; + uint16_t handle; +}; + +struct smp_server_data { + const void *send_req; + uint16_t send_req_len; + const void *expect_rsp; + uint16_t expect_rsp_len; +}; + +struct smp_client_data { + const void *expect_req; + uint16_t expect_req_len; + const void *send_rsp; + uint16_t send_rsp_len; +}; + +static void mgmt_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + tester_print("%s%s", prefix, str); +} + +static void read_info_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct mgmt_rp_read_info *rp = param; + char addr[18]; + uint16_t manufacturer; + uint32_t supported_settings, current_settings; + + tester_print("Read Info callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + ba2str(&rp->bdaddr, addr); + manufacturer = btohs(rp->manufacturer); + supported_settings = btohl(rp->supported_settings); + current_settings = btohl(rp->current_settings); + + tester_print(" Address: %s", addr); + tester_print(" Version: 0x%02x", rp->version); + tester_print(" Manufacturer: 0x%04x", manufacturer); + tester_print(" Supported settings: 0x%08x", supported_settings); + tester_print(" Current settings: 0x%08x", current_settings); + tester_print(" Class: 0x%02x%02x%02x", + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); + tester_print(" Name: %s", rp->name); + tester_print(" Short name: %s", rp->short_name); + + if (strcmp(hciemu_get_address(data->hciemu), addr)) { + tester_pre_setup_failed(); + return; + } + + tester_pre_setup_complete(); +} + +static void index_added_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Added callback"); + tester_print(" Index: 0x%04x", index); + + data->mgmt_index = index; + + mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, + read_info_callback, NULL, NULL); +} + +static void index_removed_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Removed callback"); + tester_print(" Index: 0x%04x", index); + + if (index != data->mgmt_index) + return; + + mgmt_unregister_index(data->mgmt, data->mgmt_index); + + mgmt_unref(data->mgmt); + data->mgmt = NULL; + + tester_post_teardown_complete(); +} + +static void read_index_list_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Read Index List callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, + index_added_callback, NULL, NULL); + + mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, + index_removed_callback, NULL, NULL); + + data->hciemu = hciemu_new(data->hciemu_type); + if (!data->hciemu) { + tester_warn("Failed to setup HCI emulation"); + tester_pre_setup_failed(); + } + + tester_print("New hciemu instance created"); +} + +static void test_pre_setup(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + data->mgmt = mgmt_new_default(); + if (!data->mgmt) { + tester_warn("Failed to setup management interface"); + tester_pre_setup_failed(); + return; + } + + if (tester_use_debug()) + mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL); + + mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, NULL, + read_index_list_callback, NULL, NULL); +} + +static void test_post_teardown(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + if (data->io_id > 0) { + g_source_remove(data->io_id); + data->io_id = 0; + } + + hciemu_unref(data->hciemu); + data->hciemu = NULL; +} + +static void test_data_free(void *test_data) +{ + struct test_data *data = test_data; + + free(data); +} + +#define test_smp(name, data, setup, func) \ + do { \ + struct test_data *user; \ + user = malloc(sizeof(struct test_data)); \ + if (!user) \ + break; \ + user->hciemu_type = HCIEMU_TYPE_LE; \ + user->io_id = 0; \ + user->test_data = data; \ + tester_add_full(name, data, \ + test_pre_setup, setup, func, NULL, \ + test_post_teardown, 2, user, test_data_free); \ + } while (0) + +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 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 uint8_t smp_basic_req_1[] = { 0x01, /* Pairing Request */ + 0x03, /* NoInputNoOutput */ + 0x00, /* OOB Flag */ + 0x01, /* Bonding - no MITM */ + 0x10, /* Max key size */ + 0x00, /* Init. key dist. */ + 0x01, /* Rsp. key dist. */ + +}; +static const uint8_t smp_basic_req_1_rsp[] = { 0x02, /* Pairing Response */ + 0x03, /* NoInputNoOutput */ + 0x00, /* OOB Flag */ + 0x00, /* SMP auth none */ + 0x10, /* Max key size */ + 0x00, /* Init. key dist. */ + 0x00, /* Rsp. key dist. */ + +}; + +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_client_data smp_client_basic_req_1_test = { + .expect_req = smp_basic_req_1, + .expect_req_len = sizeof(smp_basic_req_1), +}; + +static void client_connectable_complete(uint16_t opcode, uint8_t status, + const void *param, uint8_t len, + void *user_data) +{ + if (opcode != BT_HCI_CMD_LE_SET_ADV_ENABLE) + return; + + tester_print("Client set connectable status 0x%02x", status); + + if (status) + tester_setup_failed(); + else + tester_setup_complete(); +} + +static void setup_powered_client_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost; + + if (status != MGMT_STATUS_SUCCESS) { + tester_setup_failed(); + return; + } + + tester_print("Controller powered on"); + + bthost = hciemu_client_get_host(data->hciemu); + bthost_set_cmd_complete_cb(bthost, client_connectable_complete, data); + bthost_set_adv_enable(bthost, 0x01); +} + +static void setup_powered_client(const void *test_data) +{ + struct test_data *data = tester_get_data(); + unsigned char param[] = { 0x01 }; + + tester_print("Powering on controller"); + + mgmt_send(data->mgmt, MGMT_OP_SET_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_POWERED, data->mgmt_index, + sizeof(param), param, setup_powered_client_callback, + NULL, NULL); +} + +static void pair_device_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + tester_warn("Pairing failed: %s", mgmt_errstr(status)); + tester_test_failed(); + return; + } + + tester_print("Pairing succeedded"); + tester_test_passed(); +} + +static void smp_server(const void *data, uint16_t len, void *user_data) +{ + struct test_data *test_data = tester_get_data(); + const struct smp_client_data *cli = test_data->test_data; + + tester_print("Received SMP request"); + + if (!cli->expect_req) { + tester_test_passed(); + return; + } + + if (cli->expect_req_len != len) { + tester_warn("Unexpected SMP request length (%u != %u)", + len, cli->expect_req_len); + goto failed; + } + + if (memcmp(cli->expect_req, data, len) != 0) { + tester_warn("Unexpected SMP request"); + goto failed; + } + + if (cli->send_rsp) { + struct bthost *bthost; + + bthost = hciemu_client_get_host(test_data->hciemu); + bthost_send_cid(bthost, test_data->handle, SMP_CID, + cli->send_rsp, cli->send_rsp_len); + return; + } + + tester_test_passed(); + return; + +failed: + tester_test_failed(); +} + +static void smp_server_new_conn(uint16_t handle, void *user_data) +{ + struct test_data *data = user_data; + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + tester_print("New server connection with handle 0x%04x", handle); + + data->handle = handle; + + bthost_add_cid_hook(bthost, handle, SMP_CID, smp_server, NULL); +} + +static void test_client(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const uint8_t *client_bdaddr; + struct mgmt_cp_pair_device cp; + struct bthost *bthost; + + client_bdaddr = hciemu_get_client_bdaddr(data->hciemu); + if (!client_bdaddr) { + tester_warn("No client bdaddr"); + tester_test_failed(); + return; + } + + bthost = hciemu_client_get_host(data->hciemu); + bthost_set_connect_cb(bthost, smp_server_new_conn, data); + + memcpy(&cp.addr.bdaddr, client_bdaddr, sizeof(bdaddr_t)); + cp.addr.type = BDADDR_LE_PUBLIC; + cp.io_cap = 0x03; /* NoInputNoOutput */ + + mgmt_send(data->mgmt, MGMT_OP_PAIR_DEVICE, data->mgmt_index, + sizeof(cp), &cp, pair_device_complete, NULL, NULL); + + tester_print("Pairing in progress"); +} + +static void setup_powered_server_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + tester_setup_failed(); + return; + } + + tester_print("Controller powered on"); + + tester_setup_complete(); +} + +static void setup_powered_server(const void *test_data) +{ + struct test_data *data = tester_get_data(); + unsigned char param[] = { 0x01 }; + + tester_print("Powering on controller"); + + mgmt_send(data->mgmt, MGMT_OP_SET_LE, 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, + sizeof(param), param, setup_powered_server_callback, + 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; + } + + bthost = hciemu_client_get_host(data->hciemu); + bthost_set_connect_cb(bthost, smp_client_new_conn, data); + + bthost_hci_connect(bthost, master_bdaddr, BDADDR_LE_PUBLIC); +} + +int main(int argc, char *argv[]) +{ + tester_init(&argc, &argv); + + test_smp("SMP Server - Basic Request 1", + &smp_server_basic_req_1_test, + setup_powered_server, test_server); + test_smp("SMP Server - Invalid Request 1", + &smp_server_nval_req_1_test, + setup_powered_server, test_server); + test_smp("SMP Server - Invalid Request 2", + &smp_server_nval_req_2_test, + setup_powered_server, test_server); + + test_smp("SMP Client - Basic Request 1", + &smp_client_basic_req_1_test, + setup_powered_client, test_client); + + return tester_run(); +}