Brad Midgley <bmidgley@xmission.com>
Henryk Ploetz <henryk@ploetzli.ch>
Philip Blundell <pb@nexus.co.uk>
-Johan Hedberg <johan.hedberg@nokia.com>
+Johan Hedberg <johan.hedberg@intel.com>
Claudio Takahasi <claudio.takahasi@indt.org.br>
Eduardo Rocha <eduardo.rocha@indt.org.br>
Denis Kenzior <denis.kenzior@trolltech.com>
Siarhei Siamashka <siarhei.siamashka@nokia.com>
Nick Pelly <npelly@google.com>
Lennart Poettering <lennart@poettering.net>
-Gustavo F. Padovan <gustavo@las.ic.unicamp.br>
+Gustavo F. Padovan <padovan@profusion.mobi>
Marc-Andre Lureau <marc-andre.lureau@nokia.com>
Bea Lam <bea.lam@nokia.com>
Zygo Blaxell <zygo.blaxell@xandros.com>
Jaikumar Ganesh <jaikumar@google.com>
Elvis Pfutzenreuter <epx@signove.com>
Santiago Carot-Nemesio <scarot@libresoft.es>
-José Antonio Santos Cadenas <jcaden@libresoft.es>
+José Antonio Santos Cadenas <jcaden@libresoft.es>
Francisco Alecrim <francisco.alecrim@openbossa.org>
Daniel Orstadius <daniel.orstadius@gmail.com>
+Anderson Briglia <anderson.briglia@openbossa.org>
+Anderson Lizardo <anderson.lizardo@openbossa.org>
+Bruna Moreira <bruna.moreira@openbossa.org>
+Brian Gix <bgix@codeaurora.org>
+Andre Guedes <andre.guedes@openbossa.org>
+Sheldon Demario <sheldon.demario@openbossa.org>
+Lucas De Marchi <lucas.demarchi@profusion.mobi>
+Szymon Janc <szymon.janc@tieto.com>
+ver 4.98:
+ Fix issue with adapter list upon initialization failure.
+ Fix issue with missing legacy property for Low Energy.
+ Fix issue with missing EIR information handling.
+ Fix issue with device address type tracking.
+ Fix issue with alert level characteristic.
+ Fix issue with headset shutdown handling.
+ Fix issue with Wiimote address handling.
+ Add support for advanced l2test options.
+ Add support for attribute protocol and multiple adapters.
+
+ver 4.97:
+ Update support for proximity profile.
+ Fix issue with SBC audio decoding quality.
+ Fix multiple issues with HFP support.
+ Fix multiple issues with A2DP support.
+ Fix multiple issues with AVDTP support.
+ Fix multiple issues with AVRCP support.
+ Add support for AVRCP meta-data transfer.
+ Add support for Bluetooth based thermometers.
+
+ver 4.96:
+ Fix issue with race condition in AVDTP stream start.
+ Fix issue with global adapter offline switching.
+ Fix issue with pairing and No Bonding devices.
+ Add support for Nintendo Wii Remote pairing.
+
+ver 4.95:
+ Fix issue with AVCTP replies with invalid PID.
+ Fix issue with AVRCP and unknown packet types.
+ Fix issue with AVRCP not using NOT_IMPLEMENTED correctly.
+ Fix issue with AVDTP discovery if all endpoints are in use.
+ Fix issue with invalid memory writes and media support.
+ Fix issue with not removing device alias and unbonding.
+ Fix issue with device disconnects and offline mode handling.
+ Add support for setting adapter name based on machine-info.
+ Add support for systemd service configuration.
+
+ver 4.94:
+ Fix issue with invalid read of memory in various modules.
+ Fix issue with buffer overflow when sending AVDTP commands.
+ Fix issue with response to vendor dependent AVRCP commands.
+ Fix issue with headset when not able to reply with ERROR.
+ Fix issue with crash when creating a device from storage.
+ Fix issue with handling non UTF-8 devices names.
+ Add support for improved discovery procedure.
+
+ver 4.93:
+ Fix issue with property type and Health Main channel.
+ Fix issue with crash when removing devices.
+ Add support for hid2hci and udev integration.
+
+ver 4.92:
+ Fix issue with handling of A2DP suspend response.
+ Fix issue with crashing when acquiring A2DP stream.
+ Fix issue with missing check for valid SCO before shutdown.
+ Fix issue with waiting for POLLERR when disconnecting SCO.
+ Fix issue with disconnect after primary service discovery.
+ Fix issue with attribute interface registration.
+ Add support for primary services over BR/EDR.
+ Add support for flushable packets of A2DP media.
+
+ver 4.91:
+ Fix issue with LMP version string and hciconfig.
+ Fix issue with missing discovery signal when scanning.
+ Fix issue with wrong state and canceling name resolving.
+ Fix issue with missing check during adapter initialization.
+ Fix issue with missing protocol not supported error and A2DP.
+ Fix issue with crash during driver unregistering and A2DP.
+ Fix issue with crash when receiving AVDTP close command.
+ Fix issue with remote SEP handling when A2DP codec changes.
+ Fix issue with SCO hangup handling and state changes.
+ Fix issue with security level and MCAP instances.
+ Fix issue with memory leak and HDP data channels.
+ Add support for discover characteristics by UUID to gatttool.
+ Add initial support for Out-of-Band association model.
+ Add initial support for SIM Access Profile.
+
ver 4.90:
Fix issue with setting of global mode property.
Fix issue with handling of RequestSession responses.
lib_LTLIBRARIES =
+noinst_LIBRARIES =
+
noinst_LTLIBRARIES =
bin_PROGRAMS =
include_HEADERS =
-if CONFIGFILES
+if DATAFILES
dbusdir = $(sysconfdir)/dbus-1/system.d
dbus_DATA = src/bluetooth.conf
statedir = $(localstatedir)/lib/bluetooth
state_DATA =
+
+if SYSTEMD
+systemdunitdir = @SYSTEMD_UNITDIR@
+
+systemdunit_DATA = src/bluetooth.service
+endif
endif
plugindir = $(libdir)/bluetooth/plugins
+if MAINTAINER_MODE
+build_plugindir = $(abs_top_srcdir)/plugins/.libs
+else
+build_plugindir = $(plugindir)
+endif
+
+
plugin_LTLIBRARIES =
lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h
local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file)))
+BUILT_SOURCES = $(local_headers) src/builtin.h
+
include_HEADERS += $(lib_headers)
lib_LTLIBRARIES += lib/libbluetooth.la
lib_libbluetooth_la_SOURCES = $(lib_headers) \
lib/bluetooth.c lib/hci.c lib/sdp.c lib/uuid.c
-lib_libbluetooth_la_LDFLAGS = -version-info 14:0:11
+lib_libbluetooth_la_LDFLAGS = -version-info 14:5:11
lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
-CLEANFILES += $(local_headers)
+noinst_LTLIBRARIES += lib/libbluetooth-private.la
+lib_libbluetooth_private_la_SOURCES = $(lib_libbluetooth_la_SOURCES)
if SBC
noinst_LTLIBRARIES += sbc/libsbc.la
sbc/sbc_primitives_neon.h sbc/sbc_primitives_neon.c \
sbc/sbc_primitives_armv6.h sbc/sbc_primitives_armv6.c
-sbc_libsbc_la_CFLAGS = -finline-functions -fgcse-after-reload \
+sbc_libsbc_la_CFLAGS = $(AM_CFLAGS) -finline-functions -fgcse-after-reload \
-funswitch-loops -funroll-loops
noinst_PROGRAMS += sbc/sbcinfo sbc/sbcdec sbc/sbcenc
noinst_PROGRAMS += sbc/sbctester
sbc_sbctester_LDADD = @SNDFILE_LIBS@ -lm
-sbc_sbctest_CFLAGS = @SNDFILE_CFLAGS@
+sbc_sbctest_CFLAGS = $(AM_CFLAGS) @SNDFILE_CFLAGS@
endif
endif
attrib_sources = attrib/att.h attrib/att.c attrib/gatt.h attrib/gatt.c \
- attrib/gattrib.h attrib/gattrib.c
+ attrib/gattrib.h attrib/gattrib.c attrib/client.h \
+ attrib/client.c attrib/gatt-service.h attrib/gatt-service.c
gdbus_sources = gdbus/gdbus.h gdbus/mainloop.c gdbus/watch.c \
gdbus/object.c gdbus/polkit.c
builtin_sources += plugins/pnat.c
endif
-if ECHOPLUGIN
-builtin_modules += echo
-builtin_sources += plugins/echo.c
-endif
-
if AUDIOPLUGIN
builtin_modules += audio
builtin_sources += audio/main.c \
audio/telephony.h audio/a2dp-codecs.h
builtin_nodist += audio/telephony.c
-noinst_LIBRARIES = audio/libtelephony.a
+noinst_LIBRARIES += audio/libtelephony.a
audio_libtelephony_a_SOURCES = audio/telephony.h audio/telephony-dummy.c \
audio/telephony-maemo5.c audio/telephony-ofono.c \
audio/telephony-maemo6.c audio/telephony-tizen.c
-
-$(audio_libtelephony_a_OBJECTS): $(local_headers)
-
endif
if SAPPLUGIN
builtin_sources += sap/main.c \
sap/manager.h sap/manager.c \
sap/server.h sap/server.c \
- sap/sap.h sap/sap-dummy.c
+ sap/sap.h
+
+builtin_nodist += sap/sap.c
+
+noinst_LIBRARIES += sap/libsap.a
+
+sap_libsap_a_SOURCES = sap/sap.h sap/sap-dummy.c sap/sap-u8500.c
endif
if INPUTPLUGIN
network/connection.h network/connection.c
endif
+if PROXIMITYPLUGIN
+builtin_modules += proximity
+builtin_sources += proximity/main.c \
+ proximity/manager.h proximity/manager.c \
+ proximity/monitor.h proximity/monitor.c \
+ proximity/reporter.h proximity/reporter.c
+endif
+
if SERVICEPLUGIN
builtin_modules += service
builtin_sources += plugins/service.c
endif
-if ATTRIBPLUGIN
-
-if READLINE
-bin_PROGRAMS += attrib/gatttool
+if GATT_EXAMPLE_PLUGIN
+builtin_modules += gatt_example
+builtin_sources += plugins/gatt-example.c
+endif
-attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
- attrib/gattrib.c btio/btio.c \
- src/glib-helper.h src/glib-helper.c \
- attrib/gatttool.h attrib/interactive.c \
- attrib/utils.c
-attrib_gatttool_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @READLINE_LIBS@
+if TIMEPLUGIN
+builtin_modules += time
+builtin_sources += time/main.c \
+ time/server.h time/server.c
endif
-builtin_modules += attrib
-builtin_sources += attrib/main.c \
- attrib/manager.h attrib/manager.c \
- attrib/client.h attrib/client.c \
- attrib/example.h attrib/example.c
+if ALERTPLUGIN
+builtin_modules += alert
+builtin_sources += alert/main.c \
+ alert/server.h alert/server.c
endif
if HEALTHPLUGIN
health/hdp_util.h health/hdp_util.c
endif
+if THERMOMETERPLUGIN
+builtin_modules += thermometer
+builtin_sources += thermometer/main.c \
+ thermometer/manager.h thermometer/manager.c \
+ thermometer/thermometer.h thermometer/thermometer.c
+endif
+
builtin_modules += hciops mgmtops
builtin_sources += plugins/hciops.c plugins/mgmtops.c
builtin_modules += storage
builtin_sources += plugins/storage.c
+builtin_modules += adaptername
+builtin_sources += plugins/adaptername.c
+
+if WIIMOTEPLUGIN
+builtin_modules += wiimote
+builtin_sources += plugins/wiimote.c
+endif
+
if MAEMO6PLUGIN
builtin_modules += maemo6
builtin_sources += plugins/maemo6.c
endif
+if DBUSOOBPLUGIN
+builtin_modules += dbusoob
+builtin_sources += plugins/dbusoob.c
+endif
+
+if MAINTAINER_MODE
+plugin_LTLIBRARIES += plugins/external-dummy.la
+plugins_external_dummy_la_SOURCES = plugins/external-dummy.c
+plugins_external_dummy_la_LDFLAGS = -module -avoid-version -no-undefined
+plugins_external_dummy_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
+endif
+
sbin_PROGRAMS += src/bluetoothd
src_bluetoothd_SOURCES = $(gdbus_sources) $(builtin_sources) \
src/sdpd-service.c src/sdpd-database.c \
src/attrib-server.h src/attrib-server.c \
src/sdp-xml.h src/sdp-xml.c \
- src/textfile.h src/textfile.c \
+ src/sdp-client.h src/sdp-client.c \
+ src/textfile.h src/textfile.c src/glib-compat.h \
src/glib-helper.h src/glib-helper.c \
src/oui.h src/oui.c src/uinput.h src/ppoll.h \
src/plugin.h src/plugin.c \
src/error.h src/error.c \
src/manager.h src/manager.c \
src/adapter.h src/adapter.c \
- src/device.h src/device.c \
+ src/device.h src/device.c src/attio.h \
src/dbus-common.c src/dbus-common.h \
- src/event.h src/event.c
-src_bluetoothd_LDADD = lib/libbluetooth.la @GLIB_LIBS@ @DBUS_LIBS@ \
+ src/event.h src/event.c \
+ src/oob.h src/oob.c src/eir.h src/eir.c
+src_bluetoothd_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @DBUS_LIBS@ \
@CAPNG_LIBS@ -ldl -lrt
src_bluetoothd_LDFLAGS = -Wl,--export-dynamic \
-Wl,--version-script=$(srcdir)/src/bluetooth.ver
-src_bluetoothd_DEPENDENCIES = lib/libbluetooth.la
+src_bluetoothd_DEPENDENCIES = lib/libbluetooth-private.la
+
+src_bluetoothd_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_PLUGIN_BUILTIN \
+ -DPLUGINDIR=\""$(build_plugindir)"\"
+src_bluetoothd_SHORTNAME = bluetoothd
builtin_files = src/builtin.h $(builtin_nodist)
man_MANS = src/bluetoothd.8
-if CONFIGFILES
+if DATAFILES
conf_DATA += src/main.conf
endif
input/input.conf serial/serial.conf \
audio/audio.conf audio/telephony-dummy.c \
audio/telephony-maemo5.c audio/telephony-ofono.c \
- audio/telephony-maemo6.c audio/telephony-tizen.c
-
+ audio/telephony-maemo6.c sap/sap-dummy.c sap/sap-u8500.c \
+ proximity/proximity.conf
if ALSA
alsadir = $(libdir)/alsa-lib
audio/rtp.h audio/ipc.h audio/ipc.c
audio_libasound_module_pcm_bluetooth_la_LDFLAGS = -module -avoid-version #-export-symbols-regex [_]*snd_pcm_.*
audio_libasound_module_pcm_bluetooth_la_LIBADD = sbc/libsbc.la \
- lib/libbluetooth.la @ALSA_LIBS@
-audio_libasound_module_pcm_bluetooth_la_CFLAGS = @ALSA_CFLAGS@
+ lib/libbluetooth-private.la @ALSA_LIBS@
+audio_libasound_module_pcm_bluetooth_la_CFLAGS = $(AM_CFLAGS) @ALSA_CFLAGS@
audio_libasound_module_ctl_bluetooth_la_SOURCES = audio/ctl_bluetooth.c \
audio/rtp.h audio/ipc.h audio/ipc.c
audio_libasound_module_ctl_bluetooth_la_LDFLAGS = -module -avoid-version #-export-symbols-regex [_]*snd_ctl_.*
-audio_libasound_module_ctl_bluetooth_la_LIBADD = lib/libbluetooth.la @ALSA_LIBS@
-audio_libasound_module_ctl_bluetooth_la_CFLAGS = @ALSA_CFLAGS@
+audio_libasound_module_ctl_bluetooth_la_LIBADD = \
+ lib/libbluetooth-private.la @ALSA_LIBS@
+audio_libasound_module_ctl_bluetooth_la_CFLAGS = $(AM_CLAGS) @ALSA_CFLAGS@
-if CONFIGFILES
+if DATAFILES
alsaconfdir = $(datadir)/alsa
alsaconf_DATA = audio/bluetooth.conf
audio/gstrtpsbcpay.h audio/gstrtpsbcpay.c \
audio/rtp.h audio/ipc.h audio/ipc.c
audio_libgstbluetooth_la_LDFLAGS = -module -avoid-version
-audio_libgstbluetooth_la_LIBADD = sbc/libsbc.la lib/libbluetooth.la \
- @GSTREAMER_LIBS@ -lgstaudio-0.10 -lgstrtp-0.10
-$(audio_libgstbluetooth_la_OBJECTS): $(local_headers)
+audio_libgstbluetooth_la_LIBADD = sbc/libsbc.la lib/libbluetooth-private.la \
+ @DBUS_LIBS@ @GSTREAMER_LIBS@ \
+ -lgstaudio-0.10 -lgstrtp-0.10
audio_libgstbluetooth_la_CFLAGS = -fvisibility=hidden -fno-strict-aliasing \
- $(AM_CFLAGS) @GSTREAMER_CFLAGS@
+ $(AM_CFLAGS) @DBUS_CFLAGS@ @GSTREAMER_CFLAGS@
endif
endif
include Makefile.tools
-if UDEVRULES
-rulesdir = @UDEV_DATADIR@
+if DATAFILES
+rulesdir = @UDEV_DIR@/rules.d
udev_files = scripts/bluetooth.rules
endif
if PCMCIA
+udevdir = @UDEV_DIR@
+
udev_files += scripts/bluetooth-serial.rules
+
+dist_udev_SCRIPTS = scripts/bluetooth_serial
endif
rules_DATA = $(foreach file,$(udev_files), scripts/97-$(notdir $(file)))
EXTRA_DIST += scripts/bluetooth.rules \
scripts/bluetooth-hid2hci.rules scripts/bluetooth-serial.rules
-if PCMCIA
-udevdir = $(libexecdir)/udev
-
-dist_udev_SCRIPTS = scripts/bluetooth_serial
-endif
-
EXTRA_DIST += doc/manager-api.txt \
doc/adapter-api.txt doc/device-api.txt \
doc/service-api.txt doc/agent-api.txt doc/attribute-api.txt \
AM_YFLAGS = -d
-AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@ \
- -DBLUETOOTH_PLUGIN_BUILTIN -DPLUGINDIR=\""$(plugindir)"\"
+AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@
INCLUDES = -I$(builddir)/lib -I$(builddir)/src -I$(srcdir)/src \
-I$(srcdir)/audio -I$(srcdir)/sbc -I$(srcdir)/gdbus \
- -I$(srcdir)/attrib -I$(srcdir)/btio
+ -I$(srcdir)/attrib -I$(srcdir)/btio -I$(srcdir)/tools \
+ -I$(builddir)/tools
if MCAP
INCLUDES += -I$(builddir)/health
endif
+unit_objects =
+
+if TEST
+unit_tests = unit/test-eir
+
+noinst_PROGRAMS += $(unit_tests)
+
+unit_test_eir_SOURCES = unit/test-eir.c src/eir.c src/glib-helper.c
+unit_test_eir_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @CHECK_LIBS@
+unit_test_eir_CFLAGS = $(AM_CFLAGS) @CHECK_CFLAGS@
+unit_objects += $(unit_test_eir_OBJECTS)
+else
+unit_tests =
+endif
+
+TESTS = $(unit_tests)
+
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = bluez.pc
-DISTCHECK_CONFIGURE_FLAGS = --disable-udevrules --enable-attrib
+DISTCHECK_CONFIGURE_FLAGS = --disable-datafiles
DISTCLEANFILES = $(pkgconfig_DATA)
aclocal.m4 configure config.h.in config.sub config.guess \
ltmain.sh depcomp compile missing install-sh mkinstalldirs ylwrap
-src/plugin.$(OBJEXT): src/builtin.h
-
src/builtin.h: src/genbuiltin $(builtin_sources)
$(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
audio/telephony.c: audio/@TELEPHONY_DRIVER@
+ $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
+
+sap/sap.c: sap/@SAP_DRIVER@
$(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
scripts/%.rules:
lib/bluetooth/%.h: lib/%.h
$(AM_V_at)$(MKDIR_P) lib/bluetooth
- $(AM_V_GEN)$(LN_S) $(abs_top_srcdir)/$< $@
+ $(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
clean-local:
$(RM) -r lib/bluetooth
if TOOLS
-if CONFIGFILES
+if DATAFILES
conf_DATA += tools/rfcomm.conf
endif
tools/kword.h tools/kword.c
EXTRA_tools_rfcomm_SOURCES = tools/parser.h tools/parser.c \
tools/lexer.c
-tools_rfcomm_LDADD = lib/libbluetooth.la
+tools_rfcomm_LDADD = lib/libbluetooth-private.la
-tools_l2ping_LDADD = lib/libbluetooth.la
+tools_l2ping_LDADD = lib/libbluetooth-private.la
tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
tools/hciattach_st.c \
tools/hciattach_tialt.c \
tools/hciattach_ath3k.c \
tools/hciattach_qualcomm.c
-tools_hciattach_LDADD = lib/libbluetooth.la
+tools_hciattach_LDADD = lib/libbluetooth-private.la
tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c \
src/textfile.h src/textfile.c
-tools_hciconfig_LDADD = lib/libbluetooth.la
+tools_hciconfig_LDADD = lib/libbluetooth-private.la
tools_hcitool_SOURCES = tools/hcitool.c src/oui.h src/oui.c \
src/textfile.h src/textfile.c
-tools_hcitool_LDADD = lib/libbluetooth.la
+tools_hcitool_LDADD = lib/libbluetooth-private.la
tools_sdptool_SOURCES = tools/sdptool.c src/sdp-xml.h src/sdp-xml.c
-tools_sdptool_LDADD = lib/libbluetooth.la
+tools_sdptool_LDADD = lib/libbluetooth-private.la
-tools_ciptool_LDADD = lib/libbluetooth.la
+tools_ciptool_LDADD = lib/libbluetooth-private.la
-tools_avinfo_LDADD = lib/libbluetooth.la
+tools_avinfo_LDADD = lib/libbluetooth-private.la
-tools_ppporc_LDADD = lib/libbluetooth.la
+tools_ppporc_LDADD = lib/libbluetooth-private.la
-tools_hcieventmask_LDADD = lib/libbluetooth.la
+tools_hcieventmask_LDADD = lib/libbluetooth-private.la
+
+noinst_PROGRAMS += mgmt/btmgmt
+mgmt_btmgmt_SOURCES = mgmt/main.c
+mgmt_btmgmt_LDADD = lib/libbluetooth-private.la
+
+if READLINE
+bin_PROGRAMS += attrib/gatttool
+
+attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \
+ attrib/gattrib.c btio/btio.c \
+ attrib/gatttool.h attrib/interactive.c \
+ attrib/utils.c
+attrib_gatttool_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @READLINE_LIBS@
+endif
dist_man_MANS += tools/rfcomm.1 tools/l2ping.8 \
tools/hciattach.8 tools/hciconfig.8 \
sbin_PROGRAMS += tracer/hcitrace
tracer_hcitrace_SOURCES = tracer/main.c
-tracer_hcitrace_LDADD = lib/libbluetooth.la \
+tracer_hcitrace_LDADD = lib/libbluetooth-private.la \
@GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@
-tracer_hcitrace_DEPENDENCIES = lib/libbluetooth.la
+tracer_hcitrace_DEPENDENCIES = lib/libbluetooth-private.la
endif
if BCCMD
tools_bccmd_SOURCES = tools/bccmd.c tools/csr.h tools/csr.c \
tools/csr_hci.c tools/csr_h4.c tools/csr_3wire.c \
tools/csr_bcsp.c tools/ubcsp.h tools/ubcsp.c
-tools_bccmd_LDADD = lib/libbluetooth.la
+tools_bccmd_LDADD = lib/libbluetooth-private.la
if USB
tools_bccmd_SOURCES += tools/csr_usb.c
endif
if HID2HCI
-sbin_PROGRAMS += tools/hid2hci
+udevdir = @UDEV_DIR@
+
+udev_PROGRAMS = tools/hid2hci
-tools_hid2hci_LDADD = @USB_LIBS@
+tools_hid2hci_LDADD = @USB_LIBS@ @UDEV_LIBS@
dist_man_MANS += tools/hid2hci.8
else
cups_bluetooth_SOURCES = $(gdbus_sources) cups/main.c cups/cups.h \
cups/sdp.c cups/spp.c cups/hcrp.c
-cups_bluetooth_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ lib/libbluetooth.la
+cups_bluetooth_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ lib/libbluetooth-private.la
endif
test/attest test/hstest test/avtest test/ipctest \
test/lmptest test/bdaddr test/agent \
test/btiotest test/test-textfile \
- test/uuidtest
+ test/uuidtest test/mpris-player
-test_hciemu_LDADD = @GLIB_LIBS@ lib/libbluetooth.la
+test_hciemu_LDADD = lib/libbluetooth-private.la
-test_l2test_LDADD = lib/libbluetooth.la
+test_l2test_LDADD = lib/libbluetooth-private.la
-test_rctest_LDADD = lib/libbluetooth.la
+test_rctest_LDADD = lib/libbluetooth-private.la
test_gaptest_LDADD = @DBUS_LIBS@
-test_sdptest_LDADD = lib/libbluetooth.la
+test_sdptest_LDADD = lib/libbluetooth-private.la
-test_scotest_LDADD = lib/libbluetooth.la
+test_scotest_LDADD = lib/libbluetooth-private.la
-test_attest_LDADD = lib/libbluetooth.la
+test_attest_LDADD = lib/libbluetooth-private.la
-test_hstest_LDADD = lib/libbluetooth.la
+test_hstest_LDADD = lib/libbluetooth-private.la
-test_avtest_LDADD = lib/libbluetooth.la
+test_avtest_LDADD = lib/libbluetooth-private.la
-test_lmptest_LDADD = lib/libbluetooth.la
+test_lmptest_LDADD = lib/libbluetooth-private.la
test_ipctest_SOURCES = test/ipctest.c audio/ipc.h audio/ipc.c
test_ipctest_LDADD= @GLIB_LIBS@ sbc/libsbc.la
test_bdaddr_SOURCES = test/bdaddr.c src/oui.h src/oui.c
-test_bdaddr_LDADD = lib/libbluetooth.la
+test_bdaddr_LDADD = lib/libbluetooth-private.la
test_agent_LDADD = @DBUS_LIBS@
test_btiotest_SOURCES = test/btiotest.c btio/btio.h btio/btio.c
-test_btiotest_LDADD = @GLIB_LIBS@ lib/libbluetooth.la
+test_btiotest_LDADD = @GLIB_LIBS@ lib/libbluetooth-private.la
test_uuidtest_SOURCES = test/uuidtest.c
-test_uuidtest_LDADD = lib/libbluetooth.la
+test_uuidtest_LDADD = lib/libbluetooth-private.la
+
+test_mpris_player_LDADD = @DBUS_LIBS@ @GLIB_LIBS@
test_test_textfile_SOURCES = test/test-textfile.c src/textfile.h src/textfile.c
EXTRA_DIST += test/rctest.1 test/hciemu.1 test/bdaddr.8
endif
-EXTRA_DIST += test/apitest test/hsplay test/hsmicro test/dbusdef.py \
- test/monitor-bluetooth test/list-devices test/test-discovery \
- test/test-manager test/test-adapter test/test-device \
- test/test-service test/test-serial test/test-telephony \
- test/test-network test/simple-agent test/simple-service \
- test/simple-endpoint test/test-audio test/test-input \
- test/test-attrib test/service-record.dtd test/service-did.xml \
+EXTRA_DIST += test/apitest test/sap-client test/hsplay test/hsmicro \
+ test/dbusdef.py test/monitor-bluetooth test/list-devices \
+ test/test-discovery test/test-manager test/test-adapter \
+ test/test-device test/test-service test/test-serial \
+ test/test-telephony test/test-network test/simple-agent \
+ test/simple-service test/simple-endpoint test/test-audio \
+ test/test-input test/test-attrib test/test-proximity \
+ test/test-sap-server test/test-oob test/test-serial-proxy \
+ test/test-thermometer test/test-health test/test-health-sink \
+ test/service-record.dtd test/service-did.xml \
test/service-spp.xml test/service-opp.xml test/service-ftp.xml
compat_hidd_SOURCES = compat/hidd.c compat/hidd.h src/uinput.h \
compat/sdp.h compat/sdp.c compat/fakehid.c \
src/textfile.h src/textfile.c
-compat_hidd_LDADD = -lm lib/libbluetooth.la
+compat_hidd_LDADD = -lm lib/libbluetooth-private.la
dist_man_MANS += compat/hidd.1
else
compat_pand_SOURCES = compat/pand.c compat/pand.h \
compat/bnep.c compat/sdp.h compat/sdp.c \
src/textfile.h src/textfile.c
-compat_pand_LDADD = lib/libbluetooth.la
+compat_pand_LDADD = lib/libbluetooth-private.la
dist_man_MANS += compat/pand.1
else
compat_dund_SOURCES = compat/dund.c compat/dund.h compat/lib.h \
compat/sdp.h compat/sdp.c compat/dun.c compat/msdun.c \
src/textfile.h src/textfile.c
-compat_dund_LDADD = lib/libbluetooth.la
+compat_dund_LDADD = lib/libbluetooth-private.la
dist_man_MANS += compat/dund.1
else
General
==========
-- UUID128 handling: Create new functions to handle UUIDs on host order.
- Functions should start with prefix "bt_uuid". In the first phase, attribute
- server/client and gatttool code should be changed to use these new functions.
- The idea is to keep the consistency for UUID-16, UUID-32 and UUID-128. SDP
- functions store UUID-16 and UUID-32 on host order, however UUID-128 is stored
- on network order/big endian. Attribute Protocol uses little endian, while
- SDP uses big endian. The idea is always store the UUID values on host order
- and use utility functions to convert to the proper byte order depending on
- the protocol: ATT or SDP.
+- UUID handling: Use the new functions created for UUID handling in all parts
+ of BlueZ code. Currently, the new bt_uuid_* functions are being used by
+ GATT-related code only.
Priority: high
- Complexity: C1
+ Complexity: C4
- Rename glib-helper file to a more convenient name. The ideia is try to keep
only sdp helpers functions. bt_* prefix shall be also changed.
ATT/GATT
========
-- For BR/EDR, primary services can be registered based on the information
- extracted from the service records. UUIDs, start and end handles information
- are available in the record, Discover All Primary Services procedure is not
- necessary. If a GATT service doesn't export a service record means that
- it should not be used over BR/EDR. Don't start this task before to move the
- attribute client code to the bluetoothd core.
+- At the moment authentication and authorization is not supported at the
+ same time, read/write requirements in the attribute server needs to
+ be extended. According to Bluetooth Specification a server shall check
+ authentication and authorization requirements before any other check is
+ performed.
Priority: Medium
Complexity: C1
Priority: Medium
Complexity: C2
-- GATT server: fix MTU exchange
-
- Priority: Medium
- Complexity: C2
-
- Implement ATT PDU validation. Malformed PDUs can cause division by zero
when decoding PDUs. A proper error PDU should be returned for this case.
See decoding function in att.c file.
Priority: Medium
Complexity: C1
+- Fix hard-coded PSM for GATT services over basic rate.
+
+ Priority: Low
+ Complexity: C1
+
- Refactor read_by_group() and read_by_type() in src/attrib-server.c
(they've grown simply too big). First step could be to move out the
long for-loops to new functions called e.g. get_groups() and get_types().
Priority: Low
Complexity: C2
+ Owner: Anderson Lizardo <anderson.lizardo@openbossa.org>
Management Interface
====================
Priority: High
Complexity: C3
-- EIR generation support
-
- Priority: High
- Complexity: C2
-
- Blacklist support
Priority: Medium
Priority: Medium
Complexity: C2
+ Owner: Andre Guedes <andre.guedes@openbossa.org>
AC_SUBST(CONFIGDIR, "${configdir}")
AC_SUBST(STORAGEDIR, "${storagedir}")
- UDEV_DATADIR="`$PKG_CONFIG --variable=udevdir udev`"
- if (test -z "${UDEV_DATADIR}"); then
- UDEV_DATADIR="${sysconfdir}/udev/rules.d"
- else
- UDEV_DATADIR="${UDEV_DATADIR}/rules.d"
+ UDEV_DIR="`$PKG_CONFIG --variable=udevdir udev`"
+ if (test -z "${UDEV_DIR}"); then
+ UDEV_DIR="/lib/udev"
fi
- AC_SUBST(UDEV_DATADIR)
+ AC_SUBST(UDEV_DIR)
])
AC_DEFUN([AC_PATH_DBUS], [
])
AC_DEFUN([AC_PATH_GSTREAMER], [
- PKG_CHECK_MODULES(GSTREAMER, gstreamer-0.10 gstreamer-plugins-base-0.10, gstreamer_found=yes, gstreamer_found=no)
+ PKG_CHECK_MODULES(GSTREAMER, gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10, gstreamer_found=yes,
+ AC_MSG_WARN(GStreamer library version 0.10.30 or later is required);gstreamer_found=no)
AC_SUBST(GSTREAMER_CFLAGS)
AC_SUBST(GSTREAMER_LIBS)
GSTREAMER_PLUGINSDIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-0.10`
AC_SUBST(GSTREAMER_PLUGINSDIR)
])
-AC_DEFUN([AC_PATH_PULSE], [
- PKG_CHECK_MODULES(PULSE, libpulse, pulse_found=yes, pulse_found=no)
- AC_SUBST(PULSE_CFLAGS)
- AC_SUBST(PULSE_LIBS)
-])
-
AC_DEFUN([AC_PATH_ALSA], [
PKG_CHECK_MODULES(ALSA, alsa, alsa_found=yes, alsa_found=no)
AC_CHECK_LIB(rt, clock_gettime, ALSA_LIBS="$ALSA_LIBS -lrt", alsa_found=no)
[Define to 1 if you need the usb_interrupt_read() function.]))
])
+AC_DEFUN([AC_PATH_UDEV], [
+ PKG_CHECK_MODULES(UDEV, libudev, udev_found=yes, udev_found=no)
+ AC_SUBST(UDEV_CFLAGS)
+ AC_SUBST(UDEV_LIBS)
+])
+
AC_DEFUN([AC_PATH_SNDFILE], [
PKG_CHECK_MODULES(SNDFILE, sndfile, sndfile_found=yes, sndfile_found=no)
AC_SUBST(SNDFILE_CFLAGS)
[])
])
+AC_DEFUN([AC_PATH_CHECK], [
+ PKG_CHECK_MODULES(CHECK, check >= 0.9.6, check_found=yes, check_found=no)
+ AC_SUBST(CHECK_CFLAGS)
+ AC_SUBST(CHECK_LIBS)
+])
+
AC_DEFUN([AC_PATH_OUI], [
AC_ARG_WITH(ouifile,
AS_HELP_STRING([--with-ouifile=PATH],[Path to the oui.txt file @<:@auto@:>@]),
fortify_enable=yes
pie_enable=yes
sndfile_enable=${sndfile_found}
- hal_enable=${hal_found}
+ hal_enable=no
usb_enable=${usb_found}
alsa_enable=${alsa_found}
gstreamer_enable=${gstreamer_found}
serial_enable=yes
network_enable=yes
sap_enable=no
+ proximity_enable=no
+ time_enable=no
+ alert_enable=no
service_enable=yes
- health_enable=yes
+ health_enable=no
pnat_enable=no
- attrib_enable=no
+ gatt_example_enable=no
tracer_enable=no
tools_enable=yes
hidd_enable=no
pcmcia_enable=no
hid2hci_enable=no
dfutool_enable=no
- udevrules_enable=yes
- configfiles_enable=yes
+ datafiles_enable=yes
telephony_driver=dummy
maemo6_enable=no
+ sap_driver=dummy
+ dbusoob_enable=no
+ wiimote_enable=no
+ thermometer_enable=no
AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable code optimization]), [
optimization_enable=${enableval}
sap_enable=${enableval}
])
+ AC_ARG_WITH(sap, AC_HELP_STRING([--with-sap=DRIVER], [select SAP driver]), [
+ sap_driver=${withval}
+ ])
+ AC_SUBST([SAP_DRIVER], [sap-${sap_driver}.c])
+
+ AC_ARG_ENABLE(proximity, AC_HELP_STRING([--enable-proximity], [enable proximity plugin]), [
+ proximity_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(time, AC_HELP_STRING([--enable-time], [enable Time Profile plugin]), [
+ time_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(alert, AC_HELP_STRING([--enable-alert], [enable Phone Alert Profile plugin]), [
+ alert_enable=${enableval}
+ ])
+
AC_ARG_ENABLE(serial, AC_HELP_STRING([--disable-serial], [disable serial plugin]), [
serial_enable=${enableval}
])
pnat_enable=${enableval}
])
- AC_ARG_ENABLE(attrib, AC_HELP_STRING([--enable-attrib], [enable attrib plugin]), [
- attrib_enable=${enableval}
+ AC_ARG_ENABLE(gatt-example, AC_HELP_STRING([--enable-gatt-example], [enable GATT example plugin]), [
+ gatt_example_enable=${enableval}
])
AC_ARG_ENABLE(gstreamer, AC_HELP_STRING([--enable-gstreamer], [enable GStreamer support]), [
test_enable=${enableval}
])
- AC_ARG_ENABLE(udevrules, AC_HELP_STRING([--enable-udevrules], [install Bluetooth udev rules]), [
- udevrules_enable=${enableval}
- ])
-
- AC_ARG_ENABLE(configfiles, AC_HELP_STRING([--enable-configfiles], [install Bluetooth configuration files]), [
- configfiles_enable=${enableval}
+ AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--enable-datafiles], [install Bluetooth configuration and data files]), [
+ datafiles_enable=${enableval}
])
AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable compiling with debugging information]), [
maemo6_enable=${enableval}
])
+ AC_ARG_ENABLE(dbusoob, AC_HELP_STRING([--enable-dbusoob], [compile with D-Bus OOB plugin]), [
+ dbusoob_enable=${enableval}
+ ])
+
+ AC_ARG_ENABLE(wiimote, AC_HELP_STRING([--enable-wiimote], [compile with Wii Remote plugin]), [
+ wiimote_enable=${enableval}
+ ])
+
AC_ARG_ENABLE(hal, AC_HELP_STRING([--enable-hal], [Use HAL to determine adapter class]), [
hal_enable=${enableval}
])
+ AC_ARG_ENABLE(thermometer, AC_HELP_STRING([--enable-thermometer], [enable thermometer plugin]), [
+ thermometer_enable=${enableval}
+ ])
+
if (test "${fortify_enable}" = "yes"); then
CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2"
fi
AM_CONDITIONAL(SERIALPLUGIN, test "${serial_enable}" = "yes")
AM_CONDITIONAL(NETWORKPLUGIN, test "${network_enable}" = "yes")
AM_CONDITIONAL(SAPPLUGIN, test "${sap_enable}" = "yes")
+ AM_CONDITIONAL(PROXIMITYPLUGIN, test "${proximity_enable}" = "yes")
+ AM_CONDITIONAL(TIMEPLUGIN, test "${time_enable}" = "yes")
+ AM_CONDITIONAL(ALERTPLUGIN, test "${alert_enable}" = "yes")
AM_CONDITIONAL(SERVICEPLUGIN, test "${service_enable}" = "yes")
AM_CONDITIONAL(HEALTHPLUGIN, test "${health_enable}" = "yes")
AM_CONDITIONAL(MCAP, test "${health_enable}" = "yes")
AM_CONDITIONAL(HAL, test "${hal_enable}" = "yes")
AM_CONDITIONAL(READLINE, test "${readline_found}" = "yes")
- AM_CONDITIONAL(ATTRIBPLUGIN, test "${attrib_enable}" = "yes")
- AM_CONDITIONAL(ECHOPLUGIN, test "no" = "yes")
+ AM_CONDITIONAL(GATT_EXAMPLE_PLUGIN, test "${gatt_example_enable}" = "yes")
AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes")
AM_CONDITIONAL(TRACER, test "${tracer_enable}" = "yes")
AM_CONDITIONAL(HIDD, test "${hidd_enable}" = "yes")
AM_CONDITIONAL(PAND, test "${pand_enable}" = "yes")
AM_CONDITIONAL(DUND, test "${dund_enable}" = "yes")
AM_CONDITIONAL(CUPS, test "${cups_enable}" = "yes")
- AM_CONDITIONAL(TEST, test "${test_enable}" = "yes")
+ AM_CONDITIONAL(TEST, test "${test_enable}" = "yes" && test "${check_found}" = "yes")
AM_CONDITIONAL(TOOLS, test "${tools_enable}" = "yes")
AM_CONDITIONAL(BCCMD, test "${bccmd_enable}" = "yes")
AM_CONDITIONAL(PCMCIA, test "${pcmcia_enable}" = "yes")
- AM_CONDITIONAL(HID2HCI, test "${hid2hci_enable}" = "yes" && test "${usb_found}" = "yes")
+ AM_CONDITIONAL(HID2HCI, test "${hid2hci_enable}" = "yes" && test "${usb_found}" = "yes" && test "${udev_found}" = "yes")
AM_CONDITIONAL(DFUTOOL, test "${dfutool_enable}" = "yes" && test "${usb_found}" = "yes")
- AM_CONDITIONAL(UDEVRULES, test "${udevrules_enable}" = "yes")
- AM_CONDITIONAL(CONFIGFILES, test "${configfiles_enable}" = "yes")
+ AM_CONDITIONAL(DATAFILES, test "${datafiles_enable}" = "yes")
AM_CONDITIONAL(MAEMO6PLUGIN, test "${maemo6_enable}" = "yes")
+ AM_CONDITIONAL(DBUSOOBPLUGIN, test "${dbusoob_enable}" = "yes")
+ AM_CONDITIONAL(WIIMOTEPLUGIN, test "${wiimote_enable}" = "yes")
+ AM_CONDITIONAL(THERMOMETERPLUGIN, test "${thermometer_enable}" = "yes")
])
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <glib.h>
+
+#include "plugin.h"
+#include "hcid.h"
+#include "log.h"
+#include "server.h"
+
+static int alert_init(void)
+{
+ if (!main_opts.attrib_server) {
+ DBG("Attribute server is disabled");
+ return -1;
+ }
+
+ return alert_server_init();
+}
+
+static void alert_exit(void)
+{
+ if (!main_opts.attrib_server)
+ return;
+
+ alert_server_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(alert, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ alert_init, alert_exit)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "server.h"
+
+int alert_server_init(void)
+{
+ return 0;
+}
+
+void alert_server_exit(void)
+{
+}
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
*
*/
-int server_example_init(void);
-void server_example_exit(void);
+int alert_server_init(void);
+void alert_server_exit(void);
case ATT_ECODE_INVALID_HANDLE:
return "Invalid handle";
case ATT_ECODE_READ_NOT_PERM:
- return "Atribute can't be read";
+ return "Attribute can't be read";
case ATT_ECODE_WRITE_NOT_PERM:
return "Attribute can't be written";
case ATT_ECODE_INVALID_PDU:
return "Attribute PDU was invalid";
- case ATT_ECODE_INSUFF_AUTHEN:
+ case ATT_ECODE_AUTHENTICATION:
return "Attribute requires authentication before read/write";
case ATT_ECODE_REQ_NOT_SUPP:
return "Server doesn't support the request received";
case ATT_ECODE_INVALID_OFFSET:
return "Offset past the end of the attribute";
- case ATT_ECODE_INSUFF_AUTHO:
+ case ATT_ECODE_AUTHORIZATION:
return "Attribute requires authorization before read/write";
case ATT_ECODE_PREP_QUEUE_FULL:
return "Too many prepare writes have been queued";
return list;
}
-uint16_t enc_notification(struct attribute *a, uint8_t *pdu, int len)
+uint16_t enc_notification(uint16_t handle, uint8_t *value, int vlen,
+ uint8_t *pdu, int len)
{
const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
if (pdu == NULL)
return 0;
- if (len < (a->len + min_len))
+ if (len < (vlen + min_len))
return 0;
pdu[0] = ATT_OP_HANDLE_NOTIFY;
- att_put_u16(a->handle, &pdu[1]);
- memcpy(&pdu[3], a->data, a->len);
+ att_put_u16(handle, &pdu[1]);
+ memcpy(&pdu[3], value, vlen);
- return a->len + min_len;
+ return vlen + min_len;
}
-uint16_t enc_indication(struct attribute *a, uint8_t *pdu, int len)
+uint16_t enc_indication(uint16_t handle, uint8_t *value, int vlen,
+ uint8_t *pdu, int len)
{
const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
if (pdu == NULL)
return 0;
- if (len < (a->len + min_len))
+ if (len < (vlen + min_len))
return 0;
pdu[0] = ATT_OP_HANDLE_IND;
- att_put_u16(a->handle, &pdu[1]);
- memcpy(&pdu[3], a->data, a->len);
+ att_put_u16(handle, &pdu[1]);
+ memcpy(&pdu[3], value, vlen);
- return a->len + min_len;
+ return vlen + min_len;
}
-struct attribute *dec_indication(const uint8_t *pdu, int len)
+uint16_t dec_indication(const uint8_t *pdu, int len, uint16_t *handle,
+ uint8_t *value, int vlen)
{
const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
-
- struct attribute *a;
+ uint16_t dlen;
if (pdu == NULL)
- return NULL;
+ return 0;
if (pdu[0] != ATT_OP_HANDLE_IND)
- return NULL;
+ return 0;
if (len < min_len)
- return NULL;
+ return 0;
+
+ dlen = MIN(len - min_len, vlen);
- a = g_malloc0(sizeof(struct attribute) + len - min_len);
- a->len = len - min_len;
+ if (handle)
+ *handle = att_get_u16(&pdu[1]);
- a->handle = att_get_u16(&pdu[1]);
- memcpy(a->data, &pdu[3], a->len);
+ memcpy(value, &pdu[3], dlen);
- return a;
+ return dlen;
}
uint16_t enc_confirmation(uint8_t *pdu, int len)
#define GATT_SERVER_CHARAC_CFG_UUID 0x2903
#define GATT_CHARAC_FMT_UUID 0x2904
#define GATT_CHARAC_AGREG_FMT_UUID 0x2905
+#define GATT_CHARAC_VALID_RANGE_UUID 0x2906
/* Attribute Protocol Opcodes */
#define ATT_OP_ERROR 0x01
#define ATT_ECODE_READ_NOT_PERM 0x02
#define ATT_ECODE_WRITE_NOT_PERM 0x03
#define ATT_ECODE_INVALID_PDU 0x04
-#define ATT_ECODE_INSUFF_AUTHEN 0x05
+#define ATT_ECODE_AUTHENTICATION 0x05
#define ATT_ECODE_REQ_NOT_SUPP 0x06
#define ATT_ECODE_INVALID_OFFSET 0x07
-#define ATT_ECODE_INSUFF_AUTHO 0x08
+#define ATT_ECODE_AUTHORIZATION 0x08
#define ATT_ECODE_PREP_QUEUE_FULL 0x09
#define ATT_ECODE_ATTR_NOT_FOUND 0x0A
#define ATT_ECODE_ATTR_NOT_LONG 0x0B
#define ATT_CHAR_PROPER_AUTH 0x40
#define ATT_CHAR_PROPER_EXT_PROPER 0x80
+/* Client Characteristic Configuration bit field */
+#define ATT_CLIENT_CHAR_CONF_NOTIFICATION 0x0001
+#define ATT_CLIENT_CHAR_CONF_INDICATION 0x0002
#define ATT_MAX_MTU 256
#define ATT_DEFAULT_L2CAP_MTU 48
#define ATT_DEFAULT_LE_MTU 23
+#define ATT_CID 4
+#define ATT_PSM 31
+
/* Requirements for read/write operations */
enum {
ATT_NONE, /* No restrictions */
uint8_t (*write_cb)(struct attribute *a, gpointer user_data);
gpointer cb_user_data;
int len;
- uint8_t data[0];
+ uint8_t *data;
};
struct att_data_list {
uint8_t *pdu, int len);
struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len,
uint8_t *format);
-uint16_t enc_notification(struct attribute *a, uint8_t *pdu, int len);
-uint16_t enc_indication(struct attribute *a, uint8_t *pdu, int len);
-struct attribute *dec_indication(const uint8_t *pdu, int len);
+uint16_t enc_notification(uint16_t handle, uint8_t *value, int vlen,
+ uint8_t *pdu, int len);
+uint16_t enc_indication(uint16_t handle, uint8_t *value, int vlen,
+ uint8_t *pdu, int len);
+uint16_t dec_indication(const uint8_t *pdu, int len, uint16_t *handle,
+ uint8_t *value, int vlen);
uint16_t enc_confirmation(uint8_t *pdu, int len);
uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len);
#include <bluetooth/bluetooth.h>
#include <bluetooth/uuid.h>
+#include "glib-compat.h"
#include "adapter.h"
#include "device.h"
#include "log.h"
#include "att.h"
#include "gattrib.h"
+#include "attio.h"
#include "gatt.h"
#include "client.h"
#define CHAR_INTERFACE "org.bluez.Characteristic"
-struct gatt_service {
- struct btd_device *dev;
- bdaddr_t sba;
- bdaddr_t dba;
- char *path;
- GSList *primary;
- GAttrib *attrib;
- DBusMessage *msg;
- int psm;
- gboolean listen;
-};
-
struct format {
guint8 format;
guint8 exponent;
guint16 desc;
} __attribute__ ((packed));
-struct primary {
- struct gatt_service *gatt;
- struct att_primary *att;
+struct query {
+ DBusMessage *msg;
+ guint attioid;
+ GSList *list;
+};
+
+struct gatt_service {
+ struct btd_device *dev;
+ struct att_primary *prim;
+ DBusConnection *conn;
+ GAttrib *attrib;
+ guint attioid;
+ int psm;
char *path;
GSList *chars;
+ GSList *offline_chars;
GSList *watchers;
+ struct query *query;
};
struct characteristic {
- struct primary *prim;
+ struct gatt_service *gatt;
char *path;
uint16_t handle;
uint16_t end;
};
struct query_data {
- struct primary *prim;
+ struct gatt_service *gatt;
struct characteristic *chr;
- DBusMessage *msg;
uint16_t handle;
};
guint id;
char *name;
char *path;
- struct primary *prim;
+ struct gatt_service *gatt;
};
static GSList *gatt_services = NULL;
-static DBusConnection *connection;
-
static void characteristic_free(void *user_data)
{
struct characteristic *chr = user_data;
g_free(watcher);
}
-static void primary_free(void *user_data)
-{
- struct primary *prim = user_data;
- GSList *l;
-
- for (l = prim->watchers; l; l = l->next) {
- struct watcher *watcher = l->data;
- g_dbus_remove_watch(connection, watcher->id);
- }
-
- g_slist_foreach(prim->chars, (GFunc) characteristic_free, NULL);
- g_slist_free(prim->chars);
- g_free(prim->path);
- g_free(prim);
-}
-
-static void gatt_service_free(void *user_data)
+static void gatt_service_free(struct gatt_service *gatt)
{
- struct gatt_service *gatt = user_data;
-
- g_slist_foreach(gatt->primary, (GFunc) primary_free, NULL);
- g_slist_free(gatt->primary);
- g_attrib_unref(gatt->attrib);
+ g_slist_free_full(gatt->watchers, watcher_free);
+ g_slist_free_full(gatt->chars, characteristic_free);
+ g_slist_free(gatt->offline_chars);
g_free(gatt->path);
btd_device_unref(gatt->dev);
+ dbus_connection_unref(gatt->conn);
g_free(gatt);
}
-static int gatt_dev_cmp(gconstpointer a, gconstpointer b)
+static void gatt_get_address(struct gatt_service *gatt,
+ bdaddr_t *sba, bdaddr_t *dba)
{
- const struct gatt_service *gatt = a;
- const struct btd_device *dev = b;
+ struct btd_device *device = gatt->dev;
+ struct btd_adapter *adapter;
- return gatt->dev != dev;
+ adapter = device_get_adapter(device);
+ adapter_get_address(adapter, sba);
+ device_get_address(device, dba, NULL);
}
static int characteristic_handle_cmp(gconstpointer a, gconstpointer b)
static void watcher_exit(DBusConnection *conn, void *user_data)
{
struct watcher *watcher = user_data;
- struct primary *prim = watcher->prim;
- struct gatt_service *gatt = prim->gatt;
-
- DBG("%s watcher %s exited", prim->path, watcher->name);
+ struct gatt_service *gatt = watcher->gatt;
- prim->watchers = g_slist_remove(prim->watchers, watcher);
+ DBG("%s watcher %s exited", gatt->path, watcher->name);
- g_attrib_unref(gatt->attrib);
+ gatt->watchers = g_slist_remove(gatt->watchers, watcher);
}
static int characteristic_set_value(struct characteristic *chr,
{
struct watcher *w = data;
struct characteristic *chr = user_data;
+ DBusConnection *conn = w->gatt->conn;
DBusMessage *msg;
msg = dbus_message_new_method_call(w->name, w->path,
&chr->value, chr->vlen, DBUS_TYPE_INVALID);
dbus_message_set_no_reply(msg, TRUE);
- g_dbus_send_message(connection, msg);
+ g_dbus_send_message(conn, msg);
}
static void events_handler(const uint8_t *pdu, uint16_t len,
{
struct gatt_service *gatt = user_data;
struct characteristic *chr;
- struct primary *prim;
- GSList *lprim, *lchr;
+ GSList *l;
uint8_t opdu[ATT_MAX_MTU];
guint handle;
uint16_t olen;
handle = att_get_u16(&pdu[1]);
- for (lprim = gatt->primary, prim = NULL, chr = NULL; lprim;
- lprim = lprim->next) {
- prim = lprim->data;
+ l = g_slist_find_custom(gatt->chars, GUINT_TO_POINTER(handle),
+ characteristic_handle_cmp);
+ if (!l)
+ return;
- lchr = g_slist_find_custom(prim->chars,
- GUINT_TO_POINTER(handle), characteristic_handle_cmp);
- if (lchr) {
- chr = lchr->data;
- break;
- }
- }
+ chr = l->data;
if (chr == NULL) {
DBG("Attribute handle 0x%02x not found", handle);
if (characteristic_set_value(chr, &pdu[3], len - 3) < 0)
DBG("Can't change Characteristic 0x%02x", handle);
- g_slist_foreach(prim->watchers, update_watchers, chr);
+ g_slist_foreach(gatt->watchers, update_watchers, chr);
break;
}
}
-static void attrib_destroy(gpointer user_data)
+static void offline_char_written(gpointer user_data)
{
- struct gatt_service *gatt = user_data;
+ struct characteristic *chr = user_data;
+ struct gatt_service *gatt = chr->gatt;
- gatt->attrib = NULL;
+ gatt->offline_chars = g_slist_remove(gatt->offline_chars, chr);
+
+ if (gatt->offline_chars || gatt->watchers)
+ return;
+
+ btd_device_remove_attio_callback(gatt->dev, gatt->attioid);
+ gatt->attioid = 0;
}
-static void attrib_disconnect(gpointer user_data)
+static void offline_char_write(gpointer data, gpointer user_data)
{
- struct gatt_service *gatt = user_data;
+ struct characteristic *chr = data;
+ GAttrib *attrib = user_data;
- /* Remote initiated disconnection only */
- g_attrib_unref(gatt->attrib);
+ gatt_write_cmd(attrib, chr->handle, chr->value, chr->vlen,
+ offline_char_written, chr);
}
-static void connect_cb(GIOChannel *chan, GError *gerr, gpointer user_data)
+static void attio_connected(GAttrib *attrib, gpointer user_data)
{
struct gatt_service *gatt = user_data;
- if (gerr) {
- if (gatt->msg) {
- DBusMessage *reply = btd_error_failed(gatt->msg,
- gerr->message);
- g_dbus_send_message(connection, reply);
- }
-
- error("%s", gerr->message);
- goto fail;
- }
+ gatt->attrib = g_attrib_ref(attrib);
- if (gatt->attrib == NULL)
- return;
-
- /* Listen mode: used for notification and indication */
- if (gatt->listen == TRUE) {
- g_attrib_register(gatt->attrib,
- ATT_OP_HANDLE_NOTIFY,
+ g_attrib_register(gatt->attrib, ATT_OP_HANDLE_NOTIFY,
events_handler, gatt, NULL);
- g_attrib_register(gatt->attrib,
- ATT_OP_HANDLE_IND,
+ g_attrib_register(gatt->attrib, ATT_OP_HANDLE_IND,
events_handler, gatt, NULL);
- return;
- }
- return;
-fail:
- g_attrib_unref(gatt->attrib);
+ g_slist_foreach(gatt->offline_chars, offline_char_write, attrib);
}
-static int l2cap_connect(struct gatt_service *gatt, GError **gerr,
- gboolean listen)
+static void attio_disconnected(gpointer user_data)
{
- GIOChannel *io;
+ struct gatt_service *gatt = user_data;
- if (gatt->attrib != NULL) {
- gatt->attrib = g_attrib_ref(gatt->attrib);
- gatt->listen = listen;
- return 0;
+ if (gatt->attrib) {
+ g_attrib_unref(gatt->attrib);
+ gatt->attrib = NULL;
}
-
- /*
- * FIXME: If the service doesn't support Client Characteristic
- * Configuration it is necessary to poll the server from time
- * to time checking for modifications.
- */
- if (gatt->psm < 0)
- io = bt_io_connect(BT_IO_L2CAP, connect_cb, gatt, NULL, gerr,
- BT_IO_OPT_SOURCE_BDADDR, &gatt->sba,
- BT_IO_OPT_DEST_BDADDR, &gatt->dba,
- BT_IO_OPT_CID, GATT_CID,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
- BT_IO_OPT_INVALID);
- else
- io = bt_io_connect(BT_IO_L2CAP, connect_cb, gatt, NULL, gerr,
- BT_IO_OPT_SOURCE_BDADDR, &gatt->sba,
- BT_IO_OPT_DEST_BDADDR, &gatt->dba,
- BT_IO_OPT_PSM, gatt->psm,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
- BT_IO_OPT_INVALID);
- if (!io)
- return -1;
-
- gatt->attrib = g_attrib_new(io);
- g_io_channel_unref(io);
- gatt->listen = listen;
-
- g_attrib_set_destroy_function(gatt->attrib, attrib_destroy, gatt);
- g_attrib_set_disconnect_function(gatt->attrib, attrib_disconnect,
- gatt);
-
- return 0;
}
static DBusMessage *register_watcher(DBusConnection *conn,
DBusMessage *msg, void *data)
{
const char *sender = dbus_message_get_sender(msg);
- struct primary *prim = data;
+ struct gatt_service *gatt = data;
struct watcher *watcher;
- GError *gerr = NULL;
char *path;
if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID))
return btd_error_invalid_args(msg);
- if (l2cap_connect(prim->gatt, &gerr, TRUE) < 0) {
- DBusMessage *reply = btd_error_failed(msg, gerr->message);
- g_error_free(gerr);
- return reply;
- }
-
watcher = g_new0(struct watcher, 1);
watcher->name = g_strdup(sender);
- watcher->prim = prim;
+ watcher->gatt = gatt;
watcher->path = g_strdup(path);
watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
watcher, watcher_free);
- prim->watchers = g_slist_append(prim->watchers, watcher);
+ if (gatt->attioid == 0)
+ gatt->attioid = btd_device_add_attio_callback(gatt->dev,
+ attio_connected,
+ attio_disconnected,
+ gatt);
+
+ gatt->watchers = g_slist_append(gatt->watchers, watcher);
return dbus_message_new_method_return(msg);
}
DBusMessage *msg, void *data)
{
const char *sender = dbus_message_get_sender(msg);
- struct primary *prim = data;
+ struct gatt_service *gatt = data;
struct watcher *watcher, *match;
GSList *l;
char *path;
match = g_new0(struct watcher, 1);
match->name = g_strdup(sender);
match->path = g_strdup(path);
- l = g_slist_find_custom(prim->watchers, match, watcher_cmp);
+ l = g_slist_find_custom(gatt->watchers, match, watcher_cmp);
watcher_free(match);
if (!l)
return btd_error_not_authorized(msg);
watcher = l->data;
g_dbus_remove_watch(conn, watcher->id);
- prim->watchers = g_slist_remove(prim->watchers, watcher);
+ gatt->watchers = g_slist_remove(gatt->watchers, watcher);
watcher_free(watcher);
+ if (gatt->watchers == NULL && gatt->attioid) {
+ btd_device_remove_attio_callback(gatt->dev, gatt->attioid);
+ gatt->attioid = 0;
+ }
+
return dbus_message_new_method_return(msg);
}
static DBusMessage *set_value(DBusConnection *conn, DBusMessage *msg,
DBusMessageIter *iter, struct characteristic *chr)
{
- struct gatt_service *gatt = chr->prim->gatt;
+ struct gatt_service *gatt = chr->gatt;
DBusMessageIter sub;
- GError *gerr = NULL;
uint8_t *value;
int len;
dbus_message_iter_get_fixed_array(&sub, &value, &len);
- if (l2cap_connect(gatt, &gerr, FALSE) < 0) {
- DBusMessage *reply = btd_error_failed(msg, gerr->message);
- g_error_free(gerr);
- return reply;
- }
+ characteristic_set_value(chr, value, len);
- gatt_write_cmd(gatt->attrib, chr->handle, value, len, NULL, NULL);
+ if (gatt->attioid == 0)
+ gatt->attioid = btd_device_add_attio_callback(gatt->dev,
+ attio_connected,
+ attio_disconnected,
+ gatt);
- characteristic_set_value(chr, value, len);
+ if (gatt->attrib)
+ gatt_write_cmd(gatt->attrib, chr->handle, value, len,
+ NULL, NULL);
+ else
+ gatt->offline_chars = g_slist_append(gatt->offline_chars, chr);
return dbus_message_new_method_return(msg);
}
return g_string_free(characteristics, FALSE);
}
-static void store_characteristics(struct gatt_service *gatt,
- struct primary *prim)
+static void store_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint16_t start, GSList *chars)
{
char *characteristics;
- struct att_primary *att = prim->att;
- characteristics = characteristic_list_to_string(prim->chars);
+ characteristics = characteristic_list_to_string(chars);
- write_device_characteristics(&gatt->sba, &gatt->dba, att->start,
- characteristics);
+ write_device_characteristics(sba, dba, start, characteristics);
g_free(characteristics);
}
-static void register_characteristics(struct primary *prim)
+static void register_characteristic(gpointer data, gpointer user_data)
{
- GSList *lc;
-
- for (lc = prim->chars; lc; lc = lc->next) {
- struct characteristic *chr = lc->data;
- g_dbus_register_interface(connection, chr->path,
- CHAR_INTERFACE, char_methods,
- NULL, NULL, chr, NULL);
- DBG("Registered: %s", chr->path);
- }
+ struct characteristic *chr = data;
+ DBusConnection *conn = chr->gatt->conn;
+ const char *gatt_path = user_data;
+
+ chr->path = g_strdup_printf("%s/characteristic%04x", gatt_path,
+ chr->handle);
+
+ g_dbus_register_interface(conn, chr->path, CHAR_INTERFACE,
+ char_methods, NULL, NULL, chr, NULL);
+
+ DBG("Registered: %s", chr->path);
}
-static GSList *string_to_characteristic_list(struct primary *prim,
+static GSList *string_to_characteristic_list(struct gatt_service *gatt,
const char *str)
{
GSList *l = NULL;
continue;
}
- chr->prim = prim;
- chr->path = g_strdup_printf("%s/characteristic%04x",
- prim->path, chr->handle);
-
+ chr->gatt = gatt;
l = g_slist_append(l, chr);
}
return l;
}
-static void load_characteristics(gpointer data, gpointer user_data)
+static GSList *load_characteristics(struct gatt_service *gatt, uint16_t start)
{
- struct primary *prim = data;
- struct att_primary *att = prim->att;
- struct gatt_service *gatt = user_data;
GSList *chrs_list;
+ bdaddr_t sba, dba;
char *str;
- if (prim->chars) {
- DBG("Characteristics already loaded");
- return;
- }
+ gatt_get_address(gatt, &sba, &dba);
- str = read_device_characteristics(&gatt->sba, &gatt->dba, att->start);
+ str = read_device_characteristics(&sba, &dba, start);
if (str == NULL)
- return;
+ return NULL;
- chrs_list = string_to_characteristic_list(prim, str);
+ chrs_list = string_to_characteristic_list(gatt, str);
free(str);
- if (chrs_list == NULL)
- return;
-
- prim->chars = chrs_list;
- register_characteristics(prim);
-
- return;
+ return chrs_list;
}
static void store_attribute(struct gatt_service *gatt, uint16_t handle,
uint16_t type, uint8_t *value, gsize len)
{
+ bdaddr_t sba, dba;
bt_uuid_t uuid;
char *str, *tmp;
guint i;
for (i = 0, tmp = str + MAX_LEN_UUID_STR; i < len; i++, tmp += 2)
sprintf(tmp, "%02X", value[i]);
- write_device_attribute(&gatt->sba, &gatt->dba, handle, str);
+ gatt_get_address(gatt, &sba, &dba);
+
+ write_device_attribute(&sba, &dba, handle, str);
+
g_free(str);
}
+static void query_list_append(struct gatt_service *gatt, struct query_data *data)
+{
+ struct query *query = gatt->query;
+
+ query->list = g_slist_append(query->list, data);
+}
+
+static void query_list_remove(struct gatt_service *gatt, struct query_data *data)
+{
+ struct query *query = gatt->query;
+
+ query->list = g_slist_remove(query->list, data);
+ if (query->list != NULL)
+ return;
+
+ btd_device_remove_attio_callback(gatt->dev, query->attioid);
+ g_free(query);
+
+ gatt->query = NULL;
+}
+
static void update_char_desc(guint8 status, const guint8 *pdu, guint16 len,
gpointer user_data)
{
struct query_data *current = user_data;
- struct gatt_service *gatt = current->prim->gatt;
+ struct gatt_service *gatt = current->gatt;
struct characteristic *chr = current->chr;
if (status == 0) {
}
}
- g_attrib_unref(gatt->attrib);
+ query_list_remove(gatt, current);
g_free(current);
}
gpointer user_data)
{
struct query_data *current = user_data;
- struct gatt_service *gatt = current->prim->gatt;
+ struct gatt_service *gatt = current->gatt;
struct characteristic *chr = current->chr;
if (status != 0)
(void *) chr->format, sizeof(*chr->format));
done:
- g_attrib_unref(gatt->attrib);
+ query_list_remove(gatt, current);
g_free(current);
}
guint16 len, gpointer user_data)
{
struct query_data *current = user_data;
- struct gatt_service *gatt = current->prim->gatt;
+ struct gatt_service *gatt = current->gatt;
struct characteristic *chr = current->chr;
if (status == 0)
}
}
- g_attrib_unref(gatt->attrib);
+ query_list_remove(gatt, current);
g_free(current);
}
gpointer user_data)
{
struct query_data *current = user_data;
- struct gatt_service *gatt = current->prim->gatt;
+ struct gatt_service *gatt = current->gatt;
struct att_data_list *list;
guint8 format;
int i;
continue;
}
qfmt = g_new0(struct query_data, 1);
- qfmt->prim = current->prim;
+ qfmt->gatt = current->gatt;
qfmt->chr = current->chr;
qfmt->handle = handle;
if (uuid_desc16_cmp(&uuid, GATT_CHARAC_USER_DESC_UUID) == 0) {
- gatt->attrib = g_attrib_ref(gatt->attrib);
+ query_list_append(gatt, qfmt);
gatt_read_char(gatt->attrib, handle, 0, update_char_desc,
qfmt);
} else if (uuid_desc16_cmp(&uuid, GATT_CHARAC_FMT_UUID) == 0) {
- gatt->attrib = g_attrib_ref(gatt->attrib);
+ query_list_append(gatt, qfmt);
gatt_read_char(gatt->attrib, handle, 0,
update_char_format, qfmt);
} else
att_data_list_free(list);
done:
- g_attrib_unref(gatt->attrib);
+ query_list_remove(gatt, current);
g_free(current);
}
{
struct query_data *qdesc, *qvalue;
struct characteristic *chr = data;
- struct primary *prim = user_data;
- struct gatt_service *gatt = prim->gatt;
+ struct gatt_service *gatt = user_data;
qdesc = g_new0(struct query_data, 1);
- qdesc->prim = prim;
+ qdesc->gatt = gatt;
qdesc->chr = chr;
- gatt->attrib = g_attrib_ref(gatt->attrib);
+ query_list_append(gatt, qdesc);
+
gatt_find_info(gatt->attrib, chr->handle + 1, chr->end, descriptor_cb,
qdesc);
qvalue = g_new0(struct query_data, 1);
- qvalue->prim = prim;
+ qvalue->gatt = gatt;
qvalue->chr = chr;
- gatt->attrib = g_attrib_ref(gatt->attrib);
+ query_list_append(gatt, qvalue);
+
gatt_read_char(gatt->attrib, chr->handle, 0, update_char_value, qvalue);
}
DBusMessage *reply;
DBusMessageIter iter, array_iter;
struct query_data *current = user_data;
- struct primary *prim = current->prim;
- struct att_primary *att = prim->att;
- struct gatt_service *gatt = prim->gatt;
+ struct gatt_service *gatt = current->gatt;
+ struct att_primary *prim = gatt->prim;
uint16_t *previous_end = NULL;
GSList *l;
+ bdaddr_t sba, dba;
if (status != 0) {
const char *str = att_ecode2str(status);
DBG("Discover all characteristics failed: %s", str);
- reply = btd_error_failed(current->msg, str);
+ reply = btd_error_failed(gatt->query->msg, str);
goto fail;
}
guint handle = current_chr->value_handle;
GSList *lchr;
- lchr = g_slist_find_custom(prim->chars,
+ lchr = g_slist_find_custom(gatt->chars,
GUINT_TO_POINTER(handle), characteristic_handle_cmp);
if (lchr)
continue;
chr = g_new0(struct characteristic, 1);
- chr->prim = prim;
+ chr->gatt = gatt;
chr->perm = current_chr->properties;
chr->handle = current_chr->value_handle;
- chr->path = g_strdup_printf("%s/characteristic%04x",
- prim->path, chr->handle);
strncpy(chr->type, current_chr->uuid, sizeof(chr->type));
if (previous_end)
previous_end = &chr->end;
- prim->chars = g_slist_append(prim->chars, chr);
+ gatt->chars = g_slist_append(gatt->chars, chr);
}
if (previous_end)
- *previous_end = att->end;
+ *previous_end = prim->end;
+
+ gatt_get_address(gatt, &sba, &dba);
+ store_characteristics(&sba, &dba, prim->start, gatt->chars);
- store_characteristics(gatt, prim);
- register_characteristics(prim);
+ g_slist_foreach(gatt->chars, register_characteristic, gatt->path);
- reply = dbus_message_new_method_return(current->msg);
+ reply = dbus_message_new_method_return(gatt->query->msg);
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter);
- for (l = prim->chars; l; l = l->next) {
+ for (l = gatt->chars; l; l = l->next) {
struct characteristic *chr = l->data;
dbus_message_iter_append_basic(&array_iter,
dbus_message_iter_close_container(&iter, &array_iter);
- g_slist_foreach(prim->chars, update_all_chars, prim);
+ g_slist_foreach(gatt->chars, update_all_chars, gatt);
fail:
- g_dbus_send_message(connection, reply);
- g_attrib_unref(gatt->attrib);
+ g_dbus_send_message(gatt->conn, reply);
+ query_list_remove(gatt, current);
g_free(current);
}
+static void send_discover(GAttrib *attrib, gpointer user_data)
+{
+ struct query_data *qchr = user_data;
+ struct gatt_service *gatt = qchr->gatt;
+ struct att_primary *prim = gatt->prim;
+
+ gatt->attrib = g_attrib_ref(attrib);
+
+ gatt_discover_char(gatt->attrib, prim->start, prim->end, NULL,
+ char_discovered_cb, qchr);
+}
+
+static void cancel_discover(gpointer user_data)
+{
+ struct query_data *qchr = user_data;
+ struct gatt_service *gatt = qchr->gatt;
+
+ g_attrib_unref(gatt->attrib);
+ gatt->attrib = NULL;
+}
+
static DBusMessage *discover_char(DBusConnection *conn, DBusMessage *msg,
void *data)
{
- struct primary *prim = data;
- struct att_primary *att = prim->att;
- struct gatt_service *gatt = prim->gatt;
+ struct gatt_service *gatt = data;
+ struct query *query;
struct query_data *qchr;
- GError *gerr = NULL;
- if (l2cap_connect(prim->gatt, &gerr, FALSE) < 0) {
- DBusMessage *reply = btd_error_failed(msg, gerr->message);
- g_error_free(gerr);
- return reply;
- }
+ if (gatt->query)
+ return btd_error_busy(msg);
+
+ query = g_new0(struct query, 1);
qchr = g_new0(struct query_data, 1);
- qchr->prim = prim;
- qchr->msg = dbus_message_ref(msg);
+ qchr->gatt = gatt;
- gatt_discover_char(gatt->attrib, att->start, att->end,
- char_discovered_cb, qchr);
+ query->msg = dbus_message_ref(msg);
+ query->attioid = btd_device_add_attio_callback(gatt->dev,
+ send_discover,
+ cancel_discover,
+ qchr);
+
+ gatt->query = query;
+
+ query_list_append(gatt, qchr);
return NULL;
}
static DBusMessage *prim_get_properties(DBusConnection *conn, DBusMessage *msg,
void *data)
{
- struct primary *prim = data;
+ struct gatt_service *gatt = data;
DBusMessage *reply;
DBusMessageIter iter;
DBusMessageIter dict;
DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
- chars = g_new0(char *, g_slist_length(prim->chars) + 1);
+ chars = g_new0(char *, g_slist_length(gatt->chars) + 1);
- for (i = 0, l = prim->chars; l; l = l->next, i++) {
+ for (i = 0, l = gatt->chars; l; l = l->next, i++) {
struct characteristic *chr = l->data;
chars[i] = chr->path;
}
dict_append_array(&dict, "Characteristics", DBUS_TYPE_OBJECT_PATH,
&chars, i);
- uuid = prim->att->uuid;
+ uuid = gatt->prim->uuid;
dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
g_free(chars);
{ }
};
-static void register_primaries(struct gatt_service *gatt, GSList *primaries)
+static struct gatt_service *primary_register(DBusConnection *conn,
+ struct btd_device *device,
+ struct att_primary *prim,
+ int psm)
{
- GSList *l;
+ struct gatt_service *gatt;
+ const char *device_path;
- for (l = primaries; l; l = l->next) {
- struct att_primary *att = l->data;
- struct primary *prim;
+ device_path = device_get_path(device);
- prim = g_new0(struct primary, 1);
- prim->att = att;
- prim->gatt = gatt;
- prim->path = g_strdup_printf("%s/service%04x", gatt->path,
- att->start);
+ gatt = g_new0(struct gatt_service, 1);
+ gatt->dev = btd_device_ref(device);
+ gatt->prim = prim;
+ gatt->psm = psm;
+ gatt->conn = dbus_connection_ref(conn);
+ gatt->path = g_strdup_printf("%s/service%04x", device_path,
+ prim->start);
- g_dbus_register_interface(connection, prim->path,
- CHAR_INTERFACE, prim_methods,
- NULL, NULL, prim, NULL);
- DBG("Registered: %s", prim->path);
+ g_dbus_register_interface(gatt->conn, gatt->path,
+ CHAR_INTERFACE, prim_methods,
+ NULL, NULL, gatt, NULL);
+ gatt->chars = load_characteristics(gatt, prim->start);
+ g_slist_foreach(gatt->chars, register_characteristic, gatt->path);
- gatt->primary = g_slist_append(gatt->primary, prim);
- btd_device_add_service(gatt->dev, prim->path);
- load_characteristics(prim, gatt);
- }
+ return gatt;
}
-int attrib_client_register(struct btd_device *device, int psm)
+GSList *attrib_client_register(DBusConnection *connection,
+ struct btd_device *device, int psm,
+ GAttrib *attrib, GSList *primaries)
{
- struct btd_adapter *adapter = device_get_adapter(device);
- const char *path = device_get_path(device);
- struct gatt_service *gatt;
- GSList *primaries = btd_device_get_primaries(device);
- bdaddr_t sba, dba;
+ GSList *l, *services;
- adapter_get_address(adapter, &sba);
- device_get_address(device, &dba);
+ for (l = primaries, services = NULL; l; l = l->next) {
+ struct att_primary *prim = l->data;
+ struct gatt_service *gatt;
- gatt = g_new0(struct gatt_service, 1);
- gatt->dev = btd_device_ref(device);
- gatt->listen = FALSE;
- gatt->path = g_strdup(path);
- bacpy(&gatt->sba, &sba);
- bacpy(&gatt->dba, &dba);
- gatt->psm = psm;
+ gatt = primary_register(connection, device, prim, psm);
- register_primaries(gatt, primaries);
+ DBG("Registered: %s", gatt->path);
- gatt_services = g_slist_append(gatt_services, gatt);
+ services = g_slist_append(services, g_strdup(gatt->path));
+ gatt_services = g_slist_append(gatt_services, gatt);
- return 0;
+ }
+
+ return services;
}
-void attrib_client_unregister(struct btd_device *device)
+static void primary_unregister(struct gatt_service *gatt)
{
- struct gatt_service *gatt;
- GSList *l, *lp, *lc;
-
- l = g_slist_find_custom(gatt_services, device, gatt_dev_cmp);
- if (!l)
- return;
+ GSList *l;
- gatt = l->data;
- gatt_services = g_slist_remove(gatt_services, gatt);
+ if (gatt->attioid)
+ btd_device_remove_attio_callback(gatt->dev, gatt->attioid);
- for (lp = gatt->primary; lp; lp = lp->next) {
- struct primary *prim = lp->data;
- for (lc = prim->chars; lc; lc = lc->next) {
- struct characteristic *chr = lc->data;
- g_dbus_unregister_interface(connection, chr->path,
- CHAR_INTERFACE);
- }
- g_dbus_unregister_interface(connection, prim->path,
- CHAR_INTERFACE);
+ for (l = gatt->chars; l; l = l->next) {
+ struct characteristic *chr = l->data;
+ g_dbus_unregister_interface(gatt->conn, chr->path,
+ CHAR_INTERFACE);
}
- gatt_service_free(gatt);
+ g_dbus_unregister_interface(gatt->conn, gatt->path, CHAR_INTERFACE);
}
-int attrib_client_init(DBusConnection *conn)
+static int path_cmp(gconstpointer data, gconstpointer user_data)
{
+ const char *path = data;
+ const char *gatt_path = user_data;
- connection = dbus_connection_ref(conn);
-
- /*
- * FIXME: if the adapter supports BLE start scanning. Temporary
- * solution, this approach doesn't allow to control scanning based
- * on the discoverable property.
- */
-
- return 0;
+ return g_strcmp0(path, gatt_path);
}
-void attrib_client_exit(void)
+void attrib_client_unregister(GSList *services)
{
- dbus_connection_unref(connection);
+ GSList *l, *left;
+
+ for (l = gatt_services, left = NULL; l; l = l->next) {
+ struct gatt_service *gatt = l->data;
+
+ if (!g_slist_find_custom(services, gatt->path, path_cmp)) {
+ left = g_slist_append(left, gatt);
+ continue;
+ }
+
+ primary_unregister(gatt);
+ gatt_service_free(gatt);
+ }
+
+ g_slist_free(gatt_services);
+ gatt_services = left;
}
*
*/
-int attrib_client_init(DBusConnection *conn);
-void attrib_client_exit(void);
-int attrib_client_register(struct btd_device *device, int psm);
-void attrib_client_unregister(struct btd_device *device);
+GSList *attrib_client_register(DBusConnection *connection,
+ struct btd_device *device, int psm,
+ GAttrib *attrib, GSList *primaries);
+void attrib_client_unregister(GSList *services);
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <arpa/inet.h>
-
-#include <bluetooth/uuid.h>
-
-#include <glib.h>
-
-#include "log.h"
-#include "attrib-server.h"
-
-#include "att.h"
-#include "example.h"
-
-/* FIXME: Not defined by SIG? UUID128? */
-#define OPCODES_SUPPORTED_UUID 0xA001
-#define BATTERY_STATE_SVC_UUID 0xA002
-#define BATTERY_STATE_UUID 0xA003
-#define THERM_HUMIDITY_SVC_UUID 0xA004
-#define MANUFACTURER_SVC_UUID 0xA005
-#define TEMPERATURE_UUID 0xA006
-#define FMT_CELSIUS_UUID 0xA007
-#define FMT_OUTSIDE_UUID 0xA008
-#define RELATIVE_HUMIDITY_UUID 0xA009
-#define FMT_PERCENT_UUID 0xA00A
-#define BLUETOOTH_SIG_UUID 0xA00B
-#define MANUFACTURER_NAME_UUID 0xA00C
-#define MANUFACTURER_SERIAL_UUID 0xA00D
-#define VENDOR_SPECIFIC_SVC_UUID 0xA00E
-#define VENDOR_SPECIFIC_TYPE_UUID 0xA00F
-#define FMT_KILOGRAM_UUID 0xA010
-#define FMT_HANGING_UUID 0xA011
-
-static GSList *sdp_handles = NULL;
-
-static int register_attributes(void)
-{
- const char *desc_out_temp = "Outside Temperature";
- const char *desc_out_hum = "Outside Relative Humidity";
- const char *desc_weight = "Rucksack Weight";
- const char *manufacturer_name1 = "ACME Temperature Sensor";
- const char *manufacturer_name2 = "ACME Weighing Scales";
- const char *serial1 = "237495-3282-A";
- const char *serial2 = "11267-2327A00239";
-
- const uint128_t char_weight_uuid_btorder = {
- .data = { 0x80, 0x88, 0xF2, 0x18, 0x90, 0x2C, 0x45, 0x0B,
- 0xB6, 0xC4, 0x62, 0x89, 0x1E, 0x8C, 0x25, 0xE9 } };
- const uint128_t prim_weight_uuid_btorder = {
- .data = { 0x4F, 0x0A, 0xC0, 0x96, 0x35, 0xD4, 0x49, 0x11,
- 0x96, 0x31, 0xDE, 0xA8, 0xDC, 0x74, 0xEE, 0xFE } };
-
- uint128_t char_weight_uuid;
- uint8_t atval[256];
- uint32_t handle;
- bt_uuid_t uuid;
- int len;
-
- btoh128(&char_weight_uuid_btorder, &char_weight_uuid);
-
- /* Battery state service: primary service definition */
- bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
- att_put_u16(BATTERY_STATE_SVC_UUID, &atval[0]);
- attrib_db_add(0x0100, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
-
- /* Battery: battery state characteristic */
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(0x0110, &atval[1]);
- att_put_u16(BATTERY_STATE_UUID, &atval[3]);
- attrib_db_add(0x0106, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
-
- /* Battery: battery state attribute */
- bt_uuid16_create(&uuid, BATTERY_STATE_UUID);
- atval[0] = 0x04;
- attrib_db_add(0x0110, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
-
- /* Battery: Client Characteristic Configuration */
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
- atval[0] = 0x00;
- atval[1] = 0x00;
- attrib_db_add(0x0111, &uuid, ATT_NONE, ATT_AUTHENTICATION, atval, 2);
-
- /* Add an SDP record for the above service */
- handle = attrib_create_sdp(0x0100, "Battery State Service");
- if (handle)
- sdp_handles = g_slist_prepend(sdp_handles, GUINT_TO_POINTER(handle));
-
- /* Thermometer: primary service definition */
- bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
- att_put_u16(THERM_HUMIDITY_SVC_UUID, &atval[0]);
- attrib_db_add(0x0200, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
-
- /* Thermometer: Include */
- bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
- att_put_u16(0x0500, &atval[0]);
- att_put_u16(0x0504, &atval[2]);
- att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]);
- attrib_db_add(0x0201, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6);
-
- /* Thermometer: Include */
- att_put_u16(0x0550, &atval[0]);
- att_put_u16(0x0568, &atval[2]);
- att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[4]);
- attrib_db_add(0x0202, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6);
-
- /* Thermometer: temperature characteristic */
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(0x0204, &atval[1]);
- att_put_u16(TEMPERATURE_UUID, &atval[3]);
- attrib_db_add(0x0203, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
-
- /* Thermometer: temperature characteristic value */
- bt_uuid16_create(&uuid, TEMPERATURE_UUID);
- atval[0] = 0x8A;
- atval[1] = 0x02;
- attrib_db_add(0x0204, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
-
- /* Thermometer: temperature characteristic format */
- bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
- atval[0] = 0x0E;
- atval[1] = 0xFE;
- att_put_u16(FMT_CELSIUS_UUID, &atval[2]);
- atval[4] = 0x01;
- att_put_u16(FMT_OUTSIDE_UUID, &atval[5]);
- attrib_db_add(0x0205, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 7);
-
- /* Thermometer: characteristic user description */
- bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
- len = strlen(desc_out_temp);
- strncpy((char *) atval, desc_out_temp, len);
- attrib_db_add(0x0206, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
-
- /* Thermometer: relative humidity characteristic */
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(0x0212, &atval[1]);
- att_put_u16(RELATIVE_HUMIDITY_UUID, &atval[3]);
- attrib_db_add(0x0210, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
-
- /* Thermometer: relative humidity value */
- bt_uuid16_create(&uuid, RELATIVE_HUMIDITY_UUID);
- atval[0] = 0x27;
- attrib_db_add(0x0212, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
-
- /* Thermometer: relative humidity characteristic format */
- bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
- atval[0] = 0x04;
- atval[1] = 0x00;
- att_put_u16(FMT_PERCENT_UUID, &atval[2]);
- att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]);
- att_put_u16(FMT_OUTSIDE_UUID, &atval[6]);
- attrib_db_add(0x0213, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8);
-
- /* Thermometer: characteristic user description */
- bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
- len = strlen(desc_out_hum);
- strncpy((char *) atval, desc_out_hum, len);
- attrib_db_add(0x0214, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
-
- /* Add an SDP record for the above service */
- handle = attrib_create_sdp(0x0200, "Thermometer");
- if (handle)
- sdp_handles = g_slist_prepend(sdp_handles, GUINT_TO_POINTER(handle));
-
- /* Secondary Service: Manufacturer Service */
- bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
- att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
- attrib_db_add(0x0500, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
-
- /* Manufacturer name characteristic definition */
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(0x0502, &atval[1]);
- att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]);
- attrib_db_add(0x0501, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
-
- /* Manufacturer name characteristic value */
- bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
- len = strlen(manufacturer_name1);
- strncpy((char *) atval, manufacturer_name1, len);
- attrib_db_add(0x0502, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
-
- /* Manufacturer serial number characteristic */
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(0x0504, &atval[1]);
- att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]);
- attrib_db_add(0x0503, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
-
- /* Manufacturer serial number characteristic value */
- bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
- len = strlen(serial1);
- strncpy((char *) atval, serial1, len);
- attrib_db_add(0x0504, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
-
- /* Secondary Service: Manufacturer Service */
- bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
- att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
- attrib_db_add(0x0505, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
-
- /* Manufacturer name characteristic definition */
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(0x0507, &atval[1]);
- att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]);
- attrib_db_add(0x0506, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
-
- /* Secondary Service: Vendor Specific Service */
- bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
- att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]);
- attrib_db_add(0x0550, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
-
- /* Vendor Specific Type characteristic definition */
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(0x0568, &atval[1]);
- att_put_u16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]);
- attrib_db_add(0x0560, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
-
- /* Vendor Specific Type characteristic value */
- bt_uuid16_create(&uuid, VENDOR_SPECIFIC_TYPE_UUID);
- atval[0] = 0x56;
- atval[1] = 0x65;
- atval[2] = 0x6E;
- atval[3] = 0x64;
- atval[4] = 0x6F;
- atval[5] = 0x72;
- attrib_db_add(0x0568, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6);
-
- /* Manufacturer name attribute */
- bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
- len = strlen(manufacturer_name2);
- strncpy((char *) atval, manufacturer_name2, len);
- attrib_db_add(0x0507, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
-
- /* Characteristic: serial number */
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(0x0509, &atval[1]);
- att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]);
- attrib_db_add(0x0508, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
-
- /* Serial number characteristic value */
- bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
- len = strlen(serial2);
- strncpy((char *) atval, serial2, len);
- attrib_db_add(0x0509, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
-
- /* Weight service: primary service definition */
- bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
- memcpy(atval, &prim_weight_uuid_btorder, 16);
- attrib_db_add(0x0680, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 16);
-
- /* Weight: include */
- bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
- att_put_u16(0x0505, &atval[0]);
- att_put_u16(0x0509, &atval[2]);
- att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]);
- attrib_db_add(0x0681, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 6);
-
- /* Weight: characteristic */
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(0x0683, &atval[1]);
- memcpy(&atval[3], &char_weight_uuid_btorder, 16);
- attrib_db_add(0x0682, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 19);
-
- /* Weight: characteristic value */
- bt_uuid128_create(&uuid, char_weight_uuid);
- atval[0] = 0x82;
- atval[1] = 0x55;
- atval[2] = 0x00;
- atval[3] = 0x00;
- attrib_db_add(0x0683, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 4);
-
- /* Weight: characteristic format */
- bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
- atval[0] = 0x08;
- atval[1] = 0xFD;
- att_put_u16(FMT_KILOGRAM_UUID, &atval[2]);
- att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]);
- att_put_u16(FMT_HANGING_UUID, &atval[6]);
- attrib_db_add(0x0684, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 8);
-
- /* Weight: characteristic user description */
- bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
- len = strlen(desc_weight);
- strncpy((char *) atval, desc_weight, len);
- attrib_db_add(0x0685, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, len);
-
- /* Add an SDP record for the above service */
- handle = attrib_create_sdp(0x0680, "Weight Service");
- if (handle)
- sdp_handles = g_slist_prepend(sdp_handles, GUINT_TO_POINTER(handle));
-
- return 0;
-}
-
-int server_example_init(void)
-{
- return register_attributes();
-}
-
-void server_example_exit(void)
-{
- while (sdp_handles) {
- uint32_t handle = GPOINTER_TO_UINT(sdp_handles->data);
-
- attrib_free_sdp(handle);
- sdp_handles = g_slist_remove(sdp_handles, sdp_handles->data);
- }
-}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+#include <adapter.h>
+
+#include "att.h"
+#include "gattrib.h"
+#include "attrib-server.h"
+#include "gatt-service.h"
+#include "log.h"
+#include "glib-compat.h"
+
+struct gatt_info {
+ bt_uuid_t uuid;
+ uint8_t props;
+ int authentication;
+ int authorization;
+ GSList *callbacks;
+ unsigned int num_attrs;
+ uint16_t *value_handle;
+ uint16_t *ccc_handle;
+};
+
+struct attrib_cb {
+ attrib_event_t event;
+ void *fn;
+ void *user_data;
+};
+
+static GSList *parse_opts(gatt_option opt1, va_list args)
+{
+ gatt_option opt = opt1;
+ struct gatt_info *info;
+ struct attrib_cb *cb;
+ GSList *l = NULL;
+
+ info = g_new0(struct gatt_info, 1);
+ l = g_slist_append(l, info);
+
+ while (opt != GATT_OPT_INVALID) {
+ switch (opt) {
+ case GATT_OPT_CHR_UUID:
+ bt_uuid16_create(&info->uuid, va_arg(args, int));
+ /* characteristic declaration and value */
+ info->num_attrs += 2;
+ break;
+ case GATT_OPT_CHR_PROPS:
+ info->props = va_arg(args, int);
+
+ if (info->props & (ATT_CHAR_PROPER_NOTIFY |
+ ATT_CHAR_PROPER_INDICATE))
+ /* client characteristic configuration */
+ info->num_attrs += 1;
+
+ /* TODO: "Extended Properties" property requires a
+ * descriptor, but it is not supported yet. */
+ break;
+ case GATT_OPT_CHR_VALUE_CB:
+ cb = g_new0(struct attrib_cb, 1);
+ cb->event = va_arg(args, attrib_event_t);
+ cb->fn = va_arg(args, void *);
+ cb->user_data = va_arg(args, void *);
+ info->callbacks = g_slist_append(info->callbacks, cb);
+ break;
+ case GATT_OPT_CHR_VALUE_GET_HANDLE:
+ info->value_handle = va_arg(args, void *);
+ break;
+ case GATT_OPT_CCC_GET_HANDLE:
+ info->ccc_handle = va_arg(args, void *);
+ break;
+ case GATT_OPT_CHR_AUTHENTICATION:
+ info->authentication = va_arg(args, gatt_option);
+ break;
+ case GATT_OPT_CHR_AUTHORIZATION:
+ info->authorization = va_arg(args, gatt_option);
+ break;
+ default:
+ error("Invalid option: %d", opt);
+ }
+
+ opt = va_arg(args, gatt_option);
+ if (opt == GATT_OPT_CHR_UUID) {
+ info = g_new0(struct gatt_info, 1);
+ l = g_slist_append(l, info);
+ }
+ }
+
+ return l;
+}
+
+static int att_read_reqs(int authorization, int authentication, uint8_t props)
+{
+ if (authorization == GATT_CHR_VALUE_READ ||
+ authorization == GATT_CHR_VALUE_BOTH)
+ return ATT_AUTHORIZATION;
+ else if (authentication == GATT_CHR_VALUE_READ ||
+ authentication == GATT_CHR_VALUE_BOTH)
+ return ATT_AUTHENTICATION;
+ else if (!(props & ATT_CHAR_PROPER_READ))
+ return ATT_NOT_PERMITTED;
+
+ return ATT_NONE;
+}
+
+static int att_write_reqs(int authorization, int authentication, uint8_t props)
+{
+ if (authorization == GATT_CHR_VALUE_WRITE ||
+ authorization == GATT_CHR_VALUE_BOTH)
+ return ATT_AUTHORIZATION;
+ else if (authentication == GATT_CHR_VALUE_WRITE ||
+ authentication == GATT_CHR_VALUE_BOTH)
+ return ATT_AUTHENTICATION;
+ else if (!(props & (ATT_CHAR_PROPER_WRITE |
+ ATT_CHAR_PROPER_WRITE_WITHOUT_RESP)))
+ return ATT_NOT_PERMITTED;
+
+ return ATT_NONE;
+}
+
+static gint find_callback(gconstpointer a, gconstpointer b)
+{
+ const struct attrib_cb *cb = a;
+ unsigned int event = GPOINTER_TO_UINT(b);
+
+ return cb->event - event;
+}
+
+static gboolean add_characteristic(struct btd_adapter *adapter,
+ uint16_t *handle, struct gatt_info *info)
+{
+ int read_reqs, write_reqs;
+ uint16_t h = *handle;
+ struct attribute *a;
+ bt_uuid_t bt_uuid;
+ uint8_t atval[5];
+ GSList *l;
+
+ if (!info->uuid.value.u16 || !info->props) {
+ error("Characteristic UUID or properties are missing");
+ return FALSE;
+ }
+
+ read_reqs = att_read_reqs(info->authorization, info->authentication,
+ info->props);
+ write_reqs = att_write_reqs(info->authorization, info->authentication,
+ info->props);
+
+ /* TODO: static characteristic values are not supported, therefore a
+ * callback must be always provided if a read/write property is set */
+ if (read_reqs != ATT_NOT_PERMITTED) {
+ gpointer reqs = GUINT_TO_POINTER(ATTRIB_READ);
+
+ if (!g_slist_find_custom(info->callbacks, reqs,
+ find_callback)) {
+ error("Callback for read required");
+ return FALSE;
+ }
+ }
+
+ if (write_reqs != ATT_NOT_PERMITTED) {
+ gpointer reqs = GUINT_TO_POINTER(ATTRIB_WRITE);
+
+ if (!g_slist_find_custom(info->callbacks, reqs,
+ find_callback)) {
+ error("Callback for write required");
+ return FALSE;
+ }
+ }
+
+ /* characteristic declaration */
+ bt_uuid16_create(&bt_uuid, GATT_CHARAC_UUID);
+ atval[0] = info->props;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(info->uuid.value.u16, &atval[3]);
+ if (attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, sizeof(atval)) == NULL)
+ return FALSE;
+
+ /* characteristic value */
+ a = attrib_db_add(adapter, h++, &info->uuid, read_reqs, write_reqs,
+ NULL, 0);
+ if (a == NULL)
+ return FALSE;
+
+ for (l = info->callbacks; l != NULL; l = l->next) {
+ struct attrib_cb *cb = l->data;
+
+ switch (cb->event) {
+ case ATTRIB_READ:
+ a->read_cb = cb->fn;
+ break;
+ case ATTRIB_WRITE:
+ a->write_cb = cb->fn;
+ break;
+ }
+
+ a->cb_user_data = cb->user_data;
+ }
+
+ if (info->value_handle != NULL)
+ *info->value_handle = a->handle;
+
+ /* client characteristic configuration descriptor */
+ if (info->props & (ATT_CHAR_PROPER_NOTIFY | ATT_CHAR_PROPER_INDICATE)) {
+ uint8_t cfg_val[2];
+
+ bt_uuid16_create(&bt_uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ cfg_val[0] = 0x00;
+ cfg_val[1] = 0x00;
+ a = attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE,
+ ATT_AUTHENTICATION, cfg_val, sizeof(cfg_val));
+ if (a == NULL)
+ return FALSE;
+
+ if (info->ccc_handle != NULL)
+ *info->ccc_handle = a->handle;
+ }
+
+ *handle = h;
+
+ return TRUE;
+}
+
+static void free_gatt_info(void *data)
+{
+ struct gatt_info *info = data;
+
+ g_slist_free_full(info->callbacks, g_free);
+ g_free(info);
+}
+
+static void service_attr_del(struct btd_adapter *adapter, uint16_t start_handle,
+ uint16_t end_handle)
+{
+ uint16_t handle;
+
+ for (handle = start_handle; handle <= end_handle; handle++)
+ if (attrib_db_del(adapter, handle) < 0)
+ error("Can't delete handle 0x%04x", handle);
+}
+
+gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
+ uint16_t svc_uuid, gatt_option opt1, ...)
+{
+ uint16_t start_handle, h;
+ unsigned int size;
+ bt_uuid_t bt_uuid;
+ uint8_t atval[2];
+ va_list args;
+ GSList *chrs, *l;
+
+ va_start(args, opt1);
+ chrs = parse_opts(opt1, args);
+ /* calculate how many attributes are necessary for this service */
+ for (l = chrs, size = 1; l != NULL; l = l->next) {
+ struct gatt_info *info = l->data;
+ size += info->num_attrs;
+ }
+ va_end(args);
+ start_handle = attrib_db_find_avail(adapter, size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ g_slist_free_full(chrs, free_gatt_info);
+ return FALSE;
+ }
+
+ DBG("New service: handle 0x%04x, UUID 0x%04x, %d attributes",
+ start_handle, svc_uuid, size);
+
+ /* service declaration */
+ h = start_handle;
+ bt_uuid16_create(&bt_uuid, uuid);
+ att_put_u16(svc_uuid, &atval[0]);
+ if (attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, sizeof(atval)) == NULL) {
+ g_slist_free_full(chrs, free_gatt_info);
+ return FALSE;
+ }
+
+ for (l = chrs; l != NULL; l = l->next) {
+ struct gatt_info *info = l->data;
+
+ DBG("New characteristic: handle 0x%04x", h);
+ if (!add_characteristic(adapter, &h, info)) {
+ g_slist_free_full(chrs, free_gatt_info);
+ service_attr_del(adapter, start_handle, h - 1);
+ return FALSE;
+ }
+ }
+
+ g_assert(size < USHRT_MAX);
+ g_assert(h - start_handle == (uint16_t) size);
+ g_slist_free_full(chrs, free_gatt_info);
+
+ return TRUE;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+typedef enum {
+ GATT_OPT_INVALID = 0,
+ GATT_OPT_CHR_UUID,
+ GATT_OPT_CHR_PROPS,
+ GATT_OPT_CHR_VALUE_CB,
+ GATT_OPT_CHR_AUTHENTICATION,
+ GATT_OPT_CHR_AUTHORIZATION,
+
+ /* Get attribute handle for characteristic value */
+ GATT_OPT_CHR_VALUE_GET_HANDLE,
+
+ /* Get handle for ccc attribute */
+ GATT_OPT_CCC_GET_HANDLE,
+
+ /* arguments for authentication/authorization */
+ GATT_CHR_VALUE_READ,
+ GATT_CHR_VALUE_WRITE,
+ GATT_CHR_VALUE_BOTH,
+} gatt_option;
+
+typedef enum {
+ ATTRIB_READ,
+ ATTRIB_WRITE,
+} attrib_event_t;
+
+gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
+ uint16_t svc_uuid, gatt_option opt1, ...);
*
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdint.h>
+#include <stdlib.h>
#include <glib.h>
#include <bluetooth/uuid.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include "glib-compat.h"
#include "att.h"
#include "gattrib.h"
struct discover_char {
GAttrib *attrib;
- bt_uuid_t uuid;
+ bt_uuid_t *uuid;
uint16_t end;
GSList *characteristics;
gatt_cb_t cb;
static void discover_char_free(struct discover_char *dc)
{
- g_slist_foreach(dc->characteristics, (GFunc) g_free, NULL);
- g_slist_free(dc->characteristics);
+ g_slist_free_full(dc->characteristics, g_free);
g_attrib_unref(dc->attrib);
+ g_free(dc->uuid);
g_free(dc);
}
{
bt_uuid_t prim;
guint16 plen;
- uint8_t op;
bt_uuid16_create(&prim, GATT_PRIM_SVC_UUID);
if (uuid == NULL) {
/* Discover all primary services */
- op = ATT_OP_READ_BY_GROUP_REQ;
plen = enc_read_by_grp_req(start, end, &prim, pdu, len);
} else {
uint16_t u16;
int vlen;
/* Discover primary service by service UUID */
- op = ATT_OP_FIND_BY_TYPE_REQ;
if (uuid->type == BT_UUID16) {
u16 = htobs(uuid->value.u16);
struct discover_primary *dp = user_data;
GSList *ranges, *last;
struct att_range *range;
- uint8_t opdu[ATT_DEFAULT_LE_MTU];
+ uint8_t *buf;
guint16 oplen;
- int err = 0;
+ int err = 0, buflen;
if (status) {
err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status;
if (range->end == 0xffff)
goto done;
+ buf = g_attrib_get_buffer(dp->attrib, &buflen);
oplen = encode_discover_primary(range->end + 1, 0xffff, &dp->uuid,
- opdu, sizeof(opdu));
+ buf, buflen);
if (oplen == 0)
goto done;
- g_attrib_send(dp->attrib, 0, opdu[0], opdu, oplen, primary_by_uuid_cb,
+ g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_by_uuid_cb,
dp, NULL);
return;
err = 0;
if (end != 0xffff) {
- uint8_t opdu[ATT_DEFAULT_LE_MTU];
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(dp->attrib, &buflen);
guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
- opdu, sizeof(opdu));
+ buf, buflen);
- g_attrib_send(dp->attrib, 0, opdu[0], opdu, oplen,
- primary_all_cb, dp, NULL);
+ g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_all_cb,
+ dp, NULL);
return;
}
gpointer user_data)
{
struct discover_primary *dp;
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
GAttribResultFunc cb;
guint16 plen;
- plen = encode_discover_primary(0x0001, 0xffff, uuid, pdu, sizeof(pdu));
+ plen = encode_discover_primary(0x0001, 0xffff, uuid, buf, buflen);
if (plen == 0)
return 0;
dp->user_data = user_data;
if (uuid) {
- memcpy(&dp->uuid, uuid, sizeof(bt_uuid_t));
+ dp->uuid = *uuid;
cb = primary_by_uuid_cb;
} else
cb = primary_all_cb;
- return g_attrib_send(attrib, 0, pdu[0], pdu, plen, cb, dp, NULL);
+ return g_attrib_send(attrib, 0, buf[0], buf, plen, cb, dp, NULL);
}
static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen,
struct discover_char *dc = user_data;
struct att_data_list *list;
unsigned int i, err;
- uint8_t opdu[ATT_DEFAULT_LE_MTU];
+ int buflen;
+ uint8_t *buf;
guint16 oplen;
bt_uuid_t uuid;
uint16_t last = 0;
goto done;
}
+ if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid))
+ break;
+
chars->handle = last;
chars->properties = value[2];
chars->value_handle = att_get_u16(&value[3]);
err = 0;
if (last != 0) {
+ buf = g_attrib_get_buffer(dc->attrib, &buflen);
+
bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- oplen = enc_read_by_type_req(last + 1, dc->end, &uuid, opdu,
- sizeof(opdu));
+ oplen = enc_read_by_type_req(last + 1, dc->end, &uuid, buf,
+ buflen);
if (oplen == 0)
return;
- g_attrib_send(dc->attrib, 0, opdu[0], opdu, oplen,
+ g_attrib_send(dc->attrib, 0, buf[0], buf, oplen,
char_discovered_cb, dc, NULL);
return;
}
guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
- gatt_cb_t func, gpointer user_data)
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
{
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
struct discover_char *dc;
+ bt_uuid_t type_uuid;
guint16 plen;
- bt_uuid_t uuid;
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ bt_uuid16_create(&type_uuid, GATT_CHARAC_UUID);
- plen = enc_read_by_type_req(start, end, &uuid, pdu, sizeof(pdu));
+ plen = enc_read_by_type_req(start, end, &type_uuid, buf, buflen);
if (plen == 0)
return 0;
dc->cb = func;
dc->user_data = user_data;
dc->end = end;
+ dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
- return g_attrib_send(attrib, 0, pdu[0], pdu, plen, char_discovered_cb,
+ return g_attrib_send(attrib, 0, buf[0], buf, plen, char_discovered_cb,
dc, NULL);
}
bt_uuid_t *uuid, GAttribResultFunc func,
gpointer user_data)
{
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(attrib, &buflen);
guint16 plen;
- plen = enc_read_by_type_req(start, end, uuid, pdu, sizeof(pdu));
+ plen = enc_read_by_type_req(start, end, uuid, buf, buflen);
if (plen == 0)
return 0;
return g_attrib_send(attrib, 0, ATT_OP_READ_BY_TYPE_REQ,
- pdu, plen, func, user_data, NULL);
+ buf, plen, func, user_data, NULL);
}
struct read_long_data {
gpointer user_data)
{
struct read_long_data *long_read = user_data;
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ uint8_t *buf;
+ int buflen;
guint8 *tmp;
guint16 plen;
guint id;
long_read->buffer = tmp;
long_read->size += rlen - 1;
- if (rlen < ATT_DEFAULT_LE_MTU)
+ buf = g_attrib_get_buffer(long_read->attrib, &buflen);
+ if (rlen < buflen)
goto done;
plen = enc_read_blob_req(long_read->handle, long_read->size - 1,
- pdu, sizeof(pdu));
+ buf, buflen);
id = g_attrib_send(long_read->attrib, long_read->id,
- ATT_OP_READ_BLOB_REQ, pdu, plen,
+ ATT_OP_READ_BLOB_REQ, buf, plen,
read_blob_helper, long_read, read_long_destroy);
if (id != 0) {
guint16 rlen, gpointer user_data)
{
struct read_long_data *long_read = user_data;
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ int buflen;
+ uint8_t *buf = g_attrib_get_buffer(long_read->attrib, &buflen);
guint16 plen;
guint id;
- if (status != 0 || rlen < ATT_DEFAULT_LE_MTU)
+ if (status != 0 || rlen < buflen)
goto done;
long_read->buffer = g_malloc(rlen);
memcpy(long_read->buffer, rpdu, rlen);
long_read->size = rlen;
- plen = enc_read_blob_req(long_read->handle, rlen - 1, pdu, sizeof(pdu));
+ plen = enc_read_blob_req(long_read->handle, rlen - 1, buf, buflen);
id = g_attrib_send(long_read->attrib, long_read->id,
- ATT_OP_READ_BLOB_REQ, pdu, plen, read_blob_helper,
+ ATT_OP_READ_BLOB_REQ, buf, plen, read_blob_helper,
long_read, read_long_destroy);
if (id != 0) {
guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset,
GAttribResultFunc func, gpointer user_data)
{
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ uint8_t *buf;
+ int buflen;
guint16 plen;
guint id;
struct read_long_data *long_read;
long_read->user_data = user_data;
long_read->handle = handle;
+ buf = g_attrib_get_buffer(attrib, &buflen);
if (offset > 0) {
- plen = enc_read_blob_req(long_read->handle, offset, pdu,
- sizeof(pdu));
- id = g_attrib_send(attrib, 0, ATT_OP_READ_BLOB_REQ, pdu, plen,
+ plen = enc_read_blob_req(long_read->handle, offset, buf,
+ buflen);
+ id = g_attrib_send(attrib, 0, ATT_OP_READ_BLOB_REQ, buf, plen,
read_blob_helper, long_read, read_long_destroy);
} else {
- plen = enc_read_req(handle, pdu, sizeof(pdu));
- id = g_attrib_send(attrib, 0, ATT_OP_READ_REQ, pdu, plen,
+ plen = enc_read_req(handle, buf, buflen);
+ id = g_attrib_send(attrib, 0, ATT_OP_READ_REQ, buf, plen,
read_char_helper, long_read, read_long_destroy);
}
guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value,
int vlen, GAttribResultFunc func, gpointer user_data)
{
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ uint8_t *buf;
+ int buflen;
guint16 plen;
+ buf = g_attrib_get_buffer(attrib, &buflen);
if (func)
- plen = enc_write_req(handle, value, vlen, pdu, sizeof(pdu));
+ plen = enc_write_req(handle, value, vlen, buf, buflen);
else
- plen = enc_write_cmd(handle, value, vlen, pdu, sizeof(pdu));
+ plen = enc_write_cmd(handle, value, vlen, buf, buflen);
+
+ return g_attrib_send(attrib, 0, buf[0], buf, plen, func,
+ user_data, NULL);
+}
- return g_attrib_send(attrib, 0, pdu[0], pdu, plen, func,
+guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
+ gpointer user_data)
+{
+ uint8_t *buf;
+ int buflen;
+ guint16 plen;
+
+ buf = g_attrib_get_buffer(attrib, &buflen);
+ plen = enc_mtu_req(mtu, buf, buflen);
+ return g_attrib_send(attrib, 0, ATT_OP_MTU_REQ, buf, plen, func,
user_data, NULL);
}
guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
GAttribResultFunc func, gpointer user_data)
{
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ uint8_t *buf;
+ int buflen;
guint16 plen;
- plen = enc_find_info_req(start, end, pdu, sizeof(pdu));
+ buf = g_attrib_get_buffer(attrib, &buflen);
+ plen = enc_find_info_req(start, end, buf, buflen);
if (plen == 0)
return 0;
- return g_attrib_send(attrib, 0, ATT_OP_FIND_INFO_REQ, pdu, plen, func,
+ return g_attrib_send(attrib, 0, ATT_OP_FIND_INFO_REQ, buf, plen, func,
user_data, NULL);
}
guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen,
GDestroyNotify notify, gpointer user_data)
{
- uint8_t pdu[ATT_DEFAULT_LE_MTU];
+ uint8_t *buf;
+ int buflen;
guint16 plen;
- plen = enc_write_cmd(handle, value, vlen, pdu, sizeof(pdu));
- return g_attrib_send(attrib, 0, ATT_OP_WRITE_CMD, pdu, plen, NULL,
+ buf = g_attrib_get_buffer(attrib, &buflen);
+ plen = enc_write_cmd(handle, value, vlen, buf, buflen);
+ return g_attrib_send(attrib, 0, ATT_OP_WRITE_CMD, buf, plen, NULL,
user_data, notify);
}
+
+static sdp_data_t *proto_seq_find(sdp_list_t *proto_list)
+{
+ sdp_list_t *list;
+ uuid_t proto;
+
+ sdp_uuid16_create(&proto, ATT_UUID);
+
+ for (list = proto_list; list; list = list->next) {
+ sdp_list_t *p;
+ for (p = list->data; p; p = p->next) {
+ sdp_data_t *seq = p->data;
+ if (seq && seq->dtd == SDP_UUID16 &&
+ sdp_uuid16_cmp(&proto, &seq->val.uuid) == 0)
+ return seq->next;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean parse_proto_params(sdp_list_t *proto_list, uint16_t *psm,
+ uint16_t *start, uint16_t *end)
+{
+ sdp_data_t *seq1, *seq2;
+
+ if (psm)
+ *psm = sdp_get_proto_port(proto_list, L2CAP_UUID);
+
+ /* Getting start and end handle */
+ seq1 = proto_seq_find(proto_list);
+ if (!seq1 || seq1->dtd != SDP_UINT16)
+ return FALSE;
+
+ seq2 = seq1->next;
+ if (!seq2 || seq2->dtd != SDP_UINT16)
+ return FALSE;
+
+ if (start)
+ *start = seq1->val.uint16;
+
+ if (end)
+ *end = seq2->val.uint16;
+
+ return TRUE;
+}
+
+gboolean gatt_parse_record(const sdp_record_t *rec,
+ uuid_t *prim_uuid, uint16_t *psm,
+ uint16_t *start, uint16_t *end)
+{
+ sdp_list_t *list;
+ uuid_t uuid;
+ gboolean ret;
+
+ if (sdp_get_service_classes(rec, &list) < 0)
+ return FALSE;
+
+ memcpy(&uuid, list->data, sizeof(uuid));
+ sdp_list_free(list, free);
+
+ if (sdp_get_access_protos(rec, &list) < 0)
+ return FALSE;
+
+ ret = parse_proto_params(list, psm, start, end);
+
+ sdp_list_foreach(list, (sdp_list_func_t) sdp_list_free, NULL);
+ sdp_list_free(list, NULL);
+
+ /* FIXME: replace by bt_uuid_t after uuid_t/sdp code cleanup */
+ if (ret && prim_uuid)
+ memcpy(prim_uuid, &uuid, sizeof(uuid_t));
+
+ return ret;
+}
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
-
-#define GATT_CID 4
+#include <bluetooth/sdp.h>
typedef void (*gatt_cb_t) (GSList *l, guint8 status, gpointer user_data);
gpointer user_data);
guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end,
- gatt_cb_t func, gpointer user_data);
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data);
guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset,
GAttribResultFunc func, gpointer user_data);
guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end,
bt_uuid_t *uuid, GAttribResultFunc func,
gpointer user_data);
+
+guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
+ gpointer user_data);
+
+gboolean gatt_parse_record(const sdp_record_t *rec,
+ uuid_t *prim_uuid, uint16_t *psm,
+ uint16_t *start, uint16_t *end);
struct _GAttrib {
GIOChannel *io;
gint refs;
+ uint8_t *buf;
+ int buflen;
guint read_watch;
guint write_watch;
guint timeout_watch;
g_io_channel_unref(attrib->io);
}
+ g_free(attrib->buf);
+
if (attrib->destroy)
attrib->destroy(attrib->destroy_user_data);
struct event *evt = l->data;
if (evt->expected == buf[0] ||
- evt->expected == GATTRIB_ALL_EVENTS)
+ evt->expected == GATTRIB_ALL_EVENTS ||
+ (is_response(buf[0]) == FALSE &&
+ evt->expected == GATTRIB_ALL_REQS))
evt->func(buf, len, evt->user_data);
}
GAttrib *g_attrib_new(GIOChannel *io)
{
struct _GAttrib *attrib;
+ uint16_t omtu;
g_io_channel_set_encoding(io, NULL, NULL);
g_io_channel_set_buffered(io, FALSE);
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
received_data, attrib);
+ if (bt_io_get(attrib->io, BT_IO_L2CAP, NULL,
+ BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_INVALID)) {
+ if (omtu == 0 || omtu > ATT_MAX_MTU)
+ omtu = ATT_MAX_MTU;
+ } else
+ omtu = ATT_DEFAULT_LE_MTU;
+
+ attrib->buf = g_malloc0(omtu);
+ attrib->buflen = omtu;
+
return g_attrib_ref(attrib);
}
return TRUE;
}
+uint8_t *g_attrib_get_buffer(GAttrib *attrib, int *len)
+{
+ if (len == NULL)
+ return NULL;
+
+ *len = attrib->buflen;
+
+ return attrib->buf;
+}
+
+gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu)
+{
+ if (mtu < ATT_DEFAULT_LE_MTU)
+ mtu = ATT_DEFAULT_LE_MTU;
+
+ if (mtu > ATT_MAX_MTU)
+ mtu = ATT_MAX_MTU;
+
+ if (!bt_io_set(attrib->io, BT_IO_L2CAP, NULL,
+ BT_IO_OPT_OMTU, mtu,
+ BT_IO_OPT_INVALID))
+ return FALSE;
+
+ attrib->buf = g_realloc(attrib->buf, mtu);
+
+ attrib->buflen = mtu;
+
+ return TRUE;
+}
+
guint g_attrib_register(GAttrib *attrib, guint8 opcode,
GAttribNotifyFunc func, gpointer user_data,
GDestroyNotify notify)
#endif
#define GATTRIB_ALL_EVENTS 0xFF
+#define GATTRIB_ALL_REQS 0xFE
struct _GAttrib;
typedef struct _GAttrib GAttrib;
gboolean g_attrib_is_encrypted(GAttrib *attrib);
+uint8_t *g_attrib_get_buffer(GAttrib *attrib, int *len);
+gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu);
+
gboolean g_attrib_unregister(GAttrib *attrib, guint id);
gboolean g_attrib_unregister_all(GAttrib *attrib);
static gboolean opt_interactive = FALSE;
static GMainLoop *event_loop;
static gboolean got_error = FALSE;
+static GSourceFunc operation;
struct characteristic_data {
GAttrib *attrib;
uint16_t end;
};
+static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+ GAttrib *attrib = user_data;
+ uint8_t opdu[ATT_MAX_MTU];
+ uint16_t handle, i, olen = 0;
+
+ handle = att_get_u16(&pdu[1]);
+
+ switch (pdu[0]) {
+ case ATT_OP_HANDLE_NOTIFY:
+ g_print("Notification handle = 0x%04x value: ", handle);
+ break;
+ case ATT_OP_HANDLE_IND:
+ g_print("Indication handle = 0x%04x value: ", handle);
+ break;
+ default:
+ g_print("Invalid opcode\n");
+ return;
+ }
+
+ for (i = 3; i < len; i++)
+ g_print("%02x ", pdu[i]);
+
+ g_print("\n");
+
+ if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
+ return;
+
+ olen = enc_confirmation(opdu, sizeof(opdu));
+
+ if (olen > 0)
+ g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
+}
+
+static gboolean listen_start(gpointer user_data)
+{
+ GAttrib *attrib = user_data;
+
+ g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler,
+ attrib, NULL);
+ g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler,
+ attrib, NULL);
+
+ return FALSE;
+}
+
static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
{
+ GAttrib *attrib;
+
if (err) {
g_printerr("%s\n", err->message);
got_error = TRUE;
g_main_loop_quit(event_loop);
}
+
+ attrib = g_attrib_new(io);
+
+ if (opt_listen)
+ g_idle_add(listen_start, attrib);
+
+ operation(attrib);
}
static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
g_main_loop_quit(event_loop);
}
-static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
-{
- GAttrib *attrib = user_data;
- uint8_t opdu[ATT_MAX_MTU];
- uint16_t handle, i, olen = 0;
-
- handle = att_get_u16(&pdu[1]);
-
- switch (pdu[0]) {
- case ATT_OP_HANDLE_NOTIFY:
- g_print("Notification handle = 0x%04x value: ", handle);
- break;
- case ATT_OP_HANDLE_IND:
- g_print("Indication handle = 0x%04x value: ", handle);
- break;
- default:
- g_print("Invalid opcode\n");
- return;
- }
-
- for (i = 3; i < len; i++)
- g_print("%02x ", pdu[i]);
-
- g_print("\n");
-
- if (pdu[0] == ATT_OP_HANDLE_NOTIFY)
- return;
-
- olen = enc_confirmation(opdu, sizeof(opdu));
-
- if (olen > 0)
- g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL);
-}
-
-static gboolean listen_start(gpointer user_data)
-{
- GAttrib *attrib = user_data;
-
- g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler,
- attrib, NULL);
- g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler,
- attrib, NULL);
-
- return FALSE;
-}
-
static gboolean primary(gpointer user_data)
{
GAttrib *attrib = user_data;
{
GAttrib *attrib = user_data;
- gatt_discover_char(attrib, opt_start, opt_end, char_discovered_cb, NULL);
+ gatt_discover_char(attrib, opt_start, opt_end, opt_uuid,
+ char_discovered_cb, NULL);
return FALSE;
}
att_data_list_free(list);
- gatt_read_char_by_uuid(char_data->attrib, char_data->start,
- char_data->end, opt_uuid,
- char_read_by_uuid_cb,
- char_data);
-
- return;
done:
g_free(char_data);
g_main_loop_quit(event_loop);
goto done;
}
- g_print("Characteristic value was written sucessfully\n");
+ g_print("Characteristic value was written successfully\n");
done:
if (opt_listen == FALSE)
GOptionContext *context;
GOptionGroup *gatt_group, *params_group, *char_rw_group;
GError *gerr = NULL;
- GAttrib *attrib;
GIOChannel *chan;
- GSourceFunc callback;
opt_sec_level = g_strdup("low");
}
if (opt_primary)
- callback = primary;
+ operation = primary;
else if (opt_characteristics)
- callback = characteristics;
+ operation = characteristics;
else if (opt_char_read)
- callback = characteristics_read;
+ operation = characteristics_read;
else if (opt_char_write)
- callback = characteristics_write;
+ operation = characteristics_write;
else if (opt_char_write_req)
- callback = characteristics_write_req;
+ operation = characteristics_write_req;
else if (opt_char_desc)
- callback = characteristics_desc;
+ operation = characteristics_desc;
else {
gchar *help = g_option_context_get_help(context, TRUE, NULL);
g_print("%s\n", help);
goto done;
}
- attrib = g_attrib_new(chan);
- g_io_channel_unref(chan);
-
event_loop = g_main_loop_new(NULL, FALSE);
- if (opt_listen)
- g_idle_add(listen_start, attrib);
-
- g_idle_add(callback, attrib);
-
g_main_loop_run(event_loop);
- g_attrib_unregister_all(attrib);
-
g_main_loop_unref(event_loop);
- g_attrib_unref(attrib);
-
done:
g_option_context_free(context);
g_free(opt_src);
att_data_list_free(list);
- gatt_read_char_by_uuid(attrib, char_data->start, char_data->end,
- &char_data->uuid, char_read_by_uuid_cb,
- char_data);
-
rl_forced_update_display();
- return;
-
done:
g_free(char_data);
}
g_attrib_unref(attrib);
attrib = NULL;
+ opt_mtu = 0;
g_io_channel_shutdown(iochannel, FALSE, NULL);
g_io_channel_unref(iochannel);
}
}
- gatt_discover_char(attrib, start, end, char_cb, NULL);
+ if (argcp > 3) {
+ bt_uuid_t uuid;
+
+ if (bt_string_to_uuid(&uuid, argvp[3]) < 0) {
+ printf("Invalid UUID\n");
+ return;
+ }
+
+ gatt_discover_char(attrib, start, end, &uuid, char_cb, NULL);
+ return;
+ }
+
+ gatt_discover_char(attrib, start, end, NULL, char_cb, NULL);
}
static void cmd_char_desc(int argcp, char **argvp)
}
}
+static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ uint16_t mtu;
+
+ if (status != 0) {
+ printf("Exchange MTU Request failed: %s\n",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_mtu_resp(pdu, plen, &mtu)) {
+ printf("Protocol error\n");
+ return;
+ }
+
+ mtu = MIN(mtu, opt_mtu);
+ /* Set new value for MTU in client */
+ if (g_attrib_set_mtu(attrib, mtu))
+ printf("MTU was exchanged successfully: %d\n", mtu);
+ else
+ printf("Error exchanging MTU\n");
+}
+
+static void cmd_mtu(int argcp, char **argvp)
+{
+ if (conn_state != STATE_CONNECTED) {
+ printf("Command failed: not connected.\n");
+ return;
+ }
+
+ if (opt_psm) {
+ printf("Command failed: operation is only available for LE"
+ " transport.\n");
+ return;
+ }
+
+ if (argcp < 2) {
+ printf("Usage: mtu <value>\n");
+ return;
+ }
+
+ if (opt_mtu) {
+ printf("Command failed: MTU exchange can only occur once per"
+ " connection.\n");
+ return;
+ }
+
+ errno = 0;
+ opt_mtu = strtoll(argvp[1], NULL, 0);
+ if (errno != 0 || opt_mtu < ATT_DEFAULT_LE_MTU) {
+ printf("Invalid value. Minimum MTU size is %d\n",
+ ATT_DEFAULT_LE_MTU);
+ return;
+ }
+
+ gatt_exchange_mtu(attrib, opt_mtu, exchange_mtu_cb, NULL);
+}
+
static struct {
const char *cmd;
void (*func)(int argcp, char **argvp);
"Show this help"},
{ "exit", cmd_exit, "",
"Exit interactive mode" },
+ { "quit", cmd_exit, "",
+ "Exit interactive mode" },
{ "connect", cmd_connect, "[address]",
"Connect to a remote device" },
{ "disconnect", cmd_disconnect, "",
"Disconnect from a remote device" },
{ "primary", cmd_primary, "[UUID]",
"Primary Service Discovery" },
- { "characteristics", cmd_char, "[start hnd] [end hnd]",
+ { "characteristics", cmd_char, "[start hnd [end hnd [UUID]]]",
"Characteristics Discovery" },
{ "char-desc", cmd_char_desc, "[start hnd] [end hnd]",
"Characteristics Descriptor Discovery" },
"Characteristic Value Write (No response)" },
{ "sec-level", cmd_sec_level, "[low | medium | high]",
"Set security level. Default: low" },
+ { "mtu", cmd_mtu, "<value>",
+ "Exchange MTU for GATT/ATT" },
{ NULL, NULL, NULL}
};
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
-#include "../src/adapter.h"
-#include "../src/device.h"
-#include "hcid.h"
-
-#include "manager.h"
-#include "client.h"
-#include "example.h"
-
-#define GATT_UUID "00001801-0000-1000-8000-00805f9b34fb"
-
-static DBusConnection *connection;
-
-static int client_probe(struct btd_device *device, GSList *uuids)
-{
- const sdp_record_t *rec;
- int psm = -1;
-
- rec = btd_device_get_record(device, GATT_UUID);
- if (rec) {
- sdp_list_t *list;
- if (sdp_get_access_protos(rec, &list) < 0)
- return -1;
-
- psm = sdp_get_proto_port(list, L2CAP_UUID);
-
- sdp_list_foreach(list, (sdp_list_func_t) sdp_list_free, NULL);
- sdp_list_free(list, NULL);
-
- if (psm < 0)
- return -1;
- }
-
- return attrib_client_register(device, psm);
-}
-
-static void client_remove(struct btd_device *device)
-{
- attrib_client_unregister(device);
-}
-
-static struct btd_device_driver client_driver = {
- .name = "gatt-client",
- .uuids = BTD_UUIDS(GATT_UUID),
- .probe = client_probe,
- .remove = client_remove,
-};
-
-int attrib_manager_init(DBusConnection *conn)
-{
- connection = dbus_connection_ref(conn);
-
- attrib_client_init(connection);
-
- btd_register_device_driver(&client_driver);
-
-
- if (main_opts.attrib_server)
- return server_example_init();
-
- return 0;
-}
-
-void attrib_manager_exit(void)
-{
- btd_unregister_device_driver(&client_driver);
-
- if (main_opts.attrib_server)
- server_example_exit();
-
- attrib_client_exit();
-
- dbus_connection_unref(connection);
-}
#include <bluetooth/uuid.h>
#include <bluetooth/sdp.h>
+#include "att.h"
#include "gattrib.h"
#include "gatt.h"
#include "btio.h"
chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &dba,
- BT_IO_OPT_CID, GATT_CID,
+ BT_IO_OPT_CID, ATT_CID,
BT_IO_OPT_OMTU, mtu,
BT_IO_OPT_SEC_LEVEL, sec,
BT_IO_OPT_INVALID);
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
-#include "glib-helper.h"
+#include "glib-compat.h"
#include "log.h"
#include "device.h"
#include "manager.h"
return TRUE;
}
-static void endpoint_setconf_cb(struct a2dp_sep *sep, guint setup_id,
- gboolean ret)
-{
- struct a2dp_setup *setup = GUINT_TO_POINTER(setup_id);
+static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret)
+{
if (ret == FALSE) {
setup->err = g_new(struct avdtp_error, 1);
avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC,
ret = a2dp_sep->endpoint->set_configuration(a2dp_sep,
setup->dev, codec->data,
cap->length - sizeof(*codec),
- GPOINTER_TO_UINT(setup),
+ setup,
endpoint_setconf_cb,
a2dp_sep->user_data);
if (ret == 0)
return TRUE;
}
-static void endpoint_open_cb(struct a2dp_sep *sep, guint setup_id,
- gboolean ret)
+static void endpoint_open_cb(struct a2dp_setup *setup, gboolean ret)
{
- struct a2dp_setup *setup = GUINT_TO_POINTER(setup_id);
int err;
if (ret == FALSE) {
err = a2dp_sep->endpoint->set_configuration(a2dp_sep, dev,
codec->data, service->length -
sizeof(*codec),
- GPOINTER_TO_UINT(setup),
+ setup,
endpoint_open_cb,
a2dp_sep->user_data);
if (err == 0)
return TRUE;
}
-static void select_cb(struct a2dp_sep *sep, guint setup_id, void *ret,
- int size)
+static void select_cb(struct a2dp_setup *setup, void *ret, int size)
{
- struct a2dp_setup *setup = GUINT_TO_POINTER(setup_id);
struct avdtp_service_capability *media_transport, *media_codec;
struct avdtp_media_codec_capability *cap;
err = sep->endpoint->select_configuration(sep, codec->data,
service->length - sizeof(*codec),
- GPOINTER_TO_UINT(setup),
+ setup,
select_cb, sep->user_data);
if (err == 0)
return cb_data->id;
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
#endif
struct a2dp_sep;
+struct a2dp_setup;
-typedef void (*a2dp_endpoint_select_t) (struct a2dp_sep *sep, guint setup_id,
- void *ret, int size);
-typedef void (*a2dp_endpoint_config_t) (struct a2dp_sep *sep, guint setup_id,
- gboolean ret);
+typedef void (*a2dp_endpoint_select_t) (struct a2dp_setup *setup, void *ret,
+ int size);
+typedef void (*a2dp_endpoint_config_t) (struct a2dp_setup *setup, gboolean ret);
struct a2dp_endpoint {
const char *(*get_name) (struct a2dp_sep *sep, void *user_data);
int (*select_configuration) (struct a2dp_sep *sep,
uint8_t *capabilities,
size_t length,
- guint setup_id,
+ struct a2dp_setup *setup,
a2dp_endpoint_select_t cb,
void *user_data);
int (*set_configuration) (struct a2dp_sep *sep,
struct audio_device *dev,
uint8_t *configuration,
size_t length,
- guint setup_id,
+ struct a2dp_setup *setup,
a2dp_endpoint_config_t cb,
void *user_data);
void (*clear_configuration) (struct a2dp_sep *sep, void *user_data);
# If we want to disable support for specific services
# Defaults to supporting all implemented services
-Disable=Gateway
-Enable=Media
+#Disable=Control,Source
+
+#ifdef __SAMSUNG_PATCH__
+Enable=Socket
+#endif
# SCO routing. Either PCM or HCI (in which case audio is routed to/from ALSA)
-# Defaults to HCI
+#ifdef __SAMSUNG_PATCH__
SCORouting=PCM
+#else
+# Defaults to HCI
+#SCORouting=PCM
+#endif
# Automatically connect both A2DP and HFP/HSP profiles for incoming
# connections. Some headsets that support both profiles will only connect the
FastConnectable=false
# Just an example of potential config options for the other interfaces
+#ifdef __SAMSUNG_PATCH__
[A2DP]
SBCSources=1
-APTXSources=1
+#else
+#[A2DP]
+#SBCSources=1
#MPEG12Sources=0
+#endif
if (fd < 0) {
fd = open("/dev/misc/uinput", O_RDWR);
if (fd < 0) {
- err = errno;
+ err = -errno;
error("Can't open input device: %s (%d)",
- strerror(err), err);
- return -err;
+ strerror(-err), -err);
+ return err;
}
}
}
dev.id.version = 0x0000;
if (write(fd, &dev, sizeof(dev)) < 0) {
- err = errno;
+ err = -errno;
error("Can't write device information: %s (%d)",
- strerror(err), err);
+ strerror(-err), -err);
close(fd);
- errno = err;
- return -err;
+ return err;
}
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
- err = errno;
+ err = -errno;
error("Can't create uinput device: %s (%d)",
- strerror(err), err);
+ strerror(-err), -err);
close(fd);
- errno = err;
- return -err;
+ return err;
}
return fd;
struct avctp_header *avctp;
struct avc_header *avc;
uint8_t *pdu;
- int sk, err;
+ int sk, err = 0;
uint16_t size;
if (session->state != AVCTP_STATE_CONNECTED)
memcpy(pdu, operands, operand_count);
- err = write(sk, buf, size);
- if (err < 0) {
- g_free(buf);
- return -errno;
- }
+ if (write(sk, buf, size) < 0)
+ err = -errno;
g_free(buf);
- return 0;
+ return err;
}
unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data)
#include "manager.h"
#include "control.h"
#include "avdtp.h"
-#include "glib-helper.h"
+#include "glib-compat.h"
#include "btio.h"
#include "sink.h"
#include "source.h"
#define AVDTP_MSG_TYPE_ACCEPT 0x02
#define AVDTP_MSG_TYPE_REJECT 0x03
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
#define REQ_TIMEOUT 10
#else
#define REQ_TIMEOUT 6
#include "avctp.h"
#include "avrcp.h"
#include "sdpd.h"
-#include "glib-helper.h"
+#include "glib-compat.h"
#include "dbus-common.h"
/* Company IDs for vendor dependent commands */
memcpy(&pdu->params[1], data, sizeof(uint64_t));
break;
+ case AVRCP_EVENT_TRACK_REACHED_END:
case AVRCP_EVENT_TRACK_REACHED_START:
size = 1;
break;
memcpy(&pdu->params[1], &uid, sizeof(uint64_t));
break;
+ case AVRCP_EVENT_TRACK_REACHED_END:
case AVRCP_EVENT_TRACK_REACHED_START:
len = 1;
break;
/* Notification events */
#define AVRCP_EVENT_STATUS_CHANGED 0x01
#define AVRCP_EVENT_TRACK_CHANGED 0x02
+#define AVRCP_EVENT_TRACK_REACHED_END 0x03
#define AVRCP_EVENT_TRACK_REACHED_START 0x04
#define AVRCP_EVENT_LAST AVRCP_EVENT_TRACK_REACHED_START
struct bluetooth_data *data = ext->private_data;
char buf[BT_SUGGESTED_BUFFER_SIZE];
struct bt_control_ind *ind = (void *) buf;
- int ret;
+ int err;
const char *type, *name;
DBG("ext %p id %p", ext, id);
memset(buf, 0, sizeof(buf));
- ret = recv(data->sock, ind, BT_SUGGESTED_BUFFER_SIZE, MSG_DONTWAIT);
- if (ret < 0) {
- SNDERR("Failed while receiving data: %s (%d)",
- strerror(errno), errno);
- return -errno;
+ err = recv(data->sock, ind, BT_SUGGESTED_BUFFER_SIZE, MSG_DONTWAIT);
+ if (err < 0) {
+ err = -errno;
+ SNDERR("Failed while receiving data: %s (%d)", strerror(-err),
+ -err);
+ return err;
}
type = bt_audio_strtype(ind->h.type);
#include <gdbus.h>
#include "log.h"
-#include "textfile.h"
#include "../src/adapter.h"
#include "../src/device.h"
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
-#include "glib-helper.h"
+#include "glib-compat.h"
+#include "sdp-client.h"
#include "device.h"
#include "gateway.h"
#include "log.h"
g_io_channel_unix_get_fd(sink->server));
if (ret < 0) {
- err = errno;
+ err = -errno;
GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)",
- strerror(err), err);
- return -err;
+ strerror(-err), -err);
+ return err;
}
sink->stream = g_io_channel_unix_new(ret);
self->watch_id = 0;
sk = bt_audio_service_open();
- if (sk <= 0) {
- err = errno;
+ if (sk < 0) {
+ err = -errno;
GST_ERROR_OBJECT(self, "Cannot open connection to bt "
- "audio service: %s %d", strerror(err), err);
- goto failed;
+ "audio service: %s %d", strerror(-err), -err);
+ return FALSE;
}
self->server = g_io_channel_unix_new(sk);
ssize_t written;
const char *type, *name;
uint16_t length;
- int fd;
+ int fd, err;
length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
written = write(fd, msg, length);
if (written < 0) {
+ err = -errno;
GST_ERROR_OBJECT(self, "Error sending data to audio service:"
- " %s", strerror(errno));
- return -errno;
+ " %s", strerror(-err));
+ return err;
}
type = bt_audio_strtype(msg->type);
bytes_read = read(fd, inmsg, length);
if (bytes_read < 0) {
+ err = -errno;
GST_ERROR_OBJECT(self, "Error receiving data from "
- "audio service: %s", strerror(errno));
- return -errno;
+ "audio service: %s", strerror(-err));
+ return err;
}
type = bt_audio_strtype(inmsg->type);
#include "error.h"
#include "telephony.h"
#include "headset.h"
-#include "glib-helper.h"
+#include "glib-compat.h"
+#include "sdp-client.h"
#include "btio.h"
#include "dbus-common.h"
#include "../src/adapter.h"
guint dc_timer;
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
guint rfcomm_io_id;
#endif
gboolean hfp_active;
return 0;
}
+#ifdef __SAMSUNG_PATCH__
int telephony_list_phonebook_store_rsp(void *telephony_device, const char *buf, cme_error_t err)
{
struct audio_device *device = telephony_device;
struct headset *hs = device->headset;
struct headset_slc *slc = hs->slc;
- if ( (err != CME_ERROR_NONE) || (NULL == buf) ) {
+ if ((err != CME_ERROR_NONE) || (NULL == buf)) {
if (slc->cme_enabled)
return headset_send(hs, "\r\n+CME ERROR: %d\r\n", err);
else
return -ENODEV;
send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPBS: %s\r\n",buf);
+ "\r\n+CPBS: %s\r\n", buf);
}
return headset_send(hs, "\r\nOK\r\n");
struct headset *hs = device->headset;
struct headset_slc *slc = hs->slc;
- if (err != CME_ERROR_NONE){
+ if (err != CME_ERROR_NONE) {
if (slc->cme_enabled)
return headset_send(hs, "\r\n+CME ERROR: %d\r\n", err);
else
return headset_send(hs, "\r\nERROR\r\n");
}
- if( 0 != total && 0 != used) {
+ if (0 != total && 0 != used) {
if (!active_devices)
return -ENODEV;
send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPBS: \"%s\",%d,%d\r\n",pb_store,used,total);
+ "\r\n+CPBS: \"%s\",%d,%d\r\n",
+ pb_store, used, total);
}
return headset_send(hs, "\r\nOK\r\n");
}
struct headset *hs = device->headset;
int err;
- if( NULL != buf) {
+ if (NULL != buf) {
if (strlen(buf) < 9)
return -EINVAL;
}
if (buf[8] == '=') {
- if(buf[9] == '?') {
+ if (buf[9] == '?') {
telephony_list_phonebook_store(device);
- }
- else {
- telephony_set_phonebook_store(device,&buf[9]);
+ } else {
+ telephony_set_phonebook_store(device, &buf[9]);
return headset_send(hs, "\r\nOK\r\n");
}
}
}
int telephony_read_phonebook_attributes_rsp(void *telephony_device,
- uint32_t total, uint32_t number_length,
- uint32_t name_length,cme_error_t err)
+ uint32_t total,
+ uint32_t number_length,
+ uint32_t name_length,
+ cme_error_t err)
{
struct audio_device *device = telephony_device;
struct headset *hs = device->headset;
return headset_send(hs, "\r\nERROR\r\n");
}
- if( total != 0 && name_length !=0 && number_length != 0 ) {
+ if (total != 0 && name_length != 0 && number_length != 0) {
if (!active_devices)
return -ENODEV;
send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPBR: (1-%d),%d,%d\r\n",total,number_length,name_length);
+ "\r\n+CPBR: (1-%d),%d,%d\r\n",
+ total, number_length, name_length);
}
return headset_send(hs, "\r\nOK\r\n");
}
-int telephony_read_phonebook_rsp(void *telephony_device, const char *data, cme_error_t err)
+int telephony_read_phonebook_rsp(void *telephony_device, const char *data,
+ cme_error_t err)
{
struct audio_device *device = telephony_device;
struct headset *hs = device->headset;
return headset_send(hs, "\r\nERROR\r\n");
}
- if( NULL != data) {
+ if (NULL != data) {
if (!active_devices)
return -ENODEV;
send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPBR: %s\r\n",data);
+ "\r\n+CPBR: %s\r\n", data);
}
}
struct headset *hs = device->headset;
int err;
- if( NULL != buf) {
+ if (NULL != buf) {
if (strlen(buf) < 8)
return -EINVAL;
if (buf[9] == '?') {
telephony_read_phonebook_attributes(device);
- }
- else {
- telephony_read_phonebook(device,&buf[9]);
+ } else {
+ telephony_read_phonebook(device, &buf[9]);
}
}
return 0;
}
int telephony_find_phonebook_entry_properties_rsp(void *telephony_device,
- uint32_t max_number_length,
- uint32_t max_name_length, cme_error_t err )
+ uint32_t max_number_length,
+ uint32_t max_name_length,
+ cme_error_t err)
{
struct audio_device *device = telephony_device;
struct headset *hs = device->headset;
return headset_send(hs, "\r\nERROR\r\n");
}
- if( max_name_length !=0 && max_number_length != 0 ) {
+ if (max_name_length != 0 && max_number_length != 0) {
if (!active_devices)
return -ENODEV;
send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPBF: %d,%d\r\n",max_number_length,max_name_length);
+ "\r\n+CPBF: %d,%d\r\n",
+ max_number_length,
+ max_name_length);
}
return headset_send(hs, "\r\nOK\r\n");
}
struct headset *hs = device->headset;
int err;
- if( NULL != buf ) {
+ if (NULL != buf) {
if (strlen(buf) < 8)
return -EINVAL;
- if (buf[9] == '?') {
+ if (buf[9] == '?')
telephony_find_phonebook_entry_properties(device);
- }
- else {
- telephony_find_phonebook_entry(device,&buf[9]);
- }
+ else
+ telephony_find_phonebook_entry(device, &buf[9]);
}
return 0;
}
-int telephony_list_preffered_store_rsp(void *telephony_device, char* prefrd_list, cme_error_t err )
+int telephony_list_preffered_store_rsp(void *telephony_device,
+ char *prefrd_list,
+ cme_error_t err)
{
struct audio_device *device = telephony_device;
struct headset *hs = device->headset;
return headset_send(hs, "\r\nERROR\r\n");
}
- if( NULL != prefrd_list ) {
+ if (NULL != prefrd_list) {
if (!active_devices)
return -ENODEV;
send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPMS: %s\r\n",prefrd_list);
+ "\r\n+CPMS: %s\r\n", prefrd_list);
}
return headset_send(hs, "\r\nOK\r\n");
}
-int telephony_get_preffered_store_capacity_rsp(void *telephony_device, uint32_t store_capacity, cme_error_t err )
+int telephony_get_preffered_store_capacity_rsp(void *telephony_device,
+ uint32_t store_capacity,
+ cme_error_t err)
{
struct audio_device *device = telephony_device;
struct headset *hs = device->headset;
return headset_send(hs, "\r\nERROR\r\n");
}
- if( 0 != store_capacity ) {
+ if (0 != store_capacity) {
if (!active_devices)
return -ENODEV;
send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPMS: %d\r\n",store_capacity);
+ "\r\n+CPMS: %d\r\n", store_capacity);
}
return headset_send(hs, "\r\nOK\r\n");
}
struct headset *hs = device->headset;
int err;
- if( NULL != buf ) {
+ if (NULL != buf) {
if (strlen(buf) < 9)
return -EINVAL;
}
if (buf[8] == '=') {
- if(buf[9] == '?') {
+ if (buf[9] == '?') {
telephony_list_preffered_store(device);
- }
- else {
- //telephony_set_preffered_store_capcity(device,&buf[9]);
+ } else {
+ /* telephony_set_preffered_store_capcity(device, &buf[9]); */
return headset_send(hs, "\r\nOK\r\n");
}
}
return 0;
}
-int telephony_supported_character_generic_rsp(void *telephony_device, char* character_set_list, cme_error_t err )
+int telephony_supported_character_generic_rsp(void *telephony_device,
+ char *character_set_list,
+ cme_error_t err)
{
struct audio_device *device = telephony_device;
struct headset *hs = device->headset;
return headset_send(hs, "\r\nERROR\r\n");
}
- if( NULL != character_set_list ) {
+ if (NULL != character_set_list) {
if (!active_devices)
return -ENODEV;
send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPMS: %s\r\n",character_set_list);
+ "\r\n+CPMS: %s\r\n", character_set_list);
}
return headset_send(hs, "\r\nOK\r\n");
}
{
struct headset *hs = device->headset;
int err;
- if( NULL != buf ) {
+ if (NULL != buf) {
if (strlen(buf) < 9)
return -EINVAL;
}
if (buf[8] == '=') {
- if(buf[9] == '?') {
+ if (buf[9] == '?') {
telephony_list_supported_character(device);
- }
- else {
- //telephony_set_characterset(device,&buf[9]);
+ } else {
+ /* telephony_set_characterset(device, &buf[9]); */
return headset_send(hs, "\r\nOK\r\n");
}
}
return 0;
}
+#endif
+
static int apple_command(struct audio_device *device, const char *buf)
{
DBG("Got Apple command: %s", buf);
{ "AT+BVRA", voice_dial },
{ "AT+XAPL", apple_command },
{ "AT+IPHONEACCEV", apple_command },
-
- /*TIZEN PATCH Starts here*/
+#ifdef __SAMSUNG_PATCH__
+ /*SAMSUNG PATCH Starts here*/
{ "AT+CPBS", select_phonebook_memory },
{ "AT+CPBR", read_phonebook_entries},
{ "AT+CPBF", find_phonebook_entires },
{ "AT+CPMS", preffered_message_storage },
{ "AT+CSCS", select_character_set },
-
+#endif
{ 0 }
};
return FALSE;
hs = device->headset;
-#ifdef __TIZEN_PATCH__
- if(!hs)
+#ifdef __SAMSUNG_PATCH__
+ if (!hs)
return FALSE;
#endif
slc = hs->slc;
if (err == -EINVAL) {
error("Badly formated or unrecognized command: %s",
&slc->buf[slc->data_start]);
-#ifdef __TIZEN_PATCH__
- if (slc->cme_enabled)
- err = headset_send(hs, "\r\n+CME ERROR: %d\r\n",
- CME_ERROR_AG_FAILURE);
- else
- err = headset_send(hs, "\r\nERROR\r\n");
-#else
- err = headset_send(hs, "\r\nERROR\r\n");
-#endif
+ err = telephony_generic_rsp(device,
+ CME_ERROR_NOT_SUPPORTED);
if (err < 0)
goto failed;
} else if (err < 0)
else
hs->auto_dc = FALSE;
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
hs->rfcomm_io_id = g_io_add_watch(chan,
- G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) rfcomm_io_cb, dev);
#else
g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,
DBG("%s: Connecting to %s channel %d", dev->path, address,
hs->rfcomm_ch);
-#ifdef __TIZEN_PATCH__
- if(hs->rfcomm_io_id) {
+#ifdef __SAMSUNG_PATCH__
+ if (hs->rfcomm_io_id) {
g_source_remove(hs->rfcomm_io_id);
hs->rfcomm_io_id = 0;
}
hs->rfcomm = NULL;
}
-#ifdef __TIZEN_PATCH__
- if(hs->rfcomm_io_id) {
+#ifdef __SAMSUNG_PATCH__
+ if (hs->rfcomm_io_id) {
g_source_remove(hs->rfcomm_io_id);
hs->rfcomm_io_id = 0;
}
sk = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sk < 0) {
- err = errno;
+ err = -errno;
fprintf(stderr, "%s: Cannot open socket: %s (%d)\n",
- __FUNCTION__, strerror(err), err);
- errno = err;
+ __FUNCTION__, strerror(-err), -err);
+ errno = -err;
return -1;
}
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- err = errno;
+ err = -errno;
fprintf(stderr, "%s: connect() failed: %s (%d)\n",
- __FUNCTION__, strerror(err), err);
+ __FUNCTION__, strerror(-err), -err);
close(sk);
- errno = err;
+ errno = -err;
return -1;
}
ret = recvmsg(sk, &msgh, 0);
if (ret < 0) {
- err = errno;
+ err = -errno;
fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n",
- __FUNCTION__, strerror(err), err);
- errno = err;
+ __FUNCTION__, strerror(-err), -err);
+ errno = -err;
return -1;
}
#include "../src/device.h"
#include "log.h"
-#include "textfile.h"
#include "ipc.h"
#include "device.h"
#include "error.h"
.sink = TRUE,
.source = FALSE,
.control = TRUE,
- .socket = TRUE,
- .media = FALSE,
+ .socket = FALSE,
+ .media = TRUE,
};
static struct audio_adapter *find_adapter(GSList *list,
struct audio_device *audio_dev;
adapter_get_address(adapter, &src);
- device_get_address(device, &dst);
+ device_get_address(device, &dst, NULL);
audio_dev = manager_get_device(&src, &dst, TRUE);
if (!audio_dev) {
if (dev == device)
continue;
- if (bacmp(&dev->src, &device->src))
+ if (device && bacmp(&dev->src, &device->src) != 0)
continue;
if (!hs)
{
GSList *l;
+ if (enable && !manager_allow_headset_connection(NULL)) {
+ DBG("Refusing enabling fast connectable");
+ return;
+ }
+
for (l = adapters; l != NULL; l = l->next) {
struct audio_adapter *adapter = l->data;
*
* Copyright (C) 2006-2007 Nokia Corporation
* Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
if (ret != NULL)
return;
- headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
+ headset_shutdown(dev);
}
static void clear_configuration(struct media_endpoint *endpoint)
}
struct a2dp_config_data {
- guint setup_id;
+ struct a2dp_setup *setup;
a2dp_endpoint_config_t cb;
};
struct a2dp_select_data {
- guint setup_id;
+ struct a2dp_setup *setup;
a2dp_endpoint_select_t cb;
};
{
struct a2dp_select_data *data = user_data;
- data->cb(endpoint->sep, data->setup_id, ret, size);
+ data->cb(data->setup, ret, size);
}
static int select_config(struct a2dp_sep *sep, uint8_t *capabilities,
- size_t length, guint setup_id,
+ size_t length, struct a2dp_setup *setup,
a2dp_endpoint_select_t cb, void *user_data)
{
struct media_endpoint *endpoint = user_data;
struct a2dp_select_data *data;
data = g_new0(struct a2dp_select_data, 1);
- data->setup_id = setup_id;
+ data->setup = setup;
data->cb = cb;
if (select_configuration(endpoint, capabilities, length,
{
struct a2dp_config_data *data = user_data;
- data->cb(endpoint->sep, data->setup_id, ret ? TRUE : FALSE);
+ data->cb(data->setup, ret ? TRUE : FALSE);
}
static int set_config(struct a2dp_sep *sep, struct audio_device *dev,
uint8_t *configuration, size_t length,
- guint setup_id, a2dp_endpoint_config_t cb,
+ struct a2dp_setup *setup,
+ a2dp_endpoint_config_t cb,
void *user_data)
{
struct media_endpoint *endpoint = user_data;
struct a2dp_config_data *data;
data = g_new0(struct a2dp_config_data, 1);
- data->setup_id = setup_id;
+ data->setup = setup;
data->cb = cb;
if (set_configuration(endpoint, dev, configuration, length,
goto failed;
} else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
endpoint->sep = a2dp_add_sep(&adapter->src,
- AVDTP_SEP_TYPE_SOURCE, codec,
+ AVDTP_SEP_TYPE_SINK, codec,
delay_reporting, &a2dp_endpoint,
endpoint, a2dp_destroy_endpoint, err);
if (endpoint->sep == NULL)
static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
{
uint32_t value;
+ struct metadata_value *duration;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
return FALSE;
if (!mp->position) {
avrcp_player_event(mp->player,
AVRCP_EVENT_TRACK_REACHED_START, NULL);
+ return TRUE;
}
+ duration = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(
+ AVRCP_MEDIA_ATTRIBUTE_DURATION));
+
+ /*
+ * If position is the maximum value allowed or greater than track's
+ * duration, we send a track-reached-end event.
+ */
+ if (mp->position == UINT32_MAX ||
+ (duration && mp->position >= duration->value.num))
+ avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END,
+ NULL);
+
return TRUE;
}
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * 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
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#if 0
-#include <pulsecore/module.h>
-
-PA_MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>")
-PA_MODULE_DESCRIPTION("Bluetooth sink")
-PA_MODULE_VERSION(VERSION)
-
-int pa__init(pa_core *core, pa_module *module)
-{
- return 0;
-}
-#endif
}
/* wake up any client polling at us */
- err = write(data->pipefd[1], &c, 1);
- if (err < 0)
+ if (write(data->pipefd[1], &c, 1) < 0) {
+ err = -errno;
return err;
+ }
return 0;
}
static int avdtp_write(struct bluetooth_data *data)
{
- int ret = 0;
+ int err;
struct rtp_header *header;
struct rtp_payload *payload;
struct bluetooth_a2dp *a2dp = &data->a2dp;
header->timestamp = htonl(a2dp->nsamples);
header->ssrc = htonl(1);
- ret = send(data->stream.fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT);
- if (ret < 0) {
- DBG("send returned %d errno %s.", ret, strerror(errno));
- ret = -errno;
+ err = send(data->stream.fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT);
+ if (err < 0) {
+ err = -errno;
+ DBG("send failed: %s (%d)", strerror(-err), -err);
}
/* Reset buffer of data to send */
a2dp->samples = 0;
a2dp->seq_num++;
- return ret;
+ return err;
}
static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io,
else {
err = -errno;
SNDERR("Error sending data to audio service: %s(%d)",
- strerror(errno), errno);
+ strerror(-err), -err);
}
return err;
if (ret < 0) {
err = -errno;
SNDERR("Error receiving IPC data from bluetoothd: %s (%d)",
- strerror(errno), errno);
+ strerror(-err), -err);
} else if ((size_t) ret < sizeof(bt_audio_msg_header_t)) {
SNDERR("Too short (%d bytes) IPC packet from bluetoothd", ret);
err = -EINVAL;
data->stream.fd = -1;
sk = bt_audio_service_open();
- if (sk <= 0) {
+ if (sk < 0) {
err = -errno;
goto failed;
}
#include <bluetooth/sdp.h>
-#include "glib-helper.h"
+#include "glib-compat.h"
#include "log.h"
#include "telephony.h"
#include "error.h"
#include <bluetooth/sdp.h>
-#include "glib-helper.h"
+#include "glib-compat.h"
#include "log.h"
#include "telephony.h"
#define CSD_CALL_INTERFACE "org.tizen.csd.Call"
#define CSD_CALL_INSTANCE "org.tizen.csd.Call.Instance"
#define CSD_CALL_CONFERENCE "org.tizen.csd.Call.Conference"
-#define CSD_CALL_PATH "/org/tizen/csd/call"
+#define CSD_CALL_PATH "/org/tizen/csd/call"
#define CSD_CALL_CONFERENCE_PATH "/org/tizen/csd/call/conference"
#define CSD_CALL_SENDER_PATH "/org/tizen/csd/call/sender"
/* libcsnet D-Bus definitions */
#define CSD_CSNET_BUS_NAME "org.tizen.csd.CSNet"
-#define CSD_CSNET_PATH "/org/tizen/csd/csnet"
-#define CSD_CSNET_IFACE "org.tizen.csd.CSNet"
+#define CSD_CSNET_PATH "/org/tizen/csd/csnet"
+#define CSD_CSNET_IFACE "org.tizen.csd.CSNet"
#define CSD_CSNET_REGISTRATION "org.tizen.csd.CSNet.NetworkRegistration"
#define CSD_CSNET_OPERATOR "org.tizen.csd.CSNet.NetworkOperator"
#define CSD_CSNET_SIGNAL "org.tizen.csd.CSNet.SignalStrength"
#define CSD_CSNET_SUBSCRIBER "org.tizen.csd.CSNet.SubscriberNumber"
-#define CALL_FLAG_NONE 0
+#define CALL_FLAG_NONE 0
#define CALL_FLAG_PRESENTATION_ALLOWED 0x01
#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02
#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02
-#define DBUS_STRUCT_STRING_STRING_UINT (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID))
+#define DBUS_STRUCT_STRING_STRING_UINT (dbus_g_type_get_struct ("GValueArray",\
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID))
#define PHONEBOOK_STORE_LIST "(\"SM\",\"ME\",\"DC\",\"MC\",\"RC\")"
#define PHONEBOOK_STORE_LIST_BLUENME "(\"ME\",\"DC\",\"MC\",\"RC\")"
#define PHONEBOOK_PATH_LENGTH 5
#define PHONEBOOK_NAME_MAX_LENGTH 20
#define PHONEBOOK_NUMBER_MAX_LENGTH 20
+#define PHONEBOOK_MAX_CHARACTER_LENGTH 20
#define PHONEBOOK_READ_RESP_LENGTH 20
-typedef struct
-{
+typedef struct {
uint8_t utf_8;
uint8_t gsm;
-}GsmUnicodeTable;
+} GsmUnicodeTable;
- //0xC3 charcterset
+ /*0xC3 charcterset*/
const GsmUnicodeTable gsm_unicode_C3[] = {
{0xA8,0x04},{0xA9,0x05},{0xB9,0x06},{0xAC,0x07},{0xB2,0x08},
{0xB7,0x09},{0x98,0x0B},{0xB8,0x0C},{0x85,0x0E},{0xA5,0x0F},
{0xB6,0x7C},{0xB1,0x7D},{0xBC,0x7E},{0xA0,0x7F},
};
-//0xCE charcterset
+/*0xCE charcterset*/
const GsmUnicodeTable gsm_unicode_CE[] = {
{0x85,0x14},{0xA1,0x50},{0x98,0x19},{0xA0,0x16},{0x94,0x10},
{0xA6,0x12},{0x93,0x13},{0x9E,0x1A},{0x9B,0x14},{0xA8,0x17},
{0xA9,0x15},
};
-#define GSM_UNI_MAX_C3 (sizeof(gsm_unicode_C3)/sizeof(GsmUnicodeTable))
-#define GSM_UNI_MAX_CE (sizeof(gsm_unicode_CE)/sizeof(GsmUnicodeTable))
+#define GSM_UNI_MAX_C3 (sizeof(gsm_unicode_C3)/sizeof(GsmUnicodeTable))
+#define GSM_UNI_MAX_CE (sizeof(gsm_unicode_CE)/sizeof(GsmUnicodeTable))
static DBusConnection *ag_connection = NULL;
};
/* Supported set of call hold operations */
-//static const char *telephony_chld_str = "0,1,1x,2,2x,3,4";
+/*static const char *telephony_chld_str = "0,1,1x,2,2x,3,4";*/
static const char *telephony_chld_str = "0,1,2,3";
for (l = calls; l != NULL; l = l->next) {
struct csd_call *call = l->data;
- if(call->call_id == call_id)
+ if (call->call_id == call_id)
return call;
}
{
GSList *l;
- if( NULL != calls ) {
+ if (NULL != calls) {
for (l = calls; l != NULL; l = l->next) {
struct csd_call *call = l->data;
return -ENOMEM;
}
DBG(" Object Path =[ %s] and Call id = [%d]\n", call->object_path, call->call_id);
- if (!dbus_message_append_args(msg,DBUS_TYPE_UINT32,&call->call_id, DBUS_TYPE_INVALID))
- {
+ if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &call->call_id,
+ DBUS_TYPE_INVALID)) {
- DBG("dbus_message_append_args - ERROR\n");
+ DBG("dbus_message_append_args -ERROR\n");
dbus_message_unref(msg);
return -ENOMEM;
}
- gboolean ret = g_dbus_send_message(ag_connection, msg);
+ g_dbus_send_message(ag_connection, msg);
- DBG(" ret = [%d]\n", ret);
DBG("-\n");
return 0;
return -ENOMEM;
}
DBG(" Object Path =[ %s] and Call id = [%d]\n", call->object_path, call->call_id);
- if (!dbus_message_append_args(msg,DBUS_TYPE_UINT32,&call->call_id, DBUS_TYPE_INVALID))
- {
+ if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &call->call_id,
+ DBUS_TYPE_INVALID)) {
- DBG("dbus_message_append_args - ERROR\n");
+ DBG("dbus_message_append_args -ERROR\n");
dbus_message_unref(msg);
return -ENOMEM;
}
- gboolean ret = g_dbus_send_message(ag_connection, msg);
+ g_dbus_send_message(ag_connection, msg);
- DBG(" ret = [%d]\n", ret);
DBG("-\n");
return 0;
return -ENOMEM;
}
- if (!dbus_message_append_args(msg,DBUS_TYPE_UINT32,&call->call_id, DBUS_TYPE_INVALID))
- {
+ if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &call->call_id,
+ DBUS_TYPE_INVALID)) {
- DBG("dbus_message_append_args - ERROR\n");
+ DBG("dbus_message_append_args -ERROR\n");
dbus_message_unref(msg);
return -ENOMEM;
}
static void call_set_status(struct csd_call *call, dbus_uint32_t status)
{
dbus_uint32_t prev_status;
+ int callheld = 0;
+
DBG("+\n");
- int callheld = telephony_get_indicator(telephony_ag_indicators, "callheld");
+ callheld = telephony_get_indicator(telephony_ag_indicators, "callheld");
prev_status = call->status;
DBG("Call %s Call id %d changed from %s to %s", call->object_path, call->call_id,
if ((prev_status == CSD_CALL_STATUS_MO_ALERTING) ||
(prev_status == CSD_CALL_STATUS_COMING) ||
(prev_status == CSD_CALL_STATUS_CREATE) ||
- (prev_status == CSD_CALL_STATUS_WAITING))
- {
+ (prev_status == CSD_CALL_STATUS_WAITING)) {
telephony_update_indicator(telephony_ag_indicators,
"callsetup",
EV_CALLSETUP_INACTIVE);
}
- if( prev_status == CSD_CALL_STATUS_COMING) {
+ if (prev_status == CSD_CALL_STATUS_COMING) {
if (!call->originating)
telephony_calling_stopped_ind();
}
DBG("+n");
- if(NULL != call ) {
+ if (NULL != call) {
msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
call->object_path,
CSD_CALL_INSTANCE,
error("Unable to allocate new D-Bus message");
return -ENOMEM;
}
- if( NULL != call ) {
+ if (NULL != call) {
DBG(" Object Path =[ %s] and Call id = [%d]\n", call->object_path, call->call_id);
- if (!dbus_message_append_args(msg,DBUS_TYPE_UINT32,&call->call_id, DBUS_TYPE_INVALID))
- {
+ if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &call->call_id,
+ DBUS_TYPE_INVALID)) {
- DBG("dbus_message_append_args - ERROR\n");
+ DBG("dbus_message_append_args -ERROR\n");
dbus_message_unref(msg);
return -ENOMEM;
}
return -ENOMEM;
}
- if( NULL != call ) {
+ if (NULL != call) {
DBG(" Object Path =[ %s] and Call id = [%d]\n", call->object_path, call->call_id);
- if (!dbus_message_append_args(msg,DBUS_TYPE_UINT32,&call->call_id, DBUS_TYPE_INVALID))
- {
+ if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &call->call_id,
+ DBUS_TYPE_INVALID)) {
- DBG("dbus_message_append_args - ERROR\n");
+ DBG("dbus_message_append_args -ERROR\n");
dbus_message_unref(msg);
return -ENOMEM;
}
const char *idx;
struct csd_call *call;
int err = 0;
+ int chld_value = 0;
+
DBG("+\n");
if (strlen(cmd) > 1)
telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
#else
idx = &cmd[0];
- int chld_value = strtol(idx, NULL, 0);
+ chld_value = strtol(idx, NULL, 0);
err = dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
CSD_CALL_INSTANCE, "Threeway",
DBG("Incoming call to %s from number %s call id %d", call_path, number, call_id);
if (find_call_with_status(CSD_CALL_STATUS_ACTIVE) ||
- find_call_with_status(CSD_CALL_STATUS_HOLD)){
+ find_call_with_status(CSD_CALL_STATUS_HOLD)) {
telephony_call_waiting_ind(call->number,
number_type(call->number));
- call_set_status(call,CSD_CALL_STATUS_WAITING);
- }else {
+ call_set_status(call, CSD_CALL_STATUS_WAITING);
+ } else {
telephony_incoming_call_ind(call->number,
number_type(call->number));
- call_set_status(call,CSD_CALL_STATUS_COMING);
- }
+ call_set_status(call, CSD_CALL_STATUS_COMING);
+ }
telephony_update_indicator(telephony_ag_indicators, "callsetup",
EV_CALLSETUP_INCOMING);
DBG("-\n");
uint8_t new_status;
DBG("+\n");
- //new_status = str2status(status);
new_status = status;
if (net.status == new_status)
if (signal_bars < 0) {
DBG("signal strength smaller than expected: %d < 0",
- signal_bars);
+ signal_bars);
signal_bars = 0;
} else if (signal_bars > 5) {
DBG("signal strength greater than expected: %d > 5",
- signal_bars);
+ signal_bars);
signal_bars = 5;
}
static void update_battery_strength(int32_t battery_level)
{
+ int current_battchg = 0;
+
DBG("+\n");
- int current_battchg = telephony_get_indicator(telephony_ag_indicators, "battchg");
+
+ current_battchg = telephony_get_indicator(telephony_ag_indicators, "battchg");
if (battery_level < 0) {
DBG("Battery strength smaller than expected: %d < 0",
battery_level);
battery_level = 5;
}
- if( current_battchg == battery_level)
+ if (current_battchg == battery_level)
return;
- telephony_update_indicator(telephony_ag_indicators, "battchg", battery_level);
+ telephony_update_indicator(telephony_ag_indicators,
+ "battchg", battery_level);
DBG("-\n");
DBG("Outgoing call to %s from number %s call id %d", call_path, number, call_id);
- call_set_status(call,CSD_CALL_STATUS_CREATE);
+ call_set_status(call, CSD_CALL_STATUS_CREATE);
telephony_update_indicator(telephony_ag_indicators, "callsetup",
EV_CALLSETUP_OUTGOING);
return;
}
- DBG("status = [%d] and call_id = [%d]\n",status, call_id);
+ DBG("status = [%d] and call_id = [%d]\n", status, call_id);
call = find_call(call_id);
if (!call) {
/*
static void handle_operator_name_changed(DBusMessage *msg)
{
- DBG("+\n");
-
const char *name;
- DBG("handle_operator_name_changed()\n");
+
+ DBG("+\n");
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_STRING, &name,
static void handle_signal_bars_changed(DBusMessage *msg)
{
- DBG("+\n");
-
int32_t signal_bars;
- DBG("handle_signal_bars_changed()\n");
+
+ DBG("+\n");
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_INT32, &signal_bars,
static void handle_battery_bars_changed(DBusMessage *msg)
{
- DBG("+\n");
-
int32_t battery_level;
- DBG("handle_signal_bars_changed()\n");
+
+ DBG("+\n");
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_INT32, &battery_level,
static void handle_subscriber_number_changed(DBusMessage *msg)
{
- DBG("+\n");
-
const char *number;
- DBG("handle_subscriber_number_changed()\n");
+
+ DBG("+\n");
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_STRING, &number,
static gboolean signal_filter(DBusConnection *conn, DBusMessage *msg,
void *data)
{
+ const char *path = NULL;
+
DBG("+\n");
- const char *path = dbus_message_get_path(msg);
+ path = dbus_message_get_path(msg);
if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
handle_incoming_call(msg);
int telephony_init(void)
{
- //const char *battery_cap = "battery";
uint32_t features = AG_FEATURE_EC_ANDOR_NR |
AG_FEATURE_REJECT_A_CALL |
AG_FEATURE_ENHANCED_CALL_STATUS |
dbus_add_watch(NULL, CSD_CALL_PATH, CSD_CALL_INTERFACE, NULL);
dbus_add_watch(NULL, CSD_CALL_PATH, CSD_CALL_INSTANCE, NULL);
dbus_add_watch(NULL, CSD_CALL_PATH, CSD_CALL_CONFERENCE, NULL);
- dbus_add_watch(NULL, CSD_CSNET_PATH, CSD_CSNET_REGISTRATION, "RegistrationChanged");
- dbus_add_watch(NULL, CSD_CSNET_PATH, CSD_CSNET_OPERATOR, "OperatorNameChanged");
- dbus_add_watch(NULL, CSD_CSNET_PATH, CSD_CSNET_SIGNAL, "SignalBarsChanged");
- dbus_add_watch(NULL, CSD_CSNET_PATH, CSD_TELEPHONE_BATTERY, "BatteryBarsChanged");
- dbus_add_watch(NULL, CSD_CSNET_PATH, CSD_CSNET_SUBSCRIBER, "SubscriberNumberChanged");
+ dbus_add_watch(NULL, CSD_CSNET_PATH, CSD_CSNET_REGISTRATION,
+ "RegistrationChanged");
+ dbus_add_watch(NULL, CSD_CSNET_PATH, CSD_CSNET_OPERATOR,
+ "OperatorNameChanged");
+ dbus_add_watch(NULL, CSD_CSNET_PATH, CSD_CSNET_SIGNAL,
+ "SignalBarsChanged");
+ dbus_add_watch(NULL, CSD_CSNET_PATH, CSD_TELEPHONE_BATTERY,
+ "BatteryBarsChanged");
+ dbus_add_watch(NULL, CSD_CSNET_PATH, CSD_CSNET_SUBSCRIBER,
+ "SubscriberNumberChanged");
/* Reset indicators */
for (i = 0; telephony_ag_indicators[i].desc != NULL; i++) {
dbus_message_unref(reply);
}
-void telephony_last_dial_number_req(void *telephony_device )
-{
- int ret;
-
- ret = dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
- CSD_CALL_INTERFACE, "DialLastNo",
- telephony_dial_number_reply, telephony_device,
- DBUS_TYPE_INVALID);
- if (ret < 0) {
- telephony_dial_number_rsp(telephony_device,
- CME_ERROR_AG_FAILURE);
- }
-}
void telephony_dial_number_req(void *telephony_device, const char *number)
{
uint32_t flags = callerid;
- int ret;
if (strncmp(number, "*31#", 4) == 0) {
number += 4;
} else if (number[0] == '>') {
int location = strtol(&number[1], NULL, 0);
- ret = dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ if (0 != dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
CSD_CALL_INTERFACE, "DialMemory",
telephony_dial_number_reply, telephony_device,
DBUS_TYPE_INT32, &location,
- DBUS_TYPE_INVALID);
- if (ret < 0) {
+ DBUS_TYPE_INVALID)) {
telephony_dial_number_rsp(telephony_device,
CME_ERROR_AG_FAILURE);
}
return;
}
- ret = dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ if (0 != dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
CSD_CALL_INTERFACE, "DialNo",
NULL, NULL,
DBUS_TYPE_STRING, &number,
DBUS_TYPE_UINT32, &flags,
- DBUS_TYPE_INVALID);
- if (ret < 0) {
+ DBUS_TYPE_INVALID)) {
telephony_dial_number_rsp(telephony_device,
CME_ERROR_AG_FAILURE);
return;
else
telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
}
+
void telephony_last_dialed_number_req(void *telephony_device)
{
- telephony_last_dial_number_req(telephony_device);
+ if (0 != dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ CSD_CALL_INTERFACE, "DialLastNo",
+ telephony_dial_number_reply, telephony_device,
+ DBUS_TYPE_INVALID)) {
+ telephony_dial_number_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ }
}
+
void telephony_transmit_dtmf_req(void *telephony_device, char tone)
{
- int ret;
char buf[2] = { tone, '\0' }, *buf_ptr = buf;
- ret = dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
+ if (0 != dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
CSD_CALL_INTERFACE, "SendDtmf",
NULL, NULL,
DBUS_TYPE_STRING, &buf_ptr,
- DBUS_TYPE_INVALID);
- if (ret < 0) {
+ DBUS_TYPE_INVALID)) {
telephony_transmit_dtmf_rsp(telephony_device,
CME_ERROR_AG_FAILURE);
return;
void telephony_list_phonebook_store(void *telephony_device)
{
- /*
- For Blue & Me car kit we may have to add the patch here( similar to the patch done in H1 and H2 )
- */
- telephony_list_phonebook_store_rsp(telephony_device, PHONEBOOK_STORE_LIST,CME_ERROR_NONE);
+/*
+ For Blue & Me car kit we may have to add the
+ patch here(similar to the patch done in H1 and H2 )
+*/
+ telephony_list_phonebook_store_rsp(telephony_device,
+ PHONEBOOK_STORE_LIST, CME_ERROR_NONE);
}
-static int get_phonebook_count(const char *path, uint32_t *max_size, uint32_t *used)
+static int get_phonebook_count(const char *path, uint32_t *max_size,
+ uint32_t *used)
{
DBusConnection *conn;
DBusMessageIter iter;
"GetCallLogSize");
if (!message) {
DBG("Can't allocate new message");
+ dbus_connection_unref(conn);
return -1;
}
dbus_message_iter_init_append(message, &iter);
} else {
DBG("Failed to get contacts");
}
+ dbus_message_unref(message);
+ dbus_connection_unref(conn);
return -1;
}
if (!dbus_message_get_args(reply, &error,
DBUS_TYPE_UINT32, used,
- DBUS_TYPE_INVALID))
- {
- DBG("Can't get reply arguments\n");
- if (dbus_error_is_set(&error)) {
- DBG("%s\n", error.message);
- dbus_error_free(&error);
- }
- return -1;
+ DBUS_TYPE_INVALID)) {
+ DBG("Can't get reply arguments\n");
+ if (dbus_error_is_set(&error)) {
+ DBG("%s\n", error.message);
+ dbus_error_free(&error);
+ }
+ dbus_message_unref(reply);
+ dbus_message_unref(message);
+ dbus_connection_unref(conn);
+ return -1;
}
- if( (g_strcmp0(path, "SM") == 0 ) || (g_strcmp0(path, "ME") == 0)) {
+ if ((g_strcmp0(path, "SM") == 0) || (g_strcmp0(path, "ME") == 0)) {
*max_size = PHONEBOOK_COUNT_MAX;
-
- if( *used > PHONEBOOK_COUNT_MAX)
+
+ if (*used > PHONEBOOK_COUNT_MAX)
*used = PHONEBOOK_COUNT_MAX;
}
- if( (g_strcmp0(path, "DC") == 0) || (g_strcmp0(path, "MC") == 0) || (g_strcmp0(path, "RC") == 0) ) {
+ if ((g_strcmp0(path, "DC") == 0) || (g_strcmp0(path, "MC") == 0) ||
+ (g_strcmp0(path, "RC") == 0)) {
*max_size = CALL_LOG_COUNT_MAX;
- if( *used > CALL_LOG_COUNT_MAX)
+ if (*used > CALL_LOG_COUNT_MAX)
*used = CALL_LOG_COUNT_MAX;
}
-
- dbus_message_unref(message);
+
dbus_message_unref(reply);
+ dbus_message_unref(message);
dbus_connection_unref(conn);
return 0;
void telephony_read_phonebook_store(void *telephony_device)
{
- if( '\0' != ag_pb_info.path[0] ) {
- /* Check the phone path ag_pb_info.path[] to which path it was set. If the path is NULL then set to "SM"
- and get the max_size and used counts from the phonebook type through dbus call to pbap agent
- */
- if(!get_phonebook_count(ag_pb_info.path, &ag_pb_info.max_size, &ag_pb_info.used) ) {
- telephony_read_phonebook_store_rsp(telephony_device, ag_pb_info.path,
- ag_pb_info.max_size, ag_pb_info.used,CME_ERROR_NONE);
- } else {
- telephony_read_phonebook_store_rsp(telephony_device, ag_pb_info.path,
- ag_pb_info.max_size, ag_pb_info.used,CME_ERROR_AG_FAILURE);
- }
-
+ if ('\0' != ag_pb_info.path[0]) {
+/*
+ Check the phone path ag_pb_info.path[] to which path it was set.
+ If the path is NULL then set to "SM" and get the max_size and used
+ counts from the phonebook type through dbus call to pbap agent
+*/
+ if (!get_phonebook_count(ag_pb_info.path, &ag_pb_info.max_size,
+ &ag_pb_info.used))
+ telephony_read_phonebook_store_rsp(telephony_device,
+ ag_pb_info.path,
+ ag_pb_info.max_size,
+ ag_pb_info.used,
+ CME_ERROR_NONE);
+ else
+ telephony_read_phonebook_store_rsp(telephony_device,
+ ag_pb_info.path,
+ ag_pb_info.max_size,
+ ag_pb_info.used,
+ CME_ERROR_AG_FAILURE);
}
}
void telephony_set_phonebook_store(void *telephony_device, const char *path)
{
- if ( NULL != path ) {
- DBG("set phonebook type to [%s]\n",path);
- strncpy(ag_pb_info.path, path, 4 );
+ if (NULL != path) {
+ DBG("set phonebook type to [%s]\n", path);
+ g_strlcpy(ag_pb_info.path, path, sizeof(ag_pb_info.path));
}
}
{
uint32_t total_count = 0;
uint32_t used_count = 0;
-
- if( '\0' != ag_pb_info.path[0] ) {
- /* Check the phone path ag_pb_info.path[] to which path it was set. If the path is NULL then set to "SM"
- and get the max_size and used counts from the phonebook type through dbus call to pbap agent
- */
- telephony_read_phonebook_attributes_rsp(telephony_device,
- ag_pb_info.max_size, PHONEBOOK_NUMBER_MAX_LENGTH,
- PHONEBOOK_NAME_MAX_LENGTH, CME_ERROR_NONE);
+
+ if ('\0' != ag_pb_info.path[0]) {
+/*
+ Check the phone path ag_pb_info.path[] to which path it was set.
+ If the path is NULL then set to "SM" and get the max_size and used
+ counts from the phonebook type through dbus call to pbap agent
+*/
+ telephony_read_phonebook_attributes_rsp(telephony_device,
+ ag_pb_info.max_size, PHONEBOOK_NUMBER_MAX_LENGTH,
+ PHONEBOOK_NAME_MAX_LENGTH, CME_ERROR_NONE);
}
}
{
uint32_t i = 0;
- if( ascii == 0xC3 )
- {
- for(i =0; i< GSM_UNI_MAX_C3 ; i++ )
- {
- if( gsm_unicode_C3[i].utf_8 == utf_8 )
- {
+ if (ascii == 0xC3) {
+ for (i = 0; i < GSM_UNI_MAX_C3 ; i++) {
+ if (gsm_unicode_C3[i].utf_8 == utf_8) {
*gsm = gsm_unicode_C3[i].gsm;
return 0;
}
}
- }
- else if (ascii == 0xCE)
- {
- for(i =0; i< GSM_UNI_MAX_CE ; i++ )
- {
- if( gsm_unicode_CE[i].utf_8 == utf_8 )
- {
+ } else if (ascii == 0xCE) {
+ for (i = 0; i < GSM_UNI_MAX_CE ; i++) {
+ if (gsm_unicode_CE[i].utf_8 == utf_8) {
*gsm = gsm_unicode_CE[i].gsm;
return 0;
}
}
}
-static void get_unicode_string( const char *name, char *unicodename)
+static void get_unicode_string(const char *name, char *unicodename)
{
- if( NULL != name || NULL!= unicodename)
- {
- uint32_t len = strlen(name);
- if(len > 0 ) {
+ if (NULL != name || NULL != unicodename) {
+ int len = strlen(name);
+ if (len > 0) {
int x = 0;
- int y =0;
- if( len > 20 )
- len = 20;
- for( x =0, y = 0 ; x < len ; x++, y++ ) {
- if(x < (len-1)){
- if( convert_utf8_gsm(name[x],name[x+1] , (uint8_t *)&unicodename[y]) )
- {
+ int y = 0;
+ if (len > PHONEBOOK_MAX_CHARACTER_LENGTH)
+ len = PHONEBOOK_MAX_CHARACTER_LENGTH;
+ for (x = 0, y = 0 ; x < len ; x++, y++) {
+ if (x < (len - 1)) {
+ if (convert_utf8_gsm(name[x], name[x+1] ,
+ (uint8_t *)&unicodename[y])) {
x++;
continue;
}
}
-
- if( name[x] == '_' )
- {
+
+ if (name[x] == '_') {
unicodename[y] = ' ';
continue;
}
- unicodename[y] = name[x];
+ unicodename[y] = name[x];
}
}
}
- return;
+ return;
}
-static int send_read_phonebook_resp(void *telephony_device, int32_t index, const char *name, const char *number )
+static int send_read_phonebook_resp(void *telephony_device, int32_t index,
+ const char *name, const char *number)
{
gchar *msg = NULL;
int ret = -1;
- msg = g_new0(gchar, PHONEBOOK_NAME_MAX_LENGTH + PHONEBOOK_NUMBER_MAX_LENGTH + PHONEBOOK_READ_RESP_LENGTH + 3);
+ msg = g_new0(gchar, PHONEBOOK_NAME_MAX_LENGTH +
+ PHONEBOOK_NUMBER_MAX_LENGTH + PHONEBOOK_READ_RESP_LENGTH + 3);
- if( NULL != msg )
- {
- char nm[PHONEBOOK_NAME_MAX_LENGTH +1] = {0,};
- char nb[PHONEBOOK_NAME_MAX_LENGTH +1] = {0,};
+ if (NULL != msg) {
+ char nm[PHONEBOOK_NAME_MAX_LENGTH + 1] = {0,};
+ char nb[PHONEBOOK_NAME_MAX_LENGTH + 1] = {0,};
- get_unicode_string(name,nm);
- get_unicode_string(number,nb);
+ get_unicode_string(name, nm);
+ get_unicode_string(number, nb);
- snprintf(msg, PHONEBOOK_NAME_MAX_LENGTH + PHONEBOOK_NUMBER_MAX_LENGTH + PHONEBOOK_READ_RESP_LENGTH + 3,
- "%d,\"%s\",0,\"%s\"",index, nb,nm);
+ snprintf(msg, PHONEBOOK_NAME_MAX_LENGTH +
+ PHONEBOOK_NUMBER_MAX_LENGTH + PHONEBOOK_READ_RESP_LENGTH + 3,
+ "%d,\"%s\",0,\"%s\"", index, nb, nm);
- ret = telephony_read_phonebook_rsp(telephony_device,msg,CME_ERROR_NONE);
+ ret = telephony_read_phonebook_rsp(telephony_device, msg,
+ CME_ERROR_NONE);
g_free(msg);
}
return ret;
}
-static int get_phonebook_list(void *telephony_device, const char* path, int32_t start_index, int32_t end_index )
+static int get_phonebook_list(void *telephony_device, const char* path,
+ int32_t start_index, int32_t end_index)
{
DBusConnection *conn;
DBusMessage *message, *reply;
DBusError error;
DBusMessageIter iter, iter_struct, entry;
- int32_t idx =0;
- if( (start_index > ag_pb_info.max_size) || (start_index <=0) || (start_index > PHONEBOOK_COUNT_MAX) ) {
+ int32_t idx = 0;
+ if ((start_index > (int) ag_pb_info.max_size) || (start_index <= 0) ||
+ (start_index > PHONEBOOK_COUNT_MAX)) {
return -1;
}
- if(end_index > ag_pb_info.max_size )
+ if (end_index > (int) ag_pb_info.max_size)
end_index = PHONEBOOK_COUNT_MAX ;
-
conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
if (!conn) {
DBG("Can't get on system bus");
}
message = dbus_message_new_method_call("org.bluez.pb_agent",
- "/org/bluez/pb_agent",
- "org.bluez.PbAgent",
- "GetPhonebookList");
+ "/org/bluez/pb_agent",
+ "org.bluez.PbAgent",
+ "GetPhonebookList");
if (!message) {
DBG("Can't allocate new message");
return -1;
idx = start_index;
while (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_STRUCT) {
- dbus_message_iter_recurse(&iter_struct, &entry);
const char *name = NULL;
const char *tel = NULL;
uint32_t handle = 0;
-
+
+ dbus_message_iter_recurse(&iter_struct, &entry);
+
dbus_message_iter_get_basic(&entry, &name);
dbus_message_iter_next(&entry);
dbus_message_iter_get_basic(&entry, &tel);
DBG("[%d] handle:%d name:%s tel:%s]\n", handle, name, tel);
/*form the packet and sent to the remote headset*/
- if( -1 == end_index ) {
- if(send_read_phonebook_resp(telephony_device, start_index, name, tel ) )
+ if (-1 == end_index) {
+ if (send_read_phonebook_resp(telephony_device,
+ start_index, name, tel))
DBG("send_read_phonebook_resp - ERROR\n");
break;
- }
- else
- {
- if( idx >= start_index || idx <= end_index )
- {
- if(send_read_phonebook_resp(telephony_device, idx, name, tel ) ) {
+ } else {
+ if (idx >= start_index || idx <= end_index) {
+ if (send_read_phonebook_resp(telephony_device, idx, name, tel)) {
DBG("send_read_phonebook_resp - ERROR\n");
- telephony_read_phonebook_rsp(telephony_device,NULL, CME_ERROR_AG_FAILURE);
+ telephony_read_phonebook_rsp(telephony_device, NULL,
+ CME_ERROR_AG_FAILURE);
dbus_message_unref(message);
dbus_message_unref(reply);
dbus_message_iter_next(&iter_struct);
}
- telephony_read_phonebook_rsp(telephony_device,NULL, CME_ERROR_NONE);
+ telephony_read_phonebook_rsp(telephony_device, NULL, CME_ERROR_NONE);
dbus_message_unref(message);
dbus_message_unref(reply);
return 0;
}
-static int get_call_log_list(void *telephony_device, char* path , int32_t start_index, int32_t end_index )
+static int get_call_log_list(void *telephony_device, char* path ,
+ int32_t start_index, int32_t end_index)
{
DBusConnection *conn;
DBusMessage *message = NULL, *reply;
DBusError error;
DBusMessageIter iter, iter_struct, entry;
- int32_t idx =0;
+ int32_t idx = 0;
- if( (start_index > ag_pb_info.max_size) || (start_index <=0) || (start_index > CALL_LOG_COUNT_MAX) ) {
+ if ((start_index > (int) ag_pb_info.max_size) || (start_index <= 0) ||
+ (start_index > CALL_LOG_COUNT_MAX)) {
return -1;
}
- if(end_index > ag_pb_info.max_size )
+ if (end_index > (int) ag_pb_info.max_size)
end_index = CALL_LOG_COUNT_MAX ;
return -1;
}
- if( g_strcmp0(ag_pb_info.path, "DC") == 0) {
+ if (g_strcmp0(ag_pb_info.path, "DC") == 0) {
message = dbus_message_new_method_call("org.bluez.pb_agent",
- "/org/bluez/pb_agent",
- "org.bluez.PbAgent",
- "GetOutgoingCallsList");
- }else if(g_strcmp0(ag_pb_info.path, "MC") == 0) {
+ "/org/bluez/pb_agent",
+ "org.bluez.PbAgent",
+ "GetOutgoingCallsList");
+ } else if (g_strcmp0(ag_pb_info.path, "MC") == 0) {
message = dbus_message_new_method_call("org.bluez.pb_agent",
- "/org/bluez/pb_agent",
- "org.bluez.PbAgent",
- "GetMissedCallsList");
- }else if(g_strcmp0(ag_pb_info.path, "RC") == 0) {
+ "/org/bluez/pb_agent",
+ "org.bluez.PbAgent",
+ "GetMissedCallsList");
+ } else if (g_strcmp0(ag_pb_info.path, "RC") == 0) {
message = dbus_message_new_method_call("org.bluez.pb_agent",
- "/org/bluez/pb_agent",
- "org.bluez.PbAgent",
- "GetIncomingCallsList");
+ "/org/bluez/pb_agent",
+ "org.bluez.PbAgent",
+ "GetIncomingCallsList");
}
if (!message) {
DBG("Can't allocate new message");
idx = start_index;
while (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_STRUCT) {
- dbus_message_iter_recurse(&iter_struct, &entry);
const char *name = NULL;
const char *tel = NULL;
uint32_t handle = 0;
+ dbus_message_iter_recurse(&iter_struct, &entry);
+
dbus_message_iter_get_basic(&entry, &name);
dbus_message_iter_next(&entry);
dbus_message_iter_get_basic(&entry, &tel);
DBG("[%d] handle:%d name:%s tel:%s]\n", handle, name, tel);
/*form the packet and sent to the remote headset*/
- if( -1 == end_index ) {
- if(send_read_phonebook_resp(telephony_device, start_index, name, tel ) )
+ if (-1 == end_index) {
+ if (send_read_phonebook_resp(telephony_device,
+ start_index, name, tel))
DBG("send_read_phonebook_resp - ERROR\n");
break;
- }
- else
- {
- if( idx >= start_index || idx <= end_index )
- {
+ } else {
+ if (idx >= start_index || idx <= end_index) {
/* Need to form the time stamp pkt also */
- if(send_read_phonebook_resp(telephony_device, idx, name, tel ) ) {
+ if (send_read_phonebook_resp(telephony_device, idx, name, tel)) {
DBG("send_read_phonebook_resp - ERROR\n");
- telephony_read_phonebook_rsp(telephony_device,NULL, CME_ERROR_AG_FAILURE);
+ telephony_read_phonebook_rsp(telephony_device, NULL,
+ CME_ERROR_AG_FAILURE);
dbus_message_unref(message);
dbus_message_unref(reply);
dbus_message_iter_next(&iter_struct);
}
- telephony_read_phonebook_rsp(telephony_device,NULL, CME_ERROR_NONE);
+ telephony_read_phonebook_rsp(telephony_device, NULL, CME_ERROR_NONE);
dbus_message_unref(message);
dbus_message_unref(reply);
{
char *ptr = 0;
- if( NULL != cmd ) {
+ if (NULL != cmd) {
int32_t start_index;
int32_t end_index;
- ptr = (char*) strchr(cmd,(int32_t)',');
- if( NULL == ptr ) {
+ ptr = (char *) strchr(cmd, (int32_t)',');
+ if (NULL == ptr) {
start_index = strtol(cmd, NULL, 0);
end_index = -1;
- DBG("telephony_read_phonebook:start_index = [%d] \n", start_index);
+ DBG("start_index = [%d] \n", start_index);
} else {
ptr++;
start_index = strtol(cmd, NULL, 0);
end_index = strtol(ptr, NULL, 0);
- DBG("telephony_read_phonebook:start_index = [%d], end_index = [%d] \n", start_index,end_index);
+ DBG("start_index = [%d], end_index = [%d] \n",
+ start_index, end_index);
}
- if( (g_strcmp0(ag_pb_info.path, "SM") == 0 ) || (g_strcmp0(ag_pb_info.path, "ME") == 0 )) {
- if( get_phonebook_list(telephony_device, ag_pb_info.path, start_index, end_index) ) {
- telephony_read_phonebook_rsp(telephony_device,NULL,CME_ERROR_AG_FAILURE);
+ if ((g_strcmp0(ag_pb_info.path, "SM") == 0) ||
+ (g_strcmp0(ag_pb_info.path, "ME") == 0)) {
+ if (get_phonebook_list(telephony_device, ag_pb_info.path,
+ start_index, end_index)) {
+ telephony_read_phonebook_rsp(telephony_device, NULL,
+ CME_ERROR_AG_FAILURE);
return;
}
}
- if( (g_strcmp0(ag_pb_info.path, "DC") == 0 ) || (g_strcmp0(ag_pb_info.path, "MC") == 0 ) ||
- (g_strcmp0(ag_pb_info.path, "RC") == 0) ) {
- if(get_call_log_list(telephony_device,ag_pb_info.path, start_index, end_index) ) {
- telephony_read_phonebook_rsp(telephony_device,NULL,CME_ERROR_AG_FAILURE);
+ if ((g_strcmp0(ag_pb_info.path, "DC") == 0) ||
+ (g_strcmp0(ag_pb_info.path, "MC") == 0) ||
+ (g_strcmp0(ag_pb_info.path, "RC") == 0)) {
+ if (get_call_log_list(telephony_device, ag_pb_info.path,
+ start_index, end_index)) {
+ telephony_read_phonebook_rsp(telephony_device, NULL,
+ CME_ERROR_AG_FAILURE);
return;
}
}
-
- /*
- Using the start and end index get the contact list from the pbap agent and send the data
- to remote headset.
- */
+
+/*
+ Using the start and end index get the contact list from the pbap agent and
+ send the data to remote headset.
+*/
}
}
void telephony_find_phonebook_entry_properties(void *telephony_device)
{
- telephony_find_phonebook_entry_properties_rsp( telephony_device,
- 20,
- 128,
- CME_ERROR_NONE );
+ telephony_find_phonebook_entry_properties_rsp(telephony_device,
+ PHONEBOOK_NUMBER_MAX_LENGTH,
+ PHONEBOOK_NAME_MAX_LENGTH,
+ CME_ERROR_NONE);
}
void telephony_find_phonebook_entry(void *telephony_device, const char *cmd)
{
- /* Get the contact that matches with the string "cmd" and send it back to the remote headset
- Need a dbus API to pbap agent that does the above operation
- */
+/*
+ Get the contact that matches with the string "cmd" and send it back to the
+ remote headset Need a dbus API to pbap agent that does the above operation
+*/
}
void telephony_get_preffered_store_capacity(void *telephony_device)
{
- telephony_get_preffered_store_capacity_rsp( telephony_device,
- PREFFERED_MESSAGE_STORAGE_MAX,
- CME_ERROR_NONE );
+ telephony_get_preffered_store_capacity_rsp(telephony_device,
+ PREFFERED_MESSAGE_STORAGE_MAX,
+ CME_ERROR_NONE);
}
void telephony_list_preffered_store(void *telephony_device)
{
- telephony_list_preffered_store_rsp( telephony_device,
- PREFFERED_MESSAGE_STORAGE_LIST,
- CME_ERROR_NONE );
+ telephony_list_preffered_store_rsp(telephony_device,
+ PREFFERED_MESSAGE_STORAGE_LIST,
+ CME_ERROR_NONE);
}
/*
*/
void telephony_get_character_set(void *telephony_device)
{
- telephony_supported_character_generic_rsp( telephony_device,
- PHONEBOOK_CHARACTER_SET_SUPPORTED,
- CME_ERROR_NONE );
+ telephony_supported_character_generic_rsp(telephony_device,
+ PHONEBOOK_CHARACTER_SET_SUPPORTED,
+ CME_ERROR_NONE);
}
void telephony_list_supported_character(void *telephony_device)
{
- telephony_supported_character_generic_rsp( telephony_device,
- PHONEBOOK_CHARACTER_SET_LIST,
- CME_ERROR_NONE );
+ telephony_supported_character_generic_rsp(telephony_device,
+ PHONEBOOK_CHARACTER_SET_LIST,
+ CME_ERROR_NONE);
}
/*
{
}
*/
-
owner = media_owner_create(conn, msg, accesstype);
id = transport->resume(transport, owner);
if (id == 0) {
+ media_transport_release(transport, accesstype);
media_owner_free(owner);
return btd_error_not_authorized(msg);
}
#include "source.h"
#include "gateway.h"
#include "unix.h"
-#include "glib-helper.h"
+#include "glib-compat.h"
#define check_nul(str) (str[sizeof(str) - 1] == '\0')
sk = socket(PF_LOCAL, SOCK_STREAM, 0);
if (sk < 0) {
- err = errno;
- error("Can't create unix socket: %s (%d)", strerror(err), err);
- return -err;
+ err = -errno;
+ error("Can't create unix socket: %s (%d)", strerror(-err),
+ -err);
+ return err;
}
if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- error("Can't bind unix socket: %s (%d)", strerror(errno),
- errno);
+ err = -errno;
+ error("Can't bind unix socket: %s (%d)", strerror(-err),
+ -err);
close(sk);
- return -1;
+ return err;
}
set_nonblocking(sk);
if (listen(sk, 1) < 0) {
- error("Can't listen on unix socket: %s (%d)",
- strerror(errno), errno);
+ err = -errno;
+ error("Can't listen on unix socket: %s (%d)", strerror(-err),
+ -err);
close(sk);
- return -1;
+ return err;
}
unix_sock = sk;
int master;
uint8_t mode;
int flushable;
+ uint32_t priority;
};
struct connect {
return FALSE;
if (cond & G_IO_OUT) {
- int err = 0, sock = g_io_channel_unix_get_fd(io);
- socklen_t len = sizeof(err);
+ int err, sk_err = 0, sock = g_io_channel_unix_get_fd(io);
+ socklen_t len = sizeof(sk_err);
- if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
- err = errno;
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+ err = -errno;
+ else
+ err = -sk_err;
- if (err)
+ if (err < 0)
g_set_error(&gerr, BT_IO_ERROR,
BT_IO_ERROR_CONNECT_FAILED, "%s (%d)",
- strerror(err), err);
+ strerror(-err), -err);
} else if (cond & (G_IO_HUP | G_IO_ERR))
g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED,
"HUP or ERR on socket");
return 0;
}
+static int set_priority(int sock, uint32_t prio)
+{
+ if (setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0)
+ return -errno;
+
+ return 0;
+}
+
static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu,
uint16_t omtu, uint8_t mode, int master,
- int flushable, GError **err)
+ int flushable, uint32_t priority, GError **err)
{
if (imtu || omtu || mode) {
struct l2cap_options l2o;
return FALSE;
}
+ if (priority > 0 && set_priority(sock, priority) < 0) {
+ ERROR_FAILED(err, "set_priority", errno);
+ return FALSE;
+ }
+
if (sec_level && !set_sec_level(sock, BT_IO_L2CAP, sec_level, err))
return FALSE;
opts->sec_level = BT_IO_SEC_MEDIUM;
opts->mode = L2CAP_MODE_BASIC;
opts->flushable = -1;
+ opts->priority = 0;
while (opt != BT_IO_OPT_INVALID) {
switch (opt) {
case BT_IO_OPT_FLUSHABLE:
opts->flushable = va_arg(args, gboolean);
break;
+ case BT_IO_OPT_PRIORITY:
+ opts->priority = va_arg(args, int);
+ break;
default:
g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
"Unknown option %d", opt);
return 0;
}
+static int get_priority(int sock, uint32_t *prio)
+{
+ socklen_t len;
+
+ len = sizeof(*prio);
+ if (getsockopt(sock, SOL_SOCKET, SO_PRIORITY, prio, &len) < 0)
+ return -errno;
+
+ return 0;
+}
+
static gboolean l2cap_get(int sock, GError **err, BtIOOption opt1,
va_list args)
{
uint16_t handle;
socklen_t len;
gboolean flushable = FALSE;
+ uint32_t priority;
len = sizeof(l2o);
memset(&l2o, 0, len);
break;
case BT_IO_OPT_PSM:
*(va_arg(args, uint16_t *)) = src.l2_psm ?
- src.l2_psm : dst.l2_psm;
+ btohs(src.l2_psm) : btohs(dst.l2_psm);
break;
case BT_IO_OPT_CID:
*(va_arg(args, uint16_t *)) = src.l2_cid ?
- src.l2_cid : dst.l2_cid;
+ btohs(src.l2_cid) : btohs(dst.l2_cid);
break;
case BT_IO_OPT_OMTU:
*(va_arg(args, uint16_t *)) = l2o.omtu;
}
*(va_arg(args, gboolean *)) = flushable;
break;
+ case BT_IO_OPT_PRIORITY:
+ if (get_priority(sock, &priority) < 0) {
+ ERROR_FAILED(err, "get_priority", errno);
+ return FALSE;
+ }
+ *(va_arg(args, uint32_t *)) = priority;
+ break;
default:
g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
"Unknown option %d", opt);
case BT_IO_L2RAW:
case BT_IO_L2CAP:
return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
- opts.mode, opts.master, opts.flushable, err);
+ opts.mode, opts.master, opts.flushable,
+ opts.priority, err);
case BT_IO_RFCOMM:
return rfcomm_set(sock, opts.sec_level, opts.master, err);
case BT_IO_SCO:
if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
opts->cid, err) < 0)
goto failed;
- if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, -1, err))
+ if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, -1, 0, err))
goto failed;
break;
case BT_IO_L2CAP:
opts->cid, err) < 0)
goto failed;
if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
- opts->mode, opts->master, opts->flushable, err))
+ opts->mode, opts->master, opts->flushable,
+ opts->priority, err))
goto failed;
break;
case BT_IO_RFCOMM:
BT_IO_OPT_CLASS,
BT_IO_OPT_MODE,
BT_IO_OPT_FLUSHABLE,
+ BT_IO_OPT_PRIORITY,
} BtIOOption;
typedef enum {
while ((de = readdir(dir)) != NULL) {
char link[PATH_MAX + 1];
- int len = readlink(de->d_name, link, sizeof(link));
+ int len = readlink(de->d_name, link, PATH_MAX);
if (len > 0) {
link[len] = 0;
if (strstr(link, dev)) {
__io_canceled = 1;
}
-static void send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
{
struct uinput_event event;
- int len;
if (fd <= fileno(stderr))
- return;
+ return -EINVAL;
memset(&event, 0, sizeof(event));
event.type = type;
event.code = code;
event.value = value;
- len = write(fd, &event, sizeof(event));
+ return write(fd, &event, sizeof(event));
}
static int uinput_create(char *name, int keyboard, int mouse)
for (aux = KEY_RESERVED; aux <= KEY_UNKNOWN; aux++)
ioctl(fd, UI_SET_KEYBIT, aux);
-
- //for (aux = LED_NUML; aux <= LED_MISC; aux++)
- // ioctl(fd, UI_SET_LEDBIT, aux);
+ /*
+ *for (aux = LED_NUML; aux <= LED_MISC; aux++)
+ * ioctl(fd, UI_SET_LEDBIT, aux);
+ */
}
if (mouse) {
--- /dev/null
+EPox Presenter
+==============
+
+# hcitool inq
+Inquiring ...
+ 00:04:61:aa:bb:cc clock offset: 0x1ded class: 0x004000
+
+# hcitool info 00:04:61:aa:bb:cc
+Requesting information ...
+ BD Address: 00:04:61:aa:bb:cc
+ OUI Company: EPOX Computer Co., Ltd. (00-04-61)
+ Device Name: EPox BT-PM01B aabbcc
+ LMP Version: 1.1 (0x1) LMP Subversion: 0xf78
+ Manufacturer: Cambridge Silicon Radio (10)
+ Features: 0xff 0xff 0x0f 0x00 0x00 0x00 0x00 0x00
+ <3-slot packets> <5-slot packets> <encryption> <slot offset>
+ <timing accuracy> <role switch> <hold mode> <sniff mode>
+ <park state> <RSSI> <channel quality> <SCO link> <HV2 packets>
+ <HV3 packets> <u-law log> <A-law log> <CVSD> <paging scheme>
+ <power control> <transparent SCO>
+
+# sdptool records --raw 00:04:61:aa:bb:cc
+Sequence
+ Attribute 0x0000 - ServiceRecordHandle
+ UINT32 0x00010000
+ Attribute 0x0001 - ServiceClassIDList
+ Sequence
+ UUID16 0x1101 - SerialPort
+ Attribute 0x0004 - ProtocolDescriptorList
+ Sequence
+ Sequence
+ UUID16 0x0100 - L2CAP
+ Sequence
+ UUID16 0x0003 - RFCOMM
+ UINT8 0x01
+ Attribute 0x0100
+ String Cable Replacement
+
+
+J-Three Keyboard
+================
+
+# hcitool inq
+Inquiring ...
+ 00:0A:3A:aa:bb:cc clock offset: 0x3039 class: 0x001f00
+
+# hcitool info 00:0A:3A:aa:bb:cc
+Password:
+Requesting information ...
+ BD Address: 00:0A:3A:aa:bb:cc
+ OUI Company: J-THREE INTERNATIONAL Holding Co., Ltd. (00-0A-3A)
+ Device Name: KEYBOARD
+ LMP Version: 1.1 (0x1) LMP Subversion: 0x2c2
+ Manufacturer: Cambridge Silicon Radio (10)
+ Features: 0xbc 0x06 0x07 0x00 0x00 0x00 0x00 0x00
+ <encryption> <slot offset> <timing accuracy> <role switch>
+ <sniff mode> <RSSI> <channel quality> <CVSD> <paging scheme>
+ <power control>
+
+# sdptool records --raw 00:0A:3A:aa:bb:cc
+Sequence
+ Attribute 0x0000 - ServiceRecordHandle
+ UINT32 0x00010000
+ Attribute 0x0001 - ServiceClassIDList
+ Sequence
+ UUID16 0x1101 - SerialPort
+ Attribute 0x0004 - ProtocolDescriptorList
+ Sequence
+ Sequence
+ UUID16 0x0100 - L2CAP
+ Sequence
+ UUID16 0x0003 - RFCOMM
+ UINT8 0x01
+ Attribute 0x0006 - LanguageBaseAttributeIDList
+ Sequence
+ UINT16 0x656e
+ UINT16 0x006a
+ UINT16 0x0100
+ Attribute 0x0100
+ String SPP slave
+
+
+Celluon Laserkey Keyboard
+=========================
+
+# hcitool inq
+Inquiring ...
+ 00:0B:24:aa:bb:cc clock offset: 0x3ab6 class: 0x400210
+
+# hcitool info 00:0B:24:aa:bb:cc
+Requesting information ...
+ BD Address: 00:0B:24:aa:bb:cc
+ OUI Company: AirLogic (00-0B-24)
+ Device Name: CL800BT
+ LMP Version: 1.1 (0x1) LMP Subversion: 0x291
+ Manufacturer: Cambridge Silicon Radio (10)
+ Features: 0xff 0xff 0x0f 0x00 0x00 0x00 0x00 0x00
+ <3-slot packets> <5-slot packets> <encryption> <slot offset>
+ <timing accuracy> <role switch> <hold mode> <sniff mode>
+ <park state> <RSSI> <channel quality> <SCO link> <HV2 packets>
+ <HV3 packets> <u-law log> <A-law log> <CVSD> <paging scheme>
+ <power control> <transparent SCO>
+
+# sdptool records --raw 00:0B:24:aa:bb:cc
+Sequence
+ Attribute 0x0000 - ServiceRecordHandle
+ UINT32 0x00010000
+ Attribute 0x0001 - ServiceClassIDList
+ Sequence
+ UUID16 0x1101 - SerialPort
+ Attribute 0x0004 - ProtocolDescriptorList
+ Sequence
+ Sequence
+ UUID16 0x0100 - L2CAP
+ Sequence
+ UUID16 0x0003 - RFCOMM
+ UINT8 0x01
+ Attribute 0x0100
+ String Serial Port
+
+Packet format is as follows (all fields little-endian):
+ 0 uint16 magic # 0x5a5a
+ 2 uint32 unknown # ???
+ 6 uint8 action # 0 = keyup, 1 = keydown, 2 = repeat
+ # 3, 4, 5, 6 = ??? (Mouse mode)
+ 7 uint8 unknown[9] # ???
+ 16 uint8 action2 # ??? same as action
+ 17 uint16 x # Horizontal coordinate
+ 19 uint16 y # Vertical coordinate
+ 21 uint16 time # Some sort of timestamp
+ 23 uint8 unknown[5] # ???
+ 28 uint8 key[] # single byte keycode or 0xff byte
+ # follwed by special keycode byte.
+ Each packet followed by a checksum byte.
return err;
}
-static void enable_sixaxis(int csk)
-{
- const unsigned char buf[] = {
- 0x53 /*HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE*/,
- 0xf4, 0x42, 0x03, 0x00, 0x00 };
- int err;
-
- err = write(csk, buf, sizeof(buf));
-}
-
static int create_device(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout)
{
struct hidp_connadd_req req;
req.flags |= (1 << HIDP_BOOT_PROTOCOL_MODE);
}
- if (req.vendor == 0x054c && req.product == 0x0268)
- enable_sixaxis(csk);
-
err = ioctl(ctl, HIDPCONNADD, &req);
error:
- if (req.rd_data)
- free(req.rd_data);
+ free(req.rd_data);
return err;
}
AC_PREREQ(2.60)
-AC_INIT(bluez, 4.90)
+AC_INIT(bluez, 4.98)
-AM_INIT_AUTOMAKE([foreign subdir-objects])
+AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
AM_CONFIG_HEADER(config.h)
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_CHECK_LIB(dl, dlopen, dummy=yes,
AC_MSG_ERROR(dynamic linking loader is required))
+AC_CHECK_HEADER([sys/inotify.h],
+ [AC_DEFINE([HAVE_SYS_INOTIFY_H], 1,
+ [Define to 1 if you have <sys/inotify.h>.])],
+ [AC_MSG_ERROR(inotify headers are required and missing)])
AC_PATH_DBUS
AC_PATH_GLIB
AC_PATH_ALSA
AC_PATH_GSTREAMER
AC_PATH_USB
+AC_PATH_UDEV
AC_PATH_SNDFILE
AC_PATH_OUI
AC_PATH_READLINE
+AC_PATH_CHECK
AC_ARG_BLUEZ
AC_DEFINE(HAVE_CAPNG, 1, [Define to 1 if you have capabilities library.])
fi
+AC_ARG_WITH([systemdunitdir], AC_HELP_STRING([--with-systemdunitdir=DIR],
+ [path to systemd system service directory]), [path_systemdunit=${withval}],
+ [path_systemdunit="`$PKG_CONFIG --variable=systemdsystemunitdir systemd`"])
+if (test -n "${path_systemdunit}"); then
+ SYSTEMD_UNITDIR="${path_systemdunit}"
+ AC_SUBST(SYSTEMD_UNITDIR)
+fi
+AM_CONDITIONAL(SYSTEMD, test -n "${path_systemdunit}")
+
AC_OUTPUT(Makefile scripts/bluetooth.rules doc/version.xml
- src/bluetoothd.8 bluez.pc)
+ src/bluetoothd.8 src/bluetooth.service bluez.pc)
memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
memcpy(buf + HCRP_PDU_HDR_SIZE, &cp, HCRP_CREDIT_GRANT_CP_SIZE);
len = write(sk, buf, HCRP_PDU_HDR_SIZE + HCRP_CREDIT_GRANT_CP_SIZE);
+ if (len < 0)
+ return len;
len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ return len;
+
memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_GRANT_RP_SIZE);
hdr.plen = htons(0);
memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
len = write(sk, buf, HCRP_PDU_HDR_SIZE);
+ if (len < 0)
+ return len;
len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ return len;
+
memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_CREDIT_REQUEST_RP_SIZE);
hdr.plen = htons(0);
memcpy(buf, &hdr, HCRP_PDU_HDR_SIZE);
len = write(sk, buf, HCRP_PDU_HDR_SIZE);
+ if (len < 0)
+ return len;
len = read(sk, buf, sizeof(buf));
+ if (len < 0)
+ return len;
+
memcpy(&hdr, buf, HCRP_PDU_HDR_SIZE);
memcpy(&rp, buf + HCRP_PDU_HDR_SIZE, HCRP_GET_LPT_STATUS_RP_SIZE);
+bluez (4.98-slp2+1-1) unstable; urgency=low
+
+ * Merge with lastest private git
+ * Git: pkgs/b/bluez
+ * Tag: bluez_4.98-slp2+1-1
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Thu, 09 Feb 2012 14:31:53 +0900
+
bluez (4.90-slp2+18-2) unstable; urgency=low
* Change "SAMSUNG_PATCH" -> "TIZEN_PATCH"
dh_testdir
# Add here commands to configure the package.
- CFLAGS="$(CFLAGS) -D__TIZEN_PATCH__ $(BT_CHIP_CFLAGS)" \
+ CFLAGS="$(CFLAGS) -D__SAMSUNG_PATCH__ $(BT_CHIP_CFLAGS)" \
LDFLAGS="$(LDFLAGS) -Wl,--warn-unresolved-symbols" \
./configure --prefix=$(PREFIX) \
--sysconfdir=$(PREFIX)/etc \
--disable-test \
--enable-health \
--disable-udevrules \
+ --enable-dbusoob \
--with-telephony=tizen
build: build-stamp
void RequestSession()
- This method will request a client session that
- provides operational Bluetooth. A possible mode
- change must be confirmed by the user via the agent.
+ This method requests a client session that provides
+ operational Bluetooth. A possible mode change must be
+ confirmed by the user via the agent.
+
+ Clients may request multiple sessions. All sessions
+ are released when adapter's mode is changed to off
+ state.
Possible Errors: org.bluez.Error.Rejected
void ReleaseSession()
- Release a previous requested session.
+ Release a previously requested session. It sets
+ adapter to the mode in use on the moment of session
+ request.
+
+ SetProperty method call changes adapter's mode
+ persistently, such that session release will not
+ modify it.
Possible Errors: org.bluez.Error.DoesNotExist
boolean Powered [readwrite]
Switch an adapter on or off. This will also set the
- appropiate connectable state.
+ appropriate connectable state.
boolean Discoverable [readwrite]
this property will be updated via a PropertyChanged
signal.
- # __TIZEN_PATCH__
- boolean Limited [readwrite]
-
- Switch an adapter to limited discoverable or non-limited. This is
- a global setting and should only be used by the
- settings application.
- # __TIZEN_PATCH__
-
boolean Pairable [readwrite]
Switch an adapter to pairable or non-pairable. This is
Note that this property only affects incoming pairing
requests.
- uint32 PaireableTimeout [readwrite]
+ uint32 PairableTimeout [readwrite]
The pairable timeout in seconds. A value of zero
means that the timeout is disabled and it will stay in
During the pairing process this method might be
called multiple times to update the entered value.
+ Note that the passkey will always be a 6-digit number,
+ so the display should be zero-padded at the start if
+ the value contains less than 6 digits.
+
void RequestConfirmation(object device, uint32 passkey)
This method gets called when the service daemon
To confirm the value it should return an empty reply
or an error in case the passkey is invalid.
+ Note that the passkey will always be a 6-digit number,
+ so the display should be zero-padded at the start if
+ the value contains less than 6 digits.
+
Possible errors: org.bluez.Error.Rejected
org.bluez.Error.Canceled
Local services are children of the adapter object path. Remote services
are children of the remote device object path. This doesn't solve the
-problem where local atttributes can have different instances based on
+problem where local attributes can have different instances based on
the remote device.
In general the idea is to also represent SDP records as services so that
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+ <bookinfo>
+ <title>BlueZ Reference Manual</title>
+ <releaseinfo>Version &version;</releaseinfo>
+ <authorgroup>
+ <author>
+ <firstname>Marcel</firstname>
+ <surname>Holtmann</surname>
+ <affiliation>
+ <address>
+ <email>marcel@holtmann.org</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2002-2008</year>
+ <holder>Marcel Holtmann</holder>
+ </copyright>
+
+ <legalnotice>
+ <para>
+ Permission is granted to copy, distribute and/or modify this
+ document under the terms of the <citetitle>GNU Free
+ Documentation License</citetitle>, Version 1.1 or any later
+ version published by the Free Software Foundation with no
+ Invariant Sections, no Front-Cover Texts, and no Back-Cover
+ Texts. You may obtain a copy of the <citetitle>GNU Free
+ Documentation License</citetitle> from the Free Software
+ Foundation by visiting <ulink type="http"
+ url="http://www.fsf.org">their Web site</ulink> or by writing
+ to:
+
+ <address>
+ The Free Software Foundation, Inc.,
+ <street>59 Temple Place</street> - Suite 330,
+ <city>Boston</city>, <state>MA</state> <postcode>02111-1307</postcode>,
+ <country>USA</country>
+ </address>
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+ <reference id="manager">
+ <title>Manager interface</title>
+ <para>
+<programlisting><xi:include href="manager-api.txt" parse="text" /></programlisting>
+ </para>
+ </reference>
+
+ <reference id="adapter">
+ <title>Adapter interface</title>
+ <para>
+<programlisting><xi:include href="adapter-api.txt" parse="text" /></programlisting>
+ </para>
+ </reference>
+
+ <reference id="device">
+ <title>Device interface</title>
+ <para>
+<programlisting><xi:include href="device-api.txt" parse="text" /></programlisting>
+ </para>
+ </reference>
+
+ <reference id="agent">
+ <title>Agent interface</title>
+ <para>
+<programlisting><xi:include href="agent-api.txt" parse="text" /></programlisting>
+ </para>
+ </reference>
+
+ <reference id="reference">
+ <title>API Reference</title>
+ <partintro>
+ <para>
+ This part presents the function reference for BlueZ.
+ </para>
+ </partintro>
+ </reference>
+
+ <appendix id="license">
+ <title>License</title>
+ <para>
+<programlisting><xi:include href="../COPYING" parse="text" /></programlisting>
+ </para>
+ </appendix>
+
+ <index>
+ <title>Index</title>
+ </index>
+</book>
Possible Errors: org.bluez.Error.DoesNotExist
org.bluez.Error.InvalidArguments
- # __TIZEN_PATCH__
- dict GetVersionProperties()
-
- Returns all version properties for the device.
- See the version properties section for available
- properties.
-
- Possible Errors: org.bluez.Error.DoesNotExist
- org.bluez.Error.InvalidArguments
- # __TIZEN_PATCH__
-
void SetProperty(string name, variant value)
Changes the value of the specified property. Only
The Bluetooth remote name. This value can not be
changed. Use the Alias property instead.
+ uint16 Vendor [readonly]
+
+ Vendor unique numeric identifier.
+
+ uint16 Product [readonly]
+
+ Product unique numeric identifier.
+
+ uint16 Version [readonly]
+
+ Version unique numeric identifier.
+
string Icon [readonly]
Proposed icon name according to the freedesktop.org
Indicates if the remote device is paired.
- # __TIZEN_PATCH__
- uint32 PinLength [readonly]
-
- he PIN code length that was used in the pairing
- process.
- # __TIZEN_PATCH__
-
boolean Connected [readonly]
Indicates if the remote device is currently connected.
Note that this property can exhibit false-positives
in the case of Bluetooth 2.1 (or newer) devices that
have disabled Extended Inquiry Response support.
-
- # __TIZEN_PATCH__
-VersionProperties string Address [readonly]
-
- The Bluetooth device address of the remote device.
-
- string Name [readonly]
-
- The Bluetooth remote name. This value can not be
- changed. Use the Alias property instead.
-
- string Icon [readonly]
-
- Proposed icon name according to the freedesktop.org
- icon naming specification.
-
- uint32 Class [readonly]
-
- The Bluetooth class of device of the remote device.
-
- string Company [readonly]
-
- the company name from the OUI database of the
- Bluetooth device address. This function will need a
- valid and up-to-date oui.txt from the IEEE. This value
- will be different from the manufacturer string in the
- most cases.
-
- string Manufacturer [readonly]
-
- the manufacturer of the chip for a remote device.
-
- string Revision [readonly]
-
- the revision of the Bluetooth chip. This is a
- vendor specific value and in most cases it represents
- the firmware version. This derives only from the LMP
- subversion value.
-
- string Version [readonly]
-
- the version info for a remote device. The base for this
- string is the LMP version value and the features for
- EDR support.
- Not available can be received if the remote device was
- not contacted(connected) previously. Remote data is
- automatically retrieved in the first connection.
-
- # __TIZEN_PATCH__
--- /dev/null
+# -*- mode: makefile -*-
+
+####################################
+# Everything below here is generic #
+####################################
+
+if GTK_DOC_USE_LIBTOOL
+GTKDOC_CC = $(LIBTOOL) --mode=compile $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+GTKDOC_LD = $(LIBTOOL) --mode=link $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+else
+GTKDOC_CC = $(CC) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+GTKDOC_LD = $(CC) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+endif
+
+# We set GPATH here; this gives us semantics for GNU make
+# which are more like other make's VPATH, when it comes to
+# whether a source that is a target of one rule is then
+# searched for in VPATH/GPATH.
+#
+GPATH = $(srcdir)
+
+TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE)
+
+EXTRA_DIST = \
+ $(content_files) \
+ $(HTML_IMAGES) \
+ $(DOC_MAIN_SGML_FILE) \
+ $(DOC_MODULE)-sections.txt \
+ $(DOC_MODULE)-overrides.txt
+
+DOC_STAMPS=scan-build.stamp tmpl-build.stamp sgml-build.stamp html-build.stamp \
+ $(srcdir)/tmpl.stamp $(srcdir)/sgml.stamp $(srcdir)/html.stamp
+
+SCANOBJ_FILES = \
+ $(DOC_MODULE).args \
+ $(DOC_MODULE).hierarchy \
+ $(DOC_MODULE).interfaces \
+ $(DOC_MODULE).prerequisites \
+ $(DOC_MODULE).signals
+
+REPORT_FILES = \
+ $(DOC_MODULE)-undocumented.txt \
+ $(DOC_MODULE)-undeclared.txt \
+ $(DOC_MODULE)-unused.txt
+
+CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS)
+
+if ENABLE_GTK_DOC
+all-local: html-build.stamp
+else
+all-local:
+endif
+
+docs: html-build.stamp
+
+#### scan ####
+
+scan-build.stamp: $(HFILE_GLOB) $(CFILE_GLOB)
+ @echo 'gtk-doc: Scanning header files'
+ @-chmod -R u+w $(srcdir)
+ cd $(srcdir) && \
+ gtkdoc-scan --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --ignore-headers="$(IGNORE_HFILES)" $(SCAN_OPTIONS) $(EXTRA_HFILES)
+ if grep -l '^..*$$' $(srcdir)/$(DOC_MODULE).types > /dev/null 2>&1 ; then \
+ CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" CFLAGS="$(GTKDOC_CFLAGS)" LDFLAGS="$(GTKDOC_LIBS)" gtkdoc-scangobj $(SCANGOBJ_OPTIONS) --module=$(DOC_MODULE) --output-dir=$(srcdir) ; \
+ else \
+ cd $(srcdir) ; \
+ for i in $(SCANOBJ_FILES) ; do \
+ test -f $$i || touch $$i ; \
+ done \
+ fi
+ touch scan-build.stamp
+
+$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp
+ @true
+
+#### templates ####
+
+tmpl-build.stamp: $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt
+ @echo 'gtk-doc: Rebuilding template files'
+ @-chmod -R u+w $(srcdir)
+ cd $(srcdir) && gtkdoc-mktmpl --module=$(DOC_MODULE) $(MKTMPL_OPTIONS)
+ touch tmpl-build.stamp
+
+tmpl.stamp: tmpl-build.stamp
+ @true
+
+tmpl/*.sgml:
+ @true
+
+
+#### xml ####
+
+sgml-build.stamp: tmpl.stamp $(HFILE_GLOB) $(CFILE_GLOB) $(DOC_MODULE)-sections.txt $(srcdir)/tmpl/*.sgml $(expand_content_files)
+ @echo 'gtk-doc: Building XML'
+ @-chmod -R u+w $(srcdir)
+ cd $(srcdir) && \
+ gtkdoc-mkdb --module=$(DOC_MODULE) --source-dir=$(DOC_SOURCE_DIR) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $(MKDB_OPTIONS)
+ touch sgml-build.stamp
+
+sgml.stamp: sgml-build.stamp
+ @true
+
+#### html ####
+
+html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files)
+ @echo 'gtk-doc: Building HTML'
+ @-chmod -R u+w $(srcdir)
+ rm -rf $(srcdir)/html
+ mkdir $(srcdir)/html
+ cd $(srcdir)/html && gtkdoc-mkhtml $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE)
+ test "x$(HTML_IMAGES)" = "x" || ( cd $(srcdir) && cp $(HTML_IMAGES) html )
+ @echo 'gtk-doc: Fixing cross-references'
+ cd $(srcdir) && gtkdoc-fixxref --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS)
+ touch html-build.stamp
+
+##############
+
+clean-local:
+ rm -f *~ *.bak
+ rm -rf .libs
+
+distclean-local:
+ cd $(srcdir) && \
+ rm -rf xml $(REPORT_FILES) \
+ $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt
+
+maintainer-clean-local: clean
+ cd $(srcdir) && rm -rf xml html
+
+install-data-local:
+ -installfiles=`echo $(srcdir)/html/*`; \
+ if test "$$installfiles" = '$(srcdir)/html/*'; \
+ then echo '-- Nothing to install' ; \
+ else \
+ $(mkinstalldirs) $(DESTDIR)$(TARGET_DIR); \
+ for i in $$installfiles; do \
+ echo '-- Installing '$$i ; \
+ $(INSTALL_DATA) $$i $(DESTDIR)$(TARGET_DIR); \
+ done; \
+ echo '-- Installing $(srcdir)/html/index.sgml' ; \
+ $(INSTALL_DATA) $(srcdir)/html/index.sgml $(DESTDIR)$(TARGET_DIR) || :; \
+ which gtkdoc-rebase >/dev/null && \
+ gtkdoc-rebase --relative --dest-dir=$(DESTDIR) --html-dir=$(DESTDIR)$(TARGET_DIR) ; \
+ fi
+
+
+uninstall-local:
+ rm -f $(DESTDIR)$(TARGET_DIR)/*
+
+#
+# Require gtk-doc when making dist
+#
+if ENABLE_GTK_DOC
+dist-check-gtkdoc:
+else
+dist-check-gtkdoc:
+ @echo "*** gtk-doc must be installed and enabled in order to make dist"
+ @false
+endif
+
+dist-hook: dist-check-gtkdoc dist-hook-local
+ mkdir $(distdir)/tmpl
+ mkdir $(distdir)/xml
+ mkdir $(distdir)/html
+ -cp $(srcdir)/tmpl/*.sgml $(distdir)/tmpl
+ -cp $(srcdir)/xml/*.xml $(distdir)/xml
+ cp $(srcdir)/html/* $(distdir)/html
+ -cp $(srcdir)/$(DOC_MODULE).types $(distdir)/
+ -cp $(srcdir)/$(DOC_MODULE)-sections.txt $(distdir)/
+ cd $(distdir) && rm -f $(DISTCLEANFILES)
+ -gtkdoc-rebase --online --relative --html-dir=$(distdir)/html
+
+.PHONY : dist-hook-local docs
uint32 Position [readonly]
- Playback position in milliseconds
+ Playback position in milliseconds. Changing the
+ position may generate additional events that will be
+ sent to the remote device. When position is 0 it means
+ the track is starting and when it's greater than or
+ equal to track's duration the track has ended. Note
+ that even if duration is not available in metadata it's
+ possible to signal its end by setting position to the
+ maximum uint32 value.
MediaEndpoint hierarchy
=======================
--- /dev/null
+Bluetooth Management API
+*************************
+
+Copyright (C) 2008-2009 Marcel Holtmann <marcel@holtmann.org>
+
+
+Packet Structures
+=================
+
+ Commands:
+
+ 0 4 8 12 16 22 24 28 31 35 39 43 47
+ +-------------------+-------------------+-------------------+
+ | Command Code | Controller Index | Parameter Length |
+ +-------------------+-------------------+-------------------+
+ | |
+
+ Events:
+
+ 0 4 8 12 16 22 24 28 31 35 39 43 47
+ +-------------------+-------------------+-------------------+
+ | Event Code | Controller Index | Parameter Length |
+ +-------------------+-------------------+-------------------+
+ | |
+
+Controller Index can have a special value <non-controller> to indicate that
+command or event is not related to any controller. Possible values:
+
+ <controller id> 0x0000 to 0xFFFE
+ <non-controller> 0xFFFF
+
+
+Read Management Version Information Command
+===========================================
+
+ Command Code: 0x0001
+ Controller Index: <non-controller>
+ Command Parameters:
+ Return Parameters: Version (1 Octets)
+ Revision (2 Octets)
+
+
+Read Management Supported Features Command
+==========================================
+
+ Command Code: 0x0002
+ Controller Index: <non-controller>
+ Command Parameters:
+ Return Parameters: Features (8 Octets)
+
+ Feature Bit 0: Controller Support
+ Feature Bit 1: Tracing Support
+
+
+Read Controller Index List Command
+==================================
+
+ Command Code: 0x0003
+ Controller Index: <non-controller>
+ Command Parameters:
+ Return Parameters: Num_Controllers (2 Octets)
+ Controller_Index[i] (2 Octets)
+
+
+Read Controller Information Command
+===================================
+
+ Command Code: 0x0004
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Address (6 Octets)
+ Bluetooth_Version (1 Octet)
+ Manufacturer (2 Octets)
+ Supported_Settings (4 Octets)
+ Current_Settings (4 Octets)
+ Class_Of_Device (3 Octets)
+ Name (249 Octets)
+ Short_Name (11 Octets)
+
+ If not short name is set the Short_Name parameter will be empty
+ (begin with a nul byte).
+
+ Current_Settings & Supported_Settings is a bitmask with
+ currently the following available bits:
+
+ 1 Powered
+ 2 Connectable
+ 3 Fast Connectable
+ 4 Discoverable
+ 5 Pairable
+ 6 Link Level Security (Sec. mode 3)
+ 7 Secure Simple Pairing
+ 8 Basic Rate/Enhanced Data Rate
+ 9 High Speed
+ 10 Low Energy
+
+
+Set Powered Command
+===================
+
+ Command Code: 0x0005
+ Controller Index: <controller id>
+ Command Parameters: Powered (1 Octet)
+ Return Parameters: Current_Settings (4 Octets)
+
+
+Set Discoverable Command
+========================
+
+ Command Code: 0x0006
+ Controller Index: <controller id>
+ Command Parameters: Discoverable (1 Octet)
+ Timeout (2 Octets)
+ Return Parameters: Current_Settings (4 Octets)
+
+ Timeout is the time in seconds and is only meningful when
+ Discoverable is set to 1.
+
+
+Set Connectable Command
+=======================
+
+ Command Code: 0x0007
+ Controller Index: <controller id>
+ Command Parameters: Connectable (1 Octet)
+ Return Parameters: Current_Settings (4 Octets)
+
+
+Set Fast Connectable Command
+============================
+
+ Command Code: 0x0008
+ Controller Index: <controller id>
+ Command Parameters: Enable (1 Octet)
+ Return Parameters: Current_Settings (4 Octets)
+
+
+Set Pairable Command
+====================
+
+ Command Code: 0x0009
+ Controller Index: <controller id>
+ Command Parameters: Pairable (1 Octet)
+ Return Parameters: Current_Settings (4 Octets)
+
+
+Set Link Security Command
+=========================
+
+ Command Code: 0x000A
+ Controller Index: <controller id>
+ Command Parameters: Link_Security (1 Octet)
+ Return Parameters: Current_Settings (4 Octets)
+
+
+Set Secure Simple Pairing Command
+=================================
+
+ Command Code: 0x000B
+ Controller Index: <controller id>
+ Command Parameters: Secure_Simple_Pairing (1 Octet)
+ Return Parameters: Current_Settings (4 Octets)
+
+
+Set High Speed Command
+======================
+
+ Command Code: 0x000C
+ Controller Index: <controller id>
+ Command Parameters: High_Speed (1 Octet)
+ Return Parameters: Current_Settings (4 Octets)
+
+
+Set Low Energy Command
+======================
+
+ Command Code: 0x000D
+ Controller Index: <controller id>
+ Command Parameters: Low_Energy (1 Octet)
+ Return Parameters: Current_Settings (4 Octets)
+
+
+Set Device Class
+================
+
+ Command Code: 0x000E
+ Controller Index: <controller id>
+ Command Parameters: Major_Class (1 Octet)
+ Minor_Class (1 Octet)
+ Return Parameters: Class_Of_Device (3 Octets)
+
+ This command will also implicitly disable caching of pending CoD
+ and EIR updates.
+
+
+Set Local Name Command
+======================
+
+ Command Code: 0x000F
+ Controller Index: <controller id>
+ Command Parameters: Name (249 Octets)
+ Short_Name (11 Octets)
+ Return Parameters: Name (249 Octets)
+ Short_Name (11 Octets)
+
+ The name parameters need to always end with a nul byte (failure
+ to do so will cause the command to fail).
+
+
+Add UUID Command
+================
+
+ Command Code: 0x0010
+ Controller Index: <controller id>
+ Command Parameters: UUID (16 Octets)
+ SVC_Hint (1 Octet)
+ Return Parameters: Class_Of_Device (3 Octets)
+
+
+Remove UUID Command
+===================
+
+ Command Code: 0x0011
+ Controller Index: <controller id>
+ Command Parameters: UUID (16 Octets)
+ Return Parameters: Class_Of_Device (3 Octets)
+
+
+Load Link Keys Command
+======================
+
+ Command Code: 0x0012
+ Controller Index: <controller id>
+ Command Parameters: Debug_Keys (1 Octet)
+ Key_Count (2 Octets)
+ Key1 {
+ Address (6 Octets)
+ Type (1 Octet)
+ Value (16 Octets)
+ PIN_Length (1 Octet)
+ }
+ Key2 { }
+ ...
+ Return Parameters:
+
+
+Remove Keys Command
+===================
+
+ Command Code: 0x0013
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Disconnect (1 Octet)
+ Return Parameters: Address (6 Octets)
+ Status (1 Octet)
+
+ Removes all keys associated with the remote device.
+
+
+Disconnect Command
+==================
+
+ Command Code: 0x0014
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Return Parameters: Address (6 Octets)
+ Status (6 Octets)
+
+
+Get Connections Command
+=======================
+
+ Command Code: 0x0015
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Connection_Count (2 Octets)
+ Address1 {
+ Address (6 Octets)
+ Type (1 Octet)
+ }
+ Address2 { }
+ ...
+
+ Possible values for the Type parameter:
+ 0 BR/EDR
+ 1 LE Public
+ 2 LE Random
+
+
+PIN Code Reply Command
+=======================
+
+ Command Code: 0x0016
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Address (6 Octets)
+ PIN_Length (1 Octet)
+ PIN_Code (16 Octets)
+
+
+PIN Code Negative Reply Command
+===============================
+
+ Command Code: 0x0017
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Address (6 Octets)
+
+
+Set IO Capability Command
+=========================
+
+ Command Code: 0x0018
+ Controller Index: <controller id>
+ Command Parameters: IO_Capability (1 Octet)
+ Return Parameters:
+
+
+Pair Device Command
+===================
+
+ Command Code: 0x0019
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+ IO_Capability (1 Octet)
+ Return Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+ Status (1 Octet)
+
+ Possible values for the Address_Type parameter:
+ 0 BR/EDR
+ 1 LE Public
+ 2 LE Random
+
+
+User Confirmation Reply Command
+===============================
+
+ Command Code: 0x001A
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Return Parameters: Address (6 Octets)
+ Status (1 Octet)
+
+
+User Confirmation Negative Reply Command
+========================================
+
+ Command Code: 0x001B
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Return Parameters: Address (6 Octets)
+ Status (1 Octet)
+
+
+User Passkey Reply Command
+==========================
+
+ Command Code: 0x001C
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Passkey (4 Octets)
+ Return Parameters: Address (6 Octets)
+ Status (1 Octet)
+
+
+User Passkey Negative Reply Command
+===================================
+
+ Command Code: 0x001D
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Return Parameters: Address (6 Octets)
+ Status (1 Octet)
+
+
+Read Local Out Of Band Data Command
+===================================
+
+ Command Code: 0x001E
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Hash (16 Octets)
+ Randomizer (16 Octets)
+
+
+Add Remote Out Of Band Data Command
+===================================
+
+ Command Code: 0x001F
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Hash (16 Octets)
+ Randomizer (16 Octets)
+ Return Parameters:
+
+
+Remove Remote Out Of Band Data Command
+========================================
+
+ Command Code: 0x0020
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Return Parameters:
+
+
+Start Discovery Command
+=======================
+
+ Command Code: 0x00021
+ Controller Index: <controller id>
+ Command Parameters: Type (1 Octet)
+ Return Parameters:
+
+ Possible values for the Type parameter are a bit-wise or of the
+ following bits:
+
+ 1 BR/EDR
+ 2 LE Public
+ 3 LE Random
+
+ By combining these e.g. the following values are possible:
+
+ 1 BR/EDR
+ 6 LE (public & random)
+ 7 BR/EDR/LE (interleaved discovery)
+
+
+Stop Discovery Command
+======================
+
+ Command Code: 0x00022
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters:
+
+
+Confirm Name Command
+====================
+
+ Command Code: 0x00023
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Name_Known (1 Octet)
+ Return Parameters: Address (6 Octets)
+ Status (1 Octet)
+
+ This command is only valid during device discovery and is
+ expected for each Device Found event with the Confirm Name
+ flag set.
+
+
+Block Device Command
+====================
+
+ Command Code: 0x00024
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Return Parameters:
+
+
+Unblock Device Command
+======================
+
+ Command Code: 0x00025
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Return Parameters:
+
+
+Load Long Term Keys Command
+===========================
+
+ Command Code: 0x0026
+ Controller Index: <controller id>
+ Command Parameters: Key Count (2 Octets)
+ Key1 {
+ Address (6 Octets)
+ Authenticated (1 Octet)
+ Encryption Size (1 Octet)
+ Enc. Diversifier (2 Octets)
+ Random Number (8 Octets)
+ Value (16 Octets)
+ }
+ Key2 { }
+ ...
+
+
+Read Tracing Buffer Size Command
+================================
+
+ Command Code: <not yet assigned>
+ Controller Index: <non-controller>
+ Command Parameters:
+ Return Parameters: Status (1 Octet)
+ Buffer_Size (2 Octets)
+
+ Buffer Size in Kilobytes
+
+
+Write Tracing Buffer Size Command
+=================================
+
+ Command Code: <not yet assigned>
+ Controller Index: <non-controller>
+ Command Parameters: Buffer_Size (2 Octets)
+ Return Parameters: Status (1 Octet)
+
+ Buffer Size in Kilobytes
+
+
+Read Controller Tracing Filter Command
+=======================================
+
+ Command Code: <not yet assigned>
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Status (1 Octet)
+ Tracing_Enable (1 Octect)
+ Num_Filters (2 Octect)
+ Protocol_UUID[i] (16 Octets)
+ Protocol_Identifier[i] (16 Octets)
+
+ Tracing_Enable: 0x00 Tracing disabled
+ 0x01 Command and Event tracing
+ 0x02 Command, Event and ACL tracing
+ 0x03 Command, Event, ACL and SCO tracing
+
+
+Write Controller Tracing Filter Command
+=======================================
+
+ Command Code: <not yet assigned>
+ Controller Index: <controller id>
+ Command Parameters: Tracing_Enable (1 Octect)
+ Num_Filters (2 Octect)
+ Protocol_UUID[i] (16 Octets)
+ Protocol_Identifier[i] (16 Octets)
+ Return Parameters: Status (1 Octet)
+
+
+Command Complete Event
+======================
+
+Event Code 0x0001
+Controller Index: <controller id> or <non-controller>
+Event Parameters Command_Opcode (2 Octets)
+ Return_Parameters
+
+
+Command Status Event
+====================
+
+Event Code 0x0002
+Controller Index: <controller id> or <non-controller>
+Event Parameters Status (1 Octet)
+ Command_Opcode (2 Octets)
+
+
+Controller Error Event
+======================
+
+Event Code 0x0003
+Controller Index: <controller id>
+Event Parameters Error_Code (1 Octet)
+
+
+Index Added Event
+=================
+
+Event Code 0x0004
+Controller Index: <controller id>
+Event Parameters
+
+
+Index Removed Event
+===================
+
+Event Code 0x0005
+Controller Index: <controller id>
+Event Parameters
+
+
+New Settings Event
+==================
+
+Event Code 0x0006
+Controller Index: <controller id>
+Event Parameters: Current_Settings (4 Octets)
+
+
+Class Of Device Changed Event
+=============================
+
+Event Code 0x0007
+Controller Index: <controller id>
+Event Parameters: Class_Of_Device (3 Octets)
+
+
+Local Name Changed Event
+========================
+
+Event Code 0x0008
+Controller Index <controller id>
+Event Parameters Name (249 Octets)
+ Short_Name (11 Octets)
+
+
+New Link Key Event
+==================
+
+Event Code 0x0009
+Controller Index: <controller id>
+Event Parameters Key {
+ Address (6 Octets)
+ Type (1 Octet)
+ Value (16 Octets)
+ PIN_Length (1 Octet)
+ }
+ Old_Key_Type (1 Octet)
+
+
+Device Connected Event
+======================
+
+Event Code 0x000A
+Controller Index: <controller id>
+Event Parameters Address (6 Octets)
+ Type (1 Octet)
+
+ Possible values for the Type parameter:
+ 0 BR/EDR
+ 1 LE Public
+ 2 LE Random
+
+
+Device Disconnected Event
+=========================
+
+Event Code 0x000B
+Controller Index: <controller id>
+Event Parameters Address (6 Octets)
+ Type (1 Octet)
+
+ Possible values for the Type parameter:
+ 0 BR/EDR
+ 1 LE Public
+ 2 LE Random
+
+
+Connect Failed Event
+====================
+
+Event Code 0x000C
+Controller Index: <controller id>
+Event Parameters Address (6 Octets)
+ Type (1 Octet)
+ Status (1 Octet)
+
+ Possible values for the Type parameter:
+ 0 BR/EDR
+ 1 LE Public
+ 2 LE Random
+
+
+PIN Code Request Event
+======================
+
+Event Code 0x000D
+Controller Index: <controller id>
+Event Parameters Address (6 Octets)
+ Secure (1 Octet)
+
+ Secure: 0x01 secure PIN code required
+ 0x00 secure PIN code not required
+
+
+User Confirmation Request Event
+===============================
+
+Event Code 0x000E
+Controller Index: <controller id>
+Event Parameters Address (6 Octets)
+ Value (4 Octets)
+
+
+User Passkey Request Event
+==========================
+
+Event Code 0x000F
+Controller Index: <controller id>
+Event Parameters Address (6 Octets)
+
+
+Authentication Failed Event
+===========================
+
+Event Code 0x0010
+Controller Index: <controller id>
+Event Parameters Address (6 Octets)
+ Status (1 Octet)
+
+
+Device Found Event
+==================
+
+Event Code 0x0011
+Controller Index <controller id>
+Event Parameters Address (6 Octets)
+ Type (1 Octet)
+ Class_Of_Device (3 Octets)
+ RSSI (1 Octet)
+ Confirm Name (1 Octet)
+ EIR_Data (240 Octets)
+
+ Possible values for the Type parameter:
+ 0 BR/EDR
+ 1 LE Public
+ 2 LE Random
+
+
+Remote Name Event
+=================
+
+Event Code 0x0012
+Controller Index <controller id>
+Event Parameters Address (6 Octets)
+ Name (249 Octets)
+
+
+Discovering Event
+=================
+
+Event Code 0x00013
+Controller Index <controller id>
+Event Parameters Discovering (1 Octet)
+
+
+Device Blocked Event
+====================
+
+Event Code 0x00014
+Controller Index <controller id>
+Event Parameters Address (6 Octets)
+
+
+Device Unblocked Event
+======================
+
+Event Code 0x00015
+Controller Index <controller id>
+Event Parameters Address (6 Octets)
+
+
+New Long Term Key Event
+=======================
+
+Event Code 0x0016
+Controller Index <controller id>
+Event Parameters Store Hint (1 Octet)
+ Key {
+ Address (6 Octets)
+ Authenticated (1 Octet)
+ Encryption Size (1 Octet)
+ Enc. Diversifier (2 Octets)
+ Random Number (8 Octets)
+ Value (16 Octets)
+ }
--- /dev/null
+BlueZ D-Bus Node API description
+********************************
+
+Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+
+
+Node hierarchy
+==============
+
+Service org.bluez
+Interface org.bluez.Node
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/{node0,...}
+
+Methods dict GetProperties()
+
+ Returns all properties for the device node. See the
+ properties section for available properties.
+
+ Possible Errors: org.bluez.Error.DoesNotExist
+ org.bluez.Error.InvalidArguments
+
+Properties string Name [readonly]
+
+ The name of the node. For example "rfcomm0".
+
+ object Device [readonly]
+
+ The object path of the device this node belongs to.
--- /dev/null
+BlueZ D-Bus Out Of Band Pairing API description
+===============================================
+
+Copyright (C) 2011 Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+
+Service org.bluez
+Interface org.bluez.OutOfBand
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods array{byte} hash, array{byte} randomizer ReadLocalData()
+
+ This method reads local OOB data from adapter. Return
+ value is pair of arrays 16 bytes each.
+
+ Note: This method will generate and return new local
+ OOB data.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+
+ void AddRemoteData(string address, array{byte} hash,
+ array{byte} randomizer)
+
+ This method adds new Out Of Band data for
+ specified address. If data for specified address
+ already exists it will be overwritten with new one.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InvalidArguments
+
+ void RemoveRemoteData(string address)
+
+ This method removes Out Of Band data for specified
+ address. If data for specified address does not exist
+ nothing is removed.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InvalidArguments
--- /dev/null
+BlueZ D-Bus Proximity API description
+***********************************
+
+Copyright (C) 2011 Claudio Takahasi <claudio.takahasi@openbossa.org>
+
+
+Proximity hierarchy
+=================
+
+Service org.bluez
+Interface org.bluez.ProximityMonitor
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+Methods dict GetProperties()
+
+ Returns all properties for the interface. See the
+ properties section for available properties.
+
+ void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ properties that are listed a read-write are changeable.
+ On success this will emit a PropertyChanged signal.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of a given
+ property.
+
+Properties
+
+ string SignalLevel[readonly]
+
+ Alert indicating that a threshold has been reached.
+ Possible values: "unknown", "good", "regular", "weak"
+
+ string LinkLossAlertLevel [readwrite]
+
+ Persistent property. Sets the alert level in the
+ proximity reporter for link loss scenario. Values:
+ "none", "mild", "high".
+
+ string ImmediateAlertLevel [readwrite]
+
+ Alert level to be written in the Immediate Alert Level.
+ Property shared between Path Loss and Find Me.
+ Values: "none", "mild", "high". Default value is
+ "none". Applications can disable the alert setting
+ the value to "none". If the "Target" is not found,
+ "none" will be emitted after the configured timeout.
+ When changing the level, signal is the confirmation
+ that the value was written in the remote.
org.bluez.Error.ConnectionAttemptFailed
org.bluez.Error.NotSupported
+Methods fd ConnectFD(string pattern) [experimental]
+
+ Connects to a specific RFCOMM based service on a
+ remote device and returns a file descriptor to talk
+ with this device.
+
+ Possible patterns: UUID 128 bit as string
+ Profile short names, e.g: spp, dun
+ RFCOMM channel as string, 1-30
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.InProgress
+ org.bluez.Error.ConnectionAttemptFailed
+ org.bluez.Error.NotSupported
+
+
void Disconnect(string device)
Disconnect a RFCOMM TTY device that has been
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.DoesNotExist
+
+Serial Proxy Manager hierarchy [experimental]
+=============================================
+
+Service org.bluez
+Interface org.bluez.SerialProxyManager
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods array{string} ListProxies()
+
+ Returns an array of the object path strings of
+ all the proxies created for the adapter.
+
+ string CreateProxy(string pattern, string address)
+
+ Possible patterns: UUID 128 bit as string
+ Profile short names, e.g: spp, dun
+ RFCOMM channel as string, 1-30
+
+ Address is the path to the TTY or Unix socket to be used.
+ Only one proxy per address (TTY or Unix socket)
+ is allowed.
+
+ The object path of created proxy is returned.
+ On success this will emit a ProxyCreated signal.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.AlreadyExists
+ org.bluez.Error.Failed
+
+ void RemoveProxy(string path)
+
+ This removes the proxy object at the given path.
+ On success this will emit a ProxyRemoved signal.
+
+ Possible Errors: org.bluez.Error.DoesNotExist
+ org.bluez.Error.NotAuthorized
+
+Signals ProxyCreated(string path)
+
+ This signal indicates a proxy was created.
+ Parameter is object path of created proxy.
+
+ ProxyRemoved(string path)
+
+ This signal indicates a proxy was removed.
+ Parameter is object path of removed proxy.
+
+Serial Proxy hierarchy [experimental]
+=====================================
+
+Service org.bluez
+Interface org.bluez.SerialProxy
+Object path [variable prefix]/{hci0,hci1,...}/{proxy0,proxy1,...}
+
+Methods void Enable()
+
+ Starts to listen to the TTY or Unix socket, allocates
+ a RFCOMM channel and add record to the server.
+
+ Possible errors: org.bluez.Error.Failed
+
+ void Disable()
+
+ Stops to listen to the TTY or Unix socket, shutdown
+ the RFCOMM channel allocated for the proxy, and remove
+ record from the server.
+
+ Possible errors: org.bluez.Error.Failed
+
+ dict GetInfo()
+
+ Returns all properties for the proxy. See the
+ properties section for available properties.
+
+ void SetSerialParameters(string rate, uint8 data, uint8 stop,
+ string parity)
+
+ Configures serial communication setting baud rate,
+ data bits, stop bits and parity.
+
+ Doesn't allow change TTY settings if it is open.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotAuthorized
+
+Properties string uuid [readonly]
+
+ 128-bit UUID that represents the available remote service.
+
+ string address [readonly]
+
+ Address is the path to the TTY or Unix socket name used,
+ set when the proxy was created.
+
+ uint8 channel [readonly]
+
+ RFCOMM channel.
+
+ boolean enabled [readonly]
+
+ Indicates if the proxy is currently enabled.
+
+ boolean connected [readonly]
+
+ Indicates if the proxy is currently connected.
--- /dev/null
+BlueZ D-Bus Thermometer API description
+****************************************
+
+ Santiago Carot-Nemesio <sancane@gmail.com>
+
+Health Thermometer Profile hierarchy
+=====================================
+
+Service org.bluez
+Interface org.bluez.Thermometer
+Object path [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX
+
+
+Methods void SetProperty(string name, variant value)
+
+ Changes the value of the specified property. Only
+ read-write properties can be changed. On success
+ this will emit a PropertyChanged signal.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+ dict GetProperties()
+
+ Returns all properties for the interface. See the
+ Properties section for the available properties.
+
+ RegisterWatcher(object agent)
+
+ Registers a watcher to monitor scanned measurements.
+ This agent will be notified about final temperature
+ measurements.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+
+ UnregisterWatcher(object agent)
+
+ Unregisters a watcher.
+
+ Final and intermediate temperatures won't be notified to
+ this agent any more.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotFound
+
+ EnableIntermediateMeasurement(object agent)
+
+ Enables intermediate measurement notifications for this
+ agent if the thermometer supports it.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotSupported
+
+ DisableIntermediateMeasurement(object agent)
+
+ Disables intermediate measurement notifications for this
+ agent. It will disable notifications in the thermometer
+ when the last agent removes the watcher for intermediate
+ measurements.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotFound
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of the given
+ property.
+
+Properties boolean Intermediate [readonly]
+
+ True if the thermometer supports intermediate measurement
+ notifications.
+
+ uint16 Interval (optional) [readwrite]
+
+ The Measurement Interval defines the time (in seconds)
+ between measurements. This interval is not related to
+ the intermediate measurements and must be defined into
+ a valid range. Setting it to zero means that no periodic
+ measurements will be taken.
+
+ uint16 Maximum (optional) [readonly]
+
+ Defines the maximum value allowed for the interval
+ between periodic measurements.
+
+ uint16 Minimum (optional) [readonly]
+
+ Defines the minimum value allowed for the interval
+ between periodic measurements.
+
+
+Health Thermometer Watcher hierarchy
+====================================
+Service unique name
+Interface org.bluez.ThermometerWatcher
+Object path freely definable
+
+Methods void MeasurementReceived(dict measure)
+
+ This callback gets called when a measure has been
+ scanned in the thermometer. The Time entry in the dict
+ will be only present if the device supports storing of
+ data. The time value is expressed in seconds since epoch.
+ The value represented is (mantissa) x (10**exponent)
+ See foot note for special values when treating with
+ health devices.
+
+ Dict is defined as below:
+ {
+ "Exponent" : int8,
+ "Mantissa" : int32,
+ "Unit" : ("Celsius" or "Fahrenheit"),
+ "Time" : uint64,
+ "Type" : ("Armpit", "Body", "Ear", "Finger",
+ "Intestines", "Mouth", "Rectum", "Toe",
+ "Tympanum"),
+ "Measurement" : ("Final" or "Intermediate"),
+ }
+
+ For special cases, the exponent shall always be zero and
+ the mantissa should be one of following values:
+
+ NRes = -(2**23)
+ NaN = +(2**23-1)
+ INFINITY = (2**23-2)
+ -INFINITY = -(2**23-2)
#include "gdbus.h"
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
#include "log.h"
#else
#define info(fmt...)
/* Gather enough data to have a single complete type */
for (len = 0; len < (sizeof(type) - 1) && sig[i]; len++, i++) {
- switch (sig[i]){
+ switch (sig[i]) {
case '(':
struct_level++;
break;
for (list = pending_security; list; list = list->next) {
struct security_data *secdata = list->data;
- DBusHandlerResult result;
if (secdata->pending != pending)
continue;
pending_security = g_slist_remove(pending_security, secdata);
- result = process_message(connection, secdata->message,
+ process_message(connection, secdata->message,
secdata->method, secdata->iface_user_data);
dbus_message_unref(secdata->message);
iface->user_data) == TRUE)
return DBUS_HANDLER_RESULT_HANDLED;
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
DBG("%s: %s.%s()",dbus_message_get_path(message),
iface->name,method->name);
#endif
reply = dbus_pending_call_steal_reply(call);
if (reply == NULL)
return;
+
dbus_error_init(&err);
if (dbus_set_error_from_message(&err, reply))
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
- * Authors:
- * Santiago Carot Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
*/
-#include <gdbus.h>
-
-#include "log.h"
-#include "error.h"
#include <stdlib.h>
#include <stdint.h>
-#include <hdp_types.h>
-#include <hdp_util.h>
+#include <sdpd.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include <bluetooth/l2cap.h>
+#include <gdbus.h>
+#include <dbus-common.h>
+#include <log.h>
+#include <error.h>
#include <adapter.h>
#include <device.h>
-#include <hdp.h>
-#include <mcap.h>
#include <btio.h>
-#include <mcap_lib.h>
-#include <l2cap.h>
-#include <sdpd.h>
-#include "../src/dbus-common.h"
-#include <unistd.h>
+
+#include "mcap_lib.h"
+#include "hdp_types.h"
+#include "hdp_util.h"
+#include "hdp.h"
+#include "mcap.h"
#ifndef DBUS_TYPE_UNIX_FD
#define DBUS_TYPE_UNIX_FD -1
static struct hdp_channel *hdp_channel_ref(struct hdp_channel *chan)
{
- if (!chan)
+ if (chan == NULL)
return NULL;
chan->ref++;
static void hdp_channel_unref(struct hdp_channel *chan)
{
- if (!chan)
+ if (chan == NULL)
return;
chan->ref --;
const struct hdp_device *device = a;
bdaddr_t addr;
- device_get_address(device->dev, &addr);
+ device_get_address(device->dev, &addr, NULL);
return bacmp(&addr, dst);
}
return -1;
}
-static uint8_t get_app_id()
+static uint8_t get_app_id(void)
{
uint8_t id = next_app_id;
do {
GSList *l = g_slist_find_custom(applications, &id, cmp_app_id);
- if (!l) {
+ if (l == NULL) {
next_app_id = (id % HDP_MDEP_FINAL) + 1;
return id;
} else
static gboolean set_app_path(struct hdp_application *app)
{
app->id = get_app_id();
- if (!app->id)
+ if (app->id == 0)
return FALSE;
app->path = g_strdup_printf(MANAGER_PATH "/health_app_%d", app->id);
static void device_unref_mcl(struct hdp_device *hdp_device)
{
- if (!hdp_device->mcl)
+ if (hdp_device->mcl == NULL)
return;
mcap_close_mcl(hdp_device->mcl, FALSE);
static void free_health_device(struct hdp_device *device)
{
- if (device->conn) {
+ if (device->conn != NULL) {
dbus_connection_unref(device->conn);
device->conn = NULL;
}
- if (device->dev) {
+ if (device->dev != NULL) {
btd_device_unref(device->dev);
device->dev = NULL;
}
dbus_message_iter_init(msg, &iter);
app = hdp_get_app_config(&iter, &err);
- if (err) {
+ if (err != NULL) {
g_error_free(err);
return btd_error_invalid_args(msg);
}
name = dbus_message_get_sender(msg);
- if (!name) {
+ if (name == NULL) {
hdp_application_unref(app);
return g_dbus_create_error(msg,
ERROR_INTERFACE ".HealthError",
l = g_slist_find_custom(applications, path, cmp_app);
- if (!l)
+ if (l == NULL)
return g_dbus_create_error(msg,
ERROR_INTERFACE ".InvalidArguments",
"Invalid arguments in method call, "
char *type;
reply = dbus_message_new_method_return(msg);
- if (!reply)
+ if (reply == NULL)
return NULL;
dbus_message_iter_init_append(reply, &iter);
static void abort_mdl_cb(GError *err, gpointer data)
{
- if (err)
+ if (err != NULL)
error("Aborting error: %s", err->message);
}
DBusMessage *reply;
int fd;
- if (err) {
+ if (err != NULL) {
struct hdp_channel *chan = dc_data->hdp_chann;
GError *gerr = NULL;
GError *gerr = NULL;
uint8_t mode;
- if (err) {
+ if (err != NULL) {
hdp_conn->cb(hdp_chann->mdl, err, hdp_conn);
return;
}
GError *gerr = NULL;
DBusMessage *reply;
- if (err) {
+ if (err != NULL) {
reply = g_dbus_create_error(dc_data->msg,
ERROR_INTERFACE ".HealthError",
"Cannot reconnect: %s", err->message);
g_dbus_send_message(dc_data->conn, reply);
hdp_tmp_dc_data_unref(dc_data);
g_error_free(gerr);
- gerr = NULL;
/* Send abort request because remote side is now in PENDING state */
if (!mcap_mdl_abort(mdl, abort_mdl_cb, NULL, NULL, &gerr)) {
GError *gerr = NULL;
int fd;
- if (err) {
+ if (err != NULL) {
return g_dbus_create_error(data->msg,
ERROR_INTERFACE ".HealthError",
"%s", err->message);
reply = channel_acquire_continue(data, err);
- if (reply)
+ if (reply != NULL)
g_dbus_send_message(dc_data->conn, reply);
}
static void free_echo_data(struct hdp_echo_data *edata)
{
- if (!edata)
+ if (edata == NULL)
return;
- if (edata->tid)
+ if (edata->tid > 0)
g_source_remove(edata->tid);
- if (edata->buf)
+ if (edata->buf != NULL)
g_free(edata->buf);
struct hdp_device *dev = hdp_chan->dev;
DBG("Destroy Health Channel %s", hdp_chan->path);
- if (!g_slist_find(dev->channels, hdp_chan))
+ if (g_slist_find(dev->channels, hdp_chan) == NULL)
goto end;
dev->channels = g_slist_remove(dev->channels, hdp_chan);
{
struct hdp_channel *hdp_chann;
- if (!dev)
+ if (dev == NULL)
return NULL;
hdp_chann = g_new0(struct hdp_channel, 1);
hdp_chann->dev = health_device_ref(dev);
hdp_chann->mdlid = mdlid;
- if (mdl)
+ if (mdl != NULL)
hdp_chann->mdl = mcap_mdl_ref(mdl);
- if (app) {
+ if (app != NULL) {
hdp_chann->mdep = app->id;
hdp_chann->app = hdp_application_ref(app);
} else
struct hdp_channel *chan;
char *path;
- while (dev->channels) {
+ while (dev->channels != NULL) {
chan = dev->channels->data;
path = g_strdup(chan->path);
static void close_device_con(struct hdp_device *dev, gboolean cache)
{
- if (!dev->mcl)
+ if (dev->mcl == NULL)
return;
mcap_close_mcl(dev->mcl, cache);
DBG("MDL imtu %d omtu %d Channel imtu %d omtu %d", imtu, omtu,
chan->imtu, chan->omtu);
- if (!chan->imtu)
+ if (chan->imtu == 0)
chan->imtu = imtu;
- if (!chan->omtu)
+ if (chan->omtu == 0)
chan->omtu = omtu;
if (chan->imtu != imtu || chan->omtu != omtu)
struct hdp_channel *chan;
DBG("hdp_mcap_mdl_connected_cb");
- if (!dev->ndc)
+ if (dev->ndc == NULL)
return;
chan = dev->ndc;
- if (!chan->mdl)
+ if (chan->mdl == NULL)
chan->mdl = mcap_mdl_ref(mdl);
- if (!g_slist_find(dev->channels, chan))
+ if (g_slist_find(dev->channels, chan) == NULL)
dev->channels = g_slist_prepend(dev->channels,
hdp_channel_ref(chan));
DBUS_TYPE_OBJECT_PATH, &chan->path,
DBUS_TYPE_INVALID);
- if (dev->fr)
+ if (dev->fr != NULL)
goto end;
dev->fr = hdp_channel_ref(chan);
DBG("hdp_mcap_mdl_deleted_cb");
l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl);
- if (!l)
+ if (l == NULL)
return;
chan = l->data;
struct hdp_device *dev = data;
DBG("hdp_mcap_mdl_aborted_cb");
- if (!dev->ndc)
+ if (dev->ndc == NULL)
return;
dev->ndc->mdl = mcap_mdl_ref(mdl);
- if (!g_slist_find(dev->channels, dev->ndc))
+ if (g_slist_find(dev->channels, dev->ndc) == NULL)
dev->channels = g_slist_prepend(dev->channels,
hdp_channel_ref(dev->ndc));
}
dev->ndc = create_channel(dev, *conf, NULL, mdlid, NULL, NULL);
- if (!dev->ndc)
+ if (dev->ndc == NULL)
return MCAP_MDL_BUSY;
return MCAP_SUCCESS;
}
l = g_slist_find_custom(applications, &mdepid, cmp_app_id);
- if (!l)
+ if (l == NULL)
return MCAP_INVALID_MDEP;
app = l->data;
}
l = g_slist_find_custom(dev->channels, &mdlid, cmp_chan_mdlid);
- if (l) {
+ if (l != NULL) {
struct hdp_channel *chan = l->data;
char *path;
}
dev->ndc = create_channel(dev, *conf, NULL, mdlid, app, NULL);
- if (!dev->ndc)
+ if (dev->ndc == NULL)
return MCAP_MDL_BUSY;
return MCAP_SUCCESS;
GSList *l;
l = g_slist_find_custom(dev->channels, mdl, cmp_chan_mdl);
- if (!l)
+ if (l == NULL)
return MCAP_INVALID_MDL;
chan = l->data;
- if (!dev->fr && (chan->config != HDP_RELIABLE_DC) &&
- (chan->mdep != HDP_MDEP_ECHO))
+ if (dev->fr == NULL && chan->config != HDP_RELIABLE_DC &&
+ chan->mdep != HDP_MDEP_ECHO)
return MCAP_UNSPECIFIED_ERROR;
if (!mcap_set_data_chan_mode(dev->hdp_adapter->mi,
{
gboolean ret;
- if (!device->mcl)
+ if (device->mcl == NULL)
return FALSE;
ret = mcap_mcl_set_cb(device->mcl, device, err,
mcap_mcl_get_addr(mcl, &addr);
l = g_slist_find_custom(devices, &addr, cmp_dev_addr);
- if (!l) {
+ if (l == NULL) {
struct hdp_adapter *hdp_adapter = data;
struct btd_device *device;
char str[18];
GSList *l;
l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
- if (!l)
+ if (l == NULL)
return;
hdp_device = l->data;
GSList *l;
l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
- if (!l)
+ if (l == NULL)
return;
hdp_device = l->data;
GSList *l;
l = g_slist_find_custom(devices, mcl, cmp_dev_mcl);
- if (!l)
+ if (l == NULL)
return;
hdp_device = l->data;
DBG("Mcl uncached %s", path);
}
-static void check_devices_mcl()
+static void check_devices_mcl(void)
{
struct hdp_device *dev;
GSList *l, *to_delete = NULL;
static void release_adapter_instance(struct hdp_adapter *hdp_adapter)
{
- if (!hdp_adapter->mi)
+ if (hdp_adapter->mi == NULL)
return;
check_devices_mcl();
GError *err = NULL;
bdaddr_t addr;
- if (!applications) {
+ if (applications == NULL) {
release_adapter_instance(hdp_adapter);
goto update;
}
- if (hdp_adapter->mi)
+ if (hdp_adapter->mi != NULL)
goto update;
adapter_get_address(hdp_adapter->btd_adapter, &addr);
NULL, /* CSP is not used by now */
hdp_adapter, &err);
- if (!hdp_adapter->mi) {
+ if (hdp_adapter->mi == NULL) {
error("Error creating the MCAP instance: %s", err->message);
g_error_free(err);
return FALSE;
}
hdp_adapter->ccpsm = mcap_get_ctrl_psm(hdp_adapter->mi, &err);
- if (err) {
+ if (err != NULL) {
error("Error getting MCAP control PSM: %s", err->message);
goto fail;
}
hdp_adapter->dcpsm = mcap_get_data_psm(hdp_adapter->mi, &err);
- if (err) {
+ if (err != NULL) {
error("Error getting MCAP data PSM: %s", err->message);
goto fail;
}
fail:
release_adapter_instance(hdp_adapter);
- if (err)
+ if (err != NULL)
g_error_free(err);
+
return FALSE;
}
l = g_slist_find_custom(adapters, adapter, cmp_adapter);
- if (!l)
+ if (l == NULL)
return;
hdp_adapter = l->data;
adapters = g_slist_remove(adapters, hdp_adapter);
- if (hdp_adapter->sdp_handler)
+ if (hdp_adapter->sdp_handler > 0)
remove_record_from_server(hdp_adapter->sdp_handler);
release_adapter_instance(hdp_adapter);
btd_adapter_unref(hdp_adapter->btd_adapter);
static void delete_echo_channel_cb(GError *err, gpointer chan)
{
- if (err && err->code != MCAP_INVALID_MDL) {
+ if (err != NULL && err->code != MCAP_INVALID_MDL) {
/* TODO: Decide if more action is required here */
error("Error deleting echo channel: %s", err->message);
return;
{
struct hdp_channel *chan = data;
- if (err && err->code != MCAP_ERROR_INVALID_OPERATION) {
+ if (err != NULL && err->code != MCAP_ERROR_INVALID_OPERATION) {
error("Aborting error: %s", err->message);
if (err->code == MCAP_INVALID_MDL) {
/* MDL is removed from MCAP so we can */
hdp_create_data_unref(dc_data);
}
-static void *generate_echo_packet()
+static void *generate_echo_packet(void)
{
uint8_t *buf;
int i;
GIOChannel *io;
int fd;
- if (err) {
+ if (err != NULL) {
reply = g_dbus_create_error(hdp_conn->msg,
ERROR_INTERFACE ".HealthError",
"%s", err->message);
g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN,
check_echo, hdp_tmp_dc_data_ref(hdp_conn));
- edata->tid = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
+ edata->tid = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
ECHO_TIMEOUT, echo_timeout,
hdp_channel_ref(hdp_conn->hdp_chann),
(GDestroyNotify) hdp_channel_unref);
static void delete_mdl_cb(GError *err, gpointer data)
{
- if (err)
+ if (err != NULL)
error("Deleting error: %s", err->message);
}
struct mcap_mdl *mdl = data;
GError *gerr = NULL;
- if (err) {
+ if (err != NULL) {
error("%s", err->message);
if (err->code == MCAP_INVALID_MDL) {
/* MDL is removed from MCAP so we don't */
}
}
+static void abort_mdl_connection_cb(GError *err, gpointer data)
+{
+ struct hdp_tmp_dc_data *hdp_conn = data;
+ struct hdp_channel *hdp_chann = hdp_conn->hdp_chann;
+
+ if (err != NULL)
+ error("Aborting error: %s", err->message);
+
+ /* Connection operation has failed but we have to */
+ /* notify the channel created at MCAP level */
+ if (hdp_chann->mdep != HDP_MDEP_ECHO)
+ g_dbus_emit_signal(hdp_conn->conn,
+ device_get_path(hdp_chann->dev->dev),
+ HEALTH_DEVICE,
+ "ChannelConnected",
+ DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+ DBUS_TYPE_INVALID);
+}
+
static void hdp_mdl_conn_cb(struct mcap_mdl *mdl, GError *err, gpointer data)
{
struct hdp_tmp_dc_data *hdp_conn = data;
DBusMessage *reply;
GError *gerr = NULL;
- if (err) {
+ if (err != NULL) {
error("%s", err->message);
reply = g_dbus_create_reply(hdp_conn->msg,
DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
/* Send abort request because remote side */
/* is now in PENDING state */
- if (!mcap_mdl_abort(hdp_chann->mdl, abort_mdl_cb, NULL,
- NULL, &gerr)) {
+ if (!mcap_mdl_abort(hdp_chann->mdl, abort_mdl_connection_cb,
+ hdp_tmp_dc_data_ref(hdp_conn),
+ hdp_tmp_dc_data_destroy, &gerr)) {
+ hdp_tmp_dc_data_unref(hdp_conn);
error("%s", gerr->message);
g_error_free(gerr);
}
DBUS_TYPE_INVALID);
g_dbus_send_message(hdp_conn->conn, reply);
+ g_dbus_emit_signal(hdp_conn->conn,
+ device_get_path(hdp_chann->dev->dev),
+ HEALTH_DEVICE,
+ "ChannelConnected",
+ DBUS_TYPE_OBJECT_PATH, &hdp_chann->path,
+ DBUS_TYPE_INVALID);
+
if (!check_channel_conf(hdp_chann)) {
close_mdl(hdp_chann);
return;
}
- if (dev->fr)
+ if (dev->fr != NULL)
return;
dev->fr = hdp_channel_ref(hdp_chann);
GError *gerr = NULL;
DBusMessage *reply;
- if (err) {
+ if (err != NULL) {
reply = g_dbus_create_error(user_data->msg,
ERROR_INTERFACE ".HealthError",
"%s", err->message);
if (user_data->mdep != HDP_MDEP_ECHO &&
user_data->config == HDP_NO_PREFERENCE_DC) {
- if (!user_data->dev->fr && (conf != HDP_RELIABLE_DC)) {
+ if (user_data->dev->fr == NULL && conf != HDP_RELIABLE_DC) {
g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
"Data channel aborted, first data "
"channel should be reliable");
hdp_chan = create_channel(user_data->dev, conf, mdl,
mcap_mdl_get_mdlid(mdl),
user_data->app, &gerr);
- if (!hdp_chan)
+ if (hdp_chan == NULL)
goto fail;
- if (user_data->mdep != HDP_MDEP_ECHO)
- g_dbus_emit_signal(user_data->conn,
- device_get_path(hdp_chan->dev->dev),
- HEALTH_DEVICE,
- "ChannelConnected",
- DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
- DBUS_TYPE_INVALID);
-
hdp_conn = g_new0(struct hdp_tmp_dc_data, 1);
hdp_conn->msg = dbus_message_ref(user_data->msg);
hdp_conn->conn = dbus_connection_ref(user_data->conn);
error("%s", gerr->message);
g_error_free(gerr);
- gerr = NULL;
reply = g_dbus_create_reply(hdp_conn->msg,
DBUS_TYPE_OBJECT_PATH, &hdp_chan->path,
hdp_tmp_dc_data_unref(hdp_conn);
/* Send abort request because remote side is now in PENDING state */
- if (!mcap_mdl_abort(mdl, abort_mdl_cb, NULL, NULL, &gerr)) {
+ if (!mcap_mdl_abort(hdp_chan->mdl, abort_mdl_connection_cb,
+ hdp_tmp_dc_data_ref(hdp_conn),
+ hdp_tmp_dc_data_destroy, &gerr)) {
+ hdp_tmp_dc_data_unref(hdp_conn);
error("%s", gerr->message);
g_error_free(gerr);
}
"%s", gerr->message);
g_dbus_send_message(user_data->conn, reply);
g_error_free(gerr);
- gerr = NULL;
/* Send abort request because remote side is now in PENDING */
/* state. Then we have to delete it because we couldn't */
/* register the HealthChannel interface */
- if (!mcap_mdl_abort(mdl, abort_and_del_mdl_cb, mcap_mdl_ref(mdl), NULL,
- &gerr)) {
+ if (!mcap_mdl_abort(mdl, abort_and_del_mdl_cb, mcap_mdl_ref(mdl),
+ (GDestroyNotify) mcap_mdl_unref, &gerr)) {
error("%s", gerr->message);
g_error_free(gerr);
mcap_mdl_unref(mdl);
DBusMessage *reply;
GError *gerr = NULL;
- if (err) {
+ if (err != NULL) {
reply = g_dbus_create_error(data->msg,
ERROR_INTERFACE ".HealthError",
"%s", err->message);
return;
}
- if (!data->dev->mcl) {
+ if (data->dev->mcl == NULL) {
g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
"Mcl was closed");
goto fail;
data->cb = hdp_echo_connect_cb;
hdp_create_data_ref(data);
- if (device->mcl_conn && device->mcl) {
+ if (device->mcl_conn && device->mcl != NULL) {
if (mcap_create_mdl(device->mcl, data->mdep, data->config,
device_create_mdl_cb, data,
destroy_create_dc_data, &err))
DBusMessage *reply;
GError *gerr = NULL;
- if (err) {
+ if (err != NULL) {
reply = g_dbus_create_error(user_data->msg,
ERROR_INTERFACE ".HealthError",
"%s", err->message);
return btd_error_invalid_args(msg);
l = g_slist_find_custom(applications, app_path, cmp_app);
- if (!l)
+ if (l == NULL)
return btd_error_invalid_args(msg);
app = l->data;
DBusMessage *reply;
char *path;
- if (err && err->code != MCAP_INVALID_MDL) {
+ if (err != NULL && err->code != MCAP_INVALID_MDL) {
reply = g_dbus_create_error(del_data->msg,
ERROR_INTERFACE ".HealthError",
"%s", err->message);
GError *gerr = NULL;
DBusMessage *reply;
- if (err) {
+ if (err != NULL) {
reply = g_dbus_create_error(del_data->msg,
ERROR_INTERFACE ".HealthError",
"%s", err->message);
}
l = g_slist_find_custom(device->channels, path, cmp_chan_path);
- if (!l)
+ if (l == NULL)
return btd_error_invalid_args(msg);
hdp_chan = l->data;
char *path;
reply = dbus_message_new_method_return(msg);
- if (!reply)
+ if (reply == NULL)
return NULL;
dbus_message_iter_init_append(reply, &iter);
DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
- if (device->fr)
+ if (device->fr != NULL)
path = g_strdup(device->fr->path);
else
path = g_strdup("");
- dict_append_entry(&dict, "MainChannel", DBUS_TYPE_STRING, &path);
+ dict_append_entry(&dict, "MainChannel", DBUS_TYPE_OBJECT_PATH, &path);
g_free(path);
dbus_message_iter_close_container(&iter, &dict);
device_get_path(device->dev));
remove_channels(device);
- if (device->ndc) {
+ if (device->ndc != NULL) {
hdp_channel_unref(device->ndc);
device->ndc = NULL;
}
struct hdp_device *dev;
GSList *l;
- if (!device)
+ if (device == NULL)
return NULL;
dev = g_new0(struct hdp_device, 1);
health_device_ref(dev);
l = g_slist_find_custom(adapters, adapter, cmp_adapter);
- if (!l)
+ if (l == NULL)
goto fail;
dev->hdp_adapter = l->data;
GSList *l;
l = g_slist_find_custom(devices, device, cmp_device);
- if (l) {
+ if (l != NULL) {
hdev = l->data;
hdev->sdp_present = TRUE;
return 0;
}
hdev = create_health_device(conn, device);
- if (!hdev)
+ if (hdev == NULL)
return -1;
hdev->sdp_present = TRUE;
GSList *l;
l = g_slist_find_custom(devices, device, cmp_device);
- if (!l)
+ if (l == NULL)
return;
hdp_dev = l->data;
return 0;
}
-void hdp_manager_stop()
+void hdp_manager_stop(void)
{
g_dbus_unregister_interface(connection, MANAGER_PATH, HEALTH_MANAGER);
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
- * Authors:
- * Santiago Carot Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
void hdp_device_unregister(struct btd_device *device);
int hdp_manager_start(DBusConnection *conn);
-void hdp_manager_stop();
+void hdp_manager_stop(void);
gboolean hdp_set_mcl_cb(struct hdp_device *device, GError **err);
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
- * Authors:
- * Santiago Carot Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
- * Authors:
- * Santiago Carot Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <btio.h>
#include <adapter.h>
#include <device.h>
+#include <glib-helper.h>
+#include <log.h>
#include "hdp_types.h"
-#include "log.h"
#include "hdp_manager.h"
#include "hdp.h"
-#include "glib-helper.h"
-
static DBusConnection *connection = NULL;
static int hdp_adapter_probe(struct btd_adapter *adapter)
int hdp_manager_init(DBusConnection *conn)
{
- if (hdp_manager_start(conn))
+ if (hdp_manager_start(conn) < 0)
return -1;
connection = dbus_connection_ref(conn);
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
- * Authors:
- * Santiago Carot Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
- * Authors:
- * Santiago Carot Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
char *description; /* Options description for SDP record */
uint8_t id; /* The identification is also the mdepid */
char *oname; /* Name of the owner application */
- int dbus_watcher; /* Watch for clients disconnection */
+ guint dbus_watcher; /* Watch for clients disconnection */
gint ref; /* Reference counter */
};
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
- * Authors:
- * Santiago Carot Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <glib.h>
+
#include <gdbus.h>
#include <adapter.h>
#include <device.h>
-#include <stdint.h>
-#include <hdp_types.h>
-#include <hdp_util.h>
-#include <mcap.h>
-#include <hdp.h>
#include <sdpd.h>
-#include <sdp_lib.h>
+#include <bluetooth/sdp_lib.h>
+#include <sdp-client.h>
#include <glib-helper.h>
#include <btio.h>
-#include <mcap_lib.h>
#include <log.h>
+#include "mcap.h"
+#include "mcap_lib.h"
+#include "hdp_types.h"
+#include "hdp.h"
+#include "hdp_util.h"
+
typedef gboolean (*parse_item_f)(DBusMessageIter *iter, gpointer user_data,
GError **err);
/* set l2cap information */
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
- if (!l2cap_list) {
+ if (l2cap_list == NULL) {
ret = FALSE;
goto end;
}
psm = sdp_data_alloc(SDP_UINT16, &adapter->ccpsm);
- if (!psm) {
+ if (psm == NULL) {
ret = FALSE;
goto end;
}
- if (!sdp_list_append(l2cap_list, psm)) {
+ if (sdp_list_append(l2cap_list, psm) == NULL) {
ret = FALSE;
goto end;
}
proto_list = sdp_list_append(NULL, l2cap_list);
- if (!proto_list) {
+ if (proto_list == NULL) {
ret = FALSE;
goto end;
}
/* set mcap information */
sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
- if (!mcap_list) {
+ if (mcap_list == NULL) {
ret = FALSE;
goto end;
}
mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
- if (!mcap_ver) {
+ if (mcap_ver == NULL) {
ret = FALSE;
goto end;
}
- if (!sdp_list_append(mcap_list, mcap_ver)) {
+ if (sdp_list_append(mcap_list, mcap_ver) == NULL) {
ret = FALSE;
goto end;
}
- if (!sdp_list_append(proto_list, mcap_list)) {
+ if (sdp_list_append(proto_list, mcap_list) == NULL) {
ret = FALSE;
goto end;
}
/* attach protocol information to service record */
access_proto_list = sdp_list_append(NULL, proto_list);
- if (!access_proto_list) {
+ if (access_proto_list == NULL) {
ret = FALSE;
goto end;
}
ret = TRUE;
end:
- if (l2cap_list)
+ if (l2cap_list != NULL)
sdp_list_free(l2cap_list, NULL);
- if (mcap_list)
+ if (mcap_list != NULL)
sdp_list_free(mcap_list, NULL);
- if (proto_list)
+ if (proto_list != NULL)
sdp_list_free(proto_list, NULL);
- if (access_proto_list)
+ if (access_proto_list != NULL)
sdp_list_free(access_proto_list, NULL);
- if (psm)
+ if (psm != NULL)
sdp_data_free(psm);
- if (mcap_ver)
+ if (mcap_ver != NULL)
sdp_data_free(mcap_ver);
return ret;
sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID);
hdp_profile.version = HDP_VERSION;
profile_list = sdp_list_append(NULL, &hdp_profile);
- if (!profile_list)
+ if (profile_list == NULL)
return FALSE;
/* set profile descriptor list */
/* set l2cap information */
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
- if (!l2cap_list) {
+ if (l2cap_list == NULL) {
ret = FALSE;
goto end;
}
psm = sdp_data_alloc(SDP_UINT16, &adapter->dcpsm);
- if (!psm) {
+ if (psm == NULL) {
ret = FALSE;
goto end;
}
- if (!sdp_list_append(l2cap_list, psm)) {
+ if (sdp_list_append(l2cap_list, psm) == NULL) {
ret = FALSE;
goto end;
}
proto_list = sdp_list_append(NULL, l2cap_list);
- if (!proto_list) {
+ if (proto_list == NULL) {
ret = FALSE;
goto end;
}
/* set mcap information */
sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID);
mcap_list = sdp_list_append(NULL, &mcap_d_uuid);
- if (!mcap_list) {
+ if (mcap_list == NULL) {
ret = FALSE;
goto end;
}
- if (!sdp_list_append(proto_list, mcap_list)) {
+ if (sdp_list_append(proto_list, mcap_list) == NULL) {
ret = FALSE;
goto end;
}
/* attach protocol information to service record */
access_proto_list = sdp_list_append(NULL, proto_list);
- if (!access_proto_list) {
+ if (access_proto_list == NULL) {
ret = FALSE;
goto end;
}
ret = TRUE;
end:
- if (l2cap_list)
+ if (l2cap_list != NULL)
sdp_list_free(l2cap_list, NULL);
- if (mcap_list)
+ if (mcap_list != NULL)
sdp_list_free(mcap_list, NULL);
- if (proto_list)
+ if (proto_list != NULL)
sdp_list_free(proto_list, NULL);
- if (access_proto_list)
+ if (access_proto_list != NULL)
sdp_list_free(access_proto_list, NULL);
- if (psm)
+ if (psm != NULL)
sdp_data_free(psm);
return ret;
sdp_list_t *f_list = NULL;
mdepid = sdp_data_alloc(SDP_UINT8, &app->id);
- if (!mdepid)
+ if (mdepid == NULL)
return NULL;
dtype = sdp_data_alloc(SDP_UINT16, &app->data_type);
- if (!dtype)
+ if (dtype == NULL)
goto fail;
role = sdp_data_alloc(SDP_UINT8, &app->role);
- if (!role)
+ if (role == NULL)
goto fail;
- if (app->description) {
+ if (app->description != NULL) {
desc = sdp_data_alloc(SDP_TEXT_STR8, app->description);
- if (!desc)
+ if (desc == NULL)
goto fail;
}
f_list = sdp_list_append(NULL, mdepid);
- if (!f_list)
+ if (f_list == NULL)
goto fail;
- if (!sdp_list_append(f_list, dtype))
+ if (sdp_list_append(f_list, dtype) == NULL)
goto fail;
- if (!sdp_list_append(f_list, role))
+ if (sdp_list_append(f_list, role) == NULL)
goto fail;
- if (desc)
- if (!sdp_list_append(f_list, desc))
+ if (desc != NULL)
+ if (sdp_list_append(f_list, desc) == NULL)
goto fail;
return f_list;
fail:
- if (f_list)
+ if (f_list != NULL)
sdp_list_free(f_list, NULL);
- if (mdepid)
+ if (mdepid != NULL)
sdp_data_free(mdepid);
- if (dtype)
+ if (dtype != NULL)
sdp_data_free(dtype);
- if (role)
+ if (role != NULL)
sdp_data_free(role);
- if (desc)
+ if (desc != NULL)
sdp_data_free(desc);
return NULL;
sdp_list_t *hdp_feature;
hdp_feature = app_to_sdplist(app);
- if (!hdp_feature)
+ if (hdp_feature == NULL)
goto fail;
- if (!*sup_features) {
+ if (*sup_features == NULL) {
*sup_features = sdp_list_append(NULL, hdp_feature);
- if (!*sup_features)
+ if (*sup_features == NULL)
goto fail;
- } else if (!sdp_list_append(*sup_features, hdp_feature)) {
+ } else if (sdp_list_append(*sup_features, hdp_feature) == NULL) {
goto fail;
}
return TRUE;
fail:
- if (hdp_feature)
+ if (hdp_feature != NULL)
sdp_list_free(hdp_feature, (sdp_free_func_t)sdp_data_free);
return FALSE;
}
/* As by now 11073 is the only supported we set it by default */
spec = sdp_data_alloc(SDP_UINT8, &data_spec);
- if (!spec)
+ if (spec == NULL)
return FALSE;
if (sdp_attr_add(record, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) {
uint8_t mcap_sup_proc = MCAP_SUP_PROC;
mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc);
- if (!mcap_proc)
+ if (mcap_proc == NULL)
return FALSE;
if (sdp_attr_add(sdp_record, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES,
sdp_record_t *sdp_record;
bdaddr_t addr;
- if (adapter->sdp_handler)
+ if (adapter->sdp_handler > 0)
remove_record_from_server(adapter->sdp_handler);
- if (!app_list) {
+ if (app_list == NULL) {
adapter->sdp_handler = 0;
return TRUE;
}
sdp_record = sdp_record_alloc();
- if (!sdp_record)
+ if (sdp_record == NULL)
return FALSE;
- if (adapter->sdp_handler)
+ if (adapter->sdp_handler > 0)
sdp_record->handle = adapter->sdp_handler;
else
sdp_record->handle = 0xffffffff; /* Set automatically */
register_mcap_features(sdp_record);
- if (sdp_set_record_state(sdp_record, adapter->record_state++))
+ if (sdp_set_record_state(sdp_record, adapter->record_state++) < 0)
goto fail;
adapter_get_address(adapter->btd_adapter, &addr);
return TRUE;
fail:
- if (sdp_record)
+ if (sdp_record != NULL)
sdp_record_free(sdp_record);
return FALSE;
}
{
sdp_data_t *list, *feat;
- if (!desc && !mdep)
+ if (desc == NULL && mdep == NULL)
return TRUE;
list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST);
-
- if (list->dtd != SDP_SEQ8 && list->dtd != SDP_SEQ16 &&
- list->dtd != SDP_SEQ32)
+ if (list == NULL || (list->dtd != SDP_SEQ8 && list->dtd != SDP_SEQ16 &&
+ list->dtd != SDP_SEQ32))
return FALSE;
for (feat = list->val.dataseq; feat; feat = feat->next) {
continue;
mdepid = feat->val.dataseq;
- if (!mdepid)
+ if (mdepid == NULL)
continue;
data_type = mdepid->next;
- if (!data_type)
+ if (data_type == NULL)
continue;
role_t = data_type->next;
- if (!role_t)
+ if (role_t == NULL)
continue;
desc_t = role_t->next;
!check_role(role_t->val.uint8, role))
continue;
- if (mdep)
+ if (mdep != NULL)
*mdep = mdepid->val.uint8;
- if (desc && desc_t && (desc_t->dtd == SDP_TEXT_STR8 ||
+ if (desc != NULL && desc_t != NULL &&
+ (desc_t->dtd == SDP_TEXT_STR8 ||
desc_t->dtd == SDP_TEXT_STR16 ||
desc_t->dtd == SDP_TEXT_STR32))
*desc = g_strdup(desc_t->val.str);
GError *gerr = NULL;
uint8_t mdep;
- if (err || !recs) {
+ if (err < 0 || recs == NULL) {
g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
"Error getting remote SDP records");
mdep_data->func(0, mdep_data->data, gerr);
bdaddr_t dst, src;
uuid_t uuid;
- device_get_address(device->dev, &dst);
+ device_get_address(device->dev, &dst, NULL);
adapter_get_address(device_get_adapter(device->dev), &src);
mdep_data = g_new0(struct get_mdep_data, 1);
bt_string2uuid(&uuid, HDP_UUID);
if (bt_search_service(&src, &dst, &uuid, get_mdep_cb, mdep_data,
- free_mdep_data)) {
+ free_mdep_data) < 0) {
g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
"Can't get remote SDP record");
g_free(mdep_data);
sdp_data_t *iter;
int proto;
- if (!entry || (entry->dtd != SDP_SEQ8 && entry->dtd != SDP_SEQ16 &&
- entry->dtd != SDP_SEQ32))
+ if (entry == NULL || (entry->dtd != SDP_SEQ8 &&
+ entry->dtd != SDP_SEQ16 && entry->dtd != SDP_SEQ32))
return FALSE;
iter = entry->val.dataseq;
if (proto != type)
return FALSE;
- if (!val)
+ if (val == NULL)
return TRUE;
iter = iter->next;
{
sdp_data_t *pdl, *p0, *p1;
- if (!psm && !version)
+ if (psm == NULL && version == NULL)
return TRUE;
pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST);
- if (pdl->dtd != SDP_SEQ8 && pdl->dtd != SDP_SEQ16 &&
- pdl->dtd != SDP_SEQ32)
+ if (pdl == NULL || (pdl->dtd != SDP_SEQ8 && pdl->dtd != SDP_SEQ16 &&
+ pdl->dtd != SDP_SEQ32))
return FALSE;
p0 = pdl->val.dataseq;
{
sdp_data_t *pdl, *p0, *p1;
- if (!psm)
+ if (psm == NULL)
return TRUE;
pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST);
- if (pdl->dtd != SDP_SEQ8)
+ if (pdl == NULL || pdl->dtd != SDP_SEQ8)
return FALSE;
pdl = pdl->val.dataseq;
if (pdl->dtd != SDP_SEQ8)
static void con_mcl_data_unref(struct conn_mcl_data *conn_data)
{
- if (!conn_data)
+ if (conn_data == NULL)
return;
if (--conn_data->refs > 0)
static struct conn_mcl_data *con_mcl_data_ref(struct conn_mcl_data *conn_data)
{
- if (!conn_data)
+ if (conn_data == NULL)
return NULL;
conn_data->refs++;
struct hdp_device *device = conn_data->dev;
GError *gerr = NULL;
- if (err) {
+ if (err != NULL) {
conn_data->func(conn_data->data, err);
return;
}
- if (!device->mcl)
+ if (device->mcl == NULL)
device->mcl = mcap_mcl_ref(mcl);
device->mcl_conn = TRUE;
hdp_set_mcl_cb(device, &gerr);
conn_data->func(conn_data->data, gerr);
- if (gerr)
+ if (gerr != NULL)
g_error_free(gerr);
}
bdaddr_t dst;
uint16_t ccpsm;
- if (!conn_data->dev->hdp_adapter->mi) {
+ if (conn_data->dev->hdp_adapter->mi == NULL) {
g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
"Mcap instance released");
goto fail;
}
- if (err || !recs) {
+ if (err < 0 || recs == NULL) {
g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
"Error getting remote SDP records");
goto fail;
conn_data = con_mcl_data_ref(conn_data);
- device_get_address(conn_data->dev->dev, &dst);
+ device_get_address(conn_data->dev->dev, &dst, NULL);
if (!mcap_create_mcl(conn_data->dev->hdp_adapter->mi, &dst, ccpsm,
create_mcl_cb, conn_data,
destroy_con_mcl_data, &gerr)) {
bdaddr_t dst, src;
uuid_t uuid;
- device_get_address(device->dev, &dst);
+ device_get_address(device->dev, &dst, NULL);
adapter_get_address(device_get_adapter(device->dev), &src);
conn_data = g_new0(struct conn_mcl_data, 1);
bt_string2uuid(&uuid, HDP_UUID);
if (bt_search_service(&src, &dst, &uuid, search_cb, conn_data,
- destroy_con_mcl_data)) {
+ destroy_con_mcl_data) < 0) {
g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
"Can't get remote SDP record");
g_free(conn_data);
GError *gerr = NULL;
uint16_t dcpsm;
- if (err || !recs) {
+ if (err < 0 || recs == NULL) {
g_set_error(&gerr, HDP_ERROR, HDP_CONNECTION_ERROR,
"Error getting remote SDP records");
goto fail;
{
struct get_dcpsm_data *dcpsm_data = data;
- if (!dcpsm_data)
+ if (dcpsm_data == NULL)
return;
if (dcpsm_data->destroy)
bdaddr_t dst, src;
uuid_t uuid;
- device_get_address(device->dev, &dst);
+ device_get_address(device->dev, &dst, NULL);
adapter_get_address(device_get_adapter(device->dev), &src);
dcpsm_data = g_new0(struct get_dcpsm_data, 1);
bt_string2uuid(&uuid, HDP_UUID);
if (bt_search_service(&src, &dst, &uuid, get_dcpsm_cb, dcpsm_data,
- free_dcpsm_data)) {
+ free_dcpsm_data) < 0) {
g_set_error(err, HDP_ERROR, HDP_CONNECTION_ERROR,
"Can't get remote SDP record");
g_free(dcpsm_data);
static void hdp_free_application(struct hdp_application *app)
{
- if (app->dbus_watcher)
+ if (app->dbus_watcher > 0)
g_dbus_remove_watch(app->conn, app->dbus_watcher);
- if (app->conn)
+ if (app->conn != NULL)
dbus_connection_unref(app->conn);
g_free(app->oname);
g_free(app->description);
struct hdp_application *hdp_application_ref(struct hdp_application *app)
{
- if (!app)
+ if (app == NULL)
return NULL;
app->ref++;
void hdp_application_unref(struct hdp_application *app)
{
- if (!app)
+ if (app == NULL)
return;
- app->ref --;
+ app->ref--;
DBG("health_application_unref(%p): ref=%d", app, app->ref);
if (app->ref > 0)
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
- * Authors:
- * Santiago Carot Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
*
- * Authors:
- * Santiago Carot-Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
*
*/
-#include "log.h"
-#include "error.h"
-
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
-#include "btio.h"
+#include <glib.h>
+
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
+
+#include <btio.h>
+#include <log.h>
+#include <error.h>
+
#include "mcap.h"
#include "mcap_lib.h"
#include "mcap_internal.h"
static struct mcap_mcl *find_mcl(GSList *list, const bdaddr_t *addr)
{
- GSList *l;
struct mcap_mcl *mcl;
- for (l = list; l; l = l->next) {
- mcl = l->data;
+ for (; list; list = list->next) {
+ mcl = list->data;
if (!bacmp(&mcl->addr, addr))
return mcl;
last->ctrl &= ~MCAP_CTRL_CACHED;
if (last->ctrl & MCAP_CTRL_CONN) {
/* We have to release this MCL if */
- /* connection is not succesful */
+ /* connection is not successful */
last->ctrl |= MCAP_CTRL_FREE;
} else {
mcap_mcl_release(last);
req = cmd;
mdl_id = ntohs(req->mdl);
mcl->state = MCL_CONNECTED;
+ abrt = NULL;
for (l = mcl->mdls; l; l = l->next) {
mdl = l->data;
if (mdl_id == mdl->mdlid && mdl->state == MDL_WAITING) {
/* Initialize random seed to generate mdlids for this instance */
srand(time(NULL));
- return mcap_instance_ref(mi);;
+ return mcap_instance_ref(mi);
}
void mcap_release_instance(struct mcap_instance *mi)
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
* Copyright (C) 2010 Signove
*
- * Authors:
- * Santiago Carot-Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
- * Elvis Pfützenreuter <epx at signove.com>
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
*
- * Authors:
- * Santiago Carot-Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
*
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
*
- * Authors:
- * Santiago Carot-Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos.
* Copyright (C) 2010 Signove
*
- * Authors:
- * Santiago Carot-Nemesio <sancane at gmail.com>
- * Jose Antonio Santos-Cadenas <santoscadenas at gmail.com>
- * Elvis Pfützenreuter <epx at signove.com>
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
*
*/
-#include "btio.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <stdint.h>
#include <netinet/in.h>
#include <time.h>
#include <stdlib.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/l2cap.h>
-#include "../src/adapter.h"
-#include "../src/manager.h"
#include <sys/ioctl.h>
-#include "config.h"
-#include "log.h"
-
#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <adapter.h>
+#include <manager.h>
+#include <btio.h>
+#include <log.h>
+
#include "mcap.h"
#include "mcap_lib.h"
#include "mcap_internal.h"
#include <gdbus.h>
#include "log.h"
-#include "textfile.h"
#include "uinput.h"
#include "../src/adapter.h"
GSList *connections;
};
-GSList *devices = NULL;
+static GSList *devices = NULL;
static struct input_device *find_device_by_path(GSList *list, const char *path)
{
- GSList *l;
-
- for (l = list; l; l = l->next) {
- struct input_device *idev = l->data;
+ for (; list; list = list->next) {
+ struct input_device *idev = list->data;
if (!strcmp(idev->path, path))
return idev;
static struct input_conn *find_connection(GSList *list, const char *pattern)
{
- GSList *l;
-
- for (l = list; l; l = l->next) {
- struct input_conn *iconn = l->data;
+ for (; list; list = list->next) {
+ struct input_conn *iconn = list->data;
if (!strcasecmp(iconn->uuid, pattern))
return iconn;
if (fd < 0) {
fd = open("/dev/misc/uinput", O_RDWR);
if (fd < 0) {
- err = errno;
+ err = -errno;
error("Can't open input device: %s (%d)",
- strerror(err), err);
- return -err;
+ strerror(-err), -err);
+ return err;
}
}
}
dev.id.version = 0x0000;
if (write(fd, &dev, sizeof(dev)) < 0) {
- err = errno;
+ err = -errno;
error("Can't write device information: %s (%d)",
- strerror(err), err);
+ strerror(-err), -err);
close(fd);
- errno = err;
- return -err;
+ return err;
}
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_KEYBIT, KEY_PAGEDOWN);
if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
- err = errno;
+ err = -errno;
error("Can't create uinput device: %s (%d)",
- strerror(err), err);
+ strerror(-err), -err);
close(fd);
- errno = err;
- return -err;
+ return err;
}
return fd;
return key;
}
-static void send_event(int fd, uint16_t type, uint16_t code, int32_t value)
+static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
{
struct uinput_event event;
- int err;
memset(&event, 0, sizeof(event));
event.type = type;
event.code = code;
event.value = value;
- err = write(fd, &event, sizeof(event));
+ return write(fd, &event, sizeof(event));
}
static void send_key(int fd, uint16_t key)
*/
fake->uinput = uinput_create(idev->name);
if (fake->uinput < 0) {
+ int err = fake->uinput;
+
g_io_channel_shutdown(chan, TRUE, NULL);
reply = btd_error_failed(iconn->pending_connect,
- strerror(errno));
+ strerror(-err));
goto failed;
}
return -errno;
if (ioctl(ctl, HIDPCONNADD, req) < 0)
- err = errno;
+ err = -errno;
close(ctl);
- return -err;
+ return err;
}
static void encrypt_completed(uint8_t status, gpointer user_data)
extract_hid_record(rec, req);
sdp_record_free(rec);
- read_device_id(src_addr, dst_addr, NULL,
- &req->vendor, &req->product, &req->version);
+ req->vendor = btd_device_get_vendor(idev->device);
+ req->product = btd_device_get_product(idev->device);
+ req->version = btd_device_get_version(idev->device);
fake_hid = get_fake_hid(req->vendor, req->product);
if (fake_hid) {
struct fake_input *fake = iconn->fake;
struct hidp_conndel_req req;
struct hidp_conninfo ci;
- int ctl, err;
+ int ctl, err = 0;
/* Fake input disconnect */
if (fake) {
bacpy(&ci.bdaddr, &idev->dst);
if ((ioctl(ctl, HIDPGETCONNINFO, &ci) < 0) ||
(ci.state != BT_CONNECTED)) {
- errno = ENOTCONN;
+ err = -ENOTCONN;
goto fail;
}
bacpy(&req.bdaddr, &idev->dst);
req.flags = flags;
if (ioctl(ctl, HIDPCONNDEL, &req) < 0) {
+ err = -errno;
error("Can't delete the HID device: %s(%d)",
- strerror(errno), errno);
+ strerror(-err), -err);
goto fail;
}
- close(ctl);
-
- return 0;
-
fail:
- err = errno;
close(ctl);
- errno = err;
- return -err;
+ return err;
}
static int disconnect(struct input_device *idev, uint32_t flags)
return -1;
adapter_get_address(adapter, &src);
- device_get_address(device, &dst);
+ device_get_address(device, &dst, NULL);
return input_device_register(connection, device, path, &src, &dst,
HID_UUID, rec->handle, idle_timeout * 60);
const gchar *path = device_get_path(device);
const sdp_record_t *record;
sdp_list_t *protos;
- uint8_t ch;
+ int ch;
bdaddr_t src, dst;
DBG("path %s", path);
}
adapter_get_address(adapter, &src);
- device_get_address(device, &dst);
+ device_get_address(device, &dst, NULL);
return fake_input_register(connection, device, path, &src, &dst,
HSP_HS_UUID, ch);
{
uint16_t psm;
bdaddr_t src, dst;
+ char address[18];
GError *gerr = NULL;
int ret;
return;
}
- DBG("Incoming connection on PSM %d", psm);
+ ba2str(&dst, address);
+ DBG("Incoming connection from %s on PSM %d", address, psm);
ret = input_device_set_channel(&src, &dst, psm, chan);
if (ret == 0)
return;
+ error("Refusing input device connect: %s (%d)", strerror(-ret), -ret);
+
/* Send unplug virtual cable to unknown devices */
if (ret == -ENOENT && psm == L2CAP_PSM_HIDP_CTRL) {
unsigned char unplug = 0x15;
- int err, sk = g_io_channel_unix_get_fd(chan);
- err = write(sk, &unplug, sizeof(unplug));
+ int sk = g_io_channel_unix_get_fd(chan);
+ if (write(sk, &unplug, sizeof(unplug)) < 0)
+ error("Unable to send virtual cable unplug");
}
g_io_channel_shutdown(chan, TRUE, NULL);
}
if (server->confirm) {
- error("Refusing connection: setup in progress");
+ char address[18];
+
+ ba2str(&dst, address);
+ error("Refusing connection from %s: setup in progress",
+ address);
goto drop;
}
--- /dev/null
+/* To compile
+ * gcc -g -Wall -I../src -I../lib/ -I../include -DSTORAGEDIR=\"/var/lib/bluetooth\" -o sixpair sixpair.c ../src/storage.c ../common/libhelper.a -I../common `pkg-config --libs --cflags glib-2.0 libusb-1.0` -lbluetooth
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+#include <sdp.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp_lib.h>
+#include <glib.h>
+#include <libusb.h>
+
+#include "storage.h"
+
+/* Vendor and product ID for the Sixaxis PS3 controller */
+#define VENDOR 0x054c
+#define PRODUCT 0x0268
+
+#define PS3_PNP_RECORD "3601920900000A000100000900013503191124090004350D35061901000900113503190011090006350909656E09006A0901000900093508350619112409010009000D350F350D350619010009001335031900110901002513576972656C65737320436F6E74726F6C6C65720901012513576972656C65737320436F6E74726F6C6C6572090102251B536F6E7920436F6D707574657220456E7465727461696E6D656E740902000901000902010901000902020800090203082109020428010902052801090206359A35980822259405010904A101A102850175089501150026FF00810375019513150025013500450105091901291381027501950D0600FF8103150026FF0005010901A10075089504350046FF0009300931093209358102C0050175089527090181027508953009019102750895300901B102C0A1028502750895300901B102C0A10285EE750895300901B102C0A10285EF750895300901B102C0C0090207350835060904090901000902082800090209280109020A280109020B09010009020C093E8009020D280009020E2800"
+
+gboolean option_get_master = TRUE;
+char *option_master= NULL;
+gboolean option_store_info = TRUE;
+const char *option_device = NULL;
+gboolean option_quiet = FALSE;
+
+const GOptionEntry options[] = {
+ { "get-master", '\0', 0, G_OPTION_ARG_NONE, &option_get_master, "Get currently set master address", NULL },
+ { "set-master", '\0', 0, G_OPTION_ARG_STRING, &option_master, "Set master address (\"auto\" for automatic)", NULL },
+ { "store-info", '\0', 0, G_OPTION_ARG_NONE, &option_store_info, "Store the HID info into the input database", NULL },
+ { "device", '\0', 0, G_OPTION_ARG_STRING, &option_device, "Only handle one device (default, all supported", NULL },
+ { "quiet", 'q', 0, G_OPTION_ARG_NONE, &option_quiet, "Quieten the output", NULL },
+ { NULL }
+};
+
+static gboolean
+show_master (libusb_device_handle *devh, int itfnum)
+{
+ unsigned char msg[8];
+ int res;
+
+ res = libusb_control_transfer (devh,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+ 0x01, 0x03f5, itfnum,
+ (void*) msg, sizeof(msg),
+ 5000);
+
+ if (res < 0) {
+ g_warning ("Getting the master Bluetooth address failed");
+ return FALSE;
+ }
+ g_print ("Current Bluetooth master: %02X:%02X:%02X:%02X:%02X:%02X\n",
+ msg[2], msg[3], msg[4], msg[5], msg[6], msg[7]);
+
+ return TRUE;
+}
+
+static char *
+get_bdaddr (libusb_device_handle *devh, int itfnum)
+{
+ unsigned char msg[17];
+ char *address;
+ int res;
+
+ res = libusb_control_transfer (devh,
+ LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+ 0x01, 0x03f2, itfnum,
+ (void*) msg, sizeof(msg),
+ 5000);
+
+ if (res < 0) {
+ g_warning ("Getting the device Bluetooth address failed");
+ return NULL;
+ }
+
+ address = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+ msg[4], msg[5], msg[6], msg[7], msg[8], msg[9]);
+
+ if (option_quiet == FALSE) {
+ g_print ("Device Bluetooth address: %s\n", address);
+ }
+
+ return address;
+}
+
+static gboolean
+set_master_bdaddr (libusb_device_handle *devh, int itfnum, char *host)
+{
+ unsigned char msg[8];
+ int mac[6];
+ int res;
+
+ if (sscanf(host, "%X:%X:%X:%X:%X:%X",
+ &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) != 6) {
+ return FALSE;
+ }
+
+ msg[0] = 0x01;
+ msg[1] = 0x00;
+ msg[2] = mac[0];
+ msg[3] = mac[1];
+ msg[4] = mac[2];
+ msg[5] = mac[3];
+ msg[6] = mac[4];
+ msg[7] = mac[5];
+
+ res = libusb_control_transfer (devh,
+ LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE,
+ 0x09, 0x03f5, itfnum,
+ (void*) msg, sizeof(msg),
+ 5000);
+
+ if (res < 0) {
+ g_warning ("Setting the master Bluetooth address failed");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static char *
+get_host_bdaddr (void)
+{
+ FILE *f;
+ int mac[6];
+
+ //FIXME use dbus to get the default adapter
+
+ f = popen("hcitool dev", "r");
+
+ if (f == NULL) {
+ //FIXME
+ return NULL;
+ }
+ if (fscanf(f, "%*s\n%*s %X:%X:%X:%X:%X:%X",
+ &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) != 6) {
+ //FIXME
+ return NULL;
+ }
+
+ return g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+static int
+handle_device (libusb_device *dev, struct libusb_config_descriptor *cfg, int itfnum, const struct libusb_interface_descriptor *alt)
+{
+ libusb_device_handle *devh;
+ int res, retval;
+
+ retval = -1;
+
+ if (libusb_open (dev, &devh) < 0) {
+ g_warning ("Can't open device");
+ goto bail;
+ }
+ libusb_detach_kernel_driver (devh, itfnum);
+
+ res = libusb_claim_interface (devh, itfnum);
+ if (res < 0) {
+ g_warning ("Can't claim interface %d", itfnum);
+ goto bail;
+ }
+
+ if (option_get_master != FALSE) {
+ if (show_master (devh, itfnum) == FALSE)
+ goto bail;
+ retval = 0;
+ }
+
+ if (option_master != NULL) {
+ if (strcmp (option_master, "auto") == 0) {
+ g_free (option_master);
+ option_master = get_host_bdaddr ();
+ if (option_master == NULL) {
+ g_warning ("Can't get bdaddr from default device");
+ retval = -1;
+ goto bail;
+ }
+ }
+ } else {
+ option_master = get_host_bdaddr ();
+ if (option_master == NULL) {
+ g_warning ("Can't get bdaddr from default device");
+ retval = -1;
+ goto bail;
+ }
+ }
+
+ if (option_store_info != FALSE) {
+ sdp_record_t *rec;
+ char *device;
+ bdaddr_t dst, src;
+
+ device = get_bdaddr (devh, itfnum);
+ if (device == NULL) {
+ retval = -1;
+ goto bail;
+ }
+
+ rec = record_from_string (PS3_PNP_RECORD);
+ store_record(option_master, device, rec);
+ write_trust(option_master, device, "[all]", TRUE);
+ store_device_id(option_master, device, 0xffff, 0x054c, 0x0268, 0);
+ str2ba(option_master, &src);
+ str2ba(device, &dst);
+ write_device_profiles(&src, &dst, "");
+ write_device_name(&src, &dst, "PLAYSTATION(R)3 Controller");
+ sdp_record_free(rec);
+
+ if (set_master_bdaddr (devh, itfnum, option_master) == FALSE) {
+ retval = -1;
+ goto bail;
+ }
+ }
+
+bail:
+ libusb_release_interface (devh, itfnum);
+ res = libusb_attach_kernel_driver(devh, itfnum);
+ if (res < 0) {
+ //FIXME sometimes the kernel tells us ENOENT, but succeeds anyway...
+ g_warning ("Reattaching the driver failed: %d", res);
+ }
+ if (devh != NULL)
+ libusb_close (devh);
+
+ return retval;
+}
+
+int main (int argc, char **argv)
+{
+ GOptionContext *context;
+ GError *error = NULL;
+ libusb_device **list;
+ ssize_t num_devices, i;
+
+ context = g_option_context_new ("- Manage Sixaxis PS3 controllers");
+ g_option_context_add_main_entries (context, options, NULL);
+ if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
+ g_warning ("Couldn't parse command-line options: %s", error->message);
+ return 1;
+ }
+
+ /* Check that the passed bdaddr is correct */
+ if (option_master != NULL && strcmp (option_master, "auto") != 0) {
+ //FIXME check bdaddr
+ }
+
+ libusb_init (NULL);
+
+ /* Find device(s) */
+ num_devices = libusb_get_device_list (NULL, &list);
+ if (num_devices < 0) {
+ g_warning ("libusb_get_device_list failed");
+ return 1;
+ }
+
+ for (i = 0; i < num_devices; i++) {
+ struct libusb_config_descriptor *cfg;
+ libusb_device *dev = list[i];
+ struct libusb_device_descriptor desc;
+ guint8 j;
+
+ if (libusb_get_device_descriptor (dev, &desc) < 0) {
+ g_warning ("libusb_get_device_descriptor failed");
+ continue;
+ }
+
+ /* Here we check for the supported devices */
+ if (desc.idVendor != VENDOR || desc.idProduct != PRODUCT)
+ continue;
+
+ /* Look for the interface number that interests us */
+ for (j = 0; j < desc.bNumConfigurations; j++) {
+ struct libusb_config_descriptor *config;
+ guint8 k;
+
+ libusb_get_config_descriptor (dev, j, &config);
+
+ for (k = 0; k < config->bNumInterfaces; k++) {
+ const struct libusb_interface *itf = &config->interface[k];
+ int l;
+
+ for (l = 0; l < itf->num_altsetting ; l++) {
+ struct libusb_interface_descriptor alt;
+
+ alt = itf->altsetting[l];
+ if (alt.bInterfaceClass == 3) {
+ handle_device (dev, cfg, l, &alt);
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
return "RDA Microelectronics";
case 98:
return "Gibson Guitars";
+ case 99:
+ return "MiCommand Inc.";
+ case 100:
+ return "Band XI International, LLC";
+ case 101:
+ return "Hewlett-Packard Company";
+ case 102:
+ return "9Solutions Oy";
+ case 103:
+ return "GN Netcom A/S";
+ case 104:
+ return "General Motors";
+ case 105:
+ return "A&D Engineering, Inc.";
+ case 106:
+ return "MindTree Ltd.";
+ case 107:
+ return "Polar Electro OY";
+ case 108:
+ return "Beautiful Enterprise Co., Ltd.";
+ case 109:
+ return "BriarTek, Inc.";
+ case 110:
+ return "Summit Data Communications, Inc.";
+ case 111:
+ return "Sound ID";
+ case 112:
+ return "Monster, LLC";
+ case 113:
+ return "connectBlue AB";
+ case 114:
+ return "ShangHai Super Smart Electronics Co. Ltd.";
+ case 115:
+ return "Group Sense Ltd.";
+ case 116:
+ return "Zomm, LLC";
case 65535:
return "internal use";
default:
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define htobs(d) (d)
#define htobl(d) (d)
+#define htobll(d) (d)
#define btohs(d) (d)
#define btohl(d) (d)
+#define btohll(d) (d)
#elif __BYTE_ORDER == __BIG_ENDIAN
#define htobs(d) bswap_16(d)
#define htobl(d) bswap_32(d)
+#define htobll(d) bswap_64(d)
#define btohs(d) bswap_16(d)
#define btohl(d) bswap_32(d)
+#define btohll(d) bswap_64(d)
#else
#error "Unknown byte order"
#endif
__p->__v = (val); \
} while(0)
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline uint64_t bt_get_le64(void *ptr)
+{
+ return bt_get_unaligned((uint64_t *) ptr);
+}
+
+static inline uint64_t bt_get_be64(void *ptr)
+{
+ return bswap_64(bt_get_unaligned((uint64_t *) ptr));
+}
+
+static inline uint32_t bt_get_le32(void *ptr)
+{
+ return bt_get_unaligned((uint32_t *) ptr);
+}
+
+static inline uint32_t bt_get_be32(void *ptr)
+{
+ return bswap_32(bt_get_unaligned((uint32_t *) ptr));
+}
+
+static inline uint16_t bt_get_le16(void *ptr)
+{
+ return bt_get_unaligned((uint16_t *) ptr);
+}
+
+static inline uint16_t bt_get_be16(void *ptr)
+{
+ return bswap_16(bt_get_unaligned((uint16_t *) ptr));
+}
+#elif __BYTE_ORDER == __BIG_ENDIAN
+static inline uint64_t bt_get_le64(void *ptr)
+{
+ return bswap_64(bt_get_unaligned((uint64_t *) ptr));
+}
+
+static inline uint64_t bt_get_be64(void *ptr)
+{
+ return bt_get_unaligned((uint64_t *) ptr);
+}
+
+static inline uint32_t bt_get_le32(void *ptr)
+{
+ return bswap_32(bt_get_unaligned((uint32_t *) ptr));
+}
+
+static inline uint32_t bt_get_be32(void *ptr)
+{
+ return bt_get_unaligned((uint32_t *) ptr);
+}
+
+static inline uint16_t bt_get_le16(void *ptr)
+{
+ return bswap_16(bt_get_unaligned((uint16_t *) ptr));
+}
+
+static inline uint16_t bt_get_be16(void *ptr)
+{
+ return bt_get_unaligned((uint16_t *) ptr);
+}
+#else
+#error "Unknown byte order"
+#endif
+
/* BD Address */
typedef struct {
uint8_t b[6];
}
*fec = rp.fec;
- memcpy(data, rp.data, 240);
+ memcpy(data, rp.data, HCI_MAX_EIR_LENGTH);
return 0;
}
memset(&cp, 0, sizeof(cp));
cp.fec = fec;
- memcpy(cp.data, data, 240);
+ memcpy(cp.data, data, HCI_MAX_EIR_LENGTH);
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_HOST_CTL;
} __attribute__ ((packed)) delete_stored_link_key_rp;
#define DELETE_STORED_LINK_KEY_RP_SIZE 3
+#define HCI_MAX_NAME_LENGTH 248
+
#define OCF_CHANGE_LOCAL_NAME 0x0013
typedef struct {
- uint8_t name[248];
+ uint8_t name[HCI_MAX_NAME_LENGTH];
} __attribute__ ((packed)) change_local_name_cp;
#define CHANGE_LOCAL_NAME_CP_SIZE 248
#define OCF_READ_LOCAL_NAME 0x0014
typedef struct {
uint8_t status;
- uint8_t name[248];
+ uint8_t name[HCI_MAX_NAME_LENGTH];
} __attribute__ ((packed)) read_local_name_rp;
#define READ_LOCAL_NAME_RP_SIZE 249
} __attribute__ ((packed)) write_afh_mode_rp;
#define WRITE_AFH_MODE_RP_SIZE 1
+#define HCI_MAX_EIR_LENGTH 240
+
#define OCF_READ_EXT_INQUIRY_RESPONSE 0x0051
typedef struct {
uint8_t status;
uint8_t fec;
- uint8_t data[240];
+ uint8_t data[HCI_MAX_EIR_LENGTH];
} __attribute__ ((packed)) read_ext_inquiry_response_rp;
#define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242
#define OCF_WRITE_EXT_INQUIRY_RESPONSE 0x0052
typedef struct {
uint8_t fec;
- uint8_t data[240];
+ uint8_t data[HCI_MAX_EIR_LENGTH];
} __attribute__ ((packed)) write_ext_inquiry_response_cp;
#define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241
typedef struct {
#define OCF_READ_LOCAL_AMP_ASSOC 0x000A
typedef struct {
uint8_t handle;
- uint16_t length_so_far;
- uint16_t assoc_length;
+ uint16_t len_so_far;
+ uint16_t max_len;
} __attribute__ ((packed)) read_local_amp_assoc_cp;
-#define READ_LOCAL_AMP_ASSOC_CP_SIZE 5
+
typedef struct {
uint8_t status;
uint8_t handle;
- uint16_t length;
- uint8_t fragment[248];
+ uint16_t rem_len;
+ uint8_t frag[0];
} __attribute__ ((packed)) read_local_amp_assoc_rp;
-#define READ_LOCAL_AMP_ASSOC_RP_SIZE 252
#define OCF_WRITE_REMOTE_AMP_ASSOC 0x000B
typedef struct {
uint8_t handle;
uint16_t length_so_far;
uint16_t assoc_length;
- uint8_t fragment[248];
+ uint8_t fragment[HCI_MAX_NAME_LENGTH];
} __attribute__ ((packed)) write_remote_amp_assoc_cp;
#define WRITE_REMOTE_AMP_ASSOC_CP_SIZE 253
typedef struct {
typedef struct {
uint8_t status;
bdaddr_t bdaddr;
- uint8_t name[248];
+ uint8_t name[HCI_MAX_NAME_LENGTH];
} __attribute__ ((packed)) evt_remote_name_req_complete;
#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255
uint8_t dev_class[3];
uint16_t clock_offset;
int8_t rssi;
- uint8_t data[240];
+ uint8_t data[HCI_MAX_EIR_LENGTH];
} __attribute__ ((packed)) extended_inquiry_info;
#define EXTENDED_INQUIRY_INFO_SIZE 254
#define L2CAP_INFO_REQ 0x0a
#define L2CAP_INFO_RSP 0x0b
+/* L2CAP extended feature mask */
+#define L2CAP_FEAT_FLOWCTL 0x00000001
+#define L2CAP_FEAT_RETRANS 0x00000002
+#define L2CAP_FEAT_BIDIR_QOS 0x00000004
+#define L2CAP_FEAT_ERTM 0x00000008
+#define L2CAP_FEAT_STREAMING 0x00000010
+#define L2CAP_FEAT_FCS 0x00000020
+#define L2CAP_FEAT_EXT_FLOW 0x00000040
+#define L2CAP_FEAT_FIXED_CHAN 0x00000080
+#define L2CAP_FEAT_EXT_WINDOW 0x00000100
+#define L2CAP_FEAT_UCD 0x00000200
+
+/* L2CAP fixed channels */
+#define L2CAP_FC_L2CAP 0x02
+#define L2CAP_FC_CONNLESS 0x04
+#define L2CAP_FC_A2MP 0x08
+
/* L2CAP structures */
typedef struct {
uint16_t len;
#define L2CAP_CONF_UNACCEPT 0x0001
#define L2CAP_CONF_REJECT 0x0002
#define L2CAP_CONF_UNKNOWN 0x0003
+#define L2CAP_CONF_PENDING 0x0004
+#define L2CAP_CONF_EFS_REJECT 0x0005
typedef struct {
uint8_t type;
#define L2CAP_CONF_QOS 0x03
#define L2CAP_CONF_RFC 0x04
#define L2CAP_CONF_FCS 0x05
+#define L2CAP_CONF_EFS 0x06
+#define L2CAP_CONF_EWS 0x07
#define L2CAP_CONF_MAX_SIZE 22
#define L2CAP_MODE_ERTM 0x03
#define L2CAP_MODE_STREAMING 0x04
+#define L2CAP_SERVTYPE_NOTRAFFIC 0x00
+#define L2CAP_SERVTYPE_BESTEFFORT 0x01
+#define L2CAP_SERVTYPE_GUARANTEED 0x02
+
typedef struct {
uint16_t dcid;
uint16_t scid;
uint16_t index[0];
} __packed;
+/* Reserve one extra byte for names in management messages so that they
+ * are always guaranteed to be nul-terminated */
+#define MGMT_MAX_NAME_LENGTH (HCI_MAX_NAME_LENGTH + 1)
+#define MGMT_MAX_SHORT_NAME_LENGTH (10 + 1)
+
+#define MGMT_SETTING_POWERED 0x00000001
+#define MGMT_SETTING_CONNECTABLE 0x00000002
+#define MGMT_SETTING_FAST_CONNECTABLE 0x00000004
+#define MGMT_SETTING_DISCOVERABLE 0x00000008
+#define MGMT_SETTING_PAIRABLE 0x00000010
+#define MGMT_SETTING_LINK_SECURITY 0x00000020
+#define MGMT_SETTING_SSP 0x00000040
+#define MGMT_SETTING_BREDR 0x00000080
+#define MGMT_SETTING_HS 0x00000100
+#define MGMT_SETTING_LE 0x00000200
+
#define MGMT_OP_READ_INFO 0x0004
struct mgmt_rp_read_info {
- uint8_t type;
- uint8_t powered;
- uint8_t connectable;
- uint8_t discoverable;
- uint8_t pairable;
- uint8_t sec_mode;
bdaddr_t bdaddr;
- uint8_t dev_class[3];
- uint8_t features[8];
+ uint8_t version;
uint16_t manufacturer;
- uint8_t hci_ver;
- uint16_t hci_rev;
- uint8_t name[249];
+ uint32_t supported_settings;
+ uint32_t current_settings;
+ uint8_t dev_class[3];
+ uint8_t name[MGMT_MAX_NAME_LENGTH];
+ uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
} __packed;
struct mgmt_mode {
#define MGMT_OP_SET_POWERED 0x0005
#define MGMT_OP_SET_DISCOVERABLE 0x0006
+struct mgmt_cp_set_discoverable {
+ uint8_t val;
+ uint16_t timeout;
+} __packed;
#define MGMT_OP_SET_CONNECTABLE 0x0007
-#define MGMT_OP_SET_PAIRABLE 0x0008
+#define MGMT_OP_SET_FAST_CONNECTABLE 0x0008
-#define MGMT_OP_ADD_UUID 0x0009
-struct mgmt_cp_add_uuid {
- uint8_t uuid[16];
- uint8_t svc_hint;
-} __packed;
+#define MGMT_OP_SET_PAIRABLE 0x0009
-#define MGMT_OP_REMOVE_UUID 0x000A
-struct mgmt_cp_remove_uuid {
- uint8_t uuid[16];
-} __packed;
+#define MGMT_OP_SET_LINK_SECURITY 0x000A
+
+#define MGMT_OP_SET_SSP 0x000B
+
+#define MGMT_OP_SET_HS 0x000C
+
+#define MGMT_OP_SET_LE 0x000D
-#define MGMT_OP_SET_DEV_CLASS 0x000B
+#define MGMT_OP_SET_DEV_CLASS 0x000E
struct mgmt_cp_set_dev_class {
uint8_t major;
uint8_t minor;
} __packed;
-#define MGMT_OP_SET_SERVICE_CACHE 0x000C
-struct mgmt_cp_set_service_cache {
- uint8_t enable;
+#define MGMT_OP_SET_LOCAL_NAME 0x000F
+struct mgmt_cp_set_local_name {
+ uint8_t name[MGMT_MAX_NAME_LENGTH];
+ uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
+} __packed;
+
+#define MGMT_OP_ADD_UUID 0x0010
+struct mgmt_cp_add_uuid {
+ uint8_t uuid[16];
+ uint8_t svc_hint;
+} __packed;
+
+#define MGMT_OP_REMOVE_UUID 0x0011
+struct mgmt_cp_remove_uuid {
+ uint8_t uuid[16];
} __packed;
-struct mgmt_key_info {
+struct mgmt_link_key_info {
bdaddr_t bdaddr;
uint8_t type;
uint8_t val[16];
uint8_t pin_len;
} __packed;
-#define MGMT_OP_LOAD_KEYS 0x000D
-struct mgmt_cp_load_keys {
+#define MGMT_OP_LOAD_LINK_KEYS 0x0012
+struct mgmt_cp_load_link_keys {
uint8_t debug_keys;
uint16_t key_count;
- struct mgmt_key_info keys[0];
+ struct mgmt_link_key_info keys[0];
} __packed;
-#define MGMT_OP_REMOVE_KEY 0x000E
-struct mgmt_cp_remove_key {
+#define MGMT_OP_REMOVE_KEYS 0x0013
+struct mgmt_cp_remove_keys {
bdaddr_t bdaddr;
uint8_t disconnect;
} __packed;
+struct mgmt_rp_remove_keys {
+ bdaddr_t bdaddr;
+ uint8_t status;
+} __packed;
-#define MGMT_OP_DISCONNECT 0x000F
+#define MGMT_OP_DISCONNECT 0x0014
struct mgmt_cp_disconnect {
bdaddr_t bdaddr;
} __packed;
struct mgmt_rp_disconnect {
bdaddr_t bdaddr;
+ uint8_t status;
+} __packed;
+
+#define MGMT_ADDR_BREDR 0x00
+#define MGMT_ADDR_LE_PUBLIC 0x01
+#define MGMT_ADDR_LE_RANDOM 0x02
+#define MGMT_ADDR_INVALID 0xff
+
+struct mgmt_addr_info {
+ bdaddr_t bdaddr;
+ uint8_t type;
} __packed;
-#define MGMT_OP_GET_CONNECTIONS 0x0010
+#define MGMT_OP_GET_CONNECTIONS 0x0015
struct mgmt_rp_get_connections {
uint16_t conn_count;
- bdaddr_t conn[0];
+ struct mgmt_addr_info addr[0];
} __packed;
-#define MGMT_OP_PIN_CODE_REPLY 0x0011
+#define MGMT_OP_PIN_CODE_REPLY 0x0016
struct mgmt_cp_pin_code_reply {
bdaddr_t bdaddr;
uint8_t pin_len;
uint8_t pin_code[16];
} __packed;
-#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0012
+#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0017
struct mgmt_cp_pin_code_neg_reply {
bdaddr_t bdaddr;
} __packed;
-#define MGMT_OP_SET_IO_CAPABILITY 0x0013
+#define MGMT_OP_SET_IO_CAPABILITY 0x0018
struct mgmt_cp_set_io_capability {
uint8_t io_capability;
} __packed;
-#define MGMT_OP_PAIR_DEVICE 0x0014
+#define MGMT_OP_PAIR_DEVICE 0x0019
struct mgmt_cp_pair_device {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint8_t io_cap;
} __packed;
struct mgmt_rp_pair_device {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint8_t status;
} __packed;
-#define MGMT_OP_USER_CONFIRM_REPLY 0x0015
+#define MGMT_OP_USER_CONFIRM_REPLY 0x001A
struct mgmt_cp_user_confirm_reply {
bdaddr_t bdaddr;
} __packed;
uint8_t status;
} __packed;
-#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x0016
+#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001B
-#define MGMT_OP_SET_LOCAL_NAME 0x0017
-struct mgmt_cp_set_local_name {
- uint8_t name[249];
+#define MGMT_OP_USER_PASSKEY_REPLY 0x001C
+struct mgmt_cp_user_passkey_reply {
+ bdaddr_t bdaddr;
+ uint32_t passkey;
+} __packed;
+struct mgmt_rp_user_passkey_reply {
+ bdaddr_t bdaddr;
+ uint8_t status;
+} __packed;
+
+#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001D
+struct mgmt_cp_user_passkey_neg_reply {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_READ_LOCAL_OOB_DATA 0x001E
+struct mgmt_rp_read_local_oob_data {
+ uint8_t hash[16];
+ uint8_t randomizer[16];
+} __packed;
+
+#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x001F
+struct mgmt_cp_add_remote_oob_data {
+ bdaddr_t bdaddr;
+ uint8_t hash[16];
+ uint8_t randomizer[16];
+} __packed;
+
+#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0020
+struct mgmt_cp_remove_remote_oob_data {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_START_DISCOVERY 0x0021
+struct mgmt_cp_start_discovery {
+ uint8_t type;
+} __packed;
+
+#define MGMT_OP_STOP_DISCOVERY 0x0022
+
+#define MGMT_OP_CONFIRM_NAME 0x0023
+struct mgmt_cp_confirm_name {
+ bdaddr_t bdaddr;
+ uint8_t name_known;
+} __packed;
+struct mgmt_rp_confirm_name {
+ bdaddr_t bdaddr;
+ uint8_t status;
+} __packed;
+
+#define MGMT_OP_BLOCK_DEVICE 0x0024
+struct mgmt_cp_block_device {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_UNBLOCK_DEVICE 0x0025
+struct mgmt_cp_unblock_device {
+ bdaddr_t bdaddr;
} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
#define MGMT_EV_INDEX_REMOVED 0x0005
-#define MGMT_EV_POWERED 0x0006
-
-#define MGMT_EV_DISCOVERABLE 0x0007
+#define MGMT_EV_NEW_SETTINGS 0x0006
-#define MGMT_EV_CONNECTABLE 0x0008
-
-#define MGMT_EV_PAIRABLE 0x0009
-
-#define MGMT_EV_NEW_KEY 0x000A
-struct mgmt_ev_new_key {
- struct mgmt_key_info key;
- uint8_t old_key_type;
+#define MGMT_EV_CLASS_OF_DEV_CHANGED 0x0007
+struct mgmt_ev_class_of_dev_changed {
+ uint8_t class_of_dev[3];
} __packed;
-#define MGMT_EV_DEVICE_CONNECTED 0x000B
-struct mgmt_ev_device_connected {
- bdaddr_t bdaddr;
+#define MGMT_EV_LOCAL_NAME_CHANGED 0x0008
+struct mgmt_ev_local_name_changed {
+ uint8_t name[MGMT_MAX_NAME_LENGTH];
+ uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
} __packed;
-#define MGMT_EV_DEVICE_DISCONNECTED 0x000C
-struct mgmt_ev_device_disconnected {
- bdaddr_t bdaddr;
+#define MGMT_EV_NEW_LINK_KEY 0x0009
+struct mgmt_ev_new_link_key {
+ uint8_t store_hint;
+ struct mgmt_link_key_info key;
} __packed;
-#define MGMT_EV_CONNECT_FAILED 0x000D
+#define MGMT_EV_DEVICE_CONNECTED 0x000A
+
+#define MGMT_EV_DEVICE_DISCONNECTED 0x000B
+
+#define MGMT_EV_CONNECT_FAILED 0x000C
struct mgmt_ev_connect_failed {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint8_t status;
} __packed;
-#define MGMT_EV_PIN_CODE_REQUEST 0x000E
+#define MGMT_EV_PIN_CODE_REQUEST 0x000D
struct mgmt_ev_pin_code_request {
bdaddr_t bdaddr;
+ uint8_t secure;
} __packed;
-#define MGMT_EV_USER_CONFIRM_REQUEST 0x000F
+#define MGMT_EV_USER_CONFIRM_REQUEST 0x000E
struct mgmt_ev_user_confirm_request {
bdaddr_t bdaddr;
+ uint8_t confirm_hint;
uint32_t value;
} __packed;
+#define MGMT_EV_USER_PASSKEY_REQUEST 0x000F
+struct mgmt_ev_user_passkey_request {
+ bdaddr_t bdaddr;
+} __packed;
+
#define MGMT_EV_AUTH_FAILED 0x0010
struct mgmt_ev_auth_failed {
bdaddr_t bdaddr;
uint8_t status;
} __packed;
-#define MGMT_EV_LOCAL_NAME_CHANGED 0x0011
-struct mgmt_ev_local_name_changed {
- uint8_t name[249];
+#define MGMT_EV_DEVICE_FOUND 0x0011
+struct mgmt_ev_device_found {
+ struct mgmt_addr_info addr;
+ uint8_t dev_class[3];
+ int8_t rssi;
+ uint8_t confirm_name;
+ uint8_t eir[HCI_MAX_EIR_LENGTH];
+} __packed;
+
+#define MGMT_EV_REMOTE_NAME 0x0012
+struct mgmt_ev_remote_name {
+ bdaddr_t bdaddr;
+ uint8_t name[MGMT_MAX_NAME_LENGTH];
+} __packed;
+
+#define MGMT_EV_DISCOVERING 0x0013
+
+#define MGMT_EV_DEVICE_BLOCKED 0x0014
+struct mgmt_ev_device_blocked {
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_EV_DEVICE_UNBLOCKED 0x0015
+struct mgmt_ev_device_unblocked {
+ bdaddr_t bdaddr;
} __packed;
int data_type = 0;
uint8_t *p = buf->data + buf->data_size;
- *p++ = dtd;
+ *p = dtd;
data_type = sdp_get_data_type(buf, dtd);
buf->data_size += data_type;
*p++ = SDP_UINT16;
buf->data_size = sizeof(uint8_t);
bt_put_unaligned(htons(attr), (uint16_t *) p);
- p += sizeof(uint16_t);
buf->data_size += sizeof(uint16_t);
}
}
if (!is_seq && !is_alt) {
- if (src && buf && buf->buf_size >= buf->data_size + data_size) {
+ if (src && buf->buf_size >= buf->data_size + data_size) {
memcpy(buf->data + buf->data_size, src, data_size);
buf->data_size += data_size;
} else if (dtd != SDP_DATA_NIL) {
}
sdp_uuid16_create(uuid, ntohs(bt_get_unaligned((uint16_t *) p)));
*scanned += sizeof(uint16_t);
- p += sizeof(uint16_t);
} else if (type == SDP_UUID32) {
if (bufsize < (int) sizeof(uint32_t)) {
SDPERR("Not enough room for 32-bit UUID");
}
sdp_uuid32_create(uuid, ntohl(bt_get_unaligned((uint32_t *) p)));
*scanned += sizeof(uint32_t);
- p += sizeof(uint32_t);
} else {
if (bufsize < (int) sizeof(uint128_t)) {
SDPERR("Not enough room for 128-bit UUID");
}
sdp_uuid128_create(uuid, p);
*scanned += sizeof(uint128_t);
- p += sizeof(uint128_t);
}
return 0;
}
for (q = 0, p = list; p; q = p, p = p->next)
if (f(p->data, d) >= 0)
break;
- // insert between q and p; if !q insert at head
+ /* insert between q and p; if !q insert at head */
if (q)
q->next = n;
else
curr_data = sdpdata->val.dataseq;
while (curr_data) {
sdp_data_t *pCode = curr_data;
- sdp_data_t *pEncoding = pCode->next;
- sdp_data_t *pOffset = pEncoding->next;
- if (pEncoding && pOffset) {
- lang = malloc(sizeof(sdp_lang_attr_t));
- if (!lang) {
- sdp_list_free(*langSeq, free);
- *langSeq = NULL;
- return -1;
- }
- lang->code_ISO639 = pCode->val.uint16;
- lang->encoding = pEncoding->val.uint16;
- lang->base_offset = pOffset->val.uint16;
- SDPDBG("code_ISO639 : 0x%02x\n", lang->code_ISO639);
- SDPDBG("encoding : 0x%02x\n", lang->encoding);
- SDPDBG("base_offfset : 0x%02x\n", lang->base_offset);
- *langSeq = sdp_list_append(*langSeq, lang);
+ sdp_data_t *pEncoding;
+ sdp_data_t *pOffset;
+
+ pEncoding = pCode->next;
+ if (!pEncoding)
+ break;
+
+ pOffset = pEncoding->next;
+ if (!pOffset)
+ break;
+
+ lang = malloc(sizeof(sdp_lang_attr_t));
+ if (!lang) {
+ sdp_list_free(*langSeq, free);
+ *langSeq = NULL;
+ return -1;
}
+ lang->code_ISO639 = pCode->val.uint16;
+ lang->encoding = pEncoding->val.uint16;
+ lang->base_offset = pOffset->val.uint16;
+ SDPDBG("code_ISO639 : 0x%02x\n", lang->code_ISO639);
+ SDPDBG("encoding : 0x%02x\n", lang->encoding);
+ SDPDBG("base_offfset : 0x%02x\n", lang->base_offset);
+ *langSeq = sdp_list_append(*langSeq, lang);
+
curr_data = pOffset->next;
}
+
return 0;
}
if (dst->data_size == 0 && dtd == 0) {
/* create initial sequence */
*p = SDP_SEQ8;
- p += sizeof(uint8_t);
dst->data_size += sizeof(uint8_t);
/* reserve space for sequence size */
- p += sizeof(uint8_t);
dst->data_size += sizeof(uint8_t);
}
short offset = sizeof(uint8_t) + sizeof(uint8_t);
memmove(dst->data + offset + 1, dst->data + offset,
dst->data_size - offset);
- p = dst->data;
*p = SDP_SEQ16;
- p += sizeof(uint8_t);
dst->data_size += 1;
}
- p = dst->data;
dtd = *(uint8_t *) p;
p += sizeof(uint8_t);
switch (dtd) {
return sdp_device_record_update(session, BDADDR_ANY, rec);
}
-sdp_record_t *sdp_record_alloc()
+sdp_record_t *sdp_record_alloc(void)
{
sdp_record_t *rec = malloc(sizeof(sdp_record_t));
sdp_buf_t buf;
int i, seqlen = sdp_list_len(seq);
- // Fill up the value and the dtd arrays
+ /* Fill up the value and the dtd arrays */
SDPDBG("");
SDPDBG("Seq length : %d\n", seqlen);
uint32_t reqsize = 0, _reqsize;
uint32_t rspsize = 0, rsplen;
int seqlen = 0;
- int total_rec_count, rec_count;
+ int rec_count;
unsigned scanned, pdata_len;
uint8_t *pdata, *_pdata;
uint8_t *reqbuf, *rspbuf;
pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
reqsize = sizeof(sdp_pdu_hdr_t);
- // add service class IDs for search
+ /* add service class IDs for search */
seqlen = gen_searchseq_pdu(pdata, search);
SDPDBG("Data seq added : %d\n", seqlen);
- // set the length and increment the pointer
+ /* set the length and increment the pointer */
reqsize += seqlen;
pdata += seqlen;
- // specify the maximum svc rec count that client expects
+ /* specify the maximum svc rec count that client expects */
bt_put_unaligned(htons(max_rec_num), (uint16_t *) pdata);
reqsize += sizeof(uint16_t);
pdata += sizeof(uint16_t);
*rsp = NULL;
do {
- // Add continuation state or NULL (first time)
+ /* Add continuation state or NULL (first time) */
reqsize = _reqsize + copy_cstate(_pdata,
SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
- // Set the request header's param length
+ /* Set the request header's param length */
reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
reqhdr->tid = htons(sdp_gen_tid(session));
goto end;
}
- // net service record match count
- total_rec_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
+ /* net service record match count */
pdata += sizeof(uint16_t);
scanned += sizeof(uint16_t);
pdata_len -= sizeof(uint16_t);
pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
reqsize = sizeof(sdp_pdu_hdr_t);
- // add the service record handle
+ /* add the service record handle */
bt_put_unaligned(htonl(handle), (uint32_t *) pdata);
reqsize += sizeof(uint32_t);
pdata += sizeof(uint32_t);
- // specify the response limit
+ /* specify the response limit */
bt_put_unaligned(htons(65535), (uint16_t *) pdata);
reqsize += sizeof(uint16_t);
pdata += sizeof(uint16_t);
- // get attr seq PDU form
+ /* get attr seq PDU form */
seqlen = gen_attridseq_pdu(pdata, attrids,
reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32);
if (seqlen == -1) {
reqsize += seqlen;
SDPDBG("Attr list length : %d\n", seqlen);
- // save before Continuation State
+ /* save before Continuation State */
_pdata = pdata;
_reqsize = reqsize;
do {
int status;
- // add NULL continuation state
+ /* add NULL continuation state */
reqsize = _reqsize + copy_cstate(_pdata,
SDP_REQ_BUFFER_SIZE - _reqsize, cstate);
- // set the request header's param length
+ /* set the request header's param length */
reqhdr->tid = htons(sdp_gen_tid(session));
reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
pdata += sizeof(uint16_t);
pdata_len -= sizeof(uint16_t);
- // if continuation state set need to re-issue request before parsing
+ /*
+ * if continuation state set need to re-issue request before
+ * parsing
+ */
if (pdata_len < rsp_count + sizeof(uint8_t)) {
SDPERR("Unexpected end of packet: continuation state data missing");
goto end;
cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0;
- // build concatenated response buffer
+ /* build concatenated response buffer */
rsp_concat_buf.data = realloc(rsp_concat_buf.data, rsp_concat_buf.data_size + rsp_count);
rsp_concat_buf.buf_size = rsp_concat_buf.data_size + rsp_count;
targetPtr = rsp_concat_buf.data + rsp_concat_buf.data_size;
reqhdr->tid = htons(sdp_gen_tid(session));
reqhdr->pdu_id = SDP_SVC_SEARCH_REQ;
- // generate PDU
+ /* generate PDU */
pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
t->reqsize = sizeof(sdp_pdu_hdr_t);
- // add service class IDs for search
+ /* add service class IDs for search */
seqlen = gen_searchseq_pdu(pdata, search);
SDPDBG("Data seq added : %d\n", seqlen);
- // now set the length and increment the pointer
+ /* now set the length and increment the pointer */
t->reqsize += seqlen;
pdata += seqlen;
t->reqsize += sizeof(uint16_t);
pdata += sizeof(uint16_t);
- // set the request header's param length
+ /* set the request header's param length */
cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
reqhdr->tid = htons(sdp_gen_tid(session));
reqhdr->pdu_id = SDP_SVC_ATTR_REQ;
- // generate PDU
+ /* generate PDU */
pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
t->reqsize = sizeof(sdp_pdu_hdr_t);
- // add the service record handle
+ /* add the service record handle */
bt_put_unaligned(htonl(handle), (uint32_t *) pdata);
t->reqsize += sizeof(uint32_t);
pdata += sizeof(uint32_t);
- // specify the response limit
+ /* specify the response limit */
bt_put_unaligned(htons(65535), (uint16_t *) pdata);
t->reqsize += sizeof(uint16_t);
pdata += sizeof(uint16_t);
- // get attr seq PDU form
+ /* get attr seq PDU form */
seqlen = gen_attridseq_pdu(pdata, attrid_list,
reqtype == SDP_ATTR_REQ_INDIVIDUAL? SDP_UINT16 : SDP_UINT32);
if (seqlen == -1) {
goto end;
}
- // now set the length and increment the pointer
+ /* now set the length and increment the pointer */
t->reqsize += seqlen;
pdata += seqlen;
SDPDBG("Attr list length : %d\n", seqlen);
- // set the request header's param length
+ /* set the request header's param length */
cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
reqhdr->tid = htons(sdp_gen_tid(session));
reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;
- // generate PDU
+ /* generate PDU */
pdata = t->reqbuf + sizeof(sdp_pdu_hdr_t);
t->reqsize = sizeof(sdp_pdu_hdr_t);
- // add service class IDs for search
+ /* add service class IDs for search */
seqlen = gen_searchseq_pdu(pdata, search);
SDPDBG("Data seq added : %d\n", seqlen);
- // now set the length and increment the pointer
+ /* now set the length and increment the pointer */
t->reqsize += seqlen;
pdata += seqlen;
SDPDBG("Max attr byte count : %d\n", SDP_MAX_ATTR_LEN);
- // get attr seq PDU form
+ /* get attr seq PDU form */
seqlen = gen_attridseq_pdu(pdata, attrid_list,
reqtype == SDP_ATTR_REQ_INDIVIDUAL ? SDP_UINT16 : SDP_UINT32);
if (seqlen == -1) {
SDPDBG("Attr list length : %d\n", seqlen);
t->reqsize += seqlen;
- // set the request header's param length
+ /* set the request header's param length */
cstate_len = copy_cstate(pdata, SDP_REQ_BUFFER_SIZE - t->reqsize, NULL);
reqhdr->plen = htons((t->reqsize + cstate_len) - sizeof(sdp_pdu_hdr_t));
*/
plen = sizeof(uint16_t) + rsp_count;
- pdata += sizeof(uint16_t); // points to attribute list
+ pdata += sizeof(uint16_t); /* points to attribute list */
status = 0x0000;
break;
case SDP_ERROR_RSP:
status = ntohs(bt_get_unaligned((uint16_t *) pdata));
size = ntohs(rsphdr->plen);
- /* error code + error info */
- plen = size;
goto end;
default:
t->err = EPROTO;
reqhdr->tid = htons(sdp_gen_tid(session));
- // add continuation state
+ /* add continuation state */
cstate_len = copy_cstate(t->reqbuf + t->reqsize,
SDP_REQ_BUFFER_SIZE - t->reqsize, pcstate);
reqsize = t->reqsize + cstate_len;
- // set the request header's param length
+ /* set the request header's param length */
reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t));
if (sdp_send_req(session, t->reqbuf, reqsize) < 0) {
reqhdr = (sdp_pdu_hdr_t *) reqbuf;
reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;
- // generate PDU
+ /* generate PDU */
pdata = reqbuf + sizeof(sdp_pdu_hdr_t);
reqsize = sizeof(sdp_pdu_hdr_t);
- // add service class IDs for search
+ /* add service class IDs for search */
seqlen = gen_searchseq_pdu(pdata, search);
SDPDBG("Data seq added : %d\n", seqlen);
rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata));
attr_list_len += rsp_count;
- pdata += sizeof(uint16_t); // pdata points to attribute list
+ pdata += sizeof(uint16_t); /* pdata points to attribute list */
pdata_len -= sizeof(uint16_t);
if (pdata_len < rsp_count + sizeof(uint8_t)) {
{
return session->tid++;
}
-#ifdef __TIZEN_PATCH__
-sdp_data_t *sdp_extract_attr_safe(const uint8_t *p, int bufsize, int *size, sdp_record_t *rec)
-{
- sdp_data_t *elem;
- int n = 0;
- uint8_t dtd;
-
- if (bufsize < sizeof(uint8_t)) {
- SDPERR("Unexpected end of packet");
- return NULL;
- }
-
- dtd = *(const uint8_t *)p;
-
- SDPDBG("extract_attr: dtd=0x%x", dtd);
- switch (dtd) {
- case SDP_DATA_NIL:
- case SDP_BOOL:
- case SDP_UINT8:
- case SDP_UINT16:
- case SDP_UINT32:
- case SDP_UINT64:
- case SDP_UINT128:
- case SDP_INT8:
- case SDP_INT16:
- case SDP_INT32:
- case SDP_INT64:
- case SDP_INT128:
- elem = extract_int(p, bufsize, &n);
- break;
- case SDP_UUID16:
- case SDP_UUID32:
- case SDP_UUID128:
- elem = extract_uuid(p, bufsize, &n, rec);
- break;
- case SDP_TEXT_STR8:
- case SDP_TEXT_STR16:
- case SDP_TEXT_STR32:
- case SDP_URL_STR8:
- case SDP_URL_STR16:
- case SDP_URL_STR32:
- elem = extract_str(p, bufsize, &n);
- break;
- case SDP_SEQ8:
- case SDP_SEQ16:
- case SDP_SEQ32:
- case SDP_ALT8:
- case SDP_ALT16:
- case SDP_ALT32:
- elem = extract_seq(p, bufsize, &n, rec);
- break;
- default:
- SDPERR("Unknown data descriptor : 0x%x terminating\n", dtd);
- return NULL;
- }
- *size += n;
- return elem;
-}
-
-int sdp_extract_seqtype_safe(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size)
-{
- uint8_t dtd;
- int scanned = sizeof(uint8_t);
-
- if (bufsize < sizeof(uint8_t)) {
- SDPERR("Unexpected end of packet");
- return 0;
- }
-
- dtd = *(uint8_t *) buf;
- buf += sizeof(uint8_t);
- bufsize -= sizeof(uint8_t);
- *dtdp = dtd;
- switch (dtd) {
- case SDP_SEQ8:
- case SDP_ALT8:
- if (bufsize < sizeof(uint8_t)) {
- SDPERR("Unexpected end of packet");
- return 0;
- }
- *size = *(uint8_t *) buf;
- scanned += sizeof(uint8_t);
- break;
- case SDP_SEQ16:
- case SDP_ALT16:
- if (bufsize < sizeof(uint16_t)) {
- SDPERR("Unexpected end of packet");
- return 0;
- }
- *size = ntohs(bt_get_unaligned((uint16_t *) buf));
- scanned += sizeof(uint16_t);
- break;
- case SDP_SEQ32:
- case SDP_ALT32:
- if (bufsize < sizeof(uint32_t)) {
- SDPERR("Unexpected end of packet");
- return 0;
- }
- *size = ntohl(bt_get_unaligned((uint32_t *) buf));
- scanned += sizeof(uint32_t);
- break;
- default:
- SDPERR("Unknown sequence type, aborting\n");
- return 0;
- }
- return scanned;
-}
-
-
-sdp_record_t *sdp_extract_pdu_safe(const uint8_t *buf, int bufsize, int *scanned)
-{
- int extracted = 0, seqlen = 0;
- uint8_t dtd;
- uint16_t attr;
- sdp_record_t *rec = sdp_record_alloc();
- const uint8_t *p = buf;
-
- *scanned = sdp_extract_seqtype_safe(buf, bufsize, &dtd, &seqlen);
- p += *scanned;
- bufsize -= *scanned;
- rec->attrlist = NULL;
-
- while (extracted < seqlen && bufsize > 0) {
- int n = sizeof(uint8_t), attrlen = 0;
- sdp_data_t *data = NULL;
-
- SDPDBG("Extract PDU, sequenceLength: %d localExtractedLength: %d",
- seqlen, extracted);
-
- if (bufsize < n + sizeof(uint16_t)) {
- SDPERR("Unexpected end of packet");
- break;
- }
-
- dtd = *(uint8_t *) p;
- attr = ntohs(bt_get_unaligned((uint16_t *) (p + n)));
- n += sizeof(uint16_t);
-
- SDPDBG("DTD of attrId : %d Attr id : 0x%x \n", dtd, attr);
-
- data = sdp_extract_attr_safe(p + n, bufsize - n, &attrlen, rec);
-
- SDPDBG("Attr id : 0x%x attrValueLength : %d\n", attr, attrlen);
-
- n += attrlen;
- if (data == NULL) {
- SDPDBG("Terminating extraction of attributes");
- break;
- }
-
- if (attr == SDP_ATTR_RECORD_HANDLE)
- rec->handle = data->val.uint32;
-
- if (attr == SDP_ATTR_SVCLASS_ID_LIST)
- extract_svclass_uuid(data, &rec->svclass);
-
- extracted += n;
- p += n;
- bufsize -= n;
- sdp_attr_replace(rec, attr, data);
-
- SDPDBG("Extract PDU, seqLength: %d localExtractedLength: %d",
- seqlen, extracted);
- }
-#ifdef SDP_DEBUG
- SDPDBG("Successful extracting of Svc Rec attributes\n");
- sdp_print_service_attr(rec->attrlist);
-#endif
- *scanned += seqlen;
- return rec;
-}
-#endif
/*
* Set the supported features
*/
#define SDP_RSP_BUFFER_SIZE 65535
#define SDP_PDU_CHUNK_SIZE 1024
-#define BLUEZ_SDP_DEBUG(format, args...) printf("%s():%d " format, /*__FILE__,*/ __FUNCTION__, __LINE__, ##args) /*__SYAM__*/
/*
* All definitions are based on Bluetooth Assigned Numbers
* of the Bluetooth Specification
int state;
int local;
int flags;
- uint16_t tid; // Current transaction ID
+ uint16_t tid; /* Current transaction ID */
void *priv;
} sdp_session_t;
{
switch (src->type) {
case BT_UUID128:
- memcpy(dst, src, sizeof(bt_uuid_t));
+ *dst = *src;
break;
case BT_UUID32:
bt_uuid32_to_uuid128(src, dst);
--- /dev/null
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <poll.h>
+#include <getopt.h>
+#include <stdbool.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/mgmt.h>
+
+#ifndef NELEM
+#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+static const char *mgmt_op[] = {
+ "<0x0000>",
+ "Read Version",
+ "Read Features",
+ "Read Index List",
+ "Read Controller Info",
+ "Set Powered",
+ "Set Discoverable",
+ "Set Connectable",
+ "Set Pairable", /* 0x0008 */
+ "Add UUID",
+ "Remove UUID",
+ "Set Dev Class",
+ "Set Service Cache",
+ "Load Link Keys",
+ "Remove Keys",
+ "Disconnect",
+ "Get Connections", /* 0x0010 */
+ "PIN Code Reply",
+ "PIN Code Neg Reply",
+ "Set IO Capability",
+ "Pair Device",
+ "User Confirm Reply",
+ "User Confirm Neg Reply",
+ "Set Local Name",
+ "Read Local OOB Data", /* 0x0018 */
+ "Add Remote OOB Data",
+ "Remove Remove OOB Data",
+ "Start Discoery",
+ "Block Device",
+ "Unblock Device",
+ "Set Fast Connectable",
+};
+
+static const char *mgmt_ev[] = {
+ "<0x0000>",
+ "Command Complete",
+ "Command Status",
+ "Controller Error",
+ "Index Added",
+ "Index Removed",
+ "Powered",
+ "Discoverable",
+ "Connectable", /* 0x0008 */
+ "Pairable",
+ "New Link Key",
+ "Device Connected",
+ "Device Disconnected",
+ "Connect Failed",
+ "PIN Code Request",
+ "User Confirm Request",
+ "Authentication Failed", /* 0x0010 */
+ "Local Name Changed",
+ "Device Found",
+ "Remote Name",
+ "Discovering",
+ "Device Blocked",
+ "Device Unblocked",
+};
+
+static const char *mgmt_status[] = {
+ "Success",
+ "Unknown Command",
+ "Not Connected",
+ "Failed",
+ "Connect Failed",
+ "Authentication Failed",
+ "Not Paired",
+ "No Resources",
+ "Timeout",
+ "Already Connected",
+ "Busy",
+ "Rejected",
+ "Not Supported",
+ "Invalid Parameters",
+ "Disconnected",
+ "Not Powered",
+};
+
+static bool monitor = false;
+static bool discovery = false;
+static bool resolve_names = true;
+
+typedef void (*cmd_cb)(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data);
+
+static struct pending_cmd {
+ uint16_t op;
+ uint16_t id;
+ cmd_cb cb;
+ void *user_data;
+ struct pending_cmd *next;
+} *pending = NULL;
+
+static const char *mgmt_opstr(uint16_t op)
+{
+ if (op >= NELEM(mgmt_op))
+ return "<unknown opcode>";
+ return mgmt_op[op];
+}
+
+static const char *mgmt_evstr(uint16_t ev)
+{
+ if (ev >= NELEM(mgmt_ev))
+ return "<unknown event>";
+ return mgmt_ev[ev];
+}
+
+static const char *mgmt_errstr(uint8_t status)
+{
+ if (status >= NELEM(mgmt_status))
+ return "<unknown status>";
+ return mgmt_status[status];
+}
+
+static int mgmt_send_cmd(int mgmt_sk, uint16_t op, uint16_t id, void *data,
+ size_t len, cmd_cb func, void *user_data)
+{
+ char buf[1024];
+ struct pending_cmd *cmd;
+ struct mgmt_hdr *hdr = (void *) buf;
+
+ if (len + MGMT_HDR_SIZE > sizeof(buf))
+ return -EINVAL;
+
+ cmd = calloc(1, sizeof(struct pending_cmd));
+ if (cmd == NULL)
+ return -errno;
+
+ cmd->op = op;
+ cmd->id = id;
+ cmd->cb = func;
+ cmd->user_data = user_data;
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(op);
+ hdr->index = htobs(id);
+ hdr->len = htobs(len);
+ memcpy(buf + MGMT_HDR_SIZE, data, len);
+
+ if (write(mgmt_sk, buf, MGMT_HDR_SIZE + len) < 0) {
+ fprintf(stderr, "Unable to write to socket: %s\n",
+ strerror(errno));
+ free(cmd);
+ return -1;
+ }
+
+ cmd->next = pending;
+ pending = cmd;
+
+ return 0;
+}
+
+static int mgmt_open(void)
+{
+ struct sockaddr_hci addr;
+ int sk;
+
+ sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (sk < 0) {
+ fprintf(stderr, "socket: %s\n", strerror(errno));
+ return sk;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_family = AF_BLUETOOTH;
+ addr.hci_dev = HCI_DEV_NONE;
+ addr.hci_channel = HCI_CHANNEL_CONTROL;
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "bind: %s\n", strerror(errno));
+ close(sk);
+ return -1;
+ }
+
+ return sk;
+}
+
+static void mgmt_check_pending(int mgmt_sk, uint16_t op, uint16_t index,
+ uint16_t status, void *data, uint16_t len)
+{
+ struct pending_cmd *c, *prev;
+
+ for (c = pending, prev = NULL; c != NULL; prev = c, c = c->next) {
+ if (c->op != op)
+ continue;
+ if (c->id != index)
+ continue;
+
+ if (c == pending)
+ pending = c->next;
+ else
+ prev->next = c->next;
+
+ c->cb(mgmt_sk, op, index, status, data, len, c->user_data);
+
+ free(c);
+ }
+}
+
+static int mgmt_cmd_complete(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_cmd_complete *ev, uint16_t len)
+{
+ uint16_t op;
+
+ if (len < sizeof(*ev)) {
+ fprintf(stderr, "Too short (%u bytes) cmd complete event\n",
+ len);
+ return -EINVAL;
+ }
+
+ op = bt_get_le16(&ev->opcode);
+
+ len -= sizeof(*ev);
+
+ if (monitor)
+ printf("%s complete, opcode 0x%04x len %u\n", mgmt_opstr(op),
+ op, len);
+
+ mgmt_check_pending(mgmt_sk, op, index, 0, ev->data, len);
+
+ return 0;
+}
+
+static int mgmt_cmd_status(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_cmd_status *ev, uint16_t len)
+{
+ uint16_t opcode;
+
+ if (len < sizeof(*ev)) {
+ fprintf(stderr, "Too short (%u bytes) cmd status event\n",
+ len);
+ return -EINVAL;
+ }
+
+ opcode = bt_get_le16(&ev->opcode);
+
+ if (monitor)
+ printf("cmd status, opcode 0x%04x status 0x%02x (%s)\n",
+ opcode, ev->status, mgmt_errstr(ev->status));
+
+ if (ev->status != 0)
+ mgmt_check_pending(mgmt_sk, opcode, index, ev->status,
+ NULL, 0);
+
+ return 0;
+}
+
+static int mgmt_controller_error(uint16_t index,
+ struct mgmt_ev_controller_error *ev,
+ uint16_t len)
+{
+ if (len < sizeof(*ev)) {
+ fprintf(stderr,
+ "Too short (%u bytes) controller error event\n", len);
+ return -EINVAL;
+ }
+
+ if (monitor)
+ printf("hci%u error 0x%02x\n", index, ev->error_code);
+
+ return 0;
+}
+
+static int mgmt_index_added(int mgmt_sk, uint16_t index)
+{
+ if (monitor)
+ printf("hci%u added\n", index);
+ return 0;
+}
+
+static int mgmt_index_removed(int mgmt_sk, uint16_t index)
+{
+ if (monitor)
+ printf("hci%u removed\n", index);
+ return 0;
+}
+
+static const char *settings_str[] = {
+ "powered",
+ "connectable",
+ "fast-connectable",
+ "discoverable",
+ "pairable",
+ "link-security",
+ "ssp",
+ "br/edr",
+ "hs",
+ "le" ,
+};
+
+static void print_settings(uint32_t settings)
+{
+ unsigned i;
+
+ for (i = 0; i < NELEM(settings_str); i++) {
+ if ((settings & (1 << i)) != 0)
+ printf("%s ", settings_str[i]);
+ }
+}
+
+static int mgmt_new_settings(int mgmt_sk, uint16_t index,
+ uint32_t *ev, uint16_t len)
+{
+ if (len < sizeof(*ev)) {
+ fprintf(stderr, "Too short new_settings event (%u)\n", len);
+ return -EINVAL;
+ }
+
+ if (monitor) {
+ printf("hci%u new_settings: ", index);
+ print_settings(bt_get_le32(ev));
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static int mgmt_discovering(int mgmt_sk, uint16_t index,
+ struct mgmt_mode *ev, uint16_t len)
+{
+ if (len < sizeof(*ev)) {
+ fprintf(stderr, "Too short (%u bytes) discovering event\n",
+ len);
+ return -EINVAL;
+ }
+
+ if (ev->val == 0 && discovery)
+ exit(EXIT_SUCCESS);
+
+ if (monitor)
+ printf("hci%u discovering %s\n", index,
+ ev->val ? "on" : "off");
+
+ return 0;
+}
+
+static int mgmt_new_link_key(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_new_link_key *ev, uint16_t len)
+{
+
+ if (len != sizeof(*ev)) {
+ fprintf(stderr, "Invalid new_link_key length (%u bytes)\n",
+ len);
+ return -EINVAL;
+ }
+
+ if (monitor) {
+ char addr[18];
+ ba2str(&ev->key.bdaddr, addr);
+ printf("hci%u new_link_key %s type 0x%02x pin_len %d "
+ "store_hint %u\n", index, addr, ev->key.type,
+ ev->key.pin_len, ev->store_hint);
+ }
+
+ return 0;
+}
+
+static const char *typestr(uint8_t type)
+{
+ const char *str[] = { "BR/EDR", "LE Public", "LE Random" };
+
+ if (type <= MGMT_ADDR_LE_RANDOM)
+ return str[type];
+
+ return "(unknown)";
+}
+
+static int mgmt_connected(int mgmt_sk, uint16_t index, bool connected,
+ struct mgmt_addr_info *ev, uint16_t len)
+{
+ const char *ev_name = connected ? "connected" : "disconnected";
+
+ if (len != sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid %s event length (%u bytes)\n", ev_name, len);
+ return -EINVAL;
+ }
+
+ if (monitor) {
+ char addr[18];
+ ba2str(&ev->bdaddr, addr);
+ printf("hci%u %s type %s %s\n", index, addr,
+ typestr(ev->type), ev_name);
+ }
+
+ return 0;
+}
+
+static int mgmt_conn_failed(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_connect_failed *ev,
+ uint16_t len)
+{
+ if (len != sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid connect_failed event length (%u bytes)\n", len);
+ return -EINVAL;
+ }
+
+ if (monitor) {
+ char addr[18];
+ ba2str(&ev->addr.bdaddr, addr);
+ printf("hci%u %s type %s connect failed (status 0x%02x, %s)\n",
+ index, addr, typestr(ev->addr.type), ev->status,
+ mgmt_errstr(ev->status));
+ }
+
+ return 0;
+}
+
+static int mgmt_auth_failed(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_auth_failed *ev,
+ uint16_t len)
+{
+ if (len != sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid auth_failed event length (%u bytes)\n", len);
+ return -EINVAL;
+ }
+
+ if (monitor) {
+ char addr[18];
+ ba2str(&ev->bdaddr, addr);
+ printf("hci%u %s auth failed with status 0x%02x (%s)\n",
+ index, addr, ev->status, mgmt_errstr(ev->status));
+ }
+
+ return 0;
+}
+
+static int mgmt_name_changed(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_local_name_changed *ev,
+ uint16_t len)
+{
+ if (len != sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid local_name_changed length (%u bytes)\n", len);
+ return -EINVAL;
+ }
+
+ if (monitor)
+ printf("hci%u name changed: %s\n", index, ev->name);
+
+ return 0;
+}
+
+static void confirm_name_rsp(int mgmt_sk, uint16_t op, uint16_t id,
+ uint8_t status, void *rsp, uint16_t len,
+ void *user_data)
+{
+ struct mgmt_rp_confirm_name *rp = rsp;
+ char addr[18];
+
+ if (status != 0) {
+ fprintf(stderr,
+ "hci%u confirm_name failed with status 0x%02x (%s)\n",
+ id, status, mgmt_errstr(status));
+ return;
+ }
+
+ if (len != sizeof(*rp)) {
+ fprintf(stderr,
+ "hci%u confirm_name rsp length %u instead of %zu\n",
+ id, len, sizeof(*rp));
+ return;
+ }
+
+ ba2str(&rp->bdaddr, addr);
+
+ if (rp->status != 0)
+ fprintf(stderr,
+ "hci%u confirm_name for %s failed: 0x%02x (%s)\n",
+ id, addr, rp->status, mgmt_errstr(status));
+ else
+ printf("hci%u confirm_name succeeded for %s\n", id, addr);
+}
+
+static int mgmt_device_found(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_device_found *ev, uint16_t len)
+{
+ if (len != sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid device_found event length (%u bytes)\n", len);
+ return -EINVAL;
+ }
+
+ if (monitor || discovery) {
+ char addr[18];
+ ba2str(&ev->addr.bdaddr, addr);
+ printf("hci%u dev_found: %s type %s class 0x%02x%02x%02x "
+ "rssi %d confirm_name %u eir (%s)\n", index, addr,
+ typestr(ev->addr.type),
+ ev->dev_class[2], ev->dev_class[1], ev->dev_class[0],
+ ev->rssi, ev->confirm_name,
+ ev->eir[0] == 0 ? "no" : "yes");
+ }
+
+ if (discovery && ev->confirm_name) {
+ struct mgmt_cp_confirm_name cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, &ev->addr.bdaddr);
+ if (resolve_names)
+ cp.name_known = 0;
+ else
+ cp.name_known = 1;
+
+ mgmt_send_cmd(mgmt_sk, MGMT_OP_CONFIRM_NAME, index,
+ &cp, sizeof(cp), confirm_name_rsp,
+ NULL);
+ }
+
+ return 0;
+}
+
+static int mgmt_remote_name(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_remote_name *ev, uint16_t len)
+{
+ if (len != sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid remote_name event length (%u bytes)\n", len);
+ return -EINVAL;
+ }
+
+ if (monitor || discovery) {
+ char addr[18];
+ ba2str(&ev->bdaddr, addr);
+ printf("hci%u %s name %s\n", index, addr, ev->name);
+ }
+
+ return 0;
+}
+
+static void pin_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "hci%u PIN Code reply failed with status 0x%02x (%s)",
+ id, status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("hci%u PIN Reply successful\n", id);
+}
+
+static int mgmt_pin_reply(int mgmt_sk, uint16_t index, bdaddr_t *bdaddr,
+ const char *pin, size_t len)
+{
+ struct mgmt_cp_pin_code_reply cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.pin_len = len;
+ memcpy(cp.pin_code, pin, len);
+
+ return mgmt_send_cmd(mgmt_sk, MGMT_OP_PIN_CODE_REPLY, index,
+ &cp, sizeof(cp), pin_rsp, NULL);
+}
+
+static void pin_neg_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "hci%u PIN Neg reply failed with status 0x%02x (%s)",
+ id, status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("hci%u PIN Negative Reply successful\n", id);
+}
+
+static int mgmt_pin_neg_reply(int mgmt_sk, uint16_t index, bdaddr_t *bdaddr)
+{
+ struct mgmt_cp_pin_code_neg_reply cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ return mgmt_send_cmd(mgmt_sk, MGMT_OP_PIN_CODE_NEG_REPLY, index,
+ &cp, sizeof(cp), pin_neg_rsp, NULL);
+}
+
+static int mgmt_request_pin(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_pin_code_request *ev,
+ uint16_t len)
+{
+ char pin[18];
+ size_t pin_len;
+
+ if (len != sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid pin_code request length (%u bytes)\n", len);
+ return -EINVAL;
+ }
+
+ if (monitor) {
+ char addr[18];
+ ba2str(&ev->bdaddr, addr);
+ printf("hci%u %s request PIN\n", index, addr);
+ }
+
+ printf("PIN Request (press enter to reject) >> ");
+ fflush(stdout);
+
+ memset(pin, 0, sizeof(pin));
+
+ if (fgets(pin, sizeof(pin), stdin) == NULL || pin[0] == '\n')
+ return mgmt_pin_neg_reply(mgmt_sk, index, &ev->bdaddr);
+
+ pin_len = strlen(pin);
+ if (pin[pin_len - 1] == '\n') {
+ pin[pin_len - 1] = '\0';
+ pin_len--;
+ }
+
+ return mgmt_pin_reply(mgmt_sk, index, &ev->bdaddr, pin, pin_len);
+}
+
+static void confirm_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "hci%u User Confirm reply failed. status 0x%02x (%s)",
+ id, status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("hci%u User Confirm Reply successful\n", id);
+}
+
+static int mgmt_confirm_reply(int mgmt_sk, uint16_t index, bdaddr_t *bdaddr)
+{
+ struct mgmt_cp_user_confirm_reply cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ return mgmt_send_cmd(mgmt_sk, MGMT_OP_USER_CONFIRM_REPLY, index,
+ &cp, sizeof(cp), confirm_rsp, NULL);
+}
+
+static void confirm_neg_rsp(int mgmt_sk, uint16_t op, uint16_t id,
+ uint8_t status, void *rsp, uint16_t len,
+ void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "hci%u Confirm Neg reply failed. status 0x%02x (%s)",
+ id, status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("hci%u User Confirm Negative Reply successful\n", id);
+}
+
+static int mgmt_confirm_neg_reply(int mgmt_sk, uint16_t index,
+ bdaddr_t *bdaddr)
+{
+ struct mgmt_cp_user_confirm_reply cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ return mgmt_send_cmd(mgmt_sk, MGMT_OP_USER_CONFIRM_NEG_REPLY, index,
+ &cp, sizeof(cp), confirm_neg_rsp, NULL);
+}
+
+
+static int mgmt_user_confirm(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_user_confirm_request *ev,
+ uint16_t len)
+{
+ char rsp[5];
+ size_t rsp_len;
+ uint32_t val;
+ char addr[18];
+
+ if (len != sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid user_confirm request length (%u)\n", len);
+ return -EINVAL;
+ }
+
+ ba2str(&ev->bdaddr, addr);
+ val = bt_get_le32(&ev->value);
+
+ if (monitor)
+ printf("hci%u %s User Confirm %06u hint %u\n", index, addr,
+ val, ev->confirm_hint);
+
+ if (ev->confirm_hint)
+ printf("Accept pairing with %s (yes/no) >> ", addr);
+ else
+ printf("Confirm value %06u for %s (yes/no) >> ", val, addr);
+
+ fflush(stdout);
+
+ memset(rsp, 0, sizeof(rsp));
+
+ if (fgets(rsp, sizeof(rsp), stdin) == NULL || rsp[0] == '\n')
+ return mgmt_confirm_neg_reply(mgmt_sk, index, &ev->bdaddr);
+
+ rsp_len = strlen(rsp);
+ if (rsp[rsp_len - 1] == '\n') {
+ rsp[rsp_len - 1] = '\0';
+ rsp_len--;
+ }
+
+ if (rsp[0] == 'y' || rsp[0] == 'Y')
+ return mgmt_confirm_reply(mgmt_sk, index, &ev->bdaddr);
+ else
+ return mgmt_confirm_neg_reply(mgmt_sk, index, &ev->bdaddr);
+}
+
+static int mgmt_handle_event(int mgmt_sk, uint16_t ev, uint16_t index,
+ void *data, uint16_t len)
+{
+ if (monitor)
+ printf("event: %s\n", mgmt_evstr(ev));
+
+ switch (ev) {
+ case MGMT_EV_CMD_COMPLETE:
+ return mgmt_cmd_complete(mgmt_sk, index, data, len);
+ case MGMT_EV_CMD_STATUS:
+ return mgmt_cmd_status(mgmt_sk, index, data, len);
+ case MGMT_EV_CONTROLLER_ERROR:
+ return mgmt_controller_error(index, data, len);
+ case MGMT_EV_INDEX_ADDED:
+ return mgmt_index_added(mgmt_sk, index);
+ case MGMT_EV_INDEX_REMOVED:
+ return mgmt_index_removed(mgmt_sk, index);
+ case MGMT_EV_NEW_SETTINGS:
+ return mgmt_new_settings(mgmt_sk, index, data, len);
+ case MGMT_EV_DISCOVERING:
+ return mgmt_discovering(mgmt_sk, index, data, len);
+ case MGMT_EV_NEW_LINK_KEY:
+ return mgmt_new_link_key(mgmt_sk, index, data, len);
+ case MGMT_EV_DEVICE_CONNECTED:
+ return mgmt_connected(mgmt_sk, index, true, data, len);
+ case MGMT_EV_DEVICE_DISCONNECTED:
+ return mgmt_connected(mgmt_sk, index, false, data, len);
+ case MGMT_EV_CONNECT_FAILED:
+ return mgmt_conn_failed(mgmt_sk, index, data, len);
+ case MGMT_EV_AUTH_FAILED:
+ return mgmt_auth_failed(mgmt_sk, index, data, len);
+ case MGMT_EV_LOCAL_NAME_CHANGED:
+ return mgmt_name_changed(mgmt_sk, index, data, len);
+ case MGMT_EV_DEVICE_FOUND:
+ return mgmt_device_found(mgmt_sk, index, data, len);
+ case MGMT_EV_REMOTE_NAME:
+ return mgmt_remote_name(mgmt_sk, index, data, len);
+ case MGMT_EV_PIN_CODE_REQUEST:
+ return mgmt_request_pin(mgmt_sk, index, data, len);
+ case MGMT_EV_USER_CONFIRM_REQUEST:
+ return mgmt_user_confirm(mgmt_sk, index, data, len);
+ default:
+ if (monitor)
+ printf("Unhandled event 0x%04x (%s)\n", ev, mgmt_evstr(ev));
+ return 0;
+ }
+}
+
+static int mgmt_process_data(int mgmt_sk)
+{
+ char buf[1024];
+ struct mgmt_hdr *hdr = (void *) buf;
+ uint16_t len, ev, index;
+ ssize_t ret;
+
+ ret = read(mgmt_sk, buf, sizeof(buf));
+ if (ret < 0) {
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ return ret;
+ }
+
+ if (ret < MGMT_HDR_SIZE) {
+ fprintf(stderr, "Too small mgmt packet (%zd bytes)\n", ret);
+ return 0;
+ }
+
+ ev = bt_get_le16(&hdr->opcode);
+ index = bt_get_le16(&hdr->index);
+ len = bt_get_le16(&hdr->len);
+
+ if (monitor)
+ printf("event 0x%04x len 0x%04x index 0x%04x\n", ev, len, index);
+
+ if (ret != MGMT_HDR_SIZE + len) {
+ fprintf(stderr, "Packet length mismatch. ret %zd len %u",
+ ret, len);
+ return 0;
+ }
+
+ mgmt_handle_event(mgmt_sk, ev, index, buf + MGMT_HDR_SIZE, len);
+
+ return 0;
+}
+
+static void cmd_monitor(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ printf("Monitoring mgmt events...\n");
+ monitor = true;
+}
+
+static void info_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ struct mgmt_rp_read_info *rp = rsp;
+ char addr[18];
+
+ if (status != 0) {
+ fprintf(stderr,
+ "Reading hci%u info failed with status 0x%02x (%s)\n",
+ id, status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ fprintf(stderr, "Too small info reply (%u bytes)\n", len);
+ exit(EXIT_FAILURE);
+ }
+
+ ba2str(&rp->bdaddr, addr);
+ printf("hci%u:\taddr %s version %u manufacturer %u"
+ " class 0x%02x%02x%02x\n",
+ id, addr, rp->version, bt_get_le16(&rp->manufacturer),
+ rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
+
+ printf("\tsupported settings: ");
+ print_settings(bt_get_le32(&rp->supported_settings));
+
+ printf("\n\tcurrent settings: ");
+ print_settings(bt_get_le32(&rp->current_settings));
+
+ printf("\n\tname %s\n", rp->name);
+ printf("\tshort name %s\n", rp->short_name);
+
+ if (pending == NULL)
+ exit(EXIT_SUCCESS);
+}
+
+static void index_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ struct mgmt_rp_read_index_list *rp = rsp;
+ uint16_t count;
+ unsigned int i;
+
+ if (status != 0) {
+ fprintf(stderr,
+ "Reading index list failed with status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ fprintf(stderr, "Too small index list reply (%u bytes)\n",
+ len);
+ exit(EXIT_FAILURE);
+ }
+
+ count = bt_get_le16(&rp->num_controllers);
+
+ if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
+ fprintf(stderr,
+ "Index count (%u) doesn't match reply length (%u)\n",
+ count, len);
+ exit(EXIT_FAILURE);
+ }
+
+ if (monitor)
+ printf("Index list with %u item%s\n",
+ count, count > 1 ? "s" : "");
+
+ if (count == 0)
+ exit(EXIT_SUCCESS);
+
+ if (monitor && count > 0)
+ printf("\t");
+
+ for (i = 0; i < count; i++) {
+ uint16_t index;
+
+ index = bt_get_le16(&rp->index[i]);
+
+ if (monitor)
+ printf("hci%u ", index);
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_INFO, index, NULL,
+ 0, info_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send read_info cmd\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (monitor && count > 0)
+ printf("\n");
+}
+
+static void cmd_info(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ if (index == MGMT_INDEX_NONE) {
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_INDEX_LIST,
+ MGMT_INDEX_NONE, NULL, 0,
+ index_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send index_list cmd\n");
+ exit(EXIT_FAILURE);
+ }
+
+ return;
+ }
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_INFO, index, NULL,
+ 0, info_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send read_info cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void setting_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ uint32_t *rp = rsp;
+
+ if (status != 0) {
+ fprintf(stderr,
+ "%s for hci%u failed with status 0x%02x (%s)\n",
+ mgmt_opstr(op), id, status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ fprintf(stderr, "Too small %s response (%u bytes)\n",
+ mgmt_opstr(op), len);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("hci%u %s complete, settings: ", id, mgmt_opstr(op));
+ print_settings(bt_get_le32(rp));
+ printf("\n");
+
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_setting(int mgmt_sk, uint16_t index, uint16_t op,
+ int argc, char **argv)
+{
+ uint8_t val;
+
+ if (argc < 2) {
+ printf("Specify \"on\" or \"off\"\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
+ val = 1;
+ else if (strcasecmp(argv[1], "off") == 0)
+ val = 0;
+ else
+ val = atoi(argv[1]);
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (mgmt_send_cmd(mgmt_sk, op, index, &val, sizeof(val),
+ setting_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send %s cmd\n", mgmt_opstr(op));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_power(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ cmd_setting(mgmt_sk, index, MGMT_OP_SET_POWERED, argc, argv);
+}
+
+static void cmd_discov(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_set_discoverable cp;
+
+ if (argc < 2) {
+ printf("Usage: btmgmt %s <yes/no> [timeout]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
+ cp.val = 1;
+ else if (strcasecmp(argv[1], "off") == 0)
+ cp.val = 0;
+ else
+ cp.val = atoi(argv[1]);
+
+ if (argc > 2)
+ cp.timeout = htobs(atoi(argv[2]));
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_DISCOVERABLE, index,
+ &cp, sizeof(cp), setting_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send set_discoverable cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_connectable(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ cmd_setting(mgmt_sk, index, MGMT_OP_SET_CONNECTABLE, argc, argv);
+}
+
+static void cmd_pairable(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ cmd_setting(mgmt_sk, index, MGMT_OP_SET_PAIRABLE, argc, argv);
+}
+
+static void class_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "Setting hci%u class failed with status 0x%02x (%s)",
+ id, status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("hci%u class changed\n", id);
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_class(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ uint8_t class[2];
+
+ if (argc < 3) {
+ printf("Usage: btmgmt %s <major> <minor>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ class[0] = atoi(argv[1]);
+ class[1] = atoi(argv[2]);
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_DEV_CLASS, index,
+ class, sizeof(class), class_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send set_dev_class cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void disconnect_rsp(int mgmt_sk, uint16_t op, uint16_t id,
+ uint8_t status, void *rsp, uint16_t len,
+ void *user_data)
+{
+ struct mgmt_rp_disconnect *rp = rsp;
+ char addr[18];
+
+ if (status != 0) {
+ fprintf(stderr, "Disconnect failed with status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ fprintf(stderr, "Invalid disconnect response length (%u)\n",
+ len);
+ exit(EXIT_FAILURE);
+ }
+
+ ba2str(&rp->bdaddr, addr);
+
+ if (rp->status == 0) {
+ printf("%s disconnected\n", addr);
+ exit(EXIT_SUCCESS);
+ } else {
+ fprintf(stderr,
+ "Disconnecting %s failed with status 0x%02x (%s)\n",
+ addr, rp->status, mgmt_errstr(rp->status));
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_disconnect(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_disconnect cp;
+
+ if (argc < 2) {
+ printf("Usage: btmgmt %s <address>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ str2ba(argv[1], &cp.bdaddr);
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_DISCONNECT, index,
+ &cp, sizeof(cp), disconnect_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send disconnect cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void con_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ struct mgmt_rp_get_connections *rp = rsp;
+ uint16_t count, i;
+
+ if (len < sizeof(*rp)) {
+ fprintf(stderr, "Too small (%u bytes) get_connections rsp\n",
+ len);
+ exit(EXIT_FAILURE);
+ }
+
+ count = bt_get_le16(&rp->conn_count);
+ if (len != sizeof(*rp) + count * sizeof(struct mgmt_addr_info)) {
+ fprintf(stderr, "Invalid get_connections length "
+ " (count=%u, len=%u)\n", count, len);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = 0; i < count; i++) {
+ char addr[18];
+
+ ba2str(&rp->addr[i].bdaddr, addr);
+
+ printf("%s type %s\n", addr, typestr(rp->addr[i].type));
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_con(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_GET_CONNECTIONS, index, NULL, 0,
+ con_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send get_connections cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void find_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "Unable to start discovery. status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Discovery started\n");
+ discovery = true;
+}
+
+static void cmd_find(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_start_discovery cp;
+ uint8_t type;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ type = 0;
+ hci_set_bit(MGMT_ADDR_BREDR, &type);
+ hci_set_bit(MGMT_ADDR_LE_PUBLIC, &type);
+ hci_set_bit(MGMT_ADDR_LE_RANDOM, &type);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.type = type;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_START_DISCOVERY, index,
+ &cp, sizeof(cp), find_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send start_discovery cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void name_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr, "Unable to set local name. status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_name(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_set_local_name cp;
+
+ if (argc < 2) {
+ printf("Usage: btmgmt %s <name>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ memset(&cp, 0, sizeof(cp));
+ strncpy((char *) cp.name, argv[1], HCI_MAX_NAME_LENGTH);
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_LOCAL_NAME, index,
+ &cp, sizeof(cp), name_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send set_name cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void pair_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ struct mgmt_rp_pair_device *rp = rsp;
+ char addr[18];
+
+ if (status != 0) {
+ fprintf(stderr, "Pairing failed with status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ fprintf(stderr, "Unexpected pair_rsp len %u\n", len);
+ exit(EXIT_FAILURE);
+ }
+
+ ba2str(&rp->addr.bdaddr, addr);
+
+ if (rp->status != 0) {
+ fprintf(stderr,
+ "Pairing with %s (%s) failed. status 0x%02x (%s)\n",
+ addr, typestr(rp->addr.type), rp->status,
+ mgmt_errstr(rp->status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Paired with %s\n", addr);
+
+ exit(EXIT_SUCCESS);
+}
+
+static void pair_usage(void)
+{
+ printf("Usage: btmgmt pair [-c cap] [-t type] <remote address>\n");
+}
+
+static struct option pair_options[] = {
+ { "help", 0, 0, 'h' },
+ { "capability", 1, 0, 'c' },
+ { "type", 1, 0, 't' },
+ { 0, 0, 0, 0 }
+};
+
+static void cmd_pair(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_pair_device cp;
+ uint8_t cap = 0x01;
+ uint8_t type = MGMT_ADDR_BREDR;
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "+c:t:h", pair_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ cap = strtol(optarg, NULL, 0);
+ break;
+ case 't':
+ type = strtol(optarg, NULL, 0);
+ break;
+ case 'h':
+ default:
+ pair_usage();
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ pair_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ memset(&cp, 0, sizeof(cp));
+ str2ba(argv[0], &cp.addr.bdaddr);
+ cp.addr.type = type;
+ cp.io_cap = cap;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_PAIR_DEVICE, index, &cp, sizeof(cp),
+ pair_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send pair_device cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void remove_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ struct mgmt_rp_remove_keys *rp = rsp;
+ char addr[18];
+
+ if (status != 0) {
+ fprintf(stderr, "Remove keys failed with status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ fprintf(stderr, "Unexpected remove_keys_rsp len %u\n", len);
+ exit(EXIT_FAILURE);
+ }
+
+ ba2str(&rp->bdaddr, addr);
+
+ if (rp->status != 0) {
+ fprintf(stderr,
+ "Removing keys for %s failed. status 0x%02x (%s)\n",
+ addr, rp->status, mgmt_errstr(rp->status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Removed keys for %s\n", addr);
+
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_remove(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_remove_keys cp;
+
+ if (argc < 2) {
+ printf("Usage: btmgmt %s <remote address>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ memset(&cp, 0, sizeof(cp));
+ str2ba(argv[1], &cp.bdaddr);
+ cp.disconnect = 1;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_REMOVE_KEYS, index, &cp, sizeof(cp),
+ remove_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send remove_keys cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void keys_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr, "Load keys failed with status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Keys successfully loaded\n");
+
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_keys(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_load_link_keys cp;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ memset(&cp, 0, sizeof(cp));
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_LOAD_LINK_KEYS, index,
+ &cp, sizeof(cp), keys_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send load_keys cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static struct {
+ char *cmd;
+ void (*func)(int mgmt_sk, uint16_t index, int argc, char **argv);
+ char *doc;
+} command[] = {
+ { "monitor", cmd_monitor, "Monitor events" },
+ { "info", cmd_info, "Show controller info" },
+ { "power", cmd_power, "Toggle powered state" },
+ { "discov", cmd_discov, "Toggle discoverable state" },
+ { "connectable",cmd_connectable,"Toggle connectable state" },
+ { "pairable", cmd_pairable, "Toggle pairable state" },
+ { "class", cmd_class, "Set device major/minor class" },
+ { "disconnect", cmd_disconnect, "Disconnect device" },
+ { "con", cmd_con, "List connections" },
+ { "find", cmd_find, "Discover nearby devices" },
+ { "name", cmd_name, "Set local name" },
+ { "pair", cmd_pair, "Pair with a remote device" },
+ { "remove", cmd_remove, "Remove pairing (all keys)" },
+ { "keys", cmd_keys, "Load Keys" },
+ { NULL, NULL, 0 }
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("btmgmt ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\tbtmgmt [options] <command> [command parameters]\n");
+
+ printf("Options:\n"
+ "\t--index <id>\tSpecify adapter index\n"
+ "\t--verbose\tEnable extra logging\n"
+ "\t--help\tDisplay help\n");
+
+ printf("Commands:\n");
+ for (i = 0; command[i].cmd; i++)
+ printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
+
+ printf("\n"
+ "For more information on the usage of each command use:\n"
+ "\tbtmgmt <command> --help\n" );
+}
+
+static struct option main_options[] = {
+ { "index", 1, 0, 'i' },
+ { "verbose", 0, 0, 'v' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int opt, i, mgmt_sk;
+ uint16_t index = MGMT_INDEX_NONE;
+ struct pollfd pollfd;
+
+ while ((opt = getopt_long(argc, argv, "+hvi:",
+ main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ if (strlen(optarg) > 3 &&
+ strncasecmp(optarg, "hci", 3) == 0)
+ index = atoi(&optarg[4]);
+ else
+ index = atoi(optarg);
+ break;
+ case 'v':
+ monitor = true;
+ break;
+ case 'h':
+ default:
+ usage();
+ return 0;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ return 0;
+ }
+
+ mgmt_sk = mgmt_open();
+ if (mgmt_sk < 0) {
+ fprintf(stderr, "Unable to open mgmt socket\n");
+ return -1;
+ }
+
+ for (i = 0; command[i].cmd; i++) {
+ if (strcmp(command[i].cmd, argv[0]) != 0)
+ continue;
+
+ command[i].func(mgmt_sk, index, argc, argv);
+ break;
+ }
+
+ if (command[i].cmd == NULL) {
+ fprintf(stderr, "Unknown command: %s\n", argv[0]);
+ close(mgmt_sk);
+ return -1;
+ }
+
+ pollfd.fd = mgmt_sk;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
+
+ while (poll(&pollfd, 1, -1) >= 0) {
+ if (pollfd.revents & (POLLHUP | POLLERR | POLLNVAL))
+ break;
+
+ if (pollfd.revents & POLLIN)
+ mgmt_process_data(mgmt_sk);
+
+ pollfd.revents = 0;
+ }
+
+ close(mgmt_sk);
+
+ return 0;
+}
{ NULL }
};
-#ifdef __TIZEN_PATCH__
-struct bnep_data {
- char *devname;
- char *script;
- int pid;
-};
-
-struct bnep_data data;
-#endif
-
-
-
uint16_t bnep_service_id(const char *svc)
{
int i;
ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
if (ctl < 0) {
- int err = errno;
+ int err = -errno;
error("Failed to open control socket: %s (%d)",
- strerror(err), err);
- return -err;
+ strerror(-err), -err);
+ return err;
}
return 0;
baswap((bdaddr_t *)&req.dst, dst);
req.flags = 0;
if (ioctl(ctl, BNEPCONNDEL, &req)) {
- int err = errno;
+ int err = -errno;
error("Failed to kill connection: %s (%d)",
- strerror(err), err);
- return -err;
+ strerror(-err), -err);
+ return err;
}
return 0;
}
req.cnum = 7;
req.ci = ci;
if (ioctl(ctl, BNEPGETCONNLIST, &req)) {
- err = errno;
+ err = -errno;
error("Failed to get connection list: %s (%d)",
- strerror(err), err);
- return -err;
+ strerror(-err), -err);
+ return err;
}
for (i = 0; i < req.cnum; i++) {
req.sock = sk;
req.role = role;
if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
- int err = errno;
+ int err = -errno;
error("Failed to add device %s: %s(%d)",
- dev, strerror(err), err);
- return -err;
+ dev, strerror(-err), -err);
+ return err;
}
strncpy(dev, req.device, 16);
return 0;
}
-#ifdef __TIZEN_PATCH__
-static void bnep_child_setup(gpointer data)
-{
-}
-
-void bnep_server_connect(char *devname)
-{
- // Run bluetooth-ics
- const char *argv[5];
- GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH;
-
- argv[0] = "bluetooth-ics";
- argv[1] = devname;
- argv[2] = NULL;
- argv[3] = NULL;
-
- if (!g_spawn_async(NULL, (char **) argv, NULL, flags, bnep_child_setup, NULL,
- &data.pid, NULL)) {
- error("Unable to execute %s %s", argv[0], argv[1]);
- }
-}
-
-void bnep_server_disconnect(void)
-{
- int err = -1;
-
- // Kill bluetooth-ics
- info("data.pid: %d", data.pid);
-
- if (data.pid > 0)
- {
- /* Kill script */
- err = kill(data.pid, SIGTERM);
- if (err < 0)
- error("kill(%d, SIGTERM): %s (%d)", data.pid, strerror(errno), errno);
-
- data.pid = 0;
- }
-}
-#endif
-
int bnep_if_up(const char *devname)
{
struct ifreq ifr;
close(sk);
+ if (err < 0) {
+ error("Could not bring down %s", devname);
+ return err;
+ }
+
return 0;
}
if (sk < 0)
return -1;
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
err = ioctl(sk, SIOCBRADDBR, bridge);
if (err < 0)
{
int bnep_if_up(const char *devname);
int bnep_if_down(const char *devname);
int bnep_add_to_bridge(const char *devname, const char *bridge);
-
-#ifndef __TIZEN_PATCH__
-void bnep_server_connect(char *devname);
-void bnep_server_disconnect(void);
-#endif
#include <gdbus.h>
#include "log.h"
-#include "glib-helper.h"
+#include "glib-compat.h"
#include "btio.h"
#include "dbus-common.h"
#include "adapter.h"
static struct network_peer *find_peer(GSList *list, const char *path)
{
- GSList *l;
-
- for (l = list; l; l = l->next) {
- struct network_peer *peer = l->data;
+ for (; list; list = list->next) {
+ struct network_peer *peer = list->data;
if (!strcmp(peer->path, path))
return peer;
static struct network_conn *find_connection(GSList *list, uint16_t id)
{
- GSList *l;
-
- for (l = list; l; l = l->next) {
- struct network_conn *nc = l->data;
+ for (; list; list = list->next) {
+ struct network_conn *nc = list->data;
if (nc->id == id)
return nc;
if (connection != NULL) {
gboolean connected = FALSE;
const char *property = "";
-#ifdef __TIZEN_PATCH__
- char address[20] = {0};
- const gchar* paddr = address;
- ba2str(&nc->peer->dst, address);
-#endif
emit_property_changed(connection, nc->peer->path,
NETWORK_PEER_INTERFACE, "Connected",
DBUS_TYPE_BOOLEAN, &connected);
emit_property_changed(connection, nc->peer->path,
NETWORK_PEER_INTERFACE, "UUID",
DBUS_TYPE_STRING, &property);
-#ifdef __TIZEN_PATCH__
- emit_property_changed(connection, nc->peer->path,
- NETWORK_PEER_INTERFACE, "Address",
- DBUS_TYPE_STRING, &paddr);
-#endif
device_remove_disconnect_watch(nc->peer->device, nc->dc_id);
nc->dc_id = 0;
if (nc->watch) {
int sk;
const char *pdev, *uuid;
gboolean connected;
-#ifdef __TIZEN_PATCH__
- char address[20] = {0};
- const gchar* paddr = address;
-#endif
if (cond & G_IO_NVAL)
return FALSE;
DBUS_TYPE_INVALID);
connected = TRUE;
-#ifdef __TIZEN_PATCH__
- ba2str(&nc->peer->dst, address);
-#endif
emit_property_changed(connection, nc->peer->path,
NETWORK_PEER_INTERFACE, "Connected",
DBUS_TYPE_BOOLEAN, &connected);
emit_property_changed(connection, nc->peer->path,
NETWORK_PEER_INTERFACE, "UUID",
DBUS_TYPE_STRING, &uuid);
-#ifdef __TIZEN_PATCH__
- emit_property_changed(connection, nc->peer->path,
- NETWORK_PEER_INTERFACE, "Address",
- DBUS_TYPE_STRING, &paddr);
-#endif
+
nc->state = CONNECTED;
nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb,
nc, NULL);
return reply;
}
-static void connection_free(struct network_conn *nc)
+static void connection_free(void *data)
{
+ struct network_conn *nc = data;
+
if (nc->dc_id)
device_remove_disconnect_watch(nc->peer->device, nc->dc_id);
static void peer_free(struct network_peer *peer)
{
- g_slist_foreach(peer->connections, (GFunc) connection_free, NULL);
- g_slist_free(peer->connections);
+ g_slist_free_full(peer->connections, connection_free);
btd_device_unref(peer->device);
g_free(peer->path);
g_free(peer);
DBG("path %s", path);
adapter_get_address(adapter, &src);
- device_get_address(device, &dst);
+ device_get_address(device, &dst, NULL);
return connection_register(device, path, &src, &dst, id);
}
#include "error.h"
#include "sdpd.h"
#include "btio.h"
-#include "glib-helper.h"
+#include "glib-compat.h"
#include "common.h"
#include "server.h"
GSList *sessions; /* Active connections */
struct network_adapter *na; /* Adapter reference */
guint watch_id; /* Client service watch */
+#ifdef __SAMSUNG_PATCH__
char dev[16]; /* Interface name */
+#endif
};
static DBusConnection *connection = NULL;
static GSList *adapters = NULL;
static gboolean security = TRUE;
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
static gboolean server_disconnected_cb(GIOChannel *chan,
GIOCondition cond, gpointer user_data);
#endif
static struct network_adapter *find_adapter(GSList *list,
struct btd_adapter *adapter)
{
- GSList *l;
-
- for (l = list; l; l = l->next) {
- struct network_adapter *na = l->data;
+ for (; list; list = list->next) {
+ struct network_adapter *na = list->data;
if (na->adapter == adapter)
return na;
static struct network_server *find_server(GSList *list, uint16_t id)
{
- GSList *l;
-
- for (l = list; l; l = l->next) {
- struct network_server *ns = l->data;
+ for (; list; list = list->next) {
+ struct network_server *ns = list->data;
if (ns->id == id)
return ns;
return NULL;
}
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
static struct network_session *find_session(GSList *list, GIOChannel *io)
{
GSList *l;
info("Added new connection: %s", devname);
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
{
guint watch = 0;
session_free(setup);
}
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
static gboolean server_disconnected_cb(GIOChannel *chan,
GIOCondition cond, gpointer user_data)
{
DBUS_TYPE_STRING, &paddr,
DBUS_TYPE_INVALID);
- bnep_server_disconnect();
-
/* Remove the session info */
session = find_session(ns->sessions, chan);
if (session) {
struct bnep_setup_conn_req *req = (void *) packet;
uint16_t src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED;
int n, sk;
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
gboolean connected = TRUE;
#endif
if (server_connadd(ns, na->setup, dst_role) < 0)
goto reply;
-#ifndef __TIZEN_PATCH__
+#ifndef __SAMSUNG_PATCH__
na->setup = NULL;
#endif
rsp = BNEP_SUCCESS;
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
{
// Emit connected signal to BT application
const gchar* adapter_path = adapter_get_path(na->adapter);
g_free(ns->bridge);
ns->bridge = g_strdup(bridge);
-#ifndef __TIZEN_PATCH__
+#ifndef __SAMSUNG_PATCH__
ns->watch_id = g_dbus_add_disconnect_watch(conn,
dbus_message_get_sender(msg),
server_disconnect, ns, NULL);
server_disconnect(conn, ns);
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
/* Down the bnep interface, and disconnect all connection */
bnep_kill_all_connections();
bnep_if_down(ns->dev);
g_free(ns->name);
g_free(ns->bridge);
- if (ns->sessions) {
- g_slist_foreach(ns->sessions, (GFunc) session_free, NULL);
- g_slist_free(ns->sessions);
- }
+ g_slist_free_full(ns->sessions, session_free);
g_free(ns);
}
{ }
};
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
static GDBusSignalTable server_signals[] = {
{ "PeerConnected", "ss" },
{ "PeerDisconnected", "ss" },
path = adapter_get_path(adapter);
-#ifndef __TIZEN_PATCH__
+#ifndef __SAMSUNG_PATCH__
if (!g_dbus_register_interface(connection, path, ns->iface,
server_methods, NULL, NULL,
ns, path_unregister)) {
%build
-export CFLAGS="${CFLAGS} -D__TIZEN_PATCH__ -D__BROADCOM_PATCH__"
+export CFLAGS="${CFLAGS} -D__SAMSUNG_PATCH__ -D__BROADCOM_PATCH__"
%reconfigure --disable-static \
--localstatedir=/opt/var \
--enable-pie \
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author: Bastien Nocera <hadess@hadess.net>
+ * Marcel Holtmann <marcel@holtmann.org> (for expand_name)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <bluetooth/bluetooth.h>
+
+#include "plugin.h"
+#include "hcid.h" /* For main_opts */
+#include "adapter.h"
+#include "manager.h"
+#include "device.h" /* Needed for storage.h */
+#include "storage.h"
+#include "log.h"
+
+#include <sys/inotify.h>
+#define EVENT_SIZE (sizeof (struct inotify_event))
+#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
+
+#define MACHINE_INFO_DIR "/etc/"
+#define MACHINE_INFO_FILE "machine-info"
+
+static GIOChannel *inotify = NULL;
+static int watch_fd = -1;
+
+/* This file is part of systemd's hostnamed functionality:
+ * http://0pointer.de/public/systemd-man/machine-info.html
+ * http://www.freedesktop.org/wiki/Software/systemd/hostnamed
+ */
+static char *read_pretty_host_name(void)
+{
+ char *contents, *ret;
+ char **lines;
+ guint i;
+
+ if (g_file_get_contents(MACHINE_INFO_DIR MACHINE_INFO_FILE,
+ &contents, NULL, NULL) == FALSE)
+ return NULL;
+
+ lines = g_strsplit_set(contents, "\r\n", 0);
+ g_free(contents);
+
+ if (lines == NULL)
+ return NULL;
+
+ ret = NULL;
+ for (i = 0; lines[i] != NULL; i++) {
+ if (g_str_has_prefix(lines[i], "PRETTY_HOSTNAME=")) {
+ ret = g_strdup(lines[i] + strlen("PRETTY_HOSTNAME="));
+ break;
+ }
+ }
+
+ g_strfreev(lines);
+
+ return ret;
+}
+
+/*
+ * Device name expansion
+ * %d - device id
+ * %h - hostname
+ */
+static char *expand_name(char *dst, int size, char *str, int dev_id)
+{
+ register int sp, np, olen;
+ char *opt, buf[10];
+
+ if (!str || !dst)
+ return NULL;
+
+ sp = np = 0;
+ while (np < size - 1 && str[sp]) {
+ switch (str[sp]) {
+ case '%':
+ opt = NULL;
+
+ switch (str[sp+1]) {
+ case 'd':
+ sprintf(buf, "%d", dev_id);
+ opt = buf;
+ break;
+
+ case 'h':
+ opt = main_opts.host_name;
+ break;
+
+ case '%':
+ dst[np++] = str[sp++];
+ /* fall through */
+ default:
+ sp++;
+ continue;
+ }
+
+ if (opt) {
+ /* substitute */
+ olen = strlen(opt);
+ if (np + olen < size - 1)
+ memcpy(dst + np, opt, olen);
+ np += olen;
+ }
+ sp += 2;
+ continue;
+
+ case '\\':
+ sp++;
+ /* fall through */
+ default:
+ dst[np++] = str[sp++];
+ break;
+ }
+ }
+ dst[np] = '\0';
+ return dst;
+}
+
+static int get_default_adapter_id(void)
+{
+ struct btd_adapter *default_adapter;
+
+ default_adapter = manager_get_default_adapter();
+ if (default_adapter == NULL)
+ return -1;
+
+ return adapter_get_dev_id(default_adapter);
+}
+
+static void set_pretty_name(struct btd_adapter *adapter,
+ const char *pretty_hostname)
+{
+ int current_id;
+ int default_adapter;
+
+ default_adapter = get_default_adapter_id();
+ current_id = adapter_get_dev_id(adapter);
+
+ /* Allow us to change the name */
+ adapter_set_allow_name_changes(adapter, TRUE);
+
+ /* If it's the first device, let's assume it will be the
+ * default one, as we're not told when the default adapter
+ * changes */
+ if (default_adapter < 0)
+ default_adapter = current_id;
+
+ if (default_adapter != current_id) {
+ char *str;
+
+ /* +1 because we don't want an adapter called "Foobar's
+ * laptop #0" */
+ str = g_strdup_printf("%s #%d", pretty_hostname,
+ current_id + 1);
+ DBG("Setting name '%s' for device 'hci%d'", str, current_id);
+
+ adapter_set_name(adapter, str);
+ g_free(str);
+ } else {
+ DBG("Setting name '%s' for device 'hci%d'", pretty_hostname,
+ current_id);
+ adapter_set_name(adapter, pretty_hostname);
+ }
+
+ /* And disable the name change now */
+ adapter_set_allow_name_changes(adapter, FALSE);
+}
+
+static int adaptername_probe(struct btd_adapter *adapter)
+{
+ int current_id;
+ char name[MAX_NAME_LENGTH + 1];
+ char *pretty_hostname;
+ bdaddr_t bdaddr;
+
+ pretty_hostname = read_pretty_host_name();
+ if (pretty_hostname != NULL) {
+ set_pretty_name(adapter, pretty_hostname);
+ g_free(pretty_hostname);
+ return 0;
+ }
+
+ adapter_set_allow_name_changes(adapter, TRUE);
+ adapter_get_address(adapter, &bdaddr);
+ current_id = adapter_get_dev_id(adapter);
+
+ if (read_local_name(&bdaddr, name) < 0)
+ expand_name(name, MAX_NAME_LENGTH, main_opts.name, current_id);
+
+ DBG("Setting name '%s' for device 'hci%d'", name, current_id);
+ adapter_set_name(adapter, name);
+
+ return 0;
+}
+
+static gboolean handle_inotify_cb(GIOChannel *channel, GIOCondition cond,
+ gpointer data)
+{
+ char buf[EVENT_BUF_LEN];
+ GIOStatus err;
+ gsize len, i;
+ gboolean changed;
+
+ changed = FALSE;
+
+ err = g_io_channel_read_chars(channel, buf, EVENT_BUF_LEN, &len, NULL);
+ if (err != G_IO_STATUS_NORMAL) {
+ error("Error reading inotify event: %d\n", err);
+ return FALSE;
+ }
+
+ i = 0;
+ while (i < len) {
+ struct inotify_event *pevent = (struct inotify_event *) &buf[i];
+
+ /* check that it's ours */
+ if (pevent->len && pevent->name != NULL &&
+ strcmp(pevent->name, MACHINE_INFO_FILE) == 0)
+ changed = TRUE;
+
+ i += EVENT_SIZE + pevent->len;
+ }
+
+ if (changed != FALSE) {
+ DBG(MACHINE_INFO_DIR MACHINE_INFO_FILE
+ " changed, changing names for adapters");
+ manager_foreach_adapter((adapter_cb) adaptername_probe, NULL);
+ }
+
+ return TRUE;
+}
+
+static void adaptername_remove(struct btd_adapter *adapter)
+{
+}
+
+static struct btd_adapter_driver adaptername_driver = {
+ .name = "adaptername",
+ .probe = adaptername_probe,
+ .remove = adaptername_remove,
+};
+
+static int adaptername_init(void)
+{
+ int err;
+ int inot_fd;
+ guint32 mask;
+
+ err = btd_register_adapter_driver(&adaptername_driver);
+ if (err < 0)
+ return err;
+
+ inot_fd = inotify_init();
+ if (inot_fd < 0) {
+ error("Failed to setup inotify");
+ return 0;
+ }
+
+ mask = IN_CLOSE_WRITE;
+ mask |= IN_DELETE;
+ mask |= IN_CREATE;
+ mask |= IN_MOVED_FROM;
+ mask |= IN_MOVED_TO;
+
+ watch_fd = inotify_add_watch(inot_fd, MACHINE_INFO_DIR, mask);
+ if (watch_fd < 0) {
+ error("Failed to setup watch for '%s'", MACHINE_INFO_DIR);
+ close(inot_fd);
+ return 0;
+ }
+
+ inotify = g_io_channel_unix_new(inot_fd);
+ g_io_channel_set_close_on_unref(inotify, TRUE);
+ g_io_channel_set_encoding(inotify, NULL, NULL);
+ g_io_channel_set_flags(inotify, G_IO_FLAG_NONBLOCK, NULL);
+ g_io_add_watch(inotify, G_IO_IN, handle_inotify_cb, NULL);
+
+ return 0;
+}
+
+static void adaptername_exit(void)
+{
+ if (watch_fd >= 0)
+ close(watch_fd);
+ if (inotify != NULL) {
+ g_io_channel_shutdown(inotify, FALSE, NULL);
+ g_io_channel_unref(inotify);
+ }
+
+ btd_unregister_adapter_driver(&adaptername_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(adaptername, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_LOW, adaptername_init, adaptername_exit)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <gdbus.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "plugin.h"
+#include "log.h"
+#include "adapter.h"
+#include "device.h"
+#include "manager.h"
+#include "dbus-common.h"
+#include "event.h"
+#include "error.h"
+#include "oob.h"
+
+#define OOB_INTERFACE "org.bluez.OutOfBand"
+
+struct oob_request {
+ struct btd_adapter *adapter;
+ DBusMessage *msg;
+};
+
+static GSList *oob_requests = NULL;
+static DBusConnection *connection = NULL;
+
+static gint oob_request_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct oob_request *data = a;
+ const struct btd_adapter *adapter = b;
+
+ return data->adapter != adapter;
+}
+
+static struct oob_request *find_oob_request(struct btd_adapter *adapter)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(oob_requests, adapter, oob_request_cmp);
+
+ if (match)
+ return match->data;
+
+ return NULL;
+}
+
+static void read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer)
+{
+ struct DBusMessage *reply;
+ struct oob_request *oob_request;
+
+ oob_request = find_oob_request(adapter);
+ if (!oob_request)
+ return;
+
+ if (hash && randomizer)
+ reply = g_dbus_create_reply(oob_request->msg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, 16,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, 16,
+ DBUS_TYPE_INVALID);
+ else
+ reply = btd_error_failed(oob_request->msg,
+ "Failed to read local OOB data.");
+
+ oob_requests = g_slist_remove(oob_requests, oob_request);
+ dbus_message_unref(oob_request->msg);
+ g_free(oob_request);
+
+ if (!reply) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ if (!g_dbus_send_message(connection, reply))
+ error("D-Bus send failed");
+}
+
+static DBusMessage *read_local_data(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct oob_request *oob_request;
+
+ if (find_oob_request(adapter))
+ return btd_error_in_progress(msg);
+
+ if (btd_adapter_read_local_oob_data(adapter))
+ return btd_error_failed(msg, "Request failed.");
+
+ oob_request = g_new(struct oob_request, 1);
+ oob_request->adapter = adapter;
+ oob_requests = g_slist_append(oob_requests, oob_request);
+ oob_request->msg = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static DBusMessage *add_remote_data(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ uint8_t *hash, *randomizer;
+ int32_t hlen, rlen;
+ const char *addr;
+ bdaddr_t bdaddr;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, &hlen,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, &rlen,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (hlen != 16 || rlen != 16 || bachk(addr))
+ return btd_error_invalid_args(msg);
+
+ str2ba(addr, &bdaddr);
+
+ if (btd_adapter_add_remote_oob_data(adapter, &bdaddr, hash, randomizer))
+ return btd_error_failed(msg, "Request failed");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *remove_remote_data(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ const char *addr;
+ bdaddr_t bdaddr;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (bachk(addr))
+ return btd_error_invalid_args(msg);
+
+ str2ba(addr, &bdaddr);
+
+ if (btd_adapter_remove_remote_oob_data(adapter, &bdaddr))
+ return btd_error_failed(msg, "Request failed");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static GDBusMethodTable oob_methods[] = {
+ {"AddRemoteData", "sayay", "", add_remote_data},
+ {"RemoveRemoteData", "s", "", remove_remote_data},
+ {"ReadLocalData", "", "ayay", read_local_data,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ {}
+};
+
+static int oob_probe(struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+
+ if (!g_dbus_register_interface(connection, path, OOB_INTERFACE,
+ oob_methods, NULL, NULL, adapter, NULL)) {
+ error("OOB interface init failed on path %s", path);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void oob_remove(struct btd_adapter *adapter)
+{
+ read_local_data_complete(adapter, NULL, NULL);
+
+ g_dbus_unregister_interface(connection, adapter_get_path(adapter),
+ OOB_INTERFACE);
+}
+
+static struct btd_adapter_driver oob_driver = {
+ .name = "oob",
+ .probe = oob_probe,
+ .remove = oob_remove,
+};
+
+static int dbusoob_init(void)
+{
+ DBG("Setup dbusoob plugin");
+
+ connection = get_dbus_connection();
+
+ oob_register_cb(read_local_data_complete);
+
+ return btd_register_adapter_driver(&oob_driver);
+}
+
+static void dbusoob_exit(void)
+{
+ DBG("Cleanup dbusoob plugin");
+
+ manager_foreach_adapter((adapter_cb) oob_remove, NULL);
+
+ btd_unregister_adapter_driver(&oob_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(dbusoob, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ dbusoob_init, dbusoob_exit)
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <unistd.h>
-#include <sys/socket.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-
-#include <glib.h>
-
-#include <gdbus.h>
-
-#include "plugin.h"
-#include "adapter.h"
-#include "log.h"
-
-static gboolean session_event(GIOChannel *chan,
- GIOCondition cond, gpointer data)
-{
- unsigned char buf[672];
- gsize len, written;
- GIOError err;
-
- if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
- return FALSE;
-
- err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len);
- if (err == G_IO_ERROR_AGAIN)
- return TRUE;
-
- g_io_channel_write(chan, (const gchar *) buf, len, &written);
-
- return TRUE;
-}
-
-static gboolean connect_event(GIOChannel *chan,
- GIOCondition cond, gpointer data)
-{
- GIOChannel *io;
- struct sockaddr_rc addr;
- socklen_t optlen;
- char address[18];
- int sk, nsk;
-
- sk = g_io_channel_unix_get_fd(chan);
-
- memset(&addr, 0, sizeof(addr));
- optlen = sizeof(addr);
-
- nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
- if (nsk < 0)
- return TRUE;
-
- io = g_io_channel_unix_new(nsk);
- g_io_channel_set_close_on_unref(io, TRUE);
-
- ba2str(&addr.rc_bdaddr, address);
-
- g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- session_event, NULL);
-
- return TRUE;
-}
-
-static GIOChannel *setup_rfcomm(uint8_t channel)
-{
- GIOChannel *io;
- struct sockaddr_rc addr;
- int sk;
-
- sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
- if (sk < 0)
- return NULL;
-
- memset(&addr, 0, sizeof(addr));
- addr.rc_family = AF_BLUETOOTH;
- bacpy(&addr.rc_bdaddr, BDADDR_ANY);
- addr.rc_channel = channel;
-
- if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(sk);
- return NULL;
- }
-
- if (listen(sk, 10) < 0) {
- close(sk);
- return NULL;
- }
-
- io = g_io_channel_unix_new(sk);
- g_io_channel_set_close_on_unref(io, TRUE);
-
- g_io_add_watch(io, G_IO_IN, connect_event, NULL);
-
- return io;
-}
-
-static GIOChannel *chan = NULL;
-
-static int echo_probe(struct btd_adapter *adapter)
-{
- const char *path = adapter_get_path(adapter);
-
- DBG("path %s", path);
-
- chan = setup_rfcomm(23);
-
- return 0;
-}
-
-static void echo_remove(struct btd_adapter *adapter)
-{
- const char *path = adapter_get_path(adapter);
-
- DBG("path %s", path);
-
- g_io_channel_unref(chan);
-}
-
-static struct btd_adapter_driver echo_server = {
- .name = "echo-server",
- .probe = echo_probe,
- .remove = echo_remove,
-};
-
-static int echo_init(void)
-{
- DBG("Setup echo plugin");
-
- return btd_register_adapter_driver(&echo_server);
-}
-
-static void echo_exit(void)
-{
- DBG("Cleanup echo plugin");
-
- btd_unregister_adapter_driver(&echo_server);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(echo, VERSION,
- BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, echo_init, echo_exit)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "plugin.h"
+#include "log.h"
+
+static int dummy_init(void)
+{
+ DBG("");
+
+ return 0;
+}
+
+static void dummy_exit(void)
+{
+ DBG("");
+}
+
+BLUETOOTH_PLUGIN_DEFINE(external_dummy, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_LOW, dummy_init, dummy_exit)
"Rack Mount Chassis", "unknown",
"Sealed-case PC", "unknown",
"Multi-system", "unknown",
- "CompactPCI", "unknonw",
+ "CompactPCI", "unknown",
"AdvancedTCA", "unknown",
"Blade", "server",
- "Blade Enclosure" "unknown", /* 0x1D */
+ "Blade Enclosure", "unknown", /* 0x1D */
NULL
};
return 0;
}
- formfactor = chassis_map[chassis_type * 2];
+ formfactor = chassis_map[chassis_type * 2 - 1];
if (formfactor != NULL) {
if (g_str_equal(formfactor, "laptop") == TRUE)
minor |= (1 << 2) | (1 << 3);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2010 Nokia Corporation
+ * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <errno.h>
+#include <adapter.h>
+
+#include "plugin.h"
+#include "hcid.h"
+#include "log.h"
+#include "gattrib.h"
+#include "gatt-service.h"
+#include "att.h"
+#include "attrib-server.h"
+
+/* FIXME: Not defined by SIG? UUID128? */
+#define OPCODES_SUPPORTED_UUID 0xA001
+#define BATTERY_STATE_SVC_UUID 0xA002
+#define BATTERY_STATE_UUID 0xA003
+#define THERM_HUMIDITY_SVC_UUID 0xA004
+#define MANUFACTURER_SVC_UUID 0xA005
+#define TEMPERATURE_UUID 0xA006
+#define FMT_CELSIUS_UUID 0xA007
+#define FMT_OUTSIDE_UUID 0xA008
+#define RELATIVE_HUMIDITY_UUID 0xA009
+#define FMT_PERCENT_UUID 0xA00A
+#define BLUETOOTH_SIG_UUID 0xA00B
+#define MANUFACTURER_NAME_UUID 0xA00C
+#define MANUFACTURER_SERIAL_UUID 0xA00D
+#define VENDOR_SPECIFIC_SVC_UUID 0xA00E
+#define VENDOR_SPECIFIC_TYPE_UUID 0xA00F
+#define FMT_KILOGRAM_UUID 0xA010
+#define FMT_HANGING_UUID 0xA011
+
+struct gatt_example_adapter {
+ struct btd_adapter *adapter;
+ GSList *sdp_handles;
+};
+
+static GSList *adapters = NULL;
+
+static void gatt_example_adapter_free(struct gatt_example_adapter *gadapter)
+{
+ while (gadapter->sdp_handles != NULL) {
+ uint32_t handle = GPOINTER_TO_UINT(gadapter->sdp_handles->data);
+
+ attrib_free_sdp(handle);
+ gadapter->sdp_handles = g_slist_remove(gadapter->sdp_handles,
+ gadapter->sdp_handles->data);
+ }
+
+ if (gadapter->adapter != NULL)
+ btd_adapter_unref(gadapter->adapter);
+
+ g_free(gadapter);
+}
+
+static gint adapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_example_adapter *gatt_adapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (gatt_adapter->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static uint8_t battery_state_read(struct attribute *a, gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ uint8_t value;
+
+ value = 0x04;
+ attrib_db_update(adapter, a->handle, NULL, &value, sizeof(value), NULL);
+
+ return 0;
+}
+
+static gboolean register_battery_service(struct btd_adapter *adapter)
+{
+ return gatt_service_add(adapter, GATT_PRIM_SVC_UUID,
+ BATTERY_STATE_SVC_UUID,
+ /* battery state characteristic */
+ GATT_OPT_CHR_UUID, BATTERY_STATE_UUID,
+ GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ |
+ ATT_CHAR_PROPER_NOTIFY,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ battery_state_read, adapter,
+
+ GATT_OPT_INVALID);
+}
+
+static void register_termometer_service(struct gatt_example_adapter *adapter,
+ const uint16_t manuf1[2], const uint16_t manuf2[2])
+{
+ const char *desc_out_temp = "Outside Temperature";
+ const char *desc_out_hum = "Outside Relative Humidity";
+ uint16_t start_handle, h;
+ const int svc_size = 11;
+ uint32_t sdp_handle;
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+ int len;
+
+ start_handle = attrib_db_find_avail(adapter->adapter, svc_size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ return;
+ }
+
+ DBG("start_handle=0x%04x manuf1=0x%04x-0x%04x, manuf2=0x%04x-0x%04x",
+ start_handle, manuf1[0], manuf1[1], manuf2[0], manuf2[1]);
+
+ h = start_handle;
+
+ /* Thermometer: primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ att_put_u16(THERM_HUMIDITY_SVC_UUID, &atval[0]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 2);
+
+ bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+
+ /* Thermometer: Include */
+ if (manuf1[0] && manuf1[1]) {
+ att_put_u16(manuf1[0], &atval[0]);
+ att_put_u16(manuf1[1], &atval[2]);
+ att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE,
+ ATT_NOT_PERMITTED, atval, 6);
+ }
+
+ /* Thermometer: Include */
+ if (manuf2[0] && manuf2[1]) {
+ att_put_u16(manuf2[0], &atval[0]);
+ att_put_u16(manuf2[1], &atval[2]);
+ att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[4]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE,
+ ATT_NOT_PERMITTED, atval, 6);
+ }
+
+ /* Thermometer: temperature characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(TEMPERATURE_UUID, &atval[3]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
+
+ /* Thermometer: temperature characteristic value */
+ bt_uuid16_create(&uuid, TEMPERATURE_UUID);
+ atval[0] = 0x8A;
+ atval[1] = 0x02;
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 2);
+
+ /* Thermometer: temperature characteristic format */
+ bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+ atval[0] = 0x0E;
+ atval[1] = 0xFE;
+ att_put_u16(FMT_CELSIUS_UUID, &atval[2]);
+ atval[4] = 0x01;
+ att_put_u16(FMT_OUTSIDE_UUID, &atval[5]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 7);
+
+ /* Thermometer: characteristic user description */
+ bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+ len = strlen(desc_out_temp);
+ strncpy((char *) atval, desc_out_temp, len);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, len);
+
+ /* Thermometer: relative humidity characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(RELATIVE_HUMIDITY_UUID, &atval[3]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
+
+ /* Thermometer: relative humidity value */
+ bt_uuid16_create(&uuid, RELATIVE_HUMIDITY_UUID);
+ atval[0] = 0x27;
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 1);
+
+ /* Thermometer: relative humidity characteristic format */
+ bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+ atval[0] = 0x04;
+ atval[1] = 0x00;
+ att_put_u16(FMT_PERCENT_UUID, &atval[2]);
+ att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]);
+ att_put_u16(FMT_OUTSIDE_UUID, &atval[6]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 8);
+
+ /* Thermometer: characteristic user description */
+ bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+ len = strlen(desc_out_hum);
+ strncpy((char *) atval, desc_out_hum, len);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, len);
+
+ g_assert(h - start_handle == svc_size);
+
+ /* Add an SDP record for the above service */
+ sdp_handle = attrib_create_sdp(adapter->adapter, start_handle,
+ "Thermometer");
+ if (sdp_handle)
+ adapter->sdp_handles = g_slist_prepend(adapter->sdp_handles,
+ GUINT_TO_POINTER(sdp_handle));
+}
+
+static void register_manuf1_service(struct gatt_example_adapter *adapter,
+ uint16_t range[2])
+{
+ const char *manufacturer_name1 = "ACME Temperature Sensor";
+ const char *serial1 = "237495-3282-A";
+ uint16_t start_handle, h;
+ const int svc_size = 5;
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+ int len;
+
+ start_handle = attrib_db_find_avail(adapter->adapter, svc_size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ return;
+ }
+
+ DBG("start_handle=0x%04x", start_handle);
+
+ h = start_handle;
+
+ /* Secondary Service: Manufacturer Service */
+ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+ att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 2);
+
+ /* Manufacturer name characteristic definition */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
+
+ /* Manufacturer name characteristic value */
+ bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
+ len = strlen(manufacturer_name1);
+ strncpy((char *) atval, manufacturer_name1, len);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, len);
+
+ /* Manufacturer serial number characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
+
+ /* Manufacturer serial number characteristic value */
+ bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
+ len = strlen(serial1);
+ strncpy((char *) atval, serial1, len);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, len);
+
+ g_assert(h - start_handle == svc_size);
+
+ range[0] = start_handle;
+ range[1] = start_handle + svc_size - 1;
+}
+
+static void register_manuf2_service(struct gatt_example_adapter *adapter,
+ uint16_t range[2])
+{
+ const char *manufacturer_name2 = "ACME Weighing Scales";
+ const char *serial2 = "11267-2327A00239";
+ uint16_t start_handle, h;
+ const int svc_size = 5;
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+ int len;
+
+ start_handle = attrib_db_find_avail(adapter->adapter, svc_size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ return;
+ }
+
+ DBG("start_handle=0x%04x", start_handle);
+
+ h = start_handle;
+
+ /* Secondary Service: Manufacturer Service */
+ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+ att_put_u16(MANUFACTURER_SVC_UUID, &atval[0]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 2);
+
+ /* Manufacturer name characteristic definition */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(MANUFACTURER_NAME_UUID, &atval[3]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
+
+ /* Manufacturer name attribute */
+ bt_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
+ len = strlen(manufacturer_name2);
+ strncpy((char *) atval, manufacturer_name2, len);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, len);
+
+ /* Characteristic: serial number */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(MANUFACTURER_SERIAL_UUID, &atval[3]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
+
+ /* Serial number characteristic value */
+ bt_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
+ len = strlen(serial2);
+ strncpy((char *) atval, serial2, len);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, len);
+
+ g_assert(h - start_handle == svc_size);
+
+ range[0] = start_handle;
+ range[1] = start_handle + svc_size - 1;
+}
+
+static void register_vendor_service(struct gatt_example_adapter *adapter,
+ uint16_t range[2])
+{
+ uint16_t start_handle, h;
+ const int svc_size = 3;
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+
+ start_handle = attrib_db_find_avail(adapter->adapter, svc_size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ return;
+ }
+
+ DBG("start_handle=0x%04x", start_handle);
+
+ h = start_handle;
+
+ /* Secondary Service: Vendor Specific Service */
+ bt_uuid16_create(&uuid, GATT_SND_SVC_UUID);
+ att_put_u16(VENDOR_SPECIFIC_SVC_UUID, &atval[0]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 2);
+
+ /* Vendor Specific Type characteristic definition */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(VENDOR_SPECIFIC_TYPE_UUID, &atval[3]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
+
+ /* Vendor Specific Type characteristic value */
+ bt_uuid16_create(&uuid, VENDOR_SPECIFIC_TYPE_UUID);
+ atval[0] = 0x56;
+ atval[1] = 0x65;
+ atval[2] = 0x6E;
+ atval[3] = 0x64;
+ atval[4] = 0x6F;
+ atval[5] = 0x72;
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 6);
+
+ g_assert(h - start_handle == svc_size);
+
+ range[0] = start_handle;
+ range[1] = start_handle + svc_size - 1;
+}
+
+static void register_weight_service(struct gatt_example_adapter *adapter,
+ const uint16_t vendor[2])
+{
+ const char *desc_weight = "Rucksack Weight";
+ const uint128_t char_weight_uuid_btorder = {
+ .data = { 0x80, 0x88, 0xF2, 0x18, 0x90, 0x2C, 0x45, 0x0B,
+ 0xB6, 0xC4, 0x62, 0x89, 0x1E, 0x8C, 0x25, 0xE9 } };
+ const uint128_t prim_weight_uuid_btorder = {
+ .data = { 0x4F, 0x0A, 0xC0, 0x96, 0x35, 0xD4, 0x49, 0x11,
+ 0x96, 0x31, 0xDE, 0xA8, 0xDC, 0x74, 0xEE, 0xFE } };
+ uint128_t char_weight_uuid;
+ uint16_t start_handle, h;
+ const int svc_size = 6;
+ uint32_t sdp_handle;
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+ int len;
+
+ btoh128(&char_weight_uuid_btorder, &char_weight_uuid);
+
+ start_handle = attrib_db_find_avail(adapter->adapter, svc_size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ return;
+ }
+
+ DBG("start_handle=0x%04x, vendor=0x%04x-0x%04x", start_handle,
+ vendor[0], vendor[1]);
+
+ h = start_handle;
+
+ /* Weight service: primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ memcpy(atval, &prim_weight_uuid_btorder, 16);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 16);
+
+ if (vendor[0] && vendor[1]) {
+ /* Weight: include */
+ bt_uuid16_create(&uuid, GATT_INCLUDE_UUID);
+ att_put_u16(vendor[0], &atval[0]);
+ att_put_u16(vendor[1], &atval[2]);
+ att_put_u16(MANUFACTURER_SVC_UUID, &atval[4]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE,
+ ATT_NOT_PERMITTED, atval, 6);
+ }
+
+ /* Weight: characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ;
+ att_put_u16(h + 1, &atval[1]);
+ memcpy(&atval[3], &char_weight_uuid_btorder, 16);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 19);
+
+ /* Weight: characteristic value */
+ bt_uuid128_create(&uuid, char_weight_uuid);
+ atval[0] = 0x82;
+ atval[1] = 0x55;
+ atval[2] = 0x00;
+ atval[3] = 0x00;
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 4);
+
+ /* Weight: characteristic format */
+ bt_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
+ atval[0] = 0x08;
+ atval[1] = 0xFD;
+ att_put_u16(FMT_KILOGRAM_UUID, &atval[2]);
+ att_put_u16(BLUETOOTH_SIG_UUID, &atval[4]);
+ att_put_u16(FMT_HANGING_UUID, &atval[6]);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 8);
+
+ /* Weight: characteristic user description */
+ bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
+ len = strlen(desc_weight);
+ strncpy((char *) atval, desc_weight, len);
+ attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, len);
+
+ g_assert(h - start_handle == svc_size);
+
+ /* Add an SDP record for the above service */
+ sdp_handle = attrib_create_sdp(adapter->adapter, start_handle,
+ "Weight Service");
+ if (sdp_handle)
+ adapter->sdp_handles = g_slist_prepend(adapter->sdp_handles,
+ GUINT_TO_POINTER(sdp_handle));
+}
+
+static int gatt_example_adapter_probe(struct btd_adapter *adapter)
+{
+ uint16_t manuf1_range[2] = {0, 0}, manuf2_range[2] = {0, 0};
+ uint16_t vendor_range[2] = {0, 0};
+ struct gatt_example_adapter *gadapter;
+
+ gadapter = g_new0(struct gatt_example_adapter, 1);
+ gadapter->adapter = btd_adapter_ref(adapter);
+
+ if (!register_battery_service(adapter)) {
+ DBG("Battery service could not be registered");
+ gatt_example_adapter_free(gadapter);
+ return -EIO;
+ }
+
+ register_manuf1_service(gadapter, manuf1_range);
+ register_manuf2_service(gadapter, manuf2_range);
+ register_termometer_service(gadapter, manuf1_range, manuf2_range);
+ register_vendor_service(gadapter, vendor_range);
+ register_weight_service(gadapter, vendor_range);
+
+ adapters = g_slist_append(adapters, gadapter);
+
+ return 0;
+}
+
+static void gatt_example_adapter_remove(struct btd_adapter *adapter)
+{
+ struct gatt_example_adapter *gadapter;
+ GSList *l;
+
+ l = g_slist_find_custom(adapters, adapter, adapter_cmp);
+ if (l == NULL)
+ return;
+
+ gadapter = l->data;
+ adapters = g_slist_remove(adapters, gadapter);
+ gatt_example_adapter_free(gadapter);
+}
+
+static struct btd_adapter_driver gatt_example_adapter_driver = {
+ .name = "gatt-example-adapter-driver",
+ .probe = gatt_example_adapter_probe,
+ .remove = gatt_example_adapter_remove,
+};
+
+static int gatt_example_init(void)
+{
+ if (!main_opts.attrib_server) {
+ DBG("Attribute server is disabled");
+ return -ENOTSUP;
+ }
+
+ return btd_register_adapter_driver(&gatt_example_adapter_driver);
+}
+
+static void gatt_example_exit(void)
+{
+ if (!main_opts.attrib_server)
+ return;
+
+ btd_unregister_adapter_driver(&gatt_example_adapter_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(gatt_example, VERSION, BLUETOOTH_PLUGIN_PRIORITY_LOW,
+ gatt_example_init, gatt_example_exit)
#include <glib.h>
+#include "glib-compat.h"
#include "hcid.h"
#include "sdpd.h"
#include "btio.h"
#include "storage.h"
#include "event.h"
#include "manager.h"
+#include "oob.h"
+#include "eir.h"
+
+#define DISCOV_HALTED 0
+#define DISCOV_INQ 1
+#define DISCOV_SCAN 2
+#define DISCOV_NAMES 3
+
+#define TIMEOUT_BR_LE_SCAN 5120 /* TGAP(100)/2 */
+#define TIMEOUT_LE_SCAN 10240 /* TGAP(gen_disc_scan_min) */
+
+#define LENGTH_BR_INQ 0x08
+#define LENGTH_BR_LE_INQ 0x04
+
+static int start_scanning(int index, int timeout);
static int child_pipe[2] = { -1, -1 };
static guint child_io_id = 0;
static guint ctl_io_id = 0;
+enum adapter_type {
+ BR_EDR,
+ LE_ONLY,
+ BR_EDR_LE,
+ UNKNOWN,
+};
+
/* Commands sent by kernel on starting an adapter */
enum {
PENDING_BDADDR,
PENDING_NAME,
};
-struct uuid_info {
- uuid_t uuid;
- uint8_t svc_hint;
-};
-
struct bt_conn {
struct dev_info *dev;
bdaddr_t bdaddr;
uint8_t loc_auth;
uint8_t rem_cap;
uint8_t rem_auth;
+ uint8_t rem_oob_data;
gboolean bonding_initiator;
gboolean secmode3;
GIOChannel *io; /* For raw L2CAP socket (bonding) */
};
+struct oob_data {
+ bdaddr_t bdaddr;
+ uint8_t hash[16];
+ uint8_t randomizer[16];
+};
+
+enum name_state {
+ NAME_UNKNOWN,
+ NAME_NEEDED,
+ NAME_NOT_NEEDED,
+ NAME_PENDING,
+};
+
+struct found_dev {
+ bdaddr_t bdaddr;
+ int8_t rssi;
+ enum name_state name_state;
+};
+
static int max_dev = -1;
static struct dev_info {
int id;
int sk;
bdaddr_t bdaddr;
char name[249];
- uint8_t eir[240];
+ uint8_t eir[HCI_MAX_EIR_LENGTH];
uint8_t features[8];
+ uint8_t extfeatures[8];
uint8_t ssp_mode;
int8_t tx_power;
+ int discov_state;
+
uint32_t current_cod;
uint32_t wanted_cod;
uint32_t pending_cod;
uint16_t did_version;
gboolean up;
- unsigned long pending;
+ uint32_t pending;
GIOChannel *io;
guint watch_id;
GSList *keys;
uint8_t pin_length;
+ GSList *oob_data;
+
GSList *uuids;
GSList *connections;
+
+ GSList *found_devs;
+ GSList *need_name;
+
+ guint stop_scan_id;
} *devs = NULL;
+static int found_dev_rssi_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct found_dev *d1 = a, *d2 = b;
+ int rssi1, rssi2;
+
+ if (d2->name_state == NAME_NOT_NEEDED)
+ return -1;
+
+ rssi1 = d1->rssi < 0 ? -d1->rssi : d1->rssi;
+ rssi2 = d2->rssi < 0 ? -d2->rssi : d2->rssi;
+
+ return rssi1 - rssi2;
+}
+
+static int found_dev_bda_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct found_dev *d1 = a, *d2 = b;
+
+ return bacmp(&d1->bdaddr, &d2->bdaddr);
+}
+
+static void found_dev_cleanup(struct dev_info *info)
+{
+ g_slist_free_full(info->found_devs, g_free);
+ info->found_devs = NULL;
+
+ g_slist_free_full(info->need_name, g_free);
+ info->need_name = NULL;
+}
+
+static int resolve_name(struct dev_info *info, bdaddr_t *bdaddr)
+{
+ remote_name_req_cp cp;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d dba %s", info->id, addr);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.pscan_rep_mode = 0x02;
+
+ if (hci_send_cmd(info->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ,
+ REMOTE_NAME_REQ_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int resolve_names(struct dev_info *info, struct btd_adapter *adapter)
+{
+ struct found_dev *dev;
+
+ DBG("found_dev %u need_name %u", g_slist_length(info->found_devs),
+ g_slist_length(info->need_name));
+
+ if (g_slist_length(info->need_name) == 0)
+ return -ENOENT;
+
+ dev = info->need_name->data;
+ resolve_name(info, &dev->bdaddr);
+ dev->name_state = NAME_PENDING;
+
+ return 0;
+}
+
+static void set_state(int index, int state)
+{
+ struct btd_adapter *adapter;
+ struct dev_info *dev = &devs[index];
+
+ if (dev->discov_state == state)
+ return;
+
+ adapter = manager_find_adapter_by_id(index);
+ if (!adapter) {
+ error("No matching adapter found");
+ return;
+ }
+
+ dev->discov_state = state;
+
+ DBG("hci%d: new state %d", index, dev->discov_state);
+
+ switch (dev->discov_state) {
+ case DISCOV_HALTED:
+ found_dev_cleanup(dev);
+ adapter_set_discovering(adapter, FALSE);
+ break;
+ case DISCOV_INQ:
+ case DISCOV_SCAN:
+ adapter_set_discovering(adapter, TRUE);
+ break;
+ case DISCOV_NAMES:
+ if (resolve_names(dev, adapter) < 0)
+ set_state(index, DISCOV_HALTED);
+ break;
+ }
+}
+
+static inline gboolean is_le_capable(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ return (dev->features[4] & LMP_LE &&
+ dev->extfeatures[0] & LMP_HOST_LE) ? TRUE : FALSE;
+}
+
+static inline gboolean is_bredr_capable(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ return (dev->features[4] & LMP_NO_BREDR) == 0 ? TRUE : FALSE;
+}
+
+static int get_adapter_type(int index)
+{
+ if (is_le_capable(index) && is_bredr_capable(index))
+ return BR_EDR_LE;
+ else if (is_le_capable(index))
+ return LE_ONLY;
+ else if (is_bredr_capable(index))
+ return BR_EDR;
+
+ return UNKNOWN;
+}
+
static int ignore_device(struct hci_dev_info *di)
{
return hci_test_bit(HCI_RAW, &di->flags) || di->type >> 4 != HCI_BREDR;
dev->registered = registered;
dev->already_up = already_up;
dev->io_capability = 0x03; /* No Input No Output */
+ dev->discov_state = DISCOV_HALTED;
return dev;
}
static int hciops_stop_inquiry(int index)
{
struct dev_info *dev = &devs[index];
- struct hci_dev_info di;
- int err;
DBG("hci%d", index);
- if (hci_devinfo(index, &di) < 0)
+ if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_INQUIRY_CANCEL, 0, 0) < 0)
return -errno;
- if (hci_test_bit(HCI_INQUIRY, &di.flags))
- err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_INQUIRY_CANCEL, 0, 0);
- else
- err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_EXIT_PERIODIC_INQUIRY, 0, 0);
- if (err < 0)
- err = -errno;
-
- return err;
+ return 0;
}
static gboolean init_adapter(int index)
return FALSE;
btd_adapter_get_mode(adapter, &mode, &on_mode, &pairable);
- DBG("SYAM Mode = %d, on_mode = %d, pairable = %d", mode, on_mode, pairable);
+
if (existing_adapter)
mode = on_mode;
static void bonding_complete(struct dev_info *dev, struct bt_conn *conn,
uint8_t status)
{
+ struct btd_adapter *adapter;
+
DBG("status 0x%02x", status);
if (conn->io != NULL) {
conn->bonding_initiator = FALSE;
- btd_event_bonding_complete(&dev->bdaddr, &conn->bdaddr, status);
+ adapter = manager_find_adapter(&dev->bdaddr);
+ if (adapter)
+ adapter_bonding_complete(adapter, &conn->bdaddr, status);
}
static int get_auth_info(int index, bdaddr_t *bdaddr, uint8_t *auth)
/* Some buggy controller combinations generate a changed
* combination key for legacy pairing even when there's no
* previous key */
- if ((!conn || conn->rem_auth == 0xff) && old_key_type == 0xff)
+ if (conn->rem_auth == 0xff && old_key_type == 0xff)
key_type = 0x00;
else if (old_key_type != 0xff)
key_type = old_key_type;
btohl(req->passkey));
}
-static void remote_oob_data_request(int index, void *ptr)
+static gint oob_bdaddr_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct oob_data *data = a;
+ const bdaddr_t *bdaddr = b;
+
+ return bacmp(&data->bdaddr, bdaddr);
+}
+
+static void remote_oob_data_request(int index, bdaddr_t *bdaddr)
{
struct dev_info *dev = &devs[index];
+ GSList *match;
DBG("hci%d", index);
- hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_REMOTE_OOB_DATA_NEG_REPLY, 6, ptr);
+ match = g_slist_find_custom(dev->oob_data, bdaddr, oob_bdaddr_cmp);
+
+ if (match) {
+ struct oob_data *data;
+ remote_oob_data_reply_cp cp;
+
+ data = match->data;
+
+ bacpy(&cp.bdaddr, &data->bdaddr);
+ memcpy(cp.hash, data->hash, sizeof(cp.hash));
+ memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer));
+
+ dev->oob_data = g_slist_delete_link(dev->oob_data, match);
+
+ hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_REMOTE_OOB_DATA_REPLY,
+ REMOTE_OOB_DATA_REPLY_CP_SIZE, &cp);
+
+ } else {
+ hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_REMOTE_OOB_DATA_NEG_REPLY, 6, bdaddr);
+ }
}
static int get_io_cap(int index, bdaddr_t *bdaddr, uint8_t *cap, uint8_t *auth)
IO_CAPABILITY_NEG_REPLY_CP_SIZE, &cp);
} else {
io_capability_reply_cp cp;
+ struct bt_conn *conn;
+ GSList *match;
+
memset(&cp, 0, sizeof(cp));
bacpy(&cp.bdaddr, dba);
cp.capability = cap;
- cp.oob_data = 0x00;
cp.authentication = auth;
+
+ conn = find_connection(dev, dba);
+ match = g_slist_find_custom(dev->oob_data, dba, oob_bdaddr_cmp);
+
+ if ((conn->bonding_initiator || conn->rem_oob_data == 0x01) &&
+ match)
+ cp.oob_data = 0x01;
+ else
+ cp.oob_data = 0x00;
+
hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_IO_CAPABILITY_REPLY,
IO_CAPABILITY_REPLY_CP_SIZE, &cp);
}
if (conn) {
conn->rem_cap = evt->capability;
conn->rem_auth = evt->authentication;
+ conn->rem_oob_data = evt->oob_data;
}
}
goto reject;
}
- err = btd_event_request_pin(&dev->bdaddr, dba);
+ err = btd_event_request_pin(&dev->bdaddr, dba, FALSE);
if (err < 0) {
error("PIN code negative reply: %s", strerror(-err));
goto reject;
hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_PIN_CODE_NEG_REPLY, 6, dba);
}
-static void start_inquiry(bdaddr_t *local, uint8_t status, gboolean periodic)
-{
- struct btd_adapter *adapter;
- int state;
-
- /* Don't send the signal if the cmd failed */
- if (status) {
- error("Inquiry Failed with status 0x%02x", status);
- return;
- }
-
- adapter = manager_find_adapter(local);
- if (!adapter) {
- error("Unable to find matching adapter");
- return;
- }
-
- state = adapter_get_state(adapter);
-
- if (periodic)
- state |= STATE_PINQ;
- else
- state |= STATE_STDINQ;
-
- adapter_set_state(adapter, state);
-}
-
-static void inquiry_complete(bdaddr_t *local, uint8_t status,
- gboolean periodic)
-{
- struct btd_adapter *adapter;
- int state;
-
- /* Don't send the signal if the cmd failed */
- if (status) {
- error("Inquiry Failed with status 0x%02x", status);
- return;
- }
-
- adapter = manager_find_adapter(local);
- if (!adapter) {
- error("Unable to find matching adapter");
- return;
- }
-
- state = adapter_get_state(adapter);
- state &= ~(STATE_STDINQ | STATE_PINQ);
- adapter_set_state(adapter, state);
-}
-
static inline void remote_features_notify(int index, void *ptr)
{
struct dev_info *dev = &devs[index];
write_features_info(&dev->bdaddr, &evt->bdaddr, NULL, evt->features);
}
-static void write_le_host_complete(int index, uint8_t status)
-{
- struct dev_info *dev = &devs[index];
- uint8_t page_num = 0x01;
-
- if (status)
- return;
-
- if (hci_send_cmd(dev->sk, OGF_INFO_PARAM,
- OCF_READ_LOCAL_EXT_FEATURES, 1, &page_num) < 0)
- error("Unable to read extended local features: %s (%d)",
- strerror(errno), errno);
-}
-
static void read_local_version_complete(int index,
const read_local_version_rp *rp)
{
init_adapter(index);
}
-#define SIZEOF_UUID128 16
-
-static void eir_generate_uuid128(GSList *list, uint8_t *ptr, uint16_t *eir_len)
-{
- int i, k, uuid_count = 0;
- uint16_t len = *eir_len;
- uint8_t *uuid128;
- gboolean truncated = FALSE;
-
- /* Store UUIDs in place, skip 2 bytes to write type and length later */
- uuid128 = ptr + 2;
-
- for (; list; list = list->next) {
- struct uuid_info *uuid = list->data;
- uint8_t *uuid128_data = uuid->uuid.value.uuid128.data;
-
- if (uuid->uuid.type != SDP_UUID128)
- continue;
-
- /* Stop if not enough space to put next UUID128 */
- if ((len + 2 + SIZEOF_UUID128) > EIR_DATA_LENGTH) {
- truncated = TRUE;
- break;
- }
-
- /* Check for duplicates, EIR data is Little Endian */
- for (i = 0; i < uuid_count; i++) {
- for (k = 0; k < SIZEOF_UUID128; k++) {
- if (uuid128[i * SIZEOF_UUID128 + k] !=
- uuid128_data[SIZEOF_UUID128 - 1 - k])
- break;
- }
- if (k == SIZEOF_UUID128)
- break;
- }
-
- if (i < uuid_count)
- continue;
-
- /* EIR data is Little Endian */
- for (k = 0; k < SIZEOF_UUID128; k++)
- uuid128[uuid_count * SIZEOF_UUID128 + k] =
- uuid128_data[SIZEOF_UUID128 - 1 - k];
-
- len += SIZEOF_UUID128;
- uuid_count++;
- }
-
- if (uuid_count > 0 || truncated) {
- /* EIR Data length */
- ptr[0] = (uuid_count * SIZEOF_UUID128) + 1;
- /* EIR Data type */
- ptr[1] = truncated ? EIR_UUID128_SOME : EIR_UUID128_ALL;
- len += 2;
- *eir_len = len;
- }
-}
-
-static void create_ext_inquiry_response(int index, uint8_t *data)
-{
- struct dev_info *dev = &devs[index];
- GSList *l;
- uint8_t *ptr = data;
- uint16_t eir_len = 0;
- uint16_t uuid16[EIR_DATA_LENGTH / 2];
- int i, uuid_count = 0;
- gboolean truncated = FALSE;
- size_t name_len;
-
- name_len = strlen(dev->name);
-
- if (name_len > 0) {
- /* EIR Data type */
- if (name_len > 48) {
- name_len = 48;
- ptr[1] = EIR_NAME_SHORT;
- } else
- ptr[1] = EIR_NAME_COMPLETE;
-
- /* EIR Data length */
- ptr[0] = name_len + 1;
-
- memcpy(ptr + 2, dev->name, name_len);
-
- eir_len += (name_len + 2);
- ptr += (name_len + 2);
- }
-
- if (dev->tx_power != 0) {
- *ptr++ = 2;
- *ptr++ = EIR_TX_POWER;
- *ptr++ = (uint8_t) dev->tx_power;
- eir_len += 3;
- }
-
- if (dev->did_vendor != 0x0000) {
- uint16_t source = 0x0002;
- *ptr++ = 9;
- *ptr++ = EIR_DEVICE_ID;
- *ptr++ = (source & 0x00ff);
- *ptr++ = (source & 0xff00) >> 8;
- *ptr++ = (dev->did_vendor & 0x00ff);
- *ptr++ = (dev->did_vendor & 0xff00) >> 8;
- *ptr++ = (dev->did_product & 0x00ff);
- *ptr++ = (dev->did_product & 0xff00) >> 8;
- *ptr++ = (dev->did_version & 0x00ff);
- *ptr++ = (dev->did_version & 0xff00) >> 8;
- eir_len += 10;
- }
-
- /* Group all UUID16 types */
- for (l = dev->uuids; l != NULL; l = g_slist_next(l)) {
- struct uuid_info *uuid = l->data;
-
- if (uuid->uuid.type != SDP_UUID16)
- continue;
-
- if (uuid->uuid.value.uuid16 < 0x1100)
- continue;
-
- if (uuid->uuid.value.uuid16 == PNP_INFO_SVCLASS_ID)
- continue;
-
- /* Stop if not enough space to put next UUID16 */
- if ((eir_len + 2 + sizeof(uint16_t)) > EIR_DATA_LENGTH) {
- truncated = TRUE;
- break;
- }
-
- /* Check for duplicates */
- for (i = 0; i < uuid_count; i++)
- if (uuid16[i] == uuid->uuid.value.uuid16)
- break;
-
- if (i < uuid_count)
- continue;
-
- uuid16[uuid_count++] = uuid->uuid.value.uuid16;
- eir_len += sizeof(uint16_t);
- }
-
- if (uuid_count > 0) {
- /* EIR Data length */
- ptr[0] = (uuid_count * sizeof(uint16_t)) + 1;
- /* EIR Data type */
- ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
-
- ptr += 2;
- eir_len += 2;
-
- for (i = 0; i < uuid_count; i++) {
- *ptr++ = (uuid16[i] & 0x00ff);
- *ptr++ = (uuid16[i] & 0xff00) >> 8;
- }
- }
-
- /* Group all UUID128 types */
- if (eir_len <= EIR_DATA_LENGTH - 2)
- eir_generate_uuid128(dev->uuids, ptr, &eir_len);
-}
-
static void update_ext_inquiry_response(int index)
{
struct dev_info *dev = &devs[index];
memset(&cp, 0, sizeof(cp));
- create_ext_inquiry_response(index, cp.data);
+ eir_create(dev->name, dev->tx_power, dev->did_vendor, dev->did_product,
+ dev->did_version, dev->uuids, cp.data);
if (memcmp(cp.data, dev->eir, sizeof(cp.data)) == 0)
return;
adapter = manager_find_adapter_by_id(index);
if (adapter)
- adapter_update_local_name(adapter, name);
+ adapter_name_changed(adapter, name);
update_ext_inquiry_response(index);
}
DBG("Got name for hci%d", index);
- /* Even though it shouldn't happen (assuming the kernel behaves
- * properly) it seems like we might miss the very first
- * initialization commands that the kernel sends. So check for
- * it here (since read_local_name is one of the last init
- * commands) and resend the first ones if we haven't seen
- * their results yet */
-
- if (hci_test_bit(PENDING_FEATURES, &dev->pending))
- hci_send_cmd(dev->sk, OGF_INFO_PARAM,
- OCF_READ_LOCAL_FEATURES, 0, NULL);
-
- if (hci_test_bit(PENDING_VERSION, &dev->pending))
- hci_send_cmd(dev->sk, OGF_INFO_PARAM,
- OCF_READ_LOCAL_VERSION, 0, NULL);
-
- if (!dev->pending)
+ if (!dev->pending && dev->up)
init_adapter(index);
}
{
struct dev_info *dev = &devs[index];
read_simple_pairing_mode_rp *rp = ptr;
- struct btd_adapter *adapter;
DBG("hci%d status %u", index, rp->status);
dev->ssp_mode = rp->mode;
update_ext_inquiry_response(index);
-
- adapter = manager_find_adapter(&dev->bdaddr);
- if (!adapter) {
- error("No matching adapter found");
- return;
- }
-
- adapter_update_ssp_mode(adapter, rp->mode);
}
static void read_local_ext_features_complete(int index,
const read_local_ext_features_rp *rp)
{
- struct btd_adapter *adapter;
+ struct dev_info *dev = &devs[index];
DBG("hci%d status %u", index, rp->status);
if (rp->status)
return;
- adapter = manager_find_adapter_by_id(index);
- if (!adapter) {
- error("No matching adapter found");
- return;
- }
-
/* Local Extended feature page number is 1 */
if (rp->page_num != 1)
return;
- btd_adapter_update_local_ext_features(adapter, rp->features);
+ memcpy(dev->extfeatures, rp->features, sizeof(dev->extfeatures));
}
static void read_bd_addr_complete(int index, read_bd_addr_rp *rp)
init_adapter(index);
}
+static inline void cs_inquiry_evt(int index, uint8_t status)
+{
+ if (status) {
+ error("Inquiry Failed with status 0x%02x", status);
+ return;
+ }
+
+ set_state(index, DISCOV_INQ);
+}
+
static inline void cmd_status(int index, void *ptr)
{
- struct dev_info *dev = &devs[index];
evt_cmd_status *evt = ptr;
uint16_t opcode = btohs(evt->opcode);
if (opcode == cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY))
- start_inquiry(&dev->bdaddr, evt->status, FALSE);
+ cs_inquiry_evt(index, evt->status);
}
static void read_scan_complete(int index, uint8_t status, void *ptr)
if (dev->pending_cod == 0)
return;
- dev->current_cod = dev->pending_cod;
- dev->pending_cod = 0;
-
- adapter = manager_find_adapter(&dev->bdaddr);
- if (adapter)
- btd_adapter_class_changed(adapter, dev->current_cod);
+ dev->current_cod = dev->pending_cod;
+ dev->pending_cod = 0;
+
+ adapter = manager_find_adapter(&dev->bdaddr);
+ if (adapter)
+ btd_adapter_class_changed(adapter, dev->current_cod);
+
+ update_ext_inquiry_response(index);
+
+ if (dev->wanted_cod == dev->current_cod)
+ return;
+
+ if (dev->wanted_cod & LIMITED_BIT &&
+ !(dev->current_cod & LIMITED_BIT))
+ hciops_set_limited_discoverable(index, TRUE);
+ else if (!(dev->wanted_cod & LIMITED_BIT) &&
+ (dev->current_cod & LIMITED_BIT))
+ hciops_set_limited_discoverable(index, FALSE);
+ else
+ write_class(index, dev->wanted_cod);
+}
+
+static void read_local_oob_data_complete(int index, uint8_t status,
+ read_local_oob_data_rp *rp)
+{
+ struct btd_adapter *adapter = manager_find_adapter_by_id(index);
+
+ if (!adapter)
+ return;
+
+ if (status)
+ oob_read_local_data_complete(adapter, NULL, NULL);
+ else
+ oob_read_local_data_complete(adapter, rp->hash, rp->randomizer);
+}
+
+static inline void inquiry_complete_evt(int index, uint8_t status)
+{
+ int adapter_type;
+ struct btd_adapter *adapter;
+
+ if (status) {
+ error("Inquiry Failed with status 0x%02x", status);
+ return;
+ }
+
+ adapter = manager_find_adapter_by_id(index);
+ if (!adapter) {
+ error("No matching adapter found");
+ return;
+ }
+
+ adapter_type = get_adapter_type(index);
+
+ if (adapter_type == BR_EDR_LE &&
+ start_scanning(index, TIMEOUT_BR_LE_SCAN) == 0)
+ return;
+
+ set_state(index, DISCOV_NAMES);
+}
+
+static inline void cc_inquiry_cancel(int index, uint8_t status)
+{
+ if (status) {
+ error("Inquiry Cancel Failed with status 0x%02x", status);
+ return;
+ }
+
+ set_state(index, DISCOV_HALTED);
+}
- update_ext_inquiry_response(index);
+static inline void cc_le_set_scan_enable(int index, uint8_t status)
+{
+ struct dev_info *info = &devs[index];
- if (dev->wanted_cod == dev->current_cod)
+ if (status) {
+ error("LE Set Scan Enable Failed with status 0x%02x", status);
return;
+ }
- if (dev->wanted_cod & LIMITED_BIT &&
- !(dev->current_cod & LIMITED_BIT))
- hciops_set_limited_discoverable(index, TRUE);
- else if (!(dev->wanted_cod & LIMITED_BIT) &&
- (dev->current_cod & LIMITED_BIT))
- hciops_set_limited_discoverable(index, FALSE);
+ if (info->discov_state == DISCOV_SCAN)
+ set_state(index, DISCOV_HALTED);
else
- write_class(index, dev->wanted_cod);
+ set_state(index, DISCOV_SCAN);
}
static inline void cmd_complete(int index, void *ptr)
ptr += sizeof(evt_cmd_complete);
read_bd_addr_complete(index, ptr);
break;
- case cmd_opcode_pack(OGF_LINK_CTL, OCF_PERIODIC_INQUIRY):
- start_inquiry(&dev->bdaddr, status, TRUE);
- break;
- case cmd_opcode_pack(OGF_LINK_CTL, OCF_EXIT_PERIODIC_INQUIRY):
- inquiry_complete(&dev->bdaddr, status, TRUE);
- break;
case cmd_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY_CANCEL):
- inquiry_complete(&dev->bdaddr, status, FALSE);
- break;
- case cmd_opcode_pack(OGF_HOST_CTL, OCF_WRITE_LE_HOST_SUPPORTED):
- write_le_host_complete(index, status);
+ cc_inquiry_cancel(index, status);
break;
case cmd_opcode_pack(OGF_LE_CTL, OCF_LE_SET_SCAN_ENABLE):
- btd_event_le_set_scan_enable_complete(&dev->bdaddr, status);
+ cc_le_set_scan_enable(index, status);
break;
case cmd_opcode_pack(OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME):
if (!status)
ptr += sizeof(evt_cmd_complete);
read_tx_power_complete(index, ptr);
break;
+ case cmd_opcode_pack(OGF_HOST_CTL, OCF_READ_LOCAL_OOB_DATA):
+ ptr += sizeof(evt_cmd_complete);
+ read_local_oob_data_complete(index, status, ptr);
+ break;
};
}
{
struct dev_info *dev = &devs[index];
evt_remote_name_req_complete *evt = ptr;
+ struct btd_adapter *adapter;
char name[MAX_NAME_LENGTH + 1];
+ struct found_dev *found;
+
+ GSList *match;
DBG("hci%d status %u", index, evt->status);
memset(name, 0, sizeof(name));
- if (!evt->status)
+ if (evt->status == 0) {
memcpy(name, evt->name, MAX_NAME_LENGTH);
+ btd_event_remote_name(&dev->bdaddr, &evt->bdaddr, name);
+ }
+
+ adapter = manager_find_adapter_by_id(index);
+ if (!adapter) {
+ error("No matching adapter found");
+ return;
+ }
+
+ match = g_slist_find_custom(dev->need_name, &evt->bdaddr,
+ found_dev_bda_cmp);
+ if (match == NULL)
+ return;
+
+ found = match->data;
+ found->name_state = NAME_NOT_NEEDED;
+
+ dev->need_name = g_slist_remove_link(dev->need_name, match);
+
+ match->next = dev->found_devs;
+ dev->found_devs = match;
+ dev->found_devs = g_slist_sort(dev->found_devs, found_dev_rssi_cmp);
- btd_event_remote_name(&dev->bdaddr, &evt->bdaddr, evt->status, name);
+ if (resolve_names(dev, adapter) < 0)
+ set_state(index, DISCOV_HALTED);
}
static inline void remote_version_information(int index, void *ptr)
btohs(evt->lmp_subver));
}
+static void dev_found(struct dev_info *info, bdaddr_t *dba, addr_type_t type,
+ uint32_t cod, int8_t rssi, uint8_t cfm_name,
+ uint8_t *eir, uint8_t eir_len)
+{
+ struct found_dev *dev;
+ GSList *match;
+
+ match = g_slist_find_custom(info->found_devs, dba, found_dev_bda_cmp);
+ if (match != NULL) {
+ cfm_name = 0;
+ goto event;
+ }
+
+ dev = g_new0(struct found_dev, 1);
+ bacpy(&dev->bdaddr, dba);
+ dev->rssi = rssi;
+ if (cfm_name)
+ dev->name_state = NAME_UNKNOWN;
+ else
+ dev->name_state = NAME_NOT_NEEDED;
+
+ info->found_devs = g_slist_prepend(info->found_devs, dev);
+
+event:
+ btd_event_device_found(&info->bdaddr, dba, type, cod, rssi, cfm_name,
+ eir, eir_len);
+}
+
static inline void inquiry_result(int index, int plen, void *ptr)
{
struct dev_info *dev = &devs[index];
(info->dev_class[1] << 8) |
(info->dev_class[2] << 16);
- btd_event_device_found(&dev->bdaddr, &info->bdaddr, class,
- 0, NULL);
+ dev_found(dev, &info->bdaddr, ADDR_TYPE_BREDR, class, 0, 1,
+ NULL, 0);
ptr += INQUIRY_INFO_SIZE;
}
}
| (info->dev_class[1] << 8)
| (info->dev_class[2] << 16);
- btd_event_device_found(&dev->bdaddr, &info->bdaddr,
- class, info->rssi, NULL);
+ dev_found(dev, &info->bdaddr, ADDR_TYPE_BREDR, class,
+ info->rssi, 1, NULL, 0);
ptr += INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE;
}
} else {
| (info->dev_class[1] << 8)
| (info->dev_class[2] << 16);
- btd_event_device_found(&dev->bdaddr, &info->bdaddr,
- class, info->rssi, NULL);
+ dev_found(dev, &info->bdaddr, ADDR_TYPE_BREDR, class,
+ info->rssi, 1, NULL, 0);
ptr += INQUIRY_INFO_WITH_RSSI_SIZE;
}
}
uint32_t class = info->dev_class[0]
| (info->dev_class[1] << 8)
| (info->dev_class[2] << 16);
+ gboolean cfm_name;
+
+ if (eir_has_complete_name(info->data, sizeof(info->data)))
+ cfm_name = FALSE;
+ else
+ cfm_name = TRUE;
- btd_event_device_found(&dev->bdaddr, &info->bdaddr, class,
- info->rssi, info->data);
+ dev_found(dev, &info->bdaddr, ADDR_TYPE_BREDR, class,
+ info->rssi, cfm_name,
+ info->data, sizeof(info->data));
ptr += EXTENDED_INQUIRY_INFO_SIZE;
}
}
btd_event_remote_class(&dev->bdaddr, &evt->bdaddr, class);
}
+static inline addr_type_t le_addr_type(uint8_t bdaddr_type)
+{
+ switch (bdaddr_type) {
+ case LE_RANDOM_ADDRESS:
+ return ADDR_TYPE_LE_RANDOM;
+ case LE_PUBLIC_ADDRESS:
+ default:
+ return ADDR_TYPE_LE_PUBLIC;
+ }
+}
+
static inline void le_advertising_report(int index, evt_le_meta_event *meta)
{
struct dev_info *dev = &devs[index];
le_advertising_info *info;
- uint8_t num_reports;
+ uint8_t num_reports, rssi;
const uint8_t RSSI_SIZE = 1;
num_reports = meta->data[0];
info = (le_advertising_info *) &meta->data[1];
- btd_event_advertising_report(&dev->bdaddr, info);
+ rssi = *(info->data + info->length);
+
+ dev_found(dev, &info->bdaddr, le_addr_type(info->bdaddr_type), 0, rssi,
+ 0, info->data, info->length);
+
num_reports--;
while (num_reports--) {
info = (le_advertising_info *) (info->data + info->length +
RSSI_SIZE);
- btd_event_advertising_report(&dev->bdaddr, info);
+ rssi = *(info->data + info->length);
+
+ dev_found(dev, &info->bdaddr, le_addr_type(info->bdaddr_type),
+ 0, rssi, 0, info->data, info->length);
}
}
if (dev->watch_id > 0)
g_source_remove(dev->watch_id);
+ if (dev->stop_scan_id > 0)
+ g_source_remove(dev->stop_scan_id);
+
if (dev->io != NULL)
g_io_channel_unref(dev->io);
hci_close_dev(dev->sk);
- g_slist_foreach(dev->keys, (GFunc) g_free, NULL);
- g_slist_free(dev->keys);
-
- g_slist_foreach(dev->uuids, (GFunc) g_free, NULL);
- g_slist_free(dev->uuids);
-
- g_slist_foreach(dev->connections, (GFunc) conn_free, NULL);
- g_slist_free(dev->connections);
+ g_slist_free_full(dev->keys, g_free);
+ g_slist_free_full(dev->uuids, g_free);
+ g_slist_free_full(dev->connections, g_free);
init_dev_info(index, -1, dev->registered, dev->already_up);
}
case EVT_INQUIRY_COMPLETE:
evt = (evt_cmd_status *) ptr;
- inquiry_complete(&dev->bdaddr, evt->status, FALSE);
+ inquiry_complete_evt(index, evt->status);
break;
case EVT_INQUIRY_RESULT:
break;
case EVT_REMOTE_OOB_DATA_REQUEST:
- remote_oob_data_request(index, ptr);
+ remote_oob_data_request(index, (bdaddr_t *) ptr);
break;
}
bacpy(&dev->bdaddr, &di.bdaddr);
memcpy(dev->features, di.features, 8);
+ if (dev->features[7] & LMP_EXT_FEAT) {
+ uint8_t page_num = 0x01;
+
+ hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+ OCF_READ_LOCAL_EXT_FEATURES, 1, &page_num);
+ }
+
/* Set page timeout */
if ((main_opts.flags & (1 << HCID_SET_PAGETO))) {
write_page_timeout_cp cp;
hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_STORED_LINK_KEY,
READ_STORED_LINK_KEY_CP_SIZE, &cp);
- if (!dev->pending)
+ if (!dev->pending) {
init_adapter(index);
+ return;
+ }
+
+ /* Even though it shouldn't happen (assuming the kernel behaves
+ * properly) it seems like we might miss the very first
+ * initialization commands that the kernel sends. So check for
+ * it here and resend the ones we haven't seen their results yet */
+
+ if (hci_test_bit(PENDING_FEATURES, &dev->pending))
+ hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+ OCF_READ_LOCAL_FEATURES, 0, NULL);
+
+ if (hci_test_bit(PENDING_VERSION, &dev->pending))
+ hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+ OCF_READ_LOCAL_VERSION, 0, NULL);
+
+ if (hci_test_bit(PENDING_NAME, &dev->pending))
+ hci_send_cmd(dev->sk, OGF_HOST_CTL,
+ OCF_READ_LOCAL_NAME, 0, 0);
+
+ if (hci_test_bit(PENDING_BDADDR, &dev->pending))
+ hci_send_cmd(dev->sk, OGF_INFO_PARAM,
+ OCF_READ_BD_ADDR, 0, NULL);
}
static void init_pending(int index)
struct dev_info *dev = &devs[index];
struct hci_conn_list_req *cl;
struct hci_conn_info *ci;
- int err, i;
+ int i;
DBG("hci%d", index);
conn->handle = ci->handle;
}
- err = 0;
-
failed:
g_free(cl);
}
devs[index].up = FALSE;
devs[index].pending_cod = 0;
devs[index].cache_enable = TRUE;
+ devs[index].discov_state = DISCOV_HALTED;
if (!devs[index].pending) {
struct btd_adapter *adapter;
return err;
}
-static int hciops_start_inquiry(int index, uint8_t length, gboolean periodic)
+static int start_inquiry(int index, uint8_t length)
{
struct dev_info *dev = &devs[index];
uint8_t lap[3] = { 0x33, 0x8b, 0x9e };
- int err;
-
- DBG("hci%d length %u periodic %d", index, length, periodic);
-
- if (periodic) {
- periodic_inquiry_cp cp;
-
- memset(&cp, 0, sizeof(cp));
- memcpy(&cp.lap, lap, 3);
- cp.max_period = htobs(24);
- cp.min_period = htobs(16);
- cp.length = length;
- cp.num_rsp = 0x00;
-
- err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_PERIODIC_INQUIRY,
- PERIODIC_INQUIRY_CP_SIZE, &cp);
- } else {
- inquiry_cp inq_cp;
+ inquiry_cp inq_cp;
- memset(&inq_cp, 0, sizeof(inq_cp));
- memcpy(&inq_cp.lap, lap, 3);
- inq_cp.length = length;
- inq_cp.num_rsp = 0x00;
+ DBG("hci%d length %u", index, length);
- err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
- OCF_INQUIRY, INQUIRY_CP_SIZE, &inq_cp);
- }
+ memset(&inq_cp, 0, sizeof(inq_cp));
+ memcpy(&inq_cp.lap, lap, 3);
+ inq_cp.length = length;
+ inq_cp.num_rsp = 0x00;
- if (err < 0)
- err = -errno;
+ if (hci_send_cmd(dev->sk, OGF_LINK_CTL,
+ OCF_INQUIRY, INQUIRY_CP_SIZE, &inq_cp) < 0)
+ return -errno;
- return err;
+ return 0;
}
static int le_set_scan_enable(int index, uint8_t enable)
return 0;
}
-static int hciops_start_scanning(int index)
+static gboolean stop_le_scan_cb(gpointer user_data)
+{
+ struct dev_info *dev = user_data;
+ int err;
+
+ err = le_set_scan_enable(dev->id, 0);
+ if (err < 0)
+ return TRUE;
+
+ dev->stop_scan_id = 0;
+
+ return FALSE;
+}
+
+static int start_scanning(int index, int timeout)
{
struct dev_info *dev = &devs[index];
le_set_scan_parameters_cp cp;
+ int err;
DBG("hci%d", index);
LE_SET_SCAN_PARAMETERS_CP_SIZE, &cp) < 0)
return -errno;
- return le_set_scan_enable(index, 1);
-}
+ err = le_set_scan_enable(index, 1);
+ if (err < 0)
+ return err;
-static int hciops_stop_scanning(int index)
-{
- DBG("hci%d", index);
+ /* Schedule a le scan disable in 'timeout' milliseconds */
+ dev->stop_scan_id = g_timeout_add(timeout, stop_le_scan_cb, dev);
- return le_set_scan_enable(index, 0);
+ return 0;
}
-static int hciops_resolve_name(int index, bdaddr_t *bdaddr)
+static int hciops_stop_scanning(int index)
{
struct dev_info *dev = &devs[index];
- remote_name_req_cp cp;
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s", index, addr);
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, bdaddr);
- cp.pscan_rep_mode = 0x02;
+ DBG("hci%d", index);
- if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ,
- REMOTE_NAME_REQ_CP_SIZE, &cp) < 0)
- return -errno;
+ if (dev->stop_scan_id > 0) {
+ g_source_remove(dev->stop_scan_id);
+ dev->stop_scan_id = 0;
+ }
- return 0;
+ return le_set_scan_enable(index, 0);
}
static int hciops_set_name(int index, const char *name)
return 0;
}
-static int hciops_cancel_resolve_name(int index, bdaddr_t *bdaddr)
+static int cancel_resolve_name(int index)
{
- struct dev_info *dev = &devs[index];
+ struct dev_info *info = &devs[index];
+ struct found_dev *dev;
remote_name_req_cancel_cp cp;
- char addr[18];
+ struct btd_adapter *adapter;
- ba2str(bdaddr, addr);
- DBG("hci%d dba %s", index, addr);
+ DBG("hci%d", index);
+
+ if (g_slist_length(info->need_name) == 0)
+ return 0;
+
+ dev = info->need_name->data;
+ if (dev->name_state != NAME_PENDING)
+ return 0;
memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, bdaddr);
+ bacpy(&cp.bdaddr, &dev->bdaddr);
+
+ adapter = manager_find_adapter_by_id(index);
+ if (adapter)
+ adapter_set_discovering(adapter, FALSE);
+
+ found_dev_cleanup(info);
- if (hci_send_cmd(dev->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL,
+ if (hci_send_cmd(info->sk, OGF_LINK_CTL, OCF_REMOTE_NAME_REQ_CANCEL,
REMOTE_NAME_REQ_CANCEL_CP_SIZE, &cp) < 0)
return -errno;
return 0;
}
-static int hciops_fast_connectable(int index, gboolean enable)
+static int hciops_start_discovery(int index)
+{
+ int adapter_type = get_adapter_type(index);
+
+ DBG("hci%u", index);
+
+ switch (adapter_type) {
+ case BR_EDR_LE:
+ return start_inquiry(index, LENGTH_BR_LE_INQ);
+ case BR_EDR:
+ return start_inquiry(index, LENGTH_BR_INQ);
+ case LE_ONLY:
+ return start_scanning(index, TIMEOUT_LE_SCAN);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int hciops_stop_discovery(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("index %d", index);
+
+ switch (dev->discov_state) {
+ case DISCOV_INQ:
+ return hciops_stop_inquiry(index);
+ case DISCOV_SCAN:
+ return hciops_stop_scanning(index);
+ case DISCOV_NAMES:
+ cancel_resolve_name(index);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int hciops_set_fast_connectable(int index, gboolean enable)
{
struct dev_info *dev = &devs[index];
write_page_activity_cp cp;
return 0;
}
-static int hciops_read_local_version(int index, struct hci_version *ver)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d", index);
-
- memcpy(ver, &dev->ver, sizeof(*ver));
-
- return 0;
-}
-
-static int hciops_read_local_features(int index, uint8_t *features)
-{
- struct dev_info *dev = &devs[index];
-
- DBG("hci%d", index);
-
- memcpy(features, dev->features, 8);
-
- return 0;
-}
-
static int hciops_disconnect(int index, bdaddr_t *bdaddr)
{
DBG("hci%d", index);
return 0;
}
-static int hciops_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin)
+static int hciops_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin,
+ size_t pin_len)
{
struct dev_info *dev = &devs[index];
char addr[18];
if (pin) {
pin_code_reply_cp pr;
- size_t len = strlen(pin);
- dev->pin_length = len;
+ dev->pin_length = pin_len;
memset(&pr, 0, sizeof(pr));
bacpy(&pr.bdaddr, bdaddr);
- memcpy(pr.pin_code, pin, len);
- pr.pin_len = len;
+ memcpy(pr.pin_code, pin, pin_len);
+ pr.pin_len = pin_len;
err = hci_send_cmd(dev->sk, OGF_LINK_CTL,
OCF_PIN_CODE_REPLY,
PIN_CODE_REPLY_CP_SIZE, &pr);
return err;
}
-static int hciops_enable_le(int index)
-{
- struct dev_info *dev = &devs[index];
- write_le_host_supported_cp cp;
-
- DBG("hci%d", index);
-
- if (!(dev->features[4] & LMP_LE))
- return -ENOTSUP;
-
- cp.le = 0x01;
- cp.simul = (dev->features[6] & LMP_LE_BREDR) ? 0x01 : 0x00;
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
- OCF_WRITE_LE_HOST_SUPPORTED,
- WRITE_LE_HOST_SUPPORTED_CP_SIZE, &cp) < 0)
- return -errno;
-
- return 0;
-}
-
static uint8_t generate_service_class(int index)
{
struct dev_info *dev = &devs[index];
return 0;
}
+static int hciops_read_local_oob_data(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ DBG("hci%d", index);
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_READ_LOCAL_OOB_DATA, 0, 0)
+ < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int hciops_add_remote_oob_data(int index, bdaddr_t *bdaddr,
+ uint8_t *hash, uint8_t *randomizer)
+{
+ char addr[18];
+ struct dev_info *dev = &devs[index];
+ GSList *match;
+ struct oob_data *data;
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", index, addr);
+
+ match = g_slist_find_custom(dev->oob_data, bdaddr, oob_bdaddr_cmp);
+
+ if (match) {
+ data = match->data;
+ } else {
+ data = g_new(struct oob_data, 1);
+ bacpy(&data->bdaddr, bdaddr);
+ dev->oob_data = g_slist_prepend(dev->oob_data, data);
+ }
+
+ memcpy(data->hash, hash, sizeof(data->hash));
+ memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
+
+ return 0;
+}
+
+static int hciops_remove_remote_oob_data(int index, bdaddr_t *bdaddr)
+{
+ char addr[18];
+ struct dev_info *dev = &devs[index];
+ GSList *match;
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", index, addr);
+
+ match = g_slist_find_custom(dev->oob_data, bdaddr, oob_bdaddr_cmp);
+
+ if (!match)
+ return -ENOENT;
+
+ g_free(match->data);
+ dev->oob_data = g_slist_delete_link(dev->oob_data, match);
+
+ return 0;
+}
+
+static int hciops_confirm_name(int index, bdaddr_t *bdaddr,
+ gboolean name_known)
+{
+ struct dev_info *info = &devs[index];
+ struct found_dev *dev;
+ GSList *match;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%u %s name_known %u", index, addr, name_known);
+
+ match = g_slist_find_custom(info->found_devs, bdaddr,
+ found_dev_bda_cmp);
+ if (match == NULL)
+ return -ENOENT;
+
+ dev = match->data;
+
+ if (name_known) {
+ dev->name_state = NAME_NOT_NEEDED;
+ info->found_devs = g_slist_sort(info->found_devs,
+ found_dev_rssi_cmp);
+ return 0;
+ }
+
+ dev->name_state = NAME_NEEDED;
+ info->found_devs = g_slist_remove_link(info->found_devs, match);
+
+ match->next = info->need_name;
+ info->need_name = match;
+ info->need_name = g_slist_sort(info->need_name, found_dev_rssi_cmp);
+
+ return 0;
+}
+
static struct btd_adapter_ops hci_ops = {
.setup = hciops_setup,
.cleanup = hciops_cleanup,
.set_discoverable = hciops_set_discoverable,
.set_pairable = hciops_set_pairable,
.set_limited_discoverable = hciops_set_limited_discoverable,
- .start_inquiry = hciops_start_inquiry,
- .stop_inquiry = hciops_stop_inquiry,
- .start_scanning = hciops_start_scanning,
- .stop_scanning = hciops_stop_scanning,
- .resolve_name = hciops_resolve_name,
- .cancel_resolve_name = hciops_cancel_resolve_name,
+ .start_discovery = hciops_start_discovery,
+ .stop_discovery = hciops_stop_discovery,
.set_name = hciops_set_name,
.set_dev_class = hciops_set_dev_class,
- .set_fast_connectable = hciops_fast_connectable,
+ .set_fast_connectable = hciops_set_fast_connectable,
.read_clock = hciops_read_clock,
.read_bdaddr = hciops_read_bdaddr,
.block_device = hciops_block_device,
.unblock_device = hciops_unblock_device,
.get_conn_list = hciops_get_conn_list,
- .read_local_version = hciops_read_local_version,
- .read_local_features = hciops_read_local_features,
.disconnect = hciops_disconnect,
.remove_bonding = hciops_remove_bonding,
.pincode_reply = hciops_pincode_reply,
.confirm_reply = hciops_confirm_reply,
.passkey_reply = hciops_passkey_reply,
- .enable_le = hciops_enable_le,
.encrypt_link = hciops_encrypt_link,
.set_did = hciops_set_did,
.add_uuid = hciops_add_uuid,
.set_io_capability = hciops_set_io_capability,
.create_bonding = hciops_create_bonding,
.cancel_bonding = hciops_cancel_bonding,
+ .read_local_oob_data = hciops_read_local_oob_data,
+ .add_remote_oob_data = hciops_add_remote_oob_data,
+ .remove_remote_oob_data = hciops_remove_remote_oob_data,
+ .confirm_name = hciops_confirm_name,
};
static int hciops_init(void)
#define MCE_RADIO_STATES_CHANGE_REQ "req_radio_states_change"
#define MCE_RADIO_STATES_GET "get_radio_states"
#define MCE_RADIO_STATES_SIG "radio_states_ind"
+#define MCE_TKLOCK_MODE_SIG "tklock_mode_ind"
static guint watch_id;
+static guint tklock_watch_id;
static DBusConnection *conn = NULL;
static gboolean mce_bt_set = FALSE;
-static gboolean collision = FALSE;
+static gboolean mce_bt_on = FALSE;
+
+static gboolean mce_tklock_mode_cb(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ DBusMessageIter args;
+ const char *sigvalue;
+
+ if (!dbus_message_iter_init(message, &args)) {
+ error("message has no arguments");
+ } else if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
+ error("argument is not string");
+ } else {
+
+ dbus_message_iter_get_basic(&args, &sigvalue);
+ DBG("got signal with value %s", sigvalue);
+
+ if (g_strcmp0("unlocked", sigvalue) == 0 && mce_bt_on)
+ btd_adapter_enable_auto_connect(adapter);
+ }
+
+ return TRUE;
+}
static gboolean mce_signal_callback(DBusConnection *connection,
DBusMessage *message, void *user_data)
DBusMessageIter args;
uint32_t sigvalue;
struct btd_adapter *adapter = user_data;
+ int err;
DBG("received mce signal");
/* set the adapter according to the mce signal
and remember the value */
- mce_bt_set = sigvalue & MCE_RADIO_STATE_BLUETOOTH ?
- TRUE : FALSE;
+ mce_bt_on = sigvalue & MCE_RADIO_STATE_BLUETOOTH ? TRUE : FALSE;
- if (mce_bt_set)
- btd_adapter_switch_online(adapter);
+ if (mce_bt_on)
+ err = btd_adapter_switch_online(adapter);
else
- btd_adapter_switch_offline(adapter);
+ err = btd_adapter_switch_offline(adapter);
+
+ if (err == 0)
+ mce_bt_set = TRUE;
+
}
return TRUE;
static void read_radio_states_cb(DBusPendingCall *call, void *user_data)
{
- DBusError err;
+ DBusError derr;
DBusMessage *reply;
dbus_uint32_t radio_states;
struct btd_adapter *adapter = user_data;
+ int err;
reply = dbus_pending_call_steal_reply(call);
- dbus_error_init(&err);
- if (dbus_set_error_from_message(&err, reply)) {
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
error("mce replied with an error: %s, %s",
- err.name, err.message);
- dbus_error_free(&err);
+ derr.name, derr.message);
+ dbus_error_free(&derr);
goto done;
}
- dbus_error_init(&err);
- if (dbus_message_get_args(reply, &err,
+ dbus_error_init(&derr);
+ if (dbus_message_get_args(reply, &derr,
DBUS_TYPE_UINT32, &radio_states,
DBUS_TYPE_INVALID) == FALSE) {
error("unable to parse get_radio_states reply: %s, %s",
- err.name, err.message);
- dbus_error_free(&err);
+ derr.name, derr.message);
+ dbus_error_free(&derr);
goto done;
}
DBG("radio_states: %d", radio_states);
- mce_bt_set = radio_states & MCE_RADIO_STATE_BLUETOOTH ? TRUE : FALSE;
+ mce_bt_on = radio_states & MCE_RADIO_STATE_BLUETOOTH ? TRUE : FALSE;
- /* check if the adapter has not completed the initial power
- * cycle, if so delay action to mce_notify_powered */
- collision = mce_bt_set && adapter_powering_down(adapter);
-
- if (collision)
- goto done;
-
- if (mce_bt_set)
- btd_adapter_switch_online(adapter);
+ if (mce_bt_on)
+ err = btd_adapter_switch_online(adapter);
else
- btd_adapter_switch_offline(adapter);
+ err = btd_adapter_switch_offline(adapter);
+
+ if (err == 0)
+ mce_bt_set = TRUE;
done:
dbus_message_unref(reply);
static void adapter_powered(struct btd_adapter *adapter, gboolean powered)
{
DBusMessage *msg;
- dbus_uint32_t radio_states = 0;
- dbus_uint32_t radio_mask = MCE_RADIO_STATE_BLUETOOTH;
static gboolean startup = TRUE;
DBG("adapter_powered called with %d", powered);
if (startup) {
+ DBusPendingCall *call;
+
+ /* Initialization: sync adapter state and MCE radio state */
+
+ DBG("Startup: reading MCE Bluetooth radio state...");
startup = FALSE;
- return;
- }
- /* check if the plugin got the get_radio_states reply from the
- * mce when the adapter was not yet down during the power
- * cycling when bluetoothd is started */
- if (collision) {
- error("maemo6: powered state collision");
- collision = FALSE;
+ msg = dbus_message_new_method_call(MCE_SERVICE,
+ MCE_REQUEST_PATH, MCE_REQUEST_IF,
+ MCE_RADIO_STATES_GET);
- if (mce_bt_set)
- btd_adapter_switch_online(adapter);
+ if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) {
+ error("calling %s failed", MCE_RADIO_STATES_GET);
+ dbus_message_unref(msg);
+ return;
+ }
+ dbus_pending_call_set_notify(call, read_radio_states_cb,
+ adapter, NULL);
+ dbus_pending_call_unref(call);
+ dbus_message_unref(msg);
return;
}
- /* nothing to do if the states match */
- if (mce_bt_set == powered)
+ /* MCE initiated operation */
+ if (mce_bt_set == TRUE) {
+ mce_bt_set = FALSE;
return;
+ }
- /* set the mce value according to the state of the adapter */
- msg = dbus_message_new_method_call(MCE_SERVICE, MCE_REQUEST_PATH,
- MCE_REQUEST_IF, MCE_RADIO_STATES_CHANGE_REQ);
+ /* Non MCE operation: set MCE according to adapter state */
+ if (mce_bt_on != powered) {
+ dbus_uint32_t radio_states;
+ dbus_uint32_t radio_mask = MCE_RADIO_STATE_BLUETOOTH;
- if (powered)
- radio_states = MCE_RADIO_STATE_BLUETOOTH;
+ msg = dbus_message_new_method_call(MCE_SERVICE,
+ MCE_REQUEST_PATH, MCE_REQUEST_IF,
+ MCE_RADIO_STATES_CHANGE_REQ);
- dbus_message_append_args(msg, DBUS_TYPE_UINT32, &radio_states,
- DBUS_TYPE_UINT32, &radio_mask,
- DBUS_TYPE_INVALID);
+ radio_states = (powered ? MCE_RADIO_STATE_BLUETOOTH : 0);
- if (dbus_connection_send(conn, msg, NULL))
- mce_bt_set = powered;
- else
- error("calling %s failed", MCE_RADIO_STATES_CHANGE_REQ);
+ DBG("Changing MCE Bluetooth radio state to: %d", radio_states);
+
+ dbus_message_append_args(msg, DBUS_TYPE_UINT32, &radio_states,
+ DBUS_TYPE_UINT32, &radio_mask,
+ DBUS_TYPE_INVALID);
- dbus_message_unref(msg);
+ if (dbus_connection_send(conn, msg, NULL))
+ mce_bt_on = powered;
+ else
+ error("calling %s failed", MCE_RADIO_STATES_CHANGE_REQ);
+
+ dbus_message_unref(msg);
+ }
}
static int mce_probe(struct btd_adapter *adapter)
{
- DBusMessage *msg;
- DBusPendingCall *call;
DBG("path %s", adapter_get_path(adapter));
- msg = dbus_message_new_method_call(MCE_SERVICE, MCE_REQUEST_PATH,
- MCE_REQUEST_IF, MCE_RADIO_STATES_GET);
-
- if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) {
- error("calling %s failed", MCE_RADIO_STATES_GET);
- dbus_message_unref(msg);
- return -1;
- }
-
- dbus_pending_call_set_notify(call, read_radio_states_cb, adapter, NULL);
- dbus_pending_call_unref(call);
- dbus_message_unref(msg);
-
watch_id = g_dbus_add_signal_watch(conn, NULL, MCE_SIGNAL_PATH,
MCE_SIGNAL_IF, MCE_RADIO_STATES_SIG,
mce_signal_callback, adapter, NULL);
+ tklock_watch_id = g_dbus_add_signal_watch(conn, NULL, MCE_SIGNAL_PATH,
+ MCE_SIGNAL_IF, MCE_TKLOCK_MODE_SIG,
+ mce_tklock_mode_cb, adapter, NULL);
+
btd_adapter_register_powered_callback(adapter, adapter_powered);
return 0;
if (watch_id > 0)
g_dbus_remove_watch(conn, watch_id);
+ if (tklock_watch_id > 0)
+ g_dbus_remove_watch(conn, tklock_watch_id);
+
btd_adapter_unregister_powered_callback(adapter, adapter_powered);
}
#include "manager.h"
#include "device.h"
#include "event.h"
+#include "oob.h"
#define MGMT_BUF_SIZE 1024
static struct controller_info {
gboolean valid;
gboolean notified;
- uint8_t type;
bdaddr_t bdaddr;
- uint8_t features[8];
- uint8_t dev_class[3];
+ uint8_t version;
uint16_t manufacturer;
- uint8_t hci_ver;
- uint16_t hci_rev;
- gboolean enabled;
- gboolean connectable;
- gboolean discoverable;
- gboolean pairable;
- uint8_t sec_mode;
+ uint32_t supported_settings;
+ uint32_t current_settings;
+ uint8_t dev_class[3];
GSList *connections;
} *controllers = NULL;
static int mgmt_set_discoverable(int index, gboolean discoverable)
{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_discoverable)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_set_discoverable *cp = (void *) &buf[sizeof(*hdr)];
+
DBG("index %d discoverable %d", index, discoverable);
- return mgmt_set_mode(index, MGMT_OP_SET_DISCOVERABLE, discoverable);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_SET_DISCOVERABLE);
+ hdr->index = htobs(index);
+ hdr->len = htobs(sizeof(*cp));
+
+ cp->val = discoverable;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
}
static int mgmt_set_pairable(int index, gboolean pairable)
return mgmt_set_mode(index, MGMT_OP_SET_PAIRABLE, pairable);
}
-static int mgmt_update_powered(int index, uint8_t powered)
+static inline int mgmt_powered(uint32_t settings)
{
- struct controller_info *info;
- struct btd_adapter *adapter;
- gboolean pairable, discoverable;
- uint8_t on_mode;
+ return (settings & MGMT_SETTING_POWERED) != 0;
+}
- if (index > max_index) {
- error("Unexpected index %u", index);
- return -ENODEV;
- }
+static inline int mgmt_connectable(uint32_t settings)
+{
+ return (settings & MGMT_SETTING_CONNECTABLE) != 0;
+}
- info = &controllers[index];
+static inline int mgmt_fast_connectable(uint32_t settings)
+{
+ return (settings & MGMT_SETTING_FAST_CONNECTABLE) != 0;
+}
- info->enabled = powered;
+static inline int mgmt_discoverable(uint32_t settings)
+{
+ return (settings & MGMT_SETTING_DISCOVERABLE) != 0;
+}
- adapter = manager_find_adapter(&info->bdaddr);
- if (adapter == NULL) {
- DBG("Adapter not found");
- return -ENODEV;
- }
+static inline int mgmt_pairable(uint32_t settings)
+{
+ return (settings & MGMT_SETTING_PAIRABLE) != 0;
+}
+
+static inline int mgmt_ssp(uint32_t settings)
+{
+ return (settings & MGMT_SETTING_SSP) != 0;
+}
+
+static inline int mgmt_bredr(uint32_t settings)
+{
+ return (settings & MGMT_SETTING_BREDR) != 0;
+}
+
+static inline int mgmt_high_speed(uint32_t settings)
+{
+ return (settings & MGMT_SETTING_HS) != 0;
+}
+
+static inline int mgmt_low_energy(uint32_t settings)
+{
+ return (settings & MGMT_SETTING_LE) != 0;
+}
- if (!powered) {
- info->connectable = FALSE;
- info->pairable = FALSE;
- info->discoverable = FALSE;
+static uint8_t create_mode(uint32_t settings)
+{
+ uint8_t mode = 0;
+
+ if (mgmt_connectable(settings))
+ mode |= SCAN_PAGE;
+
+ if (mgmt_discoverable(settings))
+ mode |= SCAN_INQUIRY;
+
+ return mode;
+}
+
+static int mgmt_update_powered(struct btd_adapter *adapter, uint32_t settings)
+{
+ gboolean pairable;
+ uint8_t on_mode;
+ uint16_t index;
+ if (!mgmt_powered(settings)) {
btd_adapter_stop(adapter);
return 0;
}
btd_adapter_get_mode(adapter, NULL, &on_mode, &pairable);
- discoverable = (on_mode == MODE_DISCOVERABLE);
+ index = adapter_get_dev_id(adapter);
- if (on_mode == MODE_DISCOVERABLE && !info->discoverable)
+ if (on_mode == MODE_DISCOVERABLE && !mgmt_discoverable(settings))
mgmt_set_discoverable(index, TRUE);
- else if (on_mode == MODE_CONNECTABLE && !info->connectable)
+ else if (on_mode == MODE_CONNECTABLE && !mgmt_connectable(settings))
mgmt_set_connectable(index, TRUE);
- else {
- uint8_t mode = 0;
-
- if (info->connectable)
- mode |= SCAN_PAGE;
- if (info->discoverable)
- mode |= SCAN_INQUIRY;
-
- adapter_mode_changed(adapter, mode);
- }
+ else
+ adapter_mode_changed(adapter, create_mode(settings));
- if (info->pairable != pairable)
+ if (mgmt_pairable(settings) != pairable)
mgmt_set_pairable(index, pairable);
return 0;
}
-static void mgmt_powered(int sk, uint16_t index, void *buf, size_t len)
+static int mode_changed(uint32_t s1, uint32_t s2)
{
- struct mgmt_mode *ev = buf;
+ if (mgmt_connectable(s1) != mgmt_connectable(s2))
+ return 1;
- if (len < sizeof(*ev)) {
- error("Too small powered event");
- return;
- }
-
- DBG("Controller %u powered %u", index, ev->val);
+ if (mgmt_discoverable(s1) != mgmt_discoverable(s2))
+ return 1;
- mgmt_update_powered(index, ev->val);
+ return 0;
}
-static void mgmt_discoverable(int sk, uint16_t index, void *buf, size_t len)
+static void mgmt_new_settings(int sk, uint16_t index, void *buf, size_t len)
{
- struct mgmt_mode *ev = buf;
+ uint32_t settings, *ev = buf;
struct controller_info *info;
struct btd_adapter *adapter;
- uint8_t mode;
if (len < sizeof(*ev)) {
- error("Too small discoverable event");
+ error("Too small new settings event");
return;
}
- DBG("Controller %u discoverable %u", index, ev->val);
+ DBG("hci%u new settings", index);
if (index > max_index) {
- error("Unexpected index %u in discoverable event", index);
+ error("Unexpected index %u in new_settings event", index);
return;
}
info = &controllers[index];
- info->discoverable = ev->val ? TRUE : FALSE;
-
adapter = manager_find_adapter(&info->bdaddr);
- if (!adapter)
- return;
-
- if (info->connectable)
- mode = SCAN_PAGE;
- else
- mode = 0;
-
- if (info->discoverable)
- mode |= SCAN_INQUIRY;
-
- adapter_mode_changed(adapter, mode);
-}
-
-static void mgmt_connectable(int sk, uint16_t index, void *buf, size_t len)
-{
- struct mgmt_mode *ev = buf;
- struct controller_info *info;
- struct btd_adapter *adapter;
- uint8_t mode;
-
- if (len < sizeof(*ev)) {
- error("Too small connectable event");
- return;
- }
-
- DBG("Controller %u connectable %u", index, ev->val);
-
- if (index > max_index) {
- error("Unexpected index %u in connectable event", index);
+ if (adapter == NULL) {
+ DBG("Adapter not found");
return;
}
- info = &controllers[index];
-
- info->connectable = ev->val ? TRUE : FALSE;
-
- adapter = manager_find_adapter(&info->bdaddr);
- if (!adapter)
- return;
+ settings = bt_get_le32(ev);
- if (info->discoverable)
- mode = SCAN_INQUIRY;
- else
- mode = 0;
+ if (mgmt_powered(settings) != mgmt_powered(info->current_settings))
+ mgmt_update_powered(adapter, settings);
+ else if (mode_changed(settings, info->current_settings))
+ adapter_mode_changed(adapter, create_mode(settings));
- if (info->connectable)
- mode |= SCAN_PAGE;
+ if (mgmt_pairable(settings) != mgmt_pairable(info->current_settings))
+ btd_adapter_pairable_changed(adapter, mgmt_pairable(settings));
- adapter_mode_changed(adapter, mode);
+ info->current_settings = settings;
}
-static void mgmt_pairable(int sk, uint16_t index, void *buf, size_t len)
+static void bonding_complete(struct controller_info *info, bdaddr_t *bdaddr,
+ uint8_t status)
{
- struct mgmt_mode *ev = buf;
- struct controller_info *info;
struct btd_adapter *adapter;
- if (len < sizeof(*ev)) {
- error("Too small pairable event");
- return;
- }
-
- DBG("Controller %u pairable %u", index, ev->val);
-
- if (index > max_index) {
- error("Unexpected index %u in pairable event", index);
- return;
- }
-
- info = &controllers[index];
-
- info->pairable = ev->val ? TRUE : FALSE;
-
adapter = manager_find_adapter(&info->bdaddr);
- if (!adapter)
- return;
-
- btd_adapter_pairable_changed(adapter, info->pairable);
+ if (adapter != NULL)
+ adapter_bonding_complete(adapter, bdaddr, status);
}
-static void mgmt_new_key(int sk, uint16_t index, void *buf, size_t len)
+static void mgmt_new_link_key(int sk, uint16_t index, void *buf, size_t len)
{
- struct mgmt_ev_new_key *ev = buf;
+ struct mgmt_ev_new_link_key *ev = buf;
struct controller_info *info;
if (len != sizeof(*ev)) {
info = &controllers[index];
- btd_event_link_key_notify(&info->bdaddr, &ev->key.bdaddr,
- ev->key.val, ev->key.type,
- ev->key.pin_len);
+ if (ev->store_hint)
+ btd_event_link_key_notify(&info->bdaddr, &ev->key.bdaddr,
+ ev->key.val, ev->key.type,
+ ev->key.pin_len);
- btd_event_bonding_complete(&info->bdaddr, &ev->key.bdaddr, 0);
+ bonding_complete(info, &ev->key.bdaddr, 0);
}
static void mgmt_device_connected(int sk, uint16_t index, void *buf, size_t len)
{
- struct mgmt_ev_device_connected *ev = buf;
+ struct mgmt_addr_info *ev = buf;
struct controller_info *info;
char addr[18];
static void mgmt_device_disconnected(int sk, uint16_t index, void *buf,
size_t len)
{
- struct mgmt_ev_device_disconnected *ev = buf;
+ struct mgmt_addr_info *ev = buf;
struct controller_info *info;
char addr[18];
return;
}
- ba2str(&ev->bdaddr, addr);
+ ba2str(&ev->addr.bdaddr, addr);
DBG("hci%u %s status %u", index, addr, ev->status);
info = &controllers[index];
- btd_event_conn_failed(&info->bdaddr, &ev->bdaddr, ev->status);
+ btd_event_conn_failed(&info->bdaddr, &ev->addr.bdaddr, ev->status);
/* In the case of security mode 3 devices */
- btd_event_bonding_complete(&info->bdaddr, &ev->bdaddr, ev->status);
+ bonding_complete(info, &ev->addr.bdaddr, ev->status);
}
-static int mgmt_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin)
+static int mgmt_pincode_reply(int index, bdaddr_t *bdaddr, const char *pin,
+ size_t pin_len)
{
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_pin_code_reply)];
struct mgmt_hdr *hdr = (void *) buf;
char addr[18];
ba2str(bdaddr, addr);
- DBG("index %d addr %s pin %s", index, addr, pin ? pin : "<none>");
+ DBG("index %d addr %s pinlen %zu", index, addr, pin_len);
memset(buf, 0, sizeof(buf));
buf_len = sizeof(*hdr) + sizeof(*cp);
} else {
struct mgmt_cp_pin_code_reply *cp;
- size_t pin_len;
- pin_len = strlen(pin);
if (pin_len > 16)
return -EINVAL;
info = &controllers[index];
- err = btd_event_request_pin(&info->bdaddr, &ev->bdaddr);
+ err = btd_event_request_pin(&info->bdaddr, &ev->bdaddr, ev->secure);
if (err < 0) {
error("btd_event_request_pin: %s", strerror(-err));
- mgmt_pincode_reply(index, &ev->bdaddr, NULL);
+ mgmt_pincode_reply(index, &ev->bdaddr, NULL, 0);
}
}
return 0;
}
+static int mgmt_passkey_reply(int index, bdaddr_t *bdaddr, uint32_t passkey)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_user_passkey_reply)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ size_t buf_len;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s passkey %06u", index, addr, passkey);
+
+ memset(buf, 0, sizeof(buf));
+
+ hdr->index = htobs(index);
+ if (passkey == INVALID_PASSKEY) {
+ struct mgmt_cp_user_passkey_neg_reply *cp;
+
+ hdr->opcode = htobs(MGMT_OP_USER_PASSKEY_NEG_REPLY);
+ hdr->len = htobs(sizeof(*cp));
+
+ cp = (void *) &buf[sizeof(*hdr)];
+ bacpy(&cp->bdaddr, bdaddr);
+
+ buf_len = sizeof(*hdr) + sizeof(*cp);
+ } else {
+ struct mgmt_cp_user_passkey_reply *cp;
+
+ hdr->opcode = htobs(MGMT_OP_USER_PASSKEY_REPLY);
+ hdr->len = htobs(sizeof(*cp));
+
+ cp = (void *) &buf[sizeof(*hdr)];
+ bacpy(&cp->bdaddr, bdaddr);
+ cp->passkey = htobl(passkey);
+
+ buf_len = sizeof(*hdr) + sizeof(*cp);
+ }
+
+ if (write(mgmt_sock, buf, buf_len) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static void mgmt_passkey_request(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_user_passkey_request *ev = buf;
+ struct controller_info *info;
+ char addr[18];
+ int err;
+
+ if (len < sizeof(*ev)) {
+ error("Too small passkey_request event");
+ return;
+ }
+
+ ba2str(&ev->bdaddr, addr);
+
+ DBG("hci%u %s", index, addr);
+
+ if (index > max_index) {
+ error("Unexpected index %u in passkey_request event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ err = btd_event_user_passkey(&info->bdaddr, &ev->bdaddr);
+ if (err < 0) {
+ error("btd_event_user_passkey: %s", strerror(-err));
+ mgmt_passkey_reply(index, &ev->bdaddr, INVALID_PASSKEY);
+ }
+}
+
+struct confirm_data {
+ int index;
+ bdaddr_t bdaddr;
+};
+
+static gboolean confirm_accept(gpointer user_data)
+{
+ struct confirm_data *data = user_data;
+ struct controller_info *info = &controllers[data->index];
+
+ DBG("auto-accepting incoming pairing request");
+
+ if (data->index > max_index || !info->valid)
+ return FALSE;
+
+ mgmt_confirm_reply(data->index, &data->bdaddr, TRUE);
+
+ return FALSE;
+}
+
static void mgmt_user_confirm_request(int sk, uint16_t index, void *buf,
size_t len)
{
ba2str(&ev->bdaddr, addr);
- DBG("hci%u %s", index, addr);
+ DBG("hci%u %s confirm_hint %u", index, addr, ev->confirm_hint);
if (index > max_index) {
error("Unexpected index %u in user_confirm_request event",
return;
}
+ if (ev->confirm_hint) {
+ struct confirm_data *data;
+
+ data = g_new0(struct confirm_data, 1);
+ data->index = index;
+ bacpy(&data->bdaddr, &ev->bdaddr);
+
+ g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1,
+ confirm_accept, data, g_free);
+ return;
+ }
+
info = &controllers[index];
err = btd_event_user_confirm(&info->bdaddr, &ev->bdaddr,
struct mgmt_hdr *hdr = (void *) buf;
struct mgmt_cp_add_uuid *cp = (void *) &buf[sizeof(*hdr)];
uuid_t uuid128;
+ uint128_t uint128;
DBG("index %d", index);
hdr->len = htobs(sizeof(*cp));
hdr->index = htobs(index);
- memcpy(cp->uuid, uuid128.value.uuid128.data, 16);
+ ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+ htob128(&uint128, (uint128_t *) cp->uuid);
+
cp->svc_hint = svc_hint;
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
struct mgmt_hdr *hdr = (void *) buf;
struct mgmt_cp_remove_uuid *cp = (void *) &buf[sizeof(*hdr)];
uuid_t uuid128;
+ uint128_t uint128;
DBG("index %d", index);
hdr->len = htobs(sizeof(*cp));
hdr->index = htobs(index);
- memcpy(cp->uuid, uuid128.value.uuid128.data, 16);
+ ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+ htob128(&uint128, (uint128_t *) cp->uuid);
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
return -errno;
return;
}
- mgmt_set_mode(index, MGMT_OP_SET_SERVICE_CACHE, 1);
-
info = &controllers[index];
- info->type = rp->type;
- info->enabled = rp->powered;
- info->connectable = rp->connectable;
- info->discoverable = rp->discoverable;
- info->pairable = rp->pairable;
- info->sec_mode = rp->sec_mode;
+
bacpy(&info->bdaddr, &rp->bdaddr);
- memcpy(info->dev_class, rp->dev_class, 3);
- memcpy(info->features, rp->features, 8);
+ info->version = rp->version;
info->manufacturer = btohs(bt_get_unaligned(&rp->manufacturer));
- info->hci_ver = rp->hci_ver;
- info->hci_rev = btohs(bt_get_unaligned(&rp->hci_rev));
+
+ memcpy(&info->supported_settings, &rp->supported_settings,
+ sizeof(info->supported_settings));
+ memcpy(&info->current_settings, &rp->current_settings,
+ sizeof(info->current_settings));
+
+ memcpy(info->dev_class, rp->dev_class, sizeof(info->dev_class));
ba2str(&info->bdaddr, addr);
- DBG("hci%u type %u addr %s", index, info->type, addr);
- DBG("hci%u class 0x%02x%02x%02x", index,
+ DBG("hci%u addr %s version %u manufacturer %u class 0x%02x%02x%02x\n",
+ index, addr, info->version, info->manufacturer,
info->dev_class[2], info->dev_class[1], info->dev_class[0]);
- DBG("hci%u manufacturer %d HCI ver %d:%d", index, info->manufacturer,
- info->hci_ver, info->hci_rev);
- DBG("hci%u enabled %u discoverable %u pairable %u sec_mode %u", index,
- info->enabled, info->discoverable,
- info->pairable, info->sec_mode);
+ DBG("hci%u settings", index);
DBG("hci%u name %s", index, (char *) rp->name);
+ DBG("hci%u short name %s", index, (char *) rp->short_name);
adapter = btd_manager_register_adapter(index);
if (adapter == NULL) {
return;
}
- if (info->enabled)
- mgmt_update_powered(index, TRUE);
+ if (mgmt_powered(info->current_settings))
+ mgmt_update_powered(adapter, info->current_settings);
else
mgmt_set_powered(index, TRUE);
- adapter_update_local_name(adapter, (char *) rp->name);
+ adapter_name_changed(adapter, (char *) rp->name);
btd_adapter_unref(adapter);
}
-static void set_powered_complete(int sk, uint16_t index, void *buf, size_t len)
+static void disconnect_complete(int sk, uint16_t index, void *buf, size_t len)
{
- struct mgmt_mode *rp = buf;
+ struct mgmt_rp_disconnect *rp = buf;
+ struct controller_info *info;
+ char addr[18];
if (len < sizeof(*rp)) {
- error("Too small set powered complete event");
+ error("Too small disconnect complete event");
return;
}
- DBG("hci%d powered %u", index, rp->val);
-
- mgmt_update_powered(index, rp->val);
-}
-
-static void set_discoverable_complete(int sk, uint16_t index, void *buf,
- size_t len)
-{
- struct mgmt_mode *rp = buf;
- struct controller_info *info;
- struct btd_adapter *adapter;
- uint8_t mode;
+ ba2str(&rp->bdaddr, addr);
- if (len < sizeof(*rp)) {
- error("Too small set discoverable complete event");
+ if (rp->status != 0) {
+ error("Disconnecting %s failed with status %u",
+ addr, rp->status);
return;
}
- DBG("hci%d discoverable %u", index, rp->val);
+ DBG("hci%d %s disconnected", index, addr);
if (index > max_index) {
- error("Unexpected index %u in discoverable complete", index);
+ error("Unexpected index %u in disconnect complete", index);
return;
}
info = &controllers[index];
- info->discoverable = rp->val ? TRUE : FALSE;
-
- adapter = manager_find_adapter(&info->bdaddr);
- if (!adapter)
- return;
-
- /* set_discoverable will always also change page scanning */
- mode = SCAN_PAGE;
-
- if (info->discoverable)
- mode |= SCAN_INQUIRY;
+ btd_event_disconn_complete(&info->bdaddr, &rp->bdaddr);
- adapter_mode_changed(adapter, mode);
+ bonding_complete(info, &rp->bdaddr, HCI_CONNECTION_TERMINATED);
}
-static void set_connectable_complete(int sk, uint16_t index, void *buf,
- size_t len)
+static void pair_device_complete(int sk, uint16_t index, void *buf, size_t len)
{
- struct mgmt_mode *rp = buf;
+ struct mgmt_rp_pair_device *rp = buf;
struct controller_info *info;
- struct btd_adapter *adapter;
+ char addr[18];
if (len < sizeof(*rp)) {
- error("Too small set connectable complete event");
+ error("Too small pair_device complete event");
return;
}
- DBG("hci%d connectable %u", index, rp->val);
+ ba2str(&rp->addr.bdaddr, addr);
+
+ DBG("hci%d %s pairing complete status %u", index, addr, rp->status);
if (index > max_index) {
- error("Unexpected index %u in connectable complete", index);
+ error("Unexpected index %u in pair_device complete", index);
return;
}
info = &controllers[index];
- info->connectable = rp->val ? TRUE : FALSE;
-
- adapter = manager_find_adapter(&info->bdaddr);
- if (adapter)
- adapter_mode_changed(adapter, rp->val ? SCAN_PAGE : 0);
+ bonding_complete(info, &rp->addr.bdaddr, rp->status);
}
-static void set_pairable_complete(int sk, uint16_t index, void *buf,
+static void get_connections_complete(int sk, uint16_t index, void *buf,
size_t len)
{
- struct mgmt_mode *rp = buf;
+ struct mgmt_rp_get_connections *rp = buf;
struct controller_info *info;
- struct btd_adapter *adapter;
+ int i;
if (len < sizeof(*rp)) {
- error("Too small set pairable complete event");
+ error("Too small get_connections complete event");
return;
}
- DBG("hci%d pairable %u", index, rp->val);
+ if (len < (sizeof(*rp) + (rp->conn_count * sizeof(bdaddr_t)))) {
+ error("Too small get_connections complete event");
+ return;
+ }
if (index > max_index) {
- error("Unexpected index %u in pairable complete", index);
+ error("Unexpected index %u in get_connections complete",
+ index);
return;
}
info = &controllers[index];
- info->pairable = rp->val ? TRUE : FALSE;
-
- adapter = manager_find_adapter(&info->bdaddr);
- if (!adapter)
- return;
+ for (i = 0; i < rp->conn_count; i++) {
+ bdaddr_t *bdaddr = g_memdup(&rp->addr[i], sizeof(bdaddr_t));
+ info->connections = g_slist_append(info->connections, bdaddr);
+ }
- btd_adapter_pairable_changed(adapter, info->pairable);
+ read_info(sk, index);
}
-static void disconnect_complete(int sk, uint16_t index, void *buf, size_t len)
+static void set_local_name_complete(int sk, uint16_t index, void *buf,
+ size_t len)
{
- struct mgmt_rp_disconnect *rp = buf;
+ struct mgmt_cp_set_local_name *rp = buf;
struct controller_info *info;
- char addr[18];
+ struct btd_adapter *adapter;
if (len < sizeof(*rp)) {
- error("Too small disconnect complete event");
+ error("Too small set_local_name complete event");
return;
}
- ba2str(&rp->bdaddr, addr);
-
- DBG("hci%d %s disconnected", index, addr);
+ DBG("hci%d name %s", index, (char *) rp->name);
if (index > max_index) {
- error("Unexpected index %u in disconnect complete", index);
+ error("Unexpected index %u in set_local_name complete", index);
return;
}
info = &controllers[index];
- btd_event_disconn_complete(&info->bdaddr, &rp->bdaddr);
-
- btd_event_bonding_complete(&info->bdaddr, &rp->bdaddr,
- HCI_CONNECTION_TERMINATED);
-}
-
-static void pair_device_complete(int sk, uint16_t index, void *buf, size_t len)
-{
- struct mgmt_rp_pair_device *rp = buf;
- struct controller_info *info;
- char addr[18];
-
- if (len < sizeof(*rp)) {
- error("Too small pair_device complete event");
- return;
- }
-
- ba2str(&rp->bdaddr, addr);
-
- DBG("hci%d %s pairing complete status %u", index, addr, rp->status);
-
- if (index > max_index) {
- error("Unexpected index %u in pair_device complete", index);
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (adapter == NULL) {
+ DBG("Adapter not found");
return;
}
- info = &controllers[index];
-
- btd_event_bonding_complete(&info->bdaddr, &rp->bdaddr, rp->status);
+ adapter_name_changed(adapter, (char *) rp->name);
}
-static void get_connections_complete(int sk, uint16_t index, void *buf,
+static void read_local_oob_data_complete(int sk, uint16_t index, void *buf,
size_t len)
{
- struct mgmt_rp_get_connections *rp = buf;
- struct controller_info *info;
- int i;
-
- if (len < sizeof(*rp)) {
- error("Too small get_connections complete event");
- return;
- }
+ struct mgmt_rp_read_local_oob_data *rp = buf;
+ struct btd_adapter *adapter;
- if (len < (sizeof(*rp) + (rp->conn_count * sizeof(bdaddr_t)))) {
- error("Too small get_connections complete event");
+ if (len != sizeof(*rp)) {
+ error("Wrong read_local_oob_data_complete event size");
return;
}
if (index > max_index) {
- error("Unexpected index %u in get_connections complete",
+ error("Unexpected index %u in read_local_oob_data_complete",
index);
return;
}
- info = &controllers[index];
+ DBG("hci%u", index);
- for (i = 0; i < rp->conn_count; i++) {
- bdaddr_t *bdaddr = g_memdup(&rp->conn[i], sizeof(bdaddr_t));
- info->connections = g_slist_append(info->connections, bdaddr);
- }
+ adapter = manager_find_adapter_by_id(index);
- read_info(sk, index);
+ if (adapter)
+ oob_read_local_data_complete(adapter, rp->hash, rp->randomizer);
}
-static void set_local_name_complete(int sk, uint16_t index, void *buf,
- size_t len)
+static void read_local_oob_data_failed(int sk, uint16_t index)
{
- struct mgmt_cp_set_local_name *rp = buf;
- struct controller_info *info;
struct btd_adapter *adapter;
- if (len < sizeof(*rp)) {
- error("Too small set_local_name complete event");
- return;
- }
-
- DBG("hci%d name %s", index, (char *) rp->name);
-
if (index > max_index) {
- error("Unexpected index %u in set_local_name complete", index);
+ error("Unexpected index %u in read_local_oob_data_failed",
+ index);
return;
}
- info = &controllers[index];
+ DBG("hci%u", index);
- adapter = manager_find_adapter(&info->bdaddr);
- if (adapter == NULL) {
- DBG("Adapter not found");
- return;
- }
+ adapter = manager_find_adapter_by_id(index);
- adapter_update_local_name(adapter, (char *) rp->name);
+ if (adapter)
+ oob_read_local_data_complete(adapter, NULL, NULL);
}
static void mgmt_cmd_complete(int sk, uint16_t index, void *buf, size_t len)
read_info_complete(sk, index, ev->data, len);
break;
case MGMT_OP_SET_POWERED:
- set_powered_complete(sk, index, ev->data, len);
+ mgmt_new_settings(sk, index, ev->data, len);
break;
case MGMT_OP_SET_DISCOVERABLE:
- set_discoverable_complete(sk, index, ev->data, len);
+ mgmt_new_settings(sk, index, ev->data, len);
break;
case MGMT_OP_SET_CONNECTABLE:
- set_connectable_complete(sk, index, ev->data, len);
+ mgmt_new_settings(sk, index, ev->data, len);
break;
case MGMT_OP_SET_PAIRABLE:
- set_pairable_complete(sk, index, ev->data, len);
+ mgmt_new_settings(sk, index, ev->data, len);
break;
case MGMT_OP_ADD_UUID:
DBG("add_uuid complete");
case MGMT_OP_SET_DEV_CLASS:
DBG("set_dev_class complete");
break;
- case MGMT_OP_SET_SERVICE_CACHE:
- DBG("set_service_cache complete");
+ case MGMT_OP_LOAD_LINK_KEYS:
+ DBG("load_link_keys complete");
break;
- case MGMT_OP_LOAD_KEYS:
- DBG("load_keys complete");
- break;
- case MGMT_OP_REMOVE_KEY:
- DBG("remove_key complete");
+ case MGMT_OP_REMOVE_KEYS:
+ DBG("remove_keys complete");
break;
case MGMT_OP_DISCONNECT:
DBG("disconnect complete");
case MGMT_OP_SET_LOCAL_NAME:
set_local_name_complete(sk, index, ev->data, len);
break;
+ case MGMT_OP_READ_LOCAL_OOB_DATA:
+ read_local_oob_data_complete(sk, index, ev->data, len);
+ break;
+ case MGMT_OP_ADD_REMOTE_OOB_DATA:
+ DBG("add_remote_oob_data complete");
+ break;
+ case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
+ DBG("remove_remote_oob_data complete");
+ break;
+ case MGMT_OP_BLOCK_DEVICE:
+ DBG("block_device complete");
+ break;
+ case MGMT_OP_UNBLOCK_DEVICE:
+ DBG("unblock_device complete");
+ break;
+ case MGMT_OP_SET_FAST_CONNECTABLE:
+ DBG("set_fast_connectable complete");
+ break;
+ case MGMT_OP_START_DISCOVERY:
+ DBG("start_discovery complete");
+ break;
+ case MGMT_OP_STOP_DISCOVERY:
+ DBG("stop_discovery complete");
+ break;
default:
error("Unknown command complete for opcode %u", opcode);
break;
opcode = btohs(bt_get_unaligned(&ev->opcode));
DBG("status %u opcode %u (index %u)", ev->status, opcode, index);
+
+ switch (opcode) {
+ case MGMT_OP_READ_LOCAL_OOB_DATA:
+ read_local_oob_data_failed(sk, index);
+ break;
+ }
}
static void mgmt_controller_error(int sk, uint16_t index, void *buf, size_t len)
info = &controllers[index];
- btd_event_bonding_complete(&info->bdaddr, &ev->bdaddr, ev->status);
+ bonding_complete(info, &ev->bdaddr, ev->status);
}
static void mgmt_local_name_changed(int sk, uint16_t index, void *buf, size_t len)
adapter = manager_find_adapter(&info->bdaddr);
if (adapter)
- adapter_update_local_name(adapter, (char *) ev->name);
+ adapter_name_changed(adapter, (char *) ev->name);
+}
+
+static inline addr_type_t mgmt_addr_type(uint8_t mgmt_addr_type)
+{
+ switch (mgmt_addr_type) {
+ case MGMT_ADDR_BREDR:
+ return ADDR_TYPE_BREDR;
+ case MGMT_ADDR_LE_PUBLIC:
+ return ADDR_TYPE_LE_PUBLIC;
+ case MGMT_ADDR_LE_RANDOM:
+ return ADDR_TYPE_LE_RANDOM;
+ default:
+ return ADDR_TYPE_BREDR;
+ }
+}
+
+static void mgmt_device_found(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_device_found *ev = buf;
+ struct controller_info *info;
+ char addr[18];
+ uint8_t *eir;
+ uint32_t cls;
+
+ if (len != sizeof(*ev)) {
+ error("mgmt_device_found length %zu instead of expected %zu",
+ len, sizeof(*ev));
+ return;
+ }
+
+ if (index > max_index) {
+ error("Unexpected index %u in device_found event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ cls = ev->dev_class[0] | (ev->dev_class[1] << 8) |
+ (ev->dev_class[2] << 16);
+
+ if (ev->eir[0] == 0)
+ eir = NULL;
+ else
+ eir = ev->eir;
+
+ ba2str(&ev->addr.bdaddr, addr);
+ DBG("hci%u addr %s, class %u rssi %d cfm_name %u %s", index, addr, cls,
+ ev->rssi, ev->confirm_name,
+ eir ? "eir" : "");
+
+ btd_event_device_found(&info->bdaddr, &ev->addr.bdaddr,
+ mgmt_addr_type(ev->addr.type), cls,
+ ev->rssi, ev->confirm_name,
+ eir, HCI_MAX_EIR_LENGTH);
+}
+
+static void mgmt_remote_name(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_remote_name *ev = buf;
+ struct controller_info *info;
+ char addr[18];
+
+ if (len < sizeof(*ev)) {
+ error("Too small mgmt_remote_name packet");
+ return;
+ }
+
+ if (index > max_index) {
+ error("Unexpected index %u in remote_name event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ ba2str(&ev->bdaddr, addr);
+ DBG("hci%u addr %s, name %s", index, addr, ev->name);
+
+ btd_event_remote_name(&info->bdaddr, &ev->bdaddr, (char *) ev->name);
+}
+
+static void mgmt_discovering(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_mode *ev = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+
+ if (len < sizeof(*ev)) {
+ error("Too small discovering event");
+ return;
+ }
+
+ DBG("Controller %u discovering %u", index, ev->val);
+
+ if (index > max_index) {
+ error("Unexpected index %u in discovering event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (!adapter)
+ return;
+
+ adapter_set_discovering(adapter, ev->val);
+}
+
+static void mgmt_device_blocked(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct controller_info *info;
+ struct mgmt_ev_device_blocked *ev = buf;
+ char addr[18];
+
+ if (len < sizeof(*ev)) {
+ error("Too small mgmt_device_blocked event packet");
+ return;
+ }
+
+ ba2str(&ev->bdaddr, addr);
+ DBG("Device blocked, index %u, addr %s", index, addr);
+
+ if (index > max_index) {
+ error("Unexpected index %u in device_blocked event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ btd_event_device_blocked(&info->bdaddr, &ev->bdaddr);
+}
+
+static void mgmt_device_unblocked(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct controller_info *info;
+ struct mgmt_ev_device_unblocked *ev = buf;
+ char addr[18];
+
+ if (len < sizeof(*ev)) {
+ error("Too small mgmt_device_unblocked event packet");
+ return;
+ }
+
+ ba2str(&ev->bdaddr, addr);
+ DBG("Device unblocked, index %u, addr %s", index, addr);
+
+ if (index > max_index) {
+ error("Unexpected index %u in device_unblocked event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ btd_event_device_unblocked(&info->bdaddr, &ev->bdaddr);
}
static gboolean mgmt_event(GIOChannel *io, GIOCondition cond, gpointer user_data)
case MGMT_EV_INDEX_REMOVED:
mgmt_index_removed(sk, index);
break;
- case MGMT_EV_POWERED:
- mgmt_powered(sk, index, buf + MGMT_HDR_SIZE, len);
- break;
- case MGMT_EV_DISCOVERABLE:
- mgmt_discoverable(sk, index, buf + MGMT_HDR_SIZE, len);
+ case MGMT_EV_NEW_SETTINGS:
+ mgmt_new_settings(sk, index, buf + MGMT_HDR_SIZE, len);
break;
- case MGMT_EV_CONNECTABLE:
- mgmt_connectable(sk, index, buf + MGMT_HDR_SIZE, len);
- break;
- case MGMT_EV_PAIRABLE:
- mgmt_pairable(sk, index, buf + MGMT_HDR_SIZE, len);
- break;
- case MGMT_EV_NEW_KEY:
- mgmt_new_key(sk, index, buf + MGMT_HDR_SIZE, len);
+ case MGMT_EV_NEW_LINK_KEY:
+ mgmt_new_link_key(sk, index, buf + MGMT_HDR_SIZE, len);
break;
case MGMT_EV_DEVICE_CONNECTED:
mgmt_device_connected(sk, index, buf + MGMT_HDR_SIZE, len);
case MGMT_EV_LOCAL_NAME_CHANGED:
mgmt_local_name_changed(sk, index, buf + MGMT_HDR_SIZE, len);
break;
+ case MGMT_EV_DEVICE_FOUND:
+ mgmt_device_found(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_REMOTE_NAME:
+ mgmt_remote_name(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_DISCOVERING:
+ mgmt_discovering(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_DEVICE_BLOCKED:
+ mgmt_device_blocked(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_DEVICE_UNBLOCKED:
+ mgmt_device_unblocked(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
+ case MGMT_EV_USER_PASSKEY_REQUEST:
+ mgmt_passkey_request(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
default:
error("Unknown Management opcode %u (index %u)", opcode, index);
break;
return -ENOSYS;
}
-static int mgmt_start_inquiry(int index, uint8_t length, gboolean periodic)
+static int mgmt_start_discovery(int index)
{
- DBG("index %d length %u periodic %d", index, length, periodic);
- return -ENOSYS;
-}
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_start_discovery)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_start_discovery *cp = (void *) &buf[sizeof(*hdr)];
-static int mgmt_stop_inquiry(int index)
-{
DBG("index %d", index);
- return -ENOSYS;
-}
-static int mgmt_start_scanning(int index)
-{
- DBG("index %d", index);
- return -ENOSYS;
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_START_DISCOVERY);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ hci_set_bit(MGMT_ADDR_BREDR, &cp->type);
+ hci_set_bit(MGMT_ADDR_LE_PUBLIC, &cp->type);
+ hci_set_bit(MGMT_ADDR_LE_RANDOM, &cp->type);
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
}
-static int mgmt_stop_scanning(int index)
+static int mgmt_stop_discovery(int index)
{
+ struct mgmt_hdr hdr;
+
DBG("index %d", index);
- return -ENOSYS;
-}
-static int mgmt_resolve_name(int index, bdaddr_t *bdaddr)
-{
- char addr[18];
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.opcode = htobs(MGMT_OP_STOP_DISCOVERY);
+ hdr.index = htobs(index);
- ba2str(bdaddr, addr);
- DBG("index %d addr %s", index, addr);
+ if (write(mgmt_sock, &hdr, sizeof(hdr)) < 0)
+ return -errno;
- return -ENOSYS;
+ return 0;
}
static int mgmt_set_name(int index, const char *name)
return 0;
}
-static int mgmt_cancel_resolve_name(int index, bdaddr_t *bdaddr)
+static int mgmt_set_fast_connectable(int index, gboolean enable)
{
- char addr[18];
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_mode)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_mode *cp = (void *) &buf[sizeof(*hdr)];
- ba2str(bdaddr, addr);
- DBG("index %d addr %s", index, addr);
+ DBG("index %d enable %d", index, enable);
- return -ENOSYS;
-}
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_SET_FAST_CONNECTABLE);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
-static int mgmt_fast_connectable(int index, gboolean enable)
-{
- DBG("index %d enable %d", index, enable);
- return -ENOSYS;
+ cp->val = enable;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
}
static int mgmt_read_clock(int index, bdaddr_t *bdaddr, int which, int timeout,
static int mgmt_block_device(int index, bdaddr_t *bdaddr)
{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_block_device)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_block_device *cp;
+ size_t buf_len;
char addr[18];
ba2str(bdaddr, addr);
DBG("index %d addr %s", index, addr);
- return -ENOSYS;
+ memset(buf, 0, sizeof(buf));
+
+ hdr->opcode = htobs(MGMT_OP_BLOCK_DEVICE);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ cp = (void *) &buf[sizeof(*hdr)];
+ bacpy(&cp->bdaddr, bdaddr);
+
+ buf_len = sizeof(*hdr) + sizeof(*cp);
+
+ if (write(mgmt_sock, buf, buf_len) < 0)
+ return -errno;
+
+ return 0;
}
static int mgmt_unblock_device(int index, bdaddr_t *bdaddr)
{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_unblock_device)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_unblock_device *cp;
+ size_t buf_len;
char addr[18];
ba2str(bdaddr, addr);
DBG("index %d addr %s", index, addr);
- return -ENOSYS;
-}
-
-static int mgmt_get_conn_list(int index, GSList **conns)
-{
- struct controller_info *info = &controllers[index];
-
- DBG("index %d", index);
-
- *conns = info->connections;
- info->connections = NULL;
+ memset(buf, 0, sizeof(buf));
- return 0;
-}
+ hdr->opcode = htobs(MGMT_OP_UNBLOCK_DEVICE);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
-static int mgmt_read_local_version(int index, struct hci_version *ver)
-{
- struct controller_info *info = &controllers[index];
+ cp = (void *) &buf[sizeof(*hdr)];
+ bacpy(&cp->bdaddr, bdaddr);
- DBG("index %d", index);
+ buf_len = sizeof(*hdr) + sizeof(*cp);
- if (!info->valid)
- return -ENODEV;
-
- memset(ver, 0, sizeof(*ver));
- ver->manufacturer = info->manufacturer;
- ver->hci_ver = info->hci_ver;
- ver->hci_rev = info->hci_rev;
+ if (write(mgmt_sock, buf, buf_len) < 0)
+ return -errno;
return 0;
}
-static int mgmt_read_local_features(int index, uint8_t *features)
+static int mgmt_get_conn_list(int index, GSList **conns)
{
struct controller_info *info = &controllers[index];
DBG("index %d", index);
- if (!info->valid)
- return -ENODEV;
-
- memcpy(features, info->features, 8);
+ *conns = info->connections;
+ info->connections = NULL;
return 0;
}
static int mgmt_remove_bonding(int index, bdaddr_t *bdaddr)
{
- char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_key)];
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_keys)];
struct mgmt_hdr *hdr = (void *) buf;
- struct mgmt_cp_remove_key *cp = (void *) &buf[sizeof(*hdr)];
+ struct mgmt_cp_remove_keys *cp = (void *) &buf[sizeof(*hdr)];
char addr[18];
ba2str(bdaddr, addr);
DBG("index %d addr %s", index, addr);
memset(buf, 0, sizeof(buf));
- hdr->opcode = htobs(MGMT_OP_REMOVE_KEY);
+ hdr->opcode = htobs(MGMT_OP_REMOVE_KEYS);
hdr->len = htobs(sizeof(*cp));
hdr->index = htobs(index);
return 0;
}
-static int mgmt_passkey_reply(int index, bdaddr_t *bdaddr, uint32_t passkey)
-{
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("index %d addr %s passkey %06u", index, addr, passkey);
-
- return -ENOSYS;
-}
-
-static int mgmt_enable_le(int index)
-{
- DBG("index %d", index);
- return -ENOSYS;
-}
-
static int mgmt_encrypt_link(int index, bdaddr_t *dst, bt_hci_result_t cb,
gpointer user_data)
{
static int mgmt_disable_cod_cache(int index)
{
DBG("index %d", index);
- return mgmt_set_mode(index, MGMT_OP_SET_SERVICE_CACHE, 0);
+
+ /* The cache control is handled automatically for mgmt */
+ return 0;
}
static int mgmt_restore_powered(int index)
return -ENOSYS;
}
-static int mgmt_load_keys(int index, GSList *keys, gboolean debug_keys)
+static int mgmt_load_link_keys(int index, GSList *keys, gboolean debug_keys)
{
char *buf;
struct mgmt_hdr *hdr;
- struct mgmt_cp_load_keys *cp;
- struct mgmt_key_info *key;
+ struct mgmt_cp_load_link_keys *cp;
+ struct mgmt_link_key_info *key;
size_t key_count, cp_size;
GSList *l;
int err;
memset(buf, 0, sizeof(buf));
hdr = (void *) buf;
- hdr->opcode = htobs(MGMT_OP_LOAD_KEYS);
+ hdr->opcode = htobs(MGMT_OP_LOAD_LINK_KEYS);
hdr->len = htobs(cp_size);
hdr->index = htobs(index);
hdr->len = htobs(sizeof(*cp));
hdr->index = htobs(index);
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
cp->io_cap = io_cap;
if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
return -ENOSYS;
}
+static int mgmt_read_local_oob_data(int index)
+{
+ struct mgmt_hdr hdr;
+
+ DBG("hci%d", index);
+
+ hdr.opcode = htobs(MGMT_OP_READ_LOCAL_OOB_DATA);
+ hdr.len = 0;
+ hdr.index = htobs(index);
+
+ if (write(mgmt_sock, &hdr, sizeof(hdr)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_add_remote_oob_data(int index, bdaddr_t *bdaddr,
+ uint8_t *hash, uint8_t *randomizer)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_add_remote_oob_data)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_add_remote_oob_data *cp = (void *) &buf[sizeof(*hdr)];
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", index, addr);
+
+ memset(buf, 0, sizeof(buf));
+
+ hdr->opcode = htobs(MGMT_OP_ADD_REMOTE_OOB_DATA);
+ hdr->index = htobs(index);
+ hdr->len = htobs(sizeof(*cp));
+
+ bacpy(&cp->bdaddr, bdaddr);
+ memcpy(cp->hash, hash, 16);
+ memcpy(cp->randomizer, randomizer, 16);
+
+ if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_remove_remote_oob_data(int index, bdaddr_t *bdaddr)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_remote_oob_data)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_remove_remote_oob_data *cp = (void *) &buf[sizeof(*hdr)];
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", index, addr);
+
+ memset(buf, 0, sizeof(buf));
+
+ hdr->opcode = htobs(MGMT_OP_REMOVE_REMOTE_OOB_DATA);
+ hdr->index = htobs(index);
+ hdr->len = htobs(sizeof(*cp));
+
+ bacpy(&cp->bdaddr, bdaddr);
+
+ if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_confirm_name(int index, bdaddr_t *bdaddr, gboolean name_known)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_confirm_name)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_confirm_name *cp = (void *) &buf[sizeof(*hdr)];
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s name_known %u", index, addr, name_known);
+
+ memset(buf, 0, sizeof(buf));
+
+ hdr->opcode = htobs(MGMT_OP_CONFIRM_NAME);
+ hdr->index = htobs(index);
+ hdr->len = htobs(sizeof(*cp));
+
+ bacpy(&cp->bdaddr, bdaddr);
+ cp->name_known = name_known;
+
+ if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
static struct btd_adapter_ops mgmt_ops = {
.setup = mgmt_setup,
.cleanup = mgmt_cleanup,
.set_discoverable = mgmt_set_discoverable,
.set_pairable = mgmt_set_pairable,
.set_limited_discoverable = mgmt_set_limited_discoverable,
- .start_inquiry = mgmt_start_inquiry,
- .stop_inquiry = mgmt_stop_inquiry,
- .start_scanning = mgmt_start_scanning,
- .stop_scanning = mgmt_stop_scanning,
- .resolve_name = mgmt_resolve_name,
- .cancel_resolve_name = mgmt_cancel_resolve_name,
+ .start_discovery = mgmt_start_discovery,
+ .stop_discovery = mgmt_stop_discovery,
.set_name = mgmt_set_name,
.set_dev_class = mgmt_set_dev_class,
- .set_fast_connectable = mgmt_fast_connectable,
+ .set_fast_connectable = mgmt_set_fast_connectable,
.read_clock = mgmt_read_clock,
.read_bdaddr = mgmt_read_bdaddr,
.block_device = mgmt_block_device,
.unblock_device = mgmt_unblock_device,
.get_conn_list = mgmt_get_conn_list,
- .read_local_version = mgmt_read_local_version,
- .read_local_features = mgmt_read_local_features,
.disconnect = mgmt_disconnect,
.remove_bonding = mgmt_remove_bonding,
.pincode_reply = mgmt_pincode_reply,
.confirm_reply = mgmt_confirm_reply,
.passkey_reply = mgmt_passkey_reply,
- .enable_le = mgmt_enable_le,
.encrypt_link = mgmt_encrypt_link,
.set_did = mgmt_set_did,
.add_uuid = mgmt_add_uuid,
.remove_uuid = mgmt_remove_uuid,
.disable_cod_cache = mgmt_disable_cod_cache,
.restore_powered = mgmt_restore_powered,
- .load_keys = mgmt_load_keys,
+ .load_keys = mgmt_load_link_keys,
.set_io_capability = mgmt_set_io_capability,
.create_bonding = mgmt_create_bonding,
.cancel_bonding = mgmt_cancel_bonding,
+ .read_local_oob_data = mgmt_read_local_oob_data,
+ .add_remote_oob_data = mgmt_add_remote_oob_data,
+ .remove_remote_oob_data = mgmt_remove_remote_oob_data,
+ .confirm_name = mgmt_confirm_name,
};
static int mgmt_init(void)
sdp_record = sdp_xml_parse_record(record, len);
if (!sdp_record) {
error("Parsing of XML service record failed");
- sdp_record_free(sdp_record);
return btd_error_failed(msg,
"Parsing of XML service record failed");
}
sender = dbus_message_get_sender(msg);
if (find_pending_by_sender(serv_adapter, sender))
return btd_error_does_not_exist(msg);
-#ifdef __TIZEN_PATCH__
-
-
- info("handle %x\n", handle);
- if(1 != handle)
- {
user_record = find_record(serv_adapter, handle, sender);
if (!user_record) {
return btd_error_not_authorized(msg);
}
bt_free(uuid128);
- }
-#else
- user_record = find_record(serv_adapter, handle, sender);
- if (!user_record) {
- user_record = find_record(serv_adapter_any, handle, sender);
- if (!user_record)
- return not_authorized(msg);
- }
-
- record = sdp_record_find(user_record->handle);
- if (record == NULL)
- return not_authorized(msg);
-
- if (sdp_get_service_classes(record, &services) < 0) {
- sdp_record_free(record);
- return not_authorized(msg);
- }
- if (services == NULL)
- return not_authorized(msg);
-
- uuid = services->data;
- uuid128 = sdp_uuid_to_uuid128(uuid);
-
- sdp_list_free(services, bt_free);
-
- if (sdp_uuid2strn(uuid128, uuid_str, MAX_LEN_UUID_STR) < 0) {
- bt_free(uuid128);
- return not_authorized(msg);
- }
- bt_free(uuid128);
-#endif
auth = g_new0(struct pending_auth, 1);
auth->msg = dbus_message_ref(msg);
auth->conn = dbus_connection_ref(connection);
-#ifdef __TIZEN_PATCH__
- if(1 != handle)
- {
- auth->sender = user_record->sender;
- memcpy(auth->uuid, uuid_str, MAX_LEN_UUID_STR);
- }
- else
- {
- auth->sender = (char *)sender;
- char* uuid_l2cap = "00000100-0000-1000-8000-00805f9b34fb";
- memset(auth->uuid, 0, MAX_LEN_UUID_STR);
- memcpy(auth->uuid, uuid_l2cap, strlen(uuid_l2cap));
- }
-#else
auth->sender = user_record->sender;
memcpy(auth->uuid, uuid_str, MAX_LEN_UUID_STR);
-
-#endif
str2ba(address, &auth->dst);
serv_adapter->pending_list = g_slist_append(serv_adapter->pending_list,
return dbus_message_new_method_return(msg);
}
-#ifdef __TIZEN_PATCH__
-extern sdp_session_t *g_cached_session;
-
-static DBusMessage *siso_add_service_record_pdu(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- info("siso_add_service_record_pdu() +\n");
- DBusMessage *reply;
- const char *sender, *record;
- uint32_t len;
- dbus_uint32_t handle;
- int err;
- sdp_record_t *sdp_record;
- int scanned;
- int ret = -1;
- DBusMessageIter iter, array;
- dbus_message_iter_init(msg, &iter);
- dbus_message_iter_recurse(&iter, &array);
- dbus_message_iter_get_fixed_array(&array, &record, &len);
- if (len <= 0) {
- info("Error!!!! ... Invalid args....\n");
- return btd_error_invalid_args(msg);
- }
- info("\n");
- sdp_record = (sdp_record_t *)sdp_extract_pdu_safe(record, len, &scanned);
- if (!sdp_record) {
- info("Error!!! ---------Parsing of service record failed--------\n");
- return btd_error_invalid_args(msg);
- }
- if (scanned != len) {
- info("Warning!!!! Size mismatch of service record, scanned = %d, len = %d\n",
- scanned, len);
- /*The JSRapp service record seesm to be wrong!!!!.
- * Hence its returning the value 49 and 35.
- * Its a missmatch. Once its is corrected uncomment below code.*/
- /* sdp_record_free(sdp_record);
- return -1;
- */
- }
-
- if (sdp_record->handle < 0x10000) {
- DBG("Invalid record handle 0x%05x", sdp_record->handle);
- sdp_record_free(sdp_record);
- return btd_error_invalid_args(msg);
- }
-
- if (add_record_to_server(BDADDR_ANY, sdp_record) < 0) {
- info("Error !!!!!!!! add_record_to_server() \n");
- info("Failed to register service record for handle 0x%x\n", sdp_record->handle);
- sdp_record_free(sdp_record);
- return btd_error_invalid_args(msg);
- }
-
- handle = sdp_record->handle;
-
- info("Received handler = 0x%x\n", handle);
-
-
-
- reply = dbus_message_new_method_return(msg);
- if (!reply) {
- info("Error in reply\n");
- return btd_error_invalid_args(msg);
- }
-
- dbus_message_append_args(reply, DBUS_TYPE_UINT32, &handle,
- DBUS_TYPE_INVALID);
- info("-\n");
-
- return reply;
-
-}
-
-
-static DBusMessage *siso_remove_service_record(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- info("\n\n\n\n++++++++++++++++++ (RX) siso_remove_service_record +++++++++++++++++++++++\n\n\n\n");
- struct service_adapter *serv_adapter = data;
- dbus_uint32_t handle;
- sdp_record_t *rec;
- DBusMessage *reply;
- if (dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
- DBUS_TYPE_INVALID) == FALSE)
- {
- info("\nError!!!! ---------------- Invalid arguments ................................\n");
- return btd_error_invalid_args(msg);
- }
- DBG("RRemoving record with handle 0x%05x", handle);
-
- rec = sdp_record_find(handle);
- if (!rec)
- {
- info("\n---------------- Record Not found for the handler 0x%x-----------\n", handle);
- return btd_error_invalid_args(msg);
- }
- if (sdp_record_remove(handle) == 0) {
- update_db_timestamp();
- update_svclass_list(BDADDR_ANY);
- }
-
- sdp_record_free(rec);
- info("\nFreed SDP record\n");
-
-// return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- {
- info("Error in reply\n");
- return btd_error_invalid_args(msg);
- }
- info("-\n");
-
- return reply;
-
-}
-
-static volatile sig_atomic_t __io_finished;
-
-static void __dbus_sdp_callback_multiple_handle_complete__cb(uint8_t type, uint16_t status,
- uint8_t *rsp, size_t size, void *udata)
-{
- unsigned int i;
- int err = -1;
- int tsrc, csrc, tsrc_count, index_count = 0;
- uint32_t s_handle;
- DBusConnection *conn;
- //DBusMessage *reply;
- //DBusMessageIter iter, array_iter;
- service_dbus_ctxt_t* ctxt = (service_dbus_ctxt_t*)udata;
-
- info("__dbus_sdp_callback_multiple_handle_complete__cb.. +\n");
-#if 0 //Service search
- if( SDP_ERROR_RSP == status)
- {
- info(" sdp timed out \n");
- __io_finished = 1;
- return failed_strerror(ctxt->msg, err);
- }
- printf("\n");
-
- if (type == SDP_ERROR_RSP) {
- __io_finished = 1; /* We have to come of the loop when type == SDP_ERROR_RSP */
- return failed_strerror(ctxt->msg, err);
- }
-
- info(" after SDP_ERROR_RSP check \n");
-
- if(NULL == udata)
- {
- info(" sdp search is over \n");
-
- }
- else
- {
- info(" sdp search continues \n");
- reply = dbus_message_new_method_return(ctxt->msg);
- info( " sdp got a reply \n");
-
- if(reply)
- {
- dbus_message_iter_init_append(reply, &iter);
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_UINT32_AS_STRING, &array_iter);
- }
- else
- {
- info(" reply is NULL \n");
- }
- }
-
- info(" after iteration \n");
-#else
- info("type!!!!!!!!! %d\n",type);
- printf("\n");
- if(type == 0 )
- {
- info("timed outttttttt\n");
-
- __io_finished =1;
- return;
- }
-
- if (type == SDP_ERROR_RSP) {
- __io_finished = 1;
- err = -EIO;
- info("Dbus Failed err =%d\n", err);
- return;
- }
-
-#endif
- uint8_t *pdata = rsp;
- tsrc = ntohs(bt_get_unaligned((uint16_t *) pdata));
- if ( tsrc <= 0)
- {
- info("tsrc not found");
-// s_handle = 0xFE000000; /*Record not found*/
- goto done;
- }
- else
- {
-
-
- pdata += sizeof(uint16_t);
- csrc = ntohs(bt_get_unaligned((uint16_t *) pdata));
- if ( csrc <= 0)
- {
- info("csrc not found");
-// s_handle = 0xFE000000; /*Record not found*/
- goto done;
-
- }
- else
- {
- info("Total service record found = %d, CSR = %d", tsrc, tsrc);
- pdata += sizeof(uint16_t);
- tsrc_count = tsrc;
- index_count = 0;
- do
- {
- s_handle = ntohl(bt_get_unaligned((uint32_t*)pdata));
- pdata += sizeof(uint32_t);
-#if 0 //Service search
- dbus_message_iter_append_basic(&array_iter,
- DBUS_TYPE_UINT32, &s_handle);
-#else
- dbus_message_iter_append_basic(ctxt->array_iter,
- DBUS_TYPE_UINT32, &s_handle);
-#endif
- ++index_count;
- DBG("got handle 0x%x count %d", s_handle, index_count);
- if(index_count >= MAX_REMOTE_SERVICES) break;
- }while(--tsrc_count);
-
- }
- }
-
-
-
- info( "********Received handles = 0X%X ***********, total rx size = %d\n", index_count, size);
-
-
-done:
- __io_finished = 1;
-#if 0 //Service search
- dbus_message_iter_close_container(&iter, &array_iter);
- dbus_connection_send(ctxt->conn, reply, NULL);
- dbus_message_unref(reply);
-
- if(ctxt != NULL)
- {
- //dbus_connection_unref(ctxt->conn);
- dbus_message_unref(ctxt->msg);
- free(ctxt);
- ctxt = NULL;
-
- }
-#endif
-
- info( "__socket_sdp_callback_multiple_handle_complete__cb -\n");
- return;
-}
-
-
-static void get_remote_service_cb(sdp_list_t *recs, int err, gpointer user_data)
-{
- sdp_list_t *seq;
- int ii = 0, index_count=0;
- int struct_len;
- DBusMessage *reply;
- DBusMessageIter iter, array_iter;
-
- service_dbus_ctxt_t* service_search_ctxt = (service_dbus_ctxt_t*) user_data;
-
- info("get_remote_service_cb+");
-
- if (err < 0) {
- error("Unable to get service record: %s (%d)", strerror(-err), -err);
- goto fail;
- }
-
- if (!recs || !recs->data) {
- info("No records found\n");
- //error("No records found");
- //goto fail;
- }
-
- reply = dbus_message_new_method_return(service_search_ctxt->msg);
-
- if(reply)
- {
- dbus_message_iter_init_append(reply, &iter);
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32_AS_STRING, &array_iter);
- }
- else
- {
- info(" reply is NULL \n");
- goto fail;
- }
-
- for (seq = recs; seq; seq = seq->next)
- {
- sdp_record_t *rec = (sdp_record_t *) seq->data;
- GString *result;
-
- if (!rec)
- break;
-
- dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_UINT32, &rec->handle);
-
- ++index_count;
- }
-
-
- info( "********Received handles = 0X%X ***********\n", index_count);
-
-
-done:
- dbus_message_iter_close_container(&iter, &array_iter);
- dbus_connection_send(service_search_ctxt->conn, reply, NULL);
- dbus_message_unref(reply);
-
-fail:
- if(service_search_ctxt != NULL)
- {
- //dbus_connection_unref(service_search_ctxt->conn);
- dbus_message_unref(service_search_ctxt->msg);
- free(service_search_ctxt);
- service_search_ctxt = NULL;
- }
-
- info("get_remote_service_cb-");
- return;
-
-}
-
-
-static DBusMessage *get_remote_service_handles(DBusConnection *conn, DBusMessage *msg, void *data)
-{
- int err = 0;
- uuid_t uuid;
- char * addr, *match;
- bdaddr_t bdadd;
- service_dbus_ctxt_t* service_search_ctxt = NULL;
-
-
- info("get_remote_service_handles");
-
- if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr, DBUS_TYPE_STRING, &match,
- DBUS_TYPE_INVALID) == FALSE)
- {
- info("\n---------------- Invalid arguments ................................\n");
- return btd_error_invalid_args(msg);
- }
- info( "Addr: %s\n", addr);
- info( "UUID: %s\n", match);
- str2ba(addr, &bdadd);
-
- if (strlen(match) > 0)
- {
- if (bt_string2uuid(&uuid, match) < 0)
- {
- error("Invalid service class name");
- return btd_error_invalid_args(msg);
- }
- sdp_uuid128_to_uuid(&uuid);
- }
-
- service_search_ctxt = g_try_malloc0(sizeof(service_dbus_ctxt_t));
- if (!service_search_ctxt) {
- info("malloc() failed");
- return btd_error_failed(msg, "No Memory");
- }
- service_search_ctxt->conn = conn;
- service_search_ctxt->msg = dbus_message_ref(msg);
-
- err = bt_search_service(BDADDR_ANY, &bdadd, &uuid, get_remote_service_cb, service_search_ctxt, NULL);
- if (err < 0)
- {
- error("Invalid service class name");
- return btd_error_failed(msg, "Invalid service class name");
- }
- return NULL;
-}
-
-static DBusMessage *siso_get_remote_service_handles(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- info("\n\n\n\n++++++++++++++++++ (RX) siso_get_remote_service_handles +++++++++++++++++++++++\n\n\n\n");
- struct service_adapter *serv_adapter = data;
- dbus_uint32_t handle;
- sdp_record_t *rec;
- DBusMessage *reply;
-
- char * addr, *match;
- bdaddr_t bdadd;
- sdp_list_t *search, *attrids;
- uint32_t range = 0x0000ffff;
- uuid_t uuid;
- service_dbus_ctxt_t* ctxt = NULL;
- DBusMessageIter iter, array_iter;
-
-
- if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr, DBUS_TYPE_STRING, &match,
- DBUS_TYPE_INVALID) == FALSE)
- {
- info("\nError!!!! ---------------- Invalid arguments ................................\n");
- return btd_error_invalid_args(msg);
- }
- info( "Before sdp_connect with bdaddr %s\n", addr);
- str2ba(addr, &bdadd);
- if (strlen(match) > 0)
- {
- if (bt_string2uuid(&uuid, match) < 0)
- {
- error("Invalid service class name");
- return btd_error_invalid_args(msg);
- }
- }
-
- if(NULL == g_cached_session)
- {
- g_cached_session = sdp_connect(BDADDR_ANY, &bdadd, 0);
- if (!g_cached_session)
- {
- info("Can't connect to SDP service\n");
- goto error_done;
- //break;
- }
- }
-
- info( "Before sdp_set_notify\n");
- __io_finished = 0;
- ctxt = malloc(sizeof(service_dbus_ctxt_t));
-
- if(NULL == ctxt)
- {
- info("!!!!!Memory allocation failed for ctxt\n");
-
- goto error_done;
- }
-
- ctxt->conn = conn;
- ctxt->msg = dbus_message_ref(msg);
-
- reply = dbus_message_new_method_return(ctxt->msg);
- dbus_message_iter_init_append(reply, &iter);
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_UINT32_AS_STRING, &array_iter);
-
- ctxt->array_iter = &array_iter;
-
- sdp_set_notify(g_cached_session, __dbus_sdp_callback_multiple_handle_complete__cb, ctxt);
-
-
- search = sdp_list_append(NULL, &uuid);
-
- info( "Before sdp_list_append\n");
-
- attrids = sdp_list_append(NULL, &range);
-
- info( "Before sdp_service_search_async\n");
-
- if(0 != sdp_service_search_async(g_cached_session, search, 0xffff))
- {
- error("Error : sdp_service_search_async()");
- goto error_done;
- }
-
- info( "Before sdp_list_free\n");
-
- sdp_list_free(attrids, NULL);
-
- info( "Before sdp_list_free\n");
-
- sdp_list_free(search, NULL);
-
- while (!__io_finished)
- {
- printf(". ");
- info(" calling sdp_process [ enter] : %d\n", __io_finished);
- if (sdp_process(g_cached_session) == -1)
- {
- info( "Search Completed : error\n");
-
- }
-
- info(" calling sdp_process [leave]: %d\n", __io_finished);
- }
- dbus_message_iter_close_container(&iter, &array_iter);
-
- free(ctxt);
-
- return reply;
-error_done:
- info( "Error done .. before ctx free\n");
-
- if(ctxt != NULL)
- {
- free(ctxt);
- ctxt = NULL;
- }
-
- info( "Error done .. After ctx free\n");
-
- info("-\n");
-
- return btd_error_invalid_args(msg);
-}
-
-static void __sdp_callback_xml_complete_dbus__cb(uint8_t type, uint16_t err, uint8_t *rsp, size_t size, void *udata)
-{
- info("__sdp_callback_xml_complete_dbus__cb() +\n");
- service_dbus_ctxt_t *ctxt = (service_dbus_ctxt_t*) udata;
- sdp_record_t *rec;
- int scanned;
- GString *result;
- DBusMessage *reply, *msg;
- DBusMessageIter iter, array_iter;
-
- reply = dbus_message_new_method_return(ctxt->msg);
-
- if (err == 0xffff)
- {
- error("Invalid session!");
- goto failed;
- }
- if (type == SDP_ERROR_RSP)
- {
- error("SDP_ERROR_RSP!");
- goto failed;
- }
-
- /* check response PDU ID */
- if (type != SDP_SVC_ATTR_RSP)
- {
- error("SDP_SVC_ATTR_RSP!");
- goto failed;
- }
-
- rec = (sdp_record_t *)sdp_extract_pdu_safe(rsp, size, &scanned);
- if (rec == NULL || size != scanned)
- {
- error("Invalid service record!");
- goto failed;
- }
-
- result = g_string_new(NULL);
-
- DBG("size %d\n", size);
-
- convert_sdp_record_to_xml(rec, result, (void *) g_string_append);
-
- sdp_record_free(rec);
-
- info( "XML Converted buffer length %d\n", result->len);
-
- dbus_message_append_args(reply,
- DBUS_TYPE_STRING, result->str,
- DBUS_TYPE_INVALID);
- g_string_free(result, TRUE);
-
- dbus_connection_send(ctxt->conn, reply, NULL);
- dbus_message_unref(reply);
-
- if(NULL != ctxt)
- {
- free(ctxt);
- ctxt = NULL;
- }
- __io_finished = 1;
- info("__sdp_callback_xml_complete_dbus__cb-\n");
- return;
-failed:
-
- dbus_connection_send(ctxt->conn, reply, NULL);
- dbus_message_unref(reply);
-
- if(ctxt != NULL)
- {
- dbus_connection_unref(ctxt->conn);
- dbus_message_unref(ctxt->msg);
- free(ctxt);
- ctxt = NULL;
-
- }
-}
-
-static DBusMessage * siso_adapter_get_remote_svc_xml(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- info( "siso_adapter_get_remote_svc_xml() +\n");
- sdp_session_t *session;
- sdp_list_t *attrids;
- uint32_t range = 0x0000ffff;
- const char *dst;
- uint32_t handle;
- int err;
- bdaddr_t bdadd;
- DBusMessage *reply;
- service_dbus_ctxt_t* ctxt = NULL;
-
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &dst,
- DBUS_TYPE_UINT32, &handle,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- str2ba(dst, &bdadd);
-
- session = sdp_connect(BDADDR_ANY, &bdadd, 0);
- if (!session)
- {
- info("Can't connect to SDP service\n");
- goto error_done;
- }
- ctxt = malloc(sizeof(service_dbus_ctxt_t));
-
- if(NULL == ctxt)
- {
- info("!!!!!Memory allocation failed for ctxt\n");
- goto error_done;
- }
-
- ctxt->conn = conn;
- ctxt->msg = dbus_message_ref(msg);
-
- info( "Before sdp_set_notify +++\n");
- if (sdp_set_notify(session, __sdp_callback_xml_complete_dbus__cb, ctxt) < 0)
- {
- sdp_close(session);
- goto error_done;
- }
-
- attrids = sdp_list_append(NULL, &range);
- if (sdp_service_attr_async(session, handle, SDP_ATTR_REQ_RANGE, attrids) < 0)
- {
- sdp_list_free(attrids, NULL);
- sdp_close(session);
- goto error_done;
- }
- sdp_list_free(attrids, NULL);
- __io_finished = 0;
- while (!__io_finished)
- {
- printf(". ");
- sdp_process(session);
- }
-
- sdp_close(session);
- return 0;
-
-error_done:
- if(ctxt != NULL)
- {
- free(ctxt);
- ctxt = NULL;
- }
- info("-\n");
-
- return btd_error_invalid_args(msg);
-
-}
-
-static void sdp_get_pdu_by_handle_dbus_rsp(uint8_t type, uint16_t err,
- uint8_t *rsp, size_t size, void *udata)
-{
- sdp_record_t *rec;
- int scanned;
- DBusMessage *reply, *msg;
- DBusMessageIter iter, array_iter;
- service_dbus_ctxt_t* ctxt = NULL;
- info("sdp_get_pdu_by_handle_rsp+\n");
-
- if (err == 0xffff)
- {
- error("Invalid session!");
- goto failed;
- }
-
- if (type == SDP_ERROR_RSP)
- {
- error("SDP_ERROR_RSP!");
- goto failed;
- }
-
- /* check response PDU ID */
- if (type != SDP_SVC_ATTR_RSP)
- {
- error("SDP_SVC_ATTR_RSP!");
- goto failed;
- }
-
- ctxt = (service_dbus_ctxt_t*)udata;
- rec = (sdp_record_t *)sdp_extract_pdu_safe(rsp, size, &scanned);
- if (rec == NULL || size != scanned) {
- error("Invalid service record!");
- goto failed;
- }
-
- //sdp_store_record(ctxt->src, ctxt->dst, rec->handle, rsp, size);
-
- sdp_record_free(rec);
-
- DBG("size %d", size);
- dbus_message_iter_append_fixed_array(ctxt->array_iter,
- DBUS_TYPE_BYTE, &rsp, size);
-
- __io_finished = 1;
- info("sdp_get_pdu_by_handle_rsp -\n");
-
- return;
-
-failed:
- info("Failed to get the service record");
-
- __io_finished = 1;
- return;
-}
-
-static DBusMessage *adapter_get_remote_svc(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
-
- //sdp_session_t *session;
- sdp_list_t *attrids;
- uint32_t range = 0x0000ffff;
- const char *dst;
- uint32_t handle;
- bdaddr_t bdadd;
- int struct_len;
- service_dbus_ctxt_t* ctxt = NULL;
-#if 0 //Service search
-#else
- DBusMessage *reply;
- DBusMessageIter iter, array_iter;
-#endif
- info("...__get_sdp_record_by_handle_send_response....\n");
-
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &dst,
- DBUS_TYPE_UINT32, &handle,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- info("address %s, handle %d", dst, handle);
-
- str2ba(dst, &bdadd);
-
- if(NULL == g_cached_session)
- {
- g_cached_session = sdp_connect(BDADDR_ANY, &bdadd, 0);
- if (!g_cached_session)
- {
- info("Can't connect to SDP service\n");
- goto error_done;
- //break;
- }
- }
- info( "Before sdp_set_notify,,,,,......\n");
-
- ctxt = malloc(sizeof(service_dbus_ctxt_t));
-
- if(NULL == ctxt)
- {
- goto error_done;
- }
- __io_finished = 0;
- ctxt->conn = conn;
- ctxt->msg = dbus_message_ref(msg);
- reply = dbus_message_new_method_return(ctxt->msg);
- dbus_message_iter_init_append(reply, &iter);
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_BYTE_AS_STRING, &array_iter);
-
- ctxt->array_iter = &array_iter;
- if (sdp_set_notify(g_cached_session, sdp_get_pdu_by_handle_dbus_rsp, ctxt) < 0)
- goto error_done;
-
-
- attrids = sdp_list_append(NULL, &range);
-
- if (sdp_service_attr_async(g_cached_session, handle,
- SDP_ATTR_REQ_RANGE, attrids) < 0) {
- sdp_list_free(attrids, NULL);
- goto error_done;
- }
-
- sdp_list_free(attrids, NULL);
-
- while (!__io_finished)
- {
- printf(". ");
- sdp_process(g_cached_session);
- }
- dbus_message_iter_close_container(&iter, &array_iter);
-
- free(ctxt);
- return reply;
-
-
-error_done:
- if(ctxt != NULL)
- {
- free(ctxt);
- ctxt = NULL;
- }
- info("__get_sdp_record_by_handle_send_response failed");
-
- return btd_error_invalid_args(msg);
-
-}
-
-static DBusMessage *close_sdp_session(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- info("close_sdp_session");
- if(NULL != g_cached_session)
- {
- info("closing sdp cached session");
- sdp_close(g_cached_session);
- g_cached_session = NULL;
- }
-
- return dbus_message_new_method_return(msg);
-}
-
-
-static GDBusMethodTable service_methods[] = {
- { "AddRecord", "s", "u", add_service_record },
- { "AddServiceRecord", "ay", "u", siso_add_service_record_pdu },
- { "RemoveServiceRecord", "u", "", siso_remove_service_record },
- {"GetRemoteServiceHandles", "ss", "ay", siso_get_remote_service_handles, G_DBUS_METHOD_FLAG_ASYNC},
-// {"GetRemoteServiceHandles", "ss", "ay", get_remote_service_handles, G_DBUS_METHOD_FLAG_ASYNC},
- { "GetRemoteServiceRecord", "su", "ay", adapter_get_remote_svc, G_DBUS_METHOD_FLAG_ASYNC},
- { "CloseSdpSession", "", "", close_sdp_session},
- { "GetRemoteServiceRecordAsXML", "su", "s", siso_adapter_get_remote_svc_xml, G_DBUS_METHOD_FLAG_ASYNC },
- { "UpdateRecord", "us", "", update_service_record },
- { "RemoveRecord", "u", "", remove_service_record },
- { "RequestAuthorization","su", "", request_authorization,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "CancelAuthorization", "", "", cancel_authorization },
- { }
-};
-#else
static GDBusMethodTable service_methods[] = {
{ "AddRecord", "s", "u", add_service_record },
{ "UpdateRecord", "us", "", update_service_record },
{ }
};
-#endif
static void path_unregister(void *data)
{
struct service_adapter *serv_adapter = data;
static int register_interface(const char *path, struct btd_adapter *adapter)
{
- info("\nEntering register_interface()... +\n");
struct service_adapter *serv_adapter;
DBG("path %s", path);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 David Herrmann <dh.herrmann@googlemail.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <bluetooth/bluetooth.h>
+
+#include "plugin.h"
+#include "adapter.h"
+#include "device.h"
+#include "log.h"
+#include "storage.h"
+
+/*
+ * Nintendo Wii Remote devices require the bdaddr of the host as pin input for
+ * authentication. This plugin registers a pin-callback and forces this pin
+ * to be used for authentication.
+ *
+ * There are two ways to place the wiimote into discoverable mode.
+ * - Pressing the red-sync button on the back of the wiimote. This module
+ * supports pairing via this method. Auto-reconnect should be possible after
+ * the device was paired once.
+ * - Pressing the 1+2 buttons on the front of the wiimote. This module does
+ * not support this method since this method never enables auto-reconnect.
+ * Hence, pairing is not needed. Use it without pairing if you want.
+ * After connecting the wiimote you should immediately connect to the input
+ * service of the wiimote. If you don't, the wiimote will close the connection.
+ * The wiimote waits about 5 seconds until it turns off again.
+ * Auto-reconnect is only enabled when pairing with the wiimote via the red
+ * sync-button and then connecting to the input service. If you do not connect
+ * to the input service, then auto-reconnect is not enabled.
+ * If enabled, the wiimote connects to the host automatically when any button
+ * is pressed.
+ */
+
+static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device,
+ char *pinbuf)
+{
+ uint16_t vendor, product;
+ bdaddr_t sba, dba;
+ char addr[18];
+
+ adapter_get_address(adapter, &sba);
+ device_get_address(device, &dba, NULL);
+ ba2str(&dba, addr);
+
+ vendor = btd_device_get_vendor(device);
+ if (vendor != 0x057e)
+ return 0;
+
+ product = btd_device_get_product(device);
+ if (product == 0x0306) {
+ DBG("Forcing fixed pin on detected wiimote %s", addr);
+ memcpy(pinbuf, &sba, 6);
+ return 6;
+ }
+
+ return 0;
+}
+
+static int wii_probe(struct btd_adapter *adapter)
+{
+ btd_adapter_register_pin_cb(adapter, wii_pincb);
+
+ return 0;
+}
+
+static void wii_remove(struct btd_adapter *adapter)
+{
+ btd_adapter_unregister_pin_cb(adapter, wii_pincb);
+}
+
+static struct btd_adapter_driver wii_driver = {
+ .name = "wiimote",
+ .probe = wii_probe,
+ .remove = wii_remove,
+};
+
+static int wii_init(void)
+{
+ return btd_register_adapter_driver(&wii_driver);
+}
+
+static void wii_exit(void)
+{
+ btd_unregister_adapter_driver(&wii_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(wiimote, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_LOW, wii_init, wii_exit)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "plugin.h"
+#include "manager.h"
+
+static DBusConnection *connection = NULL;
+static GKeyFile *config = NULL;
+
+static GKeyFile *open_config_file(const char *file)
+{
+ GError *gerr = NULL;
+ GKeyFile *keyfile;
+
+ keyfile = g_key_file_new();
+
+ g_key_file_set_list_separator(keyfile, ',');
+
+ if (!g_key_file_load_from_file(keyfile, file, 0, &gerr)) {
+ error("Parsing %s failed: %s", file, gerr->message);
+ g_error_free(gerr);
+ g_key_file_free(keyfile);
+ return NULL;
+ }
+
+ return keyfile;
+}
+
+static int proximity_init(void)
+{
+
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (connection == NULL)
+ return -EIO;
+
+ config = open_config_file(CONFIGDIR "/proximity.conf");
+
+ if (proximity_manager_init(connection, config) < 0) {
+ dbus_connection_unref(connection);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void proximity_exit(void)
+{
+ if (config)
+ g_key_file_free(config);
+
+ proximity_manager_exit();
+ dbus_connection_unref(connection);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(proximity, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ proximity_init, proximity_exit)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <gdbus.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "att.h"
+#include "monitor.h"
+#include "reporter.h"
+#include "manager.h"
+
+#define IMMEDIATE_ALERT_UUID "00001802-0000-1000-8000-00805f9b34fb"
+#define LINK_LOSS_UUID "00001803-0000-1000-8000-00805f9b34fb"
+#define TX_POWER_UUID "00001804-0000-1000-8000-00805f9b34fb"
+
+static DBusConnection *connection = NULL;
+
+static struct enabled enabled = {
+ .linkloss = TRUE,
+ .pathloss = TRUE,
+ .findme = TRUE,
+};
+
+static gint primary_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct att_primary *prim = a;
+ const char *uuid = b;
+
+ return g_strcmp0(prim->uuid, uuid);
+}
+
+static int attio_device_probe(struct btd_device *device, GSList *uuids)
+{
+ struct att_primary *linkloss, *txpower, *immediate;
+ GSList *l, *primaries;
+
+ primaries = btd_device_get_primaries(device);
+
+ l = g_slist_find_custom(primaries, IMMEDIATE_ALERT_UUID,
+ primary_uuid_cmp);
+ immediate = (l ? l->data : NULL);
+
+ l = g_slist_find_custom(primaries, TX_POWER_UUID, primary_uuid_cmp);
+ txpower = (l ? l->data : NULL);
+
+ l = g_slist_find_custom(primaries, LINK_LOSS_UUID, primary_uuid_cmp);
+ linkloss = (l ? l->data : NULL);
+
+ return monitor_register(connection, device, linkloss, txpower,
+ immediate, &enabled);
+}
+
+static void attio_device_remove(struct btd_device *device)
+{
+ monitor_unregister(connection, device);
+}
+
+static struct btd_device_driver monitor_driver = {
+ .name = "Proximity GATT Driver",
+ .uuids = BTD_UUIDS(IMMEDIATE_ALERT_UUID, LINK_LOSS_UUID, TX_POWER_UUID),
+ .probe = attio_device_probe,
+ .remove = attio_device_remove,
+};
+
+static void load_config_file(GKeyFile *config)
+{
+ char **list;
+ int i;
+
+ if (config == NULL)
+ return;
+
+ list = g_key_file_get_string_list(config, "General", "Disable",
+ NULL, NULL);
+ for (i = 0; list && list[i] != NULL; i++) {
+ if (g_str_equal(list[i], "FindMe"))
+ enabled.findme = FALSE;
+ else if (g_str_equal(list[i], "LinkLoss"))
+ enabled.linkloss = FALSE;
+ else if (g_str_equal(list[i], "PathLoss"))
+ enabled.pathloss = FALSE;
+ }
+
+ g_strfreev(list);
+}
+
+int proximity_manager_init(DBusConnection *conn, GKeyFile *config)
+{
+ int ret;
+
+ load_config_file(config);
+
+ /* TODO: Register Proximity Monitor/Reporter drivers */
+ ret = btd_register_device_driver(&monitor_driver);
+ if (ret < 0)
+ return ret;
+
+ connection = dbus_connection_ref(conn);
+
+ return reporter_init();
+}
+
+void proximity_manager_exit(void)
+{
+ reporter_exit();
+ btd_unregister_device_driver(&monitor_driver);
+ dbus_connection_unref(connection);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int proximity_manager_init(DBusConnection *conn, GKeyFile *conf);
+void proximity_manager_exit(void);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <gdbus.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/uuid.h>
+
+#include "dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+#include "error.h"
+#include "log.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "attio.h"
+#include "monitor.h"
+#include "textfile.h"
+
+#define PROXIMITY_INTERFACE "org.bluez.Proximity"
+
+#define ALERT_LEVEL_CHR_UUID 0x2A06
+#define POWER_LEVEL_CHR_UUID 0x2A07
+
+#define IMMEDIATE_TIMEOUT 5
+
+enum {
+ ALERT_NONE = 0,
+ ALERT_MILD,
+ ALERT_HIGH,
+};
+
+struct monitor {
+ struct btd_device *device;
+ GAttrib *attrib;
+ DBusConnection *conn;
+ struct att_range *linkloss;
+ struct att_range *txpower;
+ struct att_range *immediate;
+ struct enabled enabled;
+ char *linklosslevel; /* Link Loss Alert Level */
+ char *fallbacklevel; /* Immediate fallback alert level */
+ char *immediatelevel; /* Immediate Alert Level */
+ char *signallevel; /* Path Loss RSSI level */
+ uint16_t linklosshandle; /* Link Loss Characteristic
+ * Value Handle */
+ uint16_t txpowerhandle; /* Tx Characteristic Value Handle */
+ uint16_t immediatehandle; /* Immediate Alert Value Handle */
+ guint immediateto; /* Reset Immediate Alert to "none" */
+ guint attioid;
+};
+
+static inline int create_filename(char *buf, size_t size,
+ const bdaddr_t *bdaddr, const char *name)
+{
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+
+ return create_name(buf, size, STORAGEDIR, addr, name);
+}
+
+static int write_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
+ const char *alert, const char *level)
+{
+ char filename[PATH_MAX + 1], addr[18], key[38];
+
+ create_filename(filename, PATH_MAX, sba, "proximity");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+
+ snprintf(key, sizeof(key), "%17s#%s", addr, alert);
+
+ return textfile_put(filename, key, level);
+}
+
+static char *read_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
+ const char *alert)
+{
+ char filename[PATH_MAX + 1], addr[18], key[38];
+ char *str, *strnew;
+
+ create_filename(filename, PATH_MAX, sba, "proximity");
+
+ ba2str(dba, addr);
+ snprintf(key, sizeof(key), "%17s#%s", addr, alert);
+
+ str = textfile_caseget(filename, key);
+ if (str == NULL)
+ return NULL;
+
+ strnew = g_strdup(str);
+ free(str);
+
+ return strnew;
+}
+
+static uint8_t str2level(const char *level)
+{
+ if (g_strcmp0("high", level) == 0)
+ return ALERT_HIGH;
+ else if (g_strcmp0("mild", level) == 0)
+ return ALERT_MILD;
+
+ return ALERT_NONE;
+}
+
+static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ struct btd_device *device = monitor->device;
+ const char *path = device_get_path(device);
+
+ if (status != 0) {
+ error("Link Loss Write Request failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_write_resp(pdu, plen)) {
+ error("Link Loss Write Request: protocol error");
+ return;
+ }
+
+ DBG("Link Loss Alert Level written");
+
+ emit_property_changed(monitor->conn, path,
+ PROXIMITY_INTERFACE, "LinkLossAlertLevel",
+ DBUS_TYPE_STRING, &monitor->linklosslevel);
+}
+
+static void char_discovered_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ struct att_char *chr;
+ uint8_t value = str2level(monitor->linklosslevel);
+
+ if (status) {
+ error("Discover Link Loss handle: %s", att_ecode2str(status));
+ return;
+ }
+
+ DBG("Setting alert level \"%s\" on Reporter", monitor->linklosslevel);
+
+ /* Assume there is a single Alert Level characteristic */
+ chr = characteristics->data;
+ monitor->linklosshandle = chr->value_handle;
+
+ gatt_write_char(monitor->attrib, monitor->linklosshandle, &value, 1,
+ linkloss_written, monitor);
+}
+
+static int write_alert_level(struct monitor *monitor)
+{
+ struct att_range *linkloss = monitor->linkloss;
+ bt_uuid_t uuid;
+
+ if (monitor->linklosshandle) {
+ uint8_t value = str2level(monitor->linklosslevel);
+
+ gatt_write_char(monitor->attrib, monitor->linklosshandle,
+ &value, 1, linkloss_written, monitor);
+ return 0;
+ }
+
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+ /* FIXME: use cache (requires service changed support) ? */
+ gatt_discover_char(monitor->attrib, linkloss->start, linkloss->end,
+ &uuid, char_discovered_cb, monitor);
+
+ return 0;
+}
+
+static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ uint8_t value[ATT_MAX_MTU];
+ int vlen;
+
+ if (status != 0) {
+ DBG("Tx Power Level read failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_read_resp(pdu, plen, value, &vlen)) {
+ DBG("Protocol error");
+ return;
+ }
+
+ if (vlen != 1) {
+ DBG("Invalid length for TX Power value: %d", vlen);
+ return;
+ }
+
+ DBG("Tx Power Level: %02x", (int8_t) value[0]);
+}
+
+static void tx_power_handle_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ struct att_char *chr;
+
+ if (status) {
+ error("Discover Tx Power handle: %s", att_ecode2str(status));
+ return;
+ }
+
+ chr = characteristics->data;
+ monitor->txpowerhandle = chr->value_handle;
+
+ DBG("Tx Power handle: 0x%04x", monitor->txpowerhandle);
+
+ gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
+ tx_power_read_cb, monitor);
+}
+
+static void read_tx_power(struct monitor *monitor)
+{
+ struct att_range *txpower = monitor->txpower;
+ bt_uuid_t uuid;
+
+ if (monitor->txpowerhandle != 0) {
+ gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
+ tx_power_read_cb, monitor);
+ return;
+ }
+
+ bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+
+ gatt_discover_char(monitor->attrib, txpower->start, txpower->end,
+ &uuid, tx_power_handle_cb, monitor);
+}
+
+static gboolean immediate_timeout(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ const char *path = device_get_path(monitor->device);
+
+ monitor->immediateto = 0;
+
+ if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+ return FALSE;
+
+ if (monitor->attrib) {
+ uint8_t value = ALERT_NONE;
+ gatt_write_cmd(monitor->attrib, monitor->immediatehandle,
+ &value, 1, NULL, NULL);
+ }
+
+ g_free(monitor->immediatelevel);
+ monitor->immediatelevel = g_strdup("none");
+ emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
+ "ImmediateAlertLevel", DBUS_TYPE_STRING,
+ &monitor->immediatelevel);
+
+ return FALSE;
+}
+
+static void immediate_written(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ const char *path = device_get_path(monitor->device);
+
+ g_free(monitor->fallbacklevel);
+ monitor->fallbacklevel = NULL;
+
+ emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
+ "ImmediateAlertLevel",
+ DBUS_TYPE_STRING, &monitor->immediatelevel);
+
+ monitor->immediateto = g_timeout_add_seconds(IMMEDIATE_TIMEOUT,
+ immediate_timeout, monitor);
+}
+
+static void write_immediate_alert(struct monitor *monitor)
+{
+ uint8_t value = str2level(monitor->immediatelevel);
+
+ gatt_write_cmd(monitor->attrib, monitor->immediatehandle, &value, 1,
+ immediate_written, monitor);
+}
+
+static void immediate_handle_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ struct att_char *chr;
+
+ if (status) {
+ error("Discover Immediate Alert handle: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ chr = characteristics->data;
+ monitor->immediatehandle = chr->value_handle;
+
+ DBG("Immediate Alert handle: 0x%04x", monitor->immediatehandle);
+
+ if (monitor->fallbacklevel)
+ write_immediate_alert(monitor);
+}
+
+static void discover_immediate_handle(struct monitor *monitor)
+{
+ struct att_range *immediate = monitor->immediate;
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+ gatt_discover_char(monitor->attrib, immediate->start, immediate->end,
+ &uuid, immediate_handle_cb, monitor);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+
+ monitor->attrib = g_attrib_ref(attrib);
+
+ if (monitor->enabled.linkloss)
+ write_alert_level(monitor);
+
+ if (monitor->enabled.pathloss)
+ read_tx_power(monitor);
+
+ if (monitor->immediatehandle == 0) {
+ if(monitor->enabled.pathloss || monitor->enabled.findme)
+ discover_immediate_handle(monitor);
+ } else if (monitor->fallbacklevel)
+ write_immediate_alert(monitor);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ const char *path = device_get_path(monitor->device);
+
+ g_attrib_unref(monitor->attrib);
+ monitor->attrib = NULL;
+
+ if (monitor->immediateto == 0)
+ return;
+
+ g_source_remove(monitor->immediateto);
+ monitor->immediateto = 0;
+
+ if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+ return;
+
+ g_free(monitor->immediatelevel);
+ monitor->immediatelevel = g_strdup("none");
+ emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
+ "ImmediateAlertLevel", DBUS_TYPE_STRING,
+ &monitor->immediatelevel);
+}
+
+static gboolean level_is_valid(const char *level)
+{
+ return (g_str_equal("none", level) ||
+ g_str_equal("mild", level) ||
+ g_str_equal("high", level));
+}
+
+static DBusMessage *set_link_loss_alert(DBusConnection *conn, DBusMessage *msg,
+ const char *level, void *data)
+{
+ struct monitor *monitor = data;
+ struct btd_device *device = monitor->device;
+ bdaddr_t sba, dba;
+
+ if (!level_is_valid(level))
+ return btd_error_invalid_args(msg);
+
+ if (g_strcmp0(monitor->linklosslevel, level) == 0)
+ return dbus_message_new_method_return(msg);
+
+ g_free(monitor->linklosslevel);
+ monitor->linklosslevel = g_strdup(level);
+
+ adapter_get_address(device_get_adapter(device), &sba);
+ device_get_address(device, &dba, NULL);
+
+ write_proximity_config(&sba, &dba, "LinkLossAlertLevel", level);
+
+ if (monitor->attrib)
+ write_alert_level(monitor);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_immediate_alert(DBusConnection *conn, DBusMessage *msg,
+ const char *level, void *data)
+{
+ struct monitor *monitor = data;
+
+ if (!level_is_valid(level))
+ return btd_error_invalid_args(msg);
+
+ if (g_strcmp0(monitor->immediatelevel, level) == 0)
+ return dbus_message_new_method_return(msg);
+
+ if (monitor->immediateto) {
+ g_source_remove(monitor->immediateto);
+ monitor->immediateto = 0;
+ }
+
+ /* Previous Immediate Alert level if connection/write fails */
+ g_free(monitor->fallbacklevel);
+ monitor->fallbacklevel = monitor->immediatelevel;
+
+ monitor->immediatelevel = g_strdup(level);
+
+ /*
+ * Means that Link/Path Loss are disabled or there is a pending
+ * writting for Find Me(Immediate Alert characteristic value).
+ * If enabled, Path Loss always registers a connection callback
+ * when the Proximity Monitor starts.
+ */
+ if (monitor->attioid == 0)
+ monitor->attioid = btd_device_add_attio_callback(monitor->device,
+ attio_connected_cb,
+ attio_disconnected_cb,
+ monitor);
+ else if (monitor->attrib)
+ write_immediate_alert(monitor);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct monitor *monitor = data;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ if (monitor->enabled.linkloss)
+ dict_append_entry(&dict, "LinkLossAlertLevel",
+ DBUS_TYPE_STRING, &monitor->linklosslevel);
+
+ if (monitor->enabled.findme || monitor->enabled.pathloss)
+ dict_append_entry(&dict, "ImmediateAlertLevel",
+ DBUS_TYPE_STRING, &monitor->immediatelevel);
+
+ if (monitor->enabled.pathloss)
+ dict_append_entry(&dict, "SignalLevel",
+ DBUS_TYPE_STRING, &monitor->signallevel);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct monitor *monitor = data;
+ const char *property;
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+ const char *level;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &level);
+
+ if (g_str_equal("ImmediateAlertLevel", property)) {
+ if (monitor->enabled.findme == FALSE &&
+ monitor->enabled.pathloss == FALSE)
+ return btd_error_not_available(msg);
+
+ return set_immediate_alert(conn, msg, level, data);
+ } else if (g_str_equal("LinkLossAlertLevel", property)) {
+ if (monitor->enabled.linkloss == FALSE)
+ return btd_error_not_available(msg);
+
+ return set_link_loss_alert(conn, msg, level, data);
+ }
+
+ return btd_error_invalid_args(msg);
+}
+
+static GDBusMethodTable monitor_methods[] = {
+ { "GetProperties", "", "a{sv}", get_properties },
+ { "SetProperty", "sv", "", set_property,
+ G_DBUS_METHOD_FLAG_ASYNC},
+ { }
+};
+
+static GDBusSignalTable monitor_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static void monitor_destroy(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+
+ if (monitor->immediateto)
+ g_source_remove(monitor->immediateto);
+
+ if (monitor->attioid)
+ btd_device_remove_attio_callback(monitor->device,
+ monitor->attioid);
+ if (monitor->attrib)
+ g_attrib_unref(monitor->attrib);
+
+ dbus_connection_unref(monitor->conn);
+ btd_device_unref(monitor->device);
+ g_free(monitor->linkloss);
+ g_free(monitor->immediate);
+ g_free(monitor->txpower);
+ g_free(monitor->linklosslevel);
+ g_free(monitor->immediatelevel);
+ g_free(monitor->signallevel);
+ g_free(monitor);
+}
+
+int monitor_register(DBusConnection *conn, struct btd_device *device,
+ struct att_primary *linkloss, struct att_primary *txpower,
+ struct att_primary *immediate, struct enabled *enabled)
+{
+ const char *path = device_get_path(device);
+ struct monitor *monitor;
+ bdaddr_t sba, dba;
+ char *level;
+
+ adapter_get_address(device_get_adapter(device), &sba);
+ device_get_address(device, &dba, NULL);
+
+ level = read_proximity_config(&sba, &dba, "LinkLossAlertLevel");
+
+ monitor = g_new0(struct monitor, 1);
+ monitor->device = btd_device_ref(device);
+ monitor->conn = dbus_connection_ref(conn);
+ monitor->linklosslevel = (level ? : g_strdup("high"));
+ monitor->signallevel = g_strdup("unknown");
+ monitor->immediatelevel = g_strdup("none");
+
+ if (g_dbus_register_interface(conn, path,
+ PROXIMITY_INTERFACE,
+ monitor_methods, monitor_signals,
+ NULL, monitor, monitor_destroy) == FALSE) {
+ error("D-Bus failed to register %s interface",
+ PROXIMITY_INTERFACE);
+ monitor_destroy(monitor);
+ return -1;
+ }
+
+ DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
+
+ if (linkloss && enabled->linkloss) {
+ monitor->linkloss = g_new0(struct att_range, 1);
+ monitor->linkloss->start = linkloss->start;
+ monitor->linkloss->end = linkloss->end;
+
+ monitor->enabled.linkloss = TRUE;
+ }
+
+ if (immediate) {
+ if (txpower && enabled->pathloss) {
+ monitor->txpower = g_new0(struct att_range, 1);
+ monitor->txpower->start = txpower->start;
+ monitor->txpower->end = txpower->end;
+
+ monitor->enabled.pathloss = TRUE;
+ }
+
+ if (enabled->pathloss || enabled->findme) {
+ monitor->immediate = g_new0(struct att_range, 1);
+ monitor->immediate->start = immediate->start;
+ monitor->immediate->end = immediate->end;
+ }
+
+ monitor->enabled.findme = enabled->findme;
+ }
+
+ DBG("Link Loss: %s, Path Loss: %s, FindMe: %s",
+ monitor->enabled.linkloss ? "TRUE" : "FALSE",
+ monitor->enabled.pathloss ? "TRUE" : "FALSE",
+ monitor->enabled.findme ? "TRUE" : "FALSE");
+
+ if (monitor->enabled.linkloss || monitor->enabled.pathloss)
+ monitor->attioid = btd_device_add_attio_callback(device,
+ attio_connected_cb,
+ attio_disconnected_cb,
+ monitor);
+
+ return 0;
+}
+
+void monitor_unregister(DBusConnection *conn, struct btd_device *device)
+{
+ const char *path = device_get_path(device);
+
+ g_dbus_unregister_interface(conn, path, PROXIMITY_INTERFACE);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct enabled {
+ gboolean linkloss;
+ gboolean pathloss;
+ gboolean findme;
+};
+
+int monitor_register(DBusConnection *conn, struct btd_device *device,
+ struct att_primary *linkloss, struct att_primary *txpower,
+ struct att_primary *immediate, struct enabled *enabled);
+void monitor_unregister(DBusConnection *conn, struct btd_device *device);
--- /dev/null
+# Configuration file for the proximity service
+
+# This section contains options which are not specific to any
+# particular interface
+[General]
+
+# Configuration to allow disabling Proximity services
+# Allowed values: LinkLoss,PathLoss,FindMe
+Disable=PathLoss
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+
+#include "log.h"
+
+#include "hcid.h"
+#include "att.h"
+#include "gattrib.h"
+#include "attrib-server.h"
+#include "reporter.h"
+
+#define IMMEDIATE_ALERT_SVC_UUID 0x1802
+#define LINK_LOSS_SVC_UUID 0x1803
+#define TX_POWER_SVC_UUID 0x1804
+#define ALERT_LEVEL_CHR_UUID 0x2A06
+#define POWER_LEVEL_CHR_UUID 0x2A07
+
+enum {
+ NO_ALERT = 0x00,
+ MILD_ALERT = 0x01,
+ HIGH_ALERT = 0x02,
+};
+
+static uint16_t tx_power_handle;
+
+static void register_link_loss(void)
+{
+ uint16_t start_handle, h;
+ const int svc_size = 3;
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+
+ /* FIXME: Provide the adapter in next function */
+ start_handle = attrib_db_find_avail(NULL, svc_size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ return;
+ }
+
+ DBG("start_handle=0x%04x", start_handle);
+
+ h = start_handle;
+
+ /* Primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ att_put_u16(LINK_LOSS_SVC_UUID, &atval[0]);
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_add(NULL, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Alert level characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_WRITE;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(ALERT_LEVEL_CHR_UUID, &atval[3]);
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_add(NULL, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Alert level value */
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+ att_put_u8(NO_ALERT, &atval[0]);
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_add(NULL, h++, &uuid, ATT_NONE, ATT_NONE, atval, 1);
+
+ g_assert(h - start_handle == svc_size);
+}
+
+static void register_tx_power(void)
+{
+ uint16_t start_handle, h;
+ const int svc_size = 4;
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+
+ /* FIXME: Provide the adapter in next function */
+ start_handle = attrib_db_find_avail(NULL, svc_size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ return;
+ }
+
+ DBG("start_handle=0x%04x", start_handle);
+
+ h = start_handle;
+
+ /* Primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ att_put_u16(TX_POWER_SVC_UUID, &atval[0]);
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_add(NULL, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Power level characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_NOTIFY;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(POWER_LEVEL_CHR_UUID, &atval[3]);
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_add(NULL, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Power level value */
+ bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+ att_put_u8(0x00, &atval[0]);
+ tx_power_handle = h;
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_add(NULL, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
+
+ /* Client characteristic configuration */
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ atval[0] = 0x00;
+ atval[1] = 0x00;
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_add(NULL, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2);
+
+ g_assert(h - start_handle == svc_size);
+}
+
+static void register_immediate_alert(void)
+{
+ uint16_t start_handle, h;
+ const int svc_size = 3;
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+
+ /* FIXME: Provide the adapter in next function */
+ start_handle = attrib_db_find_avail(NULL, svc_size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ return;
+ }
+
+ DBG("start_handle=0x%04x", start_handle);
+
+ h = start_handle;
+
+ /* Primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ att_put_u16(IMMEDIATE_ALERT_SVC_UUID, &atval[0]);
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_add(NULL, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Alert level characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = ATT_CHAR_PROPER_WRITE_WITHOUT_RESP;
+ att_put_u16(h + 1, &atval[1]);
+ att_put_u16(ALERT_LEVEL_CHR_UUID, &atval[3]);
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_add(NULL, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Alert level value */
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+ att_put_u8(NO_ALERT, &atval[0]);
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_add(NULL, h++, &uuid, ATT_NONE, ATT_NONE, atval, 1);
+
+ g_assert(h - start_handle == svc_size);
+}
+
+int reporter_init(void)
+{
+ if (!main_opts.attrib_server) {
+ DBG("Attribute server is disabled");
+ return -1;
+ }
+
+ DBG("Proximity Reporter");
+
+ register_link_loss();
+ register_tx_power();
+ register_immediate_alert();
+
+ return 0;
+}
+
+void reporter_exit(void)
+{
+}
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
*
*/
-int attrib_manager_init(DBusConnection *conn);
-void attrib_manager_exit(void);
+int reporter_init(void);
+void reporter_exit(void);
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2010 ST-Ericsson SA
+ * Copyright (C) 2011 Tieto Poland
*
* Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com>
* for ST-Ericsson
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+#include <gdbus.h>
+
#include "log.h"
#include "sap.h"
+#define SAP_DUMMY_IFACE "org.bluez.SimAccessTest"
+#define SAP_DUMMY_PATH "/org/bluez/test"
+
+enum {
+ SIM_DISCONNECTED= 0x00,
+ SIM_CONNECTED = 0x01,
+ SIM_POWERED_OFF = 0x02,
+ SIM_MISSING = 0x03
+};
+
+static DBusConnection *connection = NULL;
+
+static int sim_card_conn_status = SIM_DISCONNECTED;
+static void *sap_data = NULL; /* SAP server private data.*/
+static gboolean ongoing_call_status = FALSE;
+static int max_msg_size_supported = 512;
+
void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
{
- sap_connect_rsp(sap_device, SAP_STATUS_OK, maxmsgsize);
- sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status != SIM_DISCONNECTED) {
+ sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED,
+ maxmsgsize);
+ return;
+ } else if (max_msg_size_supported > maxmsgsize) {
+ sap_connect_rsp(sap_device, SAP_STATUS_MAX_MSG_SIZE_TOO_SMALL,
+ max_msg_size_supported);
+ return;
+ } else if (max_msg_size_supported < maxmsgsize) {
+ sap_connect_rsp(sap_device,
+ SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED,
+ max_msg_size_supported);
+ return;
+ } else if (ongoing_call_status) {
+ sap_connect_rsp(sap_device, SAP_STATUS_OK_ONGOING_CALL,
+ max_msg_size_supported);
+ return;
+ } else {
+ sim_card_conn_status = SIM_CONNECTED;
+ sap_data = sap_device;
+
+ sap_connect_rsp(sap_device, SAP_STATUS_OK, maxmsgsize);
+ sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+ }
}
void sap_disconnect_req(void *sap_device, uint8_t linkloss)
{
+ sim_card_conn_status = SIM_DISCONNECTED;
+ sap_data = NULL;
+ ongoing_call_status = FALSE;
+
+ DBG("status: %d", sim_card_conn_status);
+
+ if (linkloss)
+ return;
+
sap_disconnect_rsp(sap_device);
}
void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
{
- sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK, NULL, 0);
+ char apdu[] = "APDU response!";
+
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status == SIM_MISSING)
+ sap_transfer_apdu_rsp(sap_device,
+ SAP_RESULT_ERROR_CARD_REMOVED, NULL, 0);
+ else if (sim_card_conn_status == SIM_POWERED_OFF)
+ sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
+ NULL, 0);
+ else if (sim_card_conn_status != SIM_CONNECTED)
+ sap_transfer_apdu_rsp(sap_device,
+ SAP_RESULT_ERROR_NOT_ACCESSIBLE, NULL, 0);
+ else
+ sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK,
+ (uint8_t*)&apdu, sizeof(apdu));
}
void sap_transfer_atr_req(void *sap_device)
{
- sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK, NULL, 0);
+ char atr[] = "ATR response!";
+
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status == SIM_MISSING)
+ sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED,
+ NULL, 0);
+ else if (sim_card_conn_status == SIM_POWERED_OFF)
+ sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF,
+ NULL, 0);
+ else if (sim_card_conn_status != SIM_CONNECTED)
+ sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON,
+ NULL, 0);
+ else
+ sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK,
+ (uint8_t*)&atr, sizeof(atr));
}
void sap_power_sim_off_req(void *sap_device)
{
- sap_power_sim_off_rsp(sap_device, SAP_RESULT_OK);
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status == SIM_MISSING) {
+ sap_power_sim_off_rsp(sap_device,
+ SAP_RESULT_ERROR_CARD_REMOVED);
+ } else if (sim_card_conn_status == SIM_POWERED_OFF) {
+ sap_power_sim_off_rsp(sap_device,
+ SAP_RESULT_ERROR_POWERED_OFF);
+ } else if (sim_card_conn_status != SIM_CONNECTED) {
+ sap_power_sim_off_rsp(sap_device,
+ SAP_RESULT_ERROR_NO_REASON);
+ } else {
+ sap_power_sim_off_rsp(sap_device, SAP_RESULT_OK);
+ sim_card_conn_status = SIM_POWERED_OFF;
+ }
}
void sap_power_sim_on_req(void *sap_device)
{
- sap_power_sim_on_rsp(sap_device, SAP_RESULT_OK);
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status == SIM_MISSING) {
+ sap_power_sim_on_rsp(sap_device,
+ SAP_RESULT_ERROR_CARD_REMOVED);
+ } else if (sim_card_conn_status == SIM_POWERED_OFF) {
+ sap_power_sim_on_rsp(sap_device, SAP_RESULT_OK);
+ sim_card_conn_status = SIM_CONNECTED;
+ return;
+ } else if (sim_card_conn_status != SIM_CONNECTED) {
+ sap_power_sim_on_rsp(sap_device,
+ SAP_RESULT_ERROR_NOT_ACCESSIBLE);
+ } else {
+ sap_power_sim_on_rsp(sap_device,
+ SAP_RESULT_ERROR_NO_REASON);
+ }
}
void sap_reset_sim_req(void *sap_device)
{
- sap_reset_sim_rsp(sap_device, SAP_RESULT_OK);
- sap_status_ind(sap_device, SAP_STATUS_CHANGE_CARD_RESET);
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status == SIM_MISSING) {
+ sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_CARD_REMOVED);
+ } else if (sim_card_conn_status == SIM_POWERED_OFF) {
+ sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_POWERED_OFF);
+ } else if (sim_card_conn_status != SIM_CONNECTED) {
+ sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+ } else {
+ sap_reset_sim_rsp(sap_device, SAP_RESULT_OK);
+ }
}
void sap_transfer_card_reader_status_req(void *sap_device)
{
- sap_transfer_card_reader_status_rsp(sap_device, SAP_RESULT_OK,
- ICC_READER_CARD_POWERED_ON);
+ DBG("status: %d", sim_card_conn_status);
+
+ if (sim_card_conn_status != SIM_CONNECTED) {
+ sap_transfer_card_reader_status_rsp(sap_device,
+ SAP_RESULT_ERROR_NO_REASON, 0xF1);
+ return;
+ }
+
+ sap_transfer_card_reader_status_rsp(sap_device, SAP_RESULT_OK, 0xF1);
}
void sap_set_transport_protocol_req(void *sap_device,
sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
}
+static inline DBusMessage *invalid_args(DBusMessage *msg)
+{
+ return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
+ "Invalid arguments in method call");
+}
+
+static DBusMessage *ongoing_call(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ dbus_bool_t ongoing;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &ongoing,
+ DBUS_TYPE_INVALID))
+ return invalid_args(msg);
+
+ if (ongoing_call_status && !ongoing) {
+ /* An ongoing call has finished. Continue connection.*/
+ sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_RESET);
+ ongoing_call_status = ongoing;
+ } else if (!ongoing_call_status && ongoing) {
+ /* An ongoing call has started.*/
+ ongoing_call_status = ongoing;
+ }
+
+ DBG("OngoingCall status set to %d", ongoing_call_status);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *max_msg_size(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ dbus_uint32_t size;
+
+ if (sim_card_conn_status == SIM_CONNECTED)
+ return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+ "Can't change msg size when connected.");
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &size,
+ DBUS_TYPE_INVALID))
+ return invalid_args(msg);
+
+ max_msg_size_supported = size;
+
+ DBG("MaxMessageSize set to %d", max_msg_size_supported);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disconnect_immediate(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ if (sim_card_conn_status == SIM_DISCONNECTED)
+ return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+ "Already disconnected.");
+
+ sim_card_conn_status = SIM_DISCONNECTED;
+ sap_disconnect_ind(sap_data, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *card_status(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ dbus_uint32_t status;
+
+ DBG("status %d", sim_card_conn_status);
+
+ if (sim_card_conn_status != SIM_CONNECTED)
+ return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+ "Can't change msg size when not connected.");
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &status,
+ DBUS_TYPE_INVALID))
+ return invalid_args(msg);
+
+ switch (status) {
+ case 0: /* card removed */
+ sim_card_conn_status = SIM_MISSING;
+ sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_REMOVED);
+ break;
+
+ case 1: /* card inserted */
+ if (sim_card_conn_status == SIM_MISSING) {
+ sim_card_conn_status = SIM_CONNECTED;
+ sap_status_ind(sap_data,
+ SAP_STATUS_CHANGE_CARD_INSERTED);
+ }
+ break;
+
+ case 2: /* card not longer available*/
+ sim_card_conn_status = SIM_POWERED_OFF;
+ sap_status_ind(sap_data, SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE);
+ break;
+
+ default:
+ return g_dbus_create_error(msg, "org.bluez.Error.Failed",
+ "Unknown card status. Use 0, 1 or 2.");
+ }
+
+ DBG("Card status changed to %d", status);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable dummy_methods[] = {
+ { "OngoingCall", "b", "", ongoing_call},
+ { "MaxMessageSize", "u", "", max_msg_size},
+ { "DisconnectImmediate", "", "", disconnect_immediate},
+ { "CardStatus", "u", "", card_status},
+ { }
+};
+
int sap_init(void)
{
- DBG("SAP driver init.");
+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+
+ if (g_dbus_register_interface(connection, SAP_DUMMY_PATH,
+ SAP_DUMMY_IFACE, dummy_methods, NULL, NULL,
+ NULL, NULL) == FALSE) {
+ error("sap-dummy interface %s init failed on path %s",
+ SAP_DUMMY_IFACE, SAP_DUMMY_PATH);
+ return -1;
+ }
+
return 0;
}
void sap_exit(void)
{
- DBG("SAP driver exit.");
+ dbus_connection_unref(connection);
+ connection = NULL;
}
--- /dev/null
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * SAP Driver for ST-Ericsson U8500 platform
+ *
+ * Copyright (C) 2010-2011 ST-Ericsson SA
+ *
+ * Author: Waldemar Rymarkiewicz <waldemar.rymarkiewicz@tieto.com> for
+ * ST-Ericsson.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <glib.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "log.h"
+#include "sap.h"
+
+#define STE_SIMD_SOCK "/dev/socket/catd_a"
+#define STE_CLIENT_TAG 0x0000
+
+#ifdef STE_SAP_DEBUG
+#define DBG_VERBOSE(fmt, arg...) DBG(fmt, arg)
+#else
+#define DBG_VERBOSE(fmt...)
+#endif
+
+#define sap_error(fmt, arg...) do { \
+ error("STE U8500 SAP: " fmt, ## arg); \
+ } while (0)
+
+#define sap_info(fmt, arg...) do { \
+ info("STE U8500 SAP: " fmt, ## arg); \
+ } while (0)
+
+struct ste_message {
+ uint16_t len;
+ uint16_t id;
+ uint32_t client_tag;
+ uint8_t payload[0];
+} __attribute__((packed));
+
+#define STE_MSG_PAYLOAD_SIZE(msg) (msg->len - sizeof(*msg) + sizeof(msg->len))
+
+enum ste_protocol {
+ STE_START_SAP_REQ = 0x2D01,
+ STE_START_SAP_RSP = 0x2E01,
+ STE_END_SAP_REQ = 0x2D02,
+ STE_END_SAP_RSP = 0x2E02,
+ STE_POWER_OFF_REQ = 0x2D03,
+ STE_POWER_OFF_RSP = 0x2E03,
+ STE_POWER_ON_REQ = 0x2D04,
+ STE_POWER_ON_RSP = 0x2E04,
+ STE_RESET_REQ = 0x2D05,
+ STE_RESET_RSP = 0x2E05,
+ STE_SEND_APDU_REQ = 0x2D06,
+ STE_SEND_APDU_RSP = 0x2E06,
+ STE_GET_ATR_REQ = 0x2D07,
+ STE_GET_ATR_RSP = 0x2E07,
+ STE_GET_STATUS_REQ = 0x2D08,
+ STE_GET_STATUS_RSP = 0x2E08,
+ STE_STATUS_IND = 0x2F02,
+ STE_SIM_READY_IND = 0x2F03,
+};
+
+enum ste_msg {
+ STE_SEND_APDU_MSG,
+ STE_GET_ATR_MSG,
+ STE_POWER_OFF_MSG,
+ STE_POWER_ON_MSG,
+ STE_RESET_MSG,
+ STE_GET_STATUS_MSG,
+ STE_MSG_MAX,
+};
+
+enum ste_status {
+ STE_STATUS_OK = 0x00000000,
+ STE_STATUS_FAILURE = 0x00000001,
+ STE_STATUS_BUSY_CALL = 0x00000002,
+};
+
+enum ste_card_status {
+ STE_CARD_STATUS_UNKNOWN = 0x00,
+ STE_CARD_STATUS_ACTIVE = 0x01,
+ STE_CARD_STATUS_NOT_ACTIVE = 0x02,
+ STE_CARD_STATUS_MISSING = 0x03,
+ STE_CARD_STATUS_INVALID = 0x04,
+ STE_CARD_STATUS_DISCONNECTED = 0x05,
+};
+
+/* Card reader status bits as described in GSM 11.14, Section 12.33
+ * Bits 0-2 are for card reader identity and always zeros. */
+#define ICC_READER_REMOVABLE (1 << 3)
+#define ICC_READER_PRESENT (1 << 4)
+#define ICC_READER_ID1 (1 << 5)
+#define ICC_READER_CARD_PRESENT (1 << 6)
+#define ICC_READER_CARD_POWERED (1 << 7)
+
+enum ste_state {
+ STE_DISABLED, /* Reader not present or removed */
+ STE_POWERED_OFF, /* Card in the reader but powered off */
+ STE_NO_CARD, /* No card in the reader */
+ STE_ENABLED, /* Card in the reader and powered on */
+ STE_SIM_BUSY, /* Modem is busy with ongoing call.*/
+ STE_STATE_MAX
+};
+
+struct ste_u8500 {
+ GIOChannel *io;
+ enum ste_state state;
+ void *sap_data;
+};
+
+typedef int(*recv_state_change_cb)(void *sap, uint8_t result);
+typedef int(*recv_pdu_cb)(void *sap, uint8_t result, uint8_t *data,
+ uint16_t len);
+
+static struct ste_u8500 u8500;
+
+static const uint8_t sim2sap_result[STE_MSG_MAX][STE_STATE_MAX] = {
+ /* SAP results for SEND APDU message */
+ {
+ SAP_RESULT_ERROR_NOT_ACCESSIBLE, /* STE_DISABLED */
+ SAP_RESULT_ERROR_POWERED_OFF, /* STE_POWERED_OFF */
+ SAP_RESULT_ERROR_CARD_REMOVED, /* STE_NO_CARD */
+ SAP_RESULT_ERROR_NO_REASON /* STE_ENABLED */
+ },
+
+ /* SAP results for GET_ATR message */
+ {
+ SAP_RESULT_ERROR_NO_REASON,
+ SAP_RESULT_ERROR_POWERED_OFF,
+ SAP_RESULT_ERROR_CARD_REMOVED,
+ SAP_RESULT_ERROR_NO_REASON
+ },
+
+ /* SAP results POWER OFF message */
+ {
+ SAP_RESULT_ERROR_NO_REASON,
+ SAP_RESULT_ERROR_POWERED_OFF,
+ SAP_RESULT_ERROR_CARD_REMOVED,
+ SAP_RESULT_ERROR_NO_REASON
+ },
+
+ /* SAP results POWER ON message */
+ {
+ SAP_RESULT_ERROR_NO_REASON,
+ SAP_RESULT_ERROR_NOT_ACCESSIBLE,
+ SAP_RESULT_ERROR_CARD_REMOVED,
+ SAP_RESULT_ERROR_POWERED_ON
+ },
+
+ /* SAP results SIM RESET message */
+ {
+ SAP_RESULT_ERROR_NO_REASON,
+ SAP_RESULT_ERROR_POWERED_OFF,
+ SAP_RESULT_ERROR_CARD_REMOVED,
+ SAP_RESULT_ERROR_NOT_ACCESSIBLE
+ },
+
+ /* SAP results GET STATUS message */
+ {
+ SAP_RESULT_ERROR_NO_REASON,
+ SAP_RESULT_ERROR_NO_REASON,
+ SAP_RESULT_ERROR_NO_REASON,
+ SAP_RESULT_ERROR_NO_REASON
+ }
+};
+
+static uint8_t get_sap_result(enum ste_msg msg, uint32_t status)
+{
+ if (!u8500.io)
+ return SAP_RESULT_ERROR_NO_REASON;
+
+ switch (status) {
+ case STE_STATUS_OK:
+ return SAP_RESULT_OK;
+
+ case STE_STATUS_FAILURE:
+ return sim2sap_result[msg][u8500.state];
+
+ default:
+ DBG("Can't convert a result (status %u)", status);
+ return SAP_RESULT_ERROR_NO_REASON;
+ }
+}
+
+static int get_sap_reader_status(uint32_t card_status, uint8_t *icc_status)
+{
+ /* Card reader is present, not removable and not ID-1 size. */
+ *icc_status = ICC_READER_PRESENT;
+
+ switch (card_status) {
+ case STE_CARD_STATUS_ACTIVE:
+ *icc_status |= ICC_READER_CARD_POWERED;
+
+ case STE_CARD_STATUS_NOT_ACTIVE:
+ case STE_CARD_STATUS_INVALID:
+ *icc_status |= ICC_READER_CARD_PRESENT;
+
+ case STE_CARD_STATUS_MISSING:
+ case STE_CARD_STATUS_DISCONNECTED:
+ return 0;
+
+ default:
+ DBG("Can't convert reader status %u", card_status);
+
+ case STE_CARD_STATUS_UNKNOWN:
+ return -1;
+ }
+}
+
+static uint8_t get_sap_status_change(uint32_t card_status)
+{
+ if (!u8500.io)
+ return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
+
+ switch (card_status) {
+ case STE_CARD_STATUS_UNKNOWN:
+ return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
+
+ case STE_CARD_STATUS_ACTIVE:
+ u8500.state = STE_ENABLED;
+ return SAP_STATUS_CHANGE_CARD_RESET;
+
+ case STE_CARD_STATUS_NOT_ACTIVE:
+ u8500.state = STE_DISABLED;
+ return SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE;
+
+ case STE_CARD_STATUS_MISSING:
+ u8500.state = STE_DISABLED;
+ return SAP_STATUS_CHANGE_CARD_REMOVED;
+
+ case STE_CARD_STATUS_INVALID:
+ u8500.state = STE_DISABLED;
+ return SAP_STATUS_CHANGE_CARD_NOT_ACCESSIBLE;
+
+ default:
+ DBG("Can't convert status change %u", card_status);
+ return SAP_STATUS_CHANGE_UNKNOWN_ERROR;
+ }
+}
+
+static int send_message(GIOChannel *io, void *buf, size_t size)
+{
+ gsize written;
+
+ DBG_VERBOSE("io %p, size %zu", io, size);
+
+ if (g_io_channel_write_chars(io, buf, size, &written, NULL) !=
+ G_IO_STATUS_NORMAL)
+ return -EIO;
+
+ return written;
+}
+
+static int send_request(GIOChannel *io, uint16_t id,
+ struct sap_parameter *param)
+{
+ int ret;
+ struct ste_message *msg;
+ size_t size = sizeof(*msg);
+
+ DBG_VERBOSE("io %p", io);
+
+ if (param)
+ size += param->len;
+
+ msg = g_try_malloc0(size);
+ if (!msg) {
+ sap_error("sending request failed: %s", strerror(ENOMEM));
+ return -ENOMEM;
+ }
+
+ msg->len = size - sizeof(msg->len);
+ msg->id = id;
+ msg->client_tag = STE_CLIENT_TAG;
+
+ if (param)
+ memcpy(msg->payload, param->val, param->len);
+
+ ret = send_message(io, msg, size);
+ if (ret < 0) {
+ sap_error("sending request failed: %s", strerror(-ret));
+ } else if (ret != (int) size) {
+ sap_error("sending request failed: %d out of %zu bytes sent",
+ ret, size);
+ ret = -EIO;
+ }
+
+ g_free(msg);
+
+ return ret;
+}
+
+static void recv_status(uint32_t status)
+{
+ sap_status_ind(u8500.sap_data, get_sap_status_change(status));
+}
+
+static void recv_card_status(uint32_t status, uint8_t *param)
+{
+ uint32_t *card_status;
+ uint8_t result;
+ uint8_t iccrs;
+
+ if (status != STE_STATUS_OK)
+ return;
+
+ card_status = (uint32_t *)param;
+
+ if (get_sap_reader_status(*card_status, &iccrs) < 0)
+ result = SAP_RESULT_ERROR_NO_REASON;
+ else
+ result = get_sap_result(STE_GET_STATUS_MSG, status);
+
+ sap_transfer_card_reader_status_rsp(u8500.sap_data, result, iccrs);
+}
+
+static void recv_state_change(uint32_t ste_msg, uint32_t status,
+ uint32_t new_state, recv_state_change_cb callback)
+{
+ if (status != STE_STATUS_OK)
+ return;
+
+ u8500.state = new_state;
+
+ if (callback)
+ callback(u8500.sap_data, get_sap_result(ste_msg, status));
+}
+
+static void recv_pdu(uint32_t ste_msg, struct ste_message *msg, uint32_t status,
+ uint8_t *param, recv_pdu_cb callback)
+{
+ uint8_t *data = NULL;
+ uint8_t result;
+ int size = 0;
+
+ if (status == STE_STATUS_OK) {
+ data = param;
+ size = STE_MSG_PAYLOAD_SIZE(msg) - sizeof(status);
+ }
+
+ result = get_sap_result(ste_msg, status);
+
+ if (callback)
+ callback(u8500.sap_data, result, data, size);
+}
+
+static void simd_close(void)
+{
+ DBG("io %p", u8500.io);
+
+ if (u8500.io) {
+ g_io_channel_shutdown(u8500.io, TRUE, NULL);
+ g_io_channel_unref(u8500.io);
+ }
+
+ u8500.state = STE_DISABLED;
+ u8500.io = NULL;
+ u8500.sap_data = NULL;
+}
+
+static void recv_sim_ready()
+{
+ sap_info("sim is ready. Try to connect again");
+
+ if (send_request(u8500.io, STE_START_SAP_REQ, NULL) < 0) {
+ sap_connect_rsp(u8500.sap_data, SAP_STATUS_CONNECTION_FAILED,
+ SAP_BUF_SIZE);
+ simd_close();
+ }
+}
+
+static void recv_connect_rsp(uint32_t status)
+{
+ switch (status) {
+ case STE_STATUS_OK:
+ if (u8500.state != STE_SIM_BUSY)
+ sap_connect_rsp(u8500.sap_data,
+ SAP_STATUS_OK, 0);
+ break;
+ case STE_STATUS_BUSY_CALL:
+ if (u8500.state != STE_SIM_BUSY) {
+ sap_connect_rsp(u8500.sap_data,
+ SAP_STATUS_OK_ONGOING_CALL,
+ SAP_BUF_SIZE);
+
+ u8500.state = STE_SIM_BUSY;
+ }
+ break;
+ default:
+ sap_connect_rsp(u8500.sap_data,
+ SAP_STATUS_CONNECTION_FAILED, 0);
+ simd_close();
+ break;
+ }
+}
+
+static void recv_response(struct ste_message *msg)
+{
+ uint32_t status;
+ uint8_t *param;
+
+ DBG_VERBOSE("msg_id 0x%x", msg->id);
+
+ if (msg->id == STE_END_SAP_RSP) {
+ sap_disconnect_rsp(u8500.sap_data);
+ simd_close();
+ return;
+ }
+
+ param = msg->payload;
+ status = *(uint32_t *)param;
+ param += sizeof(status);
+
+ DBG_VERBOSE("status 0x%x", status);
+
+ switch (msg->id) {
+ case STE_START_SAP_RSP:
+ recv_connect_rsp(status);
+ break;
+ case STE_SEND_APDU_RSP:
+ recv_pdu(STE_SEND_APDU_MSG, msg, status, param,
+ sap_transfer_apdu_rsp);
+ break;
+
+ case STE_GET_ATR_RSP:
+ recv_pdu(STE_GET_ATR_MSG, msg, status, param,
+ sap_transfer_atr_rsp);
+ break;
+
+ case STE_POWER_OFF_RSP:
+ recv_state_change(STE_POWER_OFF_MSG, status, STE_POWERED_OFF,
+ sap_power_sim_off_rsp);
+ break;
+
+ case STE_POWER_ON_RSP:
+ recv_state_change(STE_POWER_ON_MSG, status, STE_ENABLED,
+ sap_power_sim_on_rsp);
+ break;
+
+ case STE_RESET_RSP:
+ recv_state_change(STE_RESET_MSG, status, STE_ENABLED,
+ sap_reset_sim_rsp);
+ break;
+
+ case STE_GET_STATUS_RSP:
+ recv_card_status(status, param);
+ break;
+
+ case STE_STATUS_IND:
+ recv_status(status);
+ break;
+
+ case STE_SIM_READY_IND:
+ recv_sim_ready();
+ break;
+
+ default:
+ sap_error("unsupported message received (id 0x%x)", msg->id);
+ }
+}
+
+static int recv_message(void *buf, size_t size)
+{
+ uint8_t *iter = buf;
+ struct ste_message *msg = buf;
+
+ do {
+ DBG_VERBOSE("size %zu msg->len %u.", size, msg->len);
+
+ if (size < sizeof(*msg)) {
+ sap_error("invalid message received (%zu bytes)", size);
+ return -EBADMSG;
+ }
+
+ /* Message must be complete. */
+ if (size < (msg->len + sizeof(msg->len))) {
+ sap_error("incomplete message received (%zu bytes)",
+ size);
+ return -EBADMSG;
+ }
+
+ recv_response(msg);
+
+ /* Reduce total buffer size by just handled frame size. */
+ size -= msg->len + sizeof(msg->len);
+
+ /* Move msg pointer to the next message if any. */
+ iter += msg->len + sizeof(msg->len);
+ msg = (struct ste_message *)iter;
+ } while (size > 0);
+
+ return 0;
+}
+
+static gboolean simd_data_cb(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+ char buf[SAP_BUF_SIZE];
+ gsize bytes_read;
+ GIOStatus gstatus;
+
+ if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) {
+ DBG("Error condition on sim socket (0x%x)", cond);
+ return FALSE;
+ }
+
+ gstatus = g_io_channel_read_chars(io, buf, sizeof(buf), &bytes_read,
+ NULL);
+ if (gstatus != G_IO_STATUS_NORMAL) {
+ sap_error("error while reading from channel (%d)", gstatus);
+ return TRUE;
+ }
+
+ if (recv_message(buf, bytes_read) < 0)
+ sap_error("error while parsing STE Sim message");
+
+ return TRUE;
+}
+
+static void simd_watch(int sock, void *sap_data)
+{
+ GIOChannel *io;
+
+ DBG("sock %d, sap_data %p ", sock, sap_data);
+
+ io = g_io_channel_unix_new(sock);
+
+ g_io_channel_set_close_on_unref(io, TRUE);
+ g_io_channel_set_encoding(io, NULL, NULL);
+ g_io_channel_set_buffered(io, FALSE);
+
+ u8500.io = io;
+ u8500.sap_data = sap_data;
+ u8500.state = STE_DISABLED;
+
+ g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ simd_data_cb, NULL, NULL);
+}
+
+static int simd_connect(void *sap_data)
+{
+ struct sockaddr_un addr;
+ int sock;
+ int err;
+
+ /* Already connected to simd */
+ if (u8500.io)
+ return -EALREADY;
+
+ sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ err = -errno;
+ sap_error("creating socket failed: %s", strerror(-err));
+ return err;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, STE_SIMD_SOCK, sizeof(STE_SIMD_SOCK) - 1);
+
+ if (connect(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ err = -errno;
+ sap_error("connect to the socket failed: %s", strerror(-err));
+ goto failed;
+ }
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) > 0) {
+ err = -errno;
+ sap_error("setting up socket failed: %s", strerror(-err));
+ goto failed;
+ }
+
+ simd_watch(sock, sap_data);
+
+ return 0;
+
+failed:
+ close(sock);
+ return err;
+}
+
+void sap_connect_req(void *sap_device, uint16_t maxmsgsize)
+{
+ DBG("sap_device %p maxmsgsize %u", sap_device, maxmsgsize);
+
+ sap_info("connect request");
+
+ if (simd_connect(sap_device) < 0) {
+ sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED, 0);
+ return;
+ }
+
+ if (send_request(u8500.io, STE_START_SAP_REQ, NULL) < 0) {
+ sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED,
+ SAP_BUF_SIZE);
+ simd_close();
+ }
+}
+
+void sap_disconnect_req(void *sap_device, uint8_t linkloss)
+{
+ DBG("sap_device %p linkloss %u", sap_device, linkloss);
+
+ sap_info("disconnect request %s", linkloss ? "by link loss" : "");
+
+ if (u8500.state == STE_DISABLED) {
+ sap_disconnect_rsp(sap_device);
+ simd_close();
+ return;
+ }
+
+ if (linkloss) {
+ simd_close();
+ return;
+ }
+
+ if (send_request(u8500.io, STE_END_SAP_REQ, NULL) < 0) {
+ sap_disconnect_rsp(sap_device);
+ return;
+ }
+}
+
+void sap_transfer_apdu_req(void *sap_device, struct sap_parameter *param)
+{
+ uint8_t result;
+
+ DBG_VERBOSE("sap_device %p param %p", sap_device, param);
+
+ if (u8500.state != STE_ENABLED) {
+ result = get_sap_result(STE_SEND_APDU_MSG, STE_STATUS_FAILURE);
+ sap_transfer_apdu_rsp(sap_device, result, NULL, 0);
+ return;
+ }
+
+ if (send_request(u8500.io, STE_SEND_APDU_REQ, param) < 0)
+ sap_transfer_apdu_rsp(sap_device, SAP_RESULT_ERROR_NO_DATA,
+ NULL, 0);
+}
+
+void sap_transfer_atr_req(void *sap_device)
+{
+ uint8_t result;
+
+ DBG("sap_device %p", sap_device);
+
+ if (u8500.state != STE_ENABLED) {
+ result = get_sap_result(STE_GET_ATR_MSG, STE_STATUS_FAILURE);
+ sap_transfer_atr_rsp(sap_device, result, NULL, 0);
+ return;
+ }
+
+ if (send_request(u8500.io, STE_GET_ATR_REQ, NULL) < 0)
+ sap_transfer_atr_rsp(sap_device, SAP_RESULT_ERROR_NO_DATA, NULL,
+ 0);
+}
+
+void sap_power_sim_off_req(void *sap_device)
+{
+ uint8_t result;
+
+ DBG("sap_device %p", sap_device);
+
+ if (u8500.state != STE_ENABLED) {
+ result = get_sap_result(STE_POWER_OFF_MSG, STE_STATUS_FAILURE);
+ sap_power_sim_off_rsp(sap_device, result);
+ return;
+ }
+
+ if (send_request(u8500.io, STE_POWER_OFF_REQ, NULL) < 0)
+ sap_power_sim_off_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+}
+
+void sap_power_sim_on_req(void *sap_device)
+{
+ uint8_t result;
+
+ DBG("sap_device %p", sap_device);
+
+ if (u8500.state != STE_POWERED_OFF) {
+ result = get_sap_result(STE_POWER_ON_MSG, STE_STATUS_FAILURE);
+ sap_power_sim_on_rsp(sap_device, result);
+ return;
+ }
+
+ if (send_request(u8500.io, STE_POWER_ON_REQ, NULL) < 0)
+ sap_power_sim_on_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+}
+
+void sap_reset_sim_req(void *sap_device)
+{
+ uint8_t result;
+
+ DBG("sap_device %p", sap_device);
+
+ if (u8500.state != STE_ENABLED) {
+ result = get_sap_result(STE_RESET_MSG, STE_STATUS_FAILURE);
+ sap_reset_sim_rsp(sap_device, result);
+ return;
+ }
+
+ if (send_request(u8500.io, STE_RESET_REQ, NULL) < 0)
+ sap_reset_sim_rsp(sap_device, SAP_RESULT_ERROR_NO_REASON);
+}
+
+void sap_transfer_card_reader_status_req(void *sap_device)
+{
+ uint8_t result;
+
+ DBG("sap_device %p", sap_device);
+
+ if (u8500.state == STE_DISABLED) {
+ result = get_sap_result(STE_GET_STATUS_MSG, STE_STATUS_FAILURE);
+ sap_transfer_card_reader_status_rsp(sap_device, result, 0);
+ return;
+ }
+
+ if (send_request(u8500.io, STE_GET_STATUS_REQ, NULL) < 0)
+ sap_transfer_card_reader_status_rsp(sap_device,
+ SAP_RESULT_ERROR_NO_DATA, 0);
+}
+
+void sap_set_transport_protocol_req(void *sap_device,
+ struct sap_parameter *param)
+{
+ DBG("sap_device %p", sap_device);
+
+ sap_transport_protocol_rsp(sap_device, SAP_RESULT_NOT_SUPPORTED);
+}
+
+int sap_init(void)
+{
+ u8500.state = STE_DISABLED;
+ info("STE U8500 SAP driver initialized");
+ return 0;
+}
+
+void sap_exit(void)
+{
+}
struct sap_parameter param[0];
} __attribute__((packed));
-enum {
- ICC_READER_UNSPECIFIED_ERROR, /* No further information available */
- ICC_READER_NOT_PRESENT, /* Card Reader removed or not present */
- ICC_READER_BUSY, /* Card Reader in use */
- ICC_READER_CARD_POWERED_ON, /* Card in reader and is powered on */
- ICC_READER_DEACTIVATED, /* Card Reader deactivated */
- ICC_READER_CARD_POWERED_OFF, /* Card in reader, but powered off */
- ICC_READER_NO_CARD, /* No card in reader */
- ICC_READER_LAST
-};
-
#define SAP_BUF_SIZE 512
#define SAP_MSG_HEADER_SIZE 4
#define SAP_PARAM_ID_DISCONNECT_IND_LEN 0x01
#define SAP_PARAM_ID_CARD_READER_STATUS_LEN 0x01
#define SAP_PARAM_ID_STATUS_CHANGE_LEN 0x01
-#define SAP_PARAM_ID_TRANSPORT_PROTOCOL_LEN 0x01
+#define SAP_PARAM_ID_TRANSPORT_PROTO_LEN 0x01
/* Transport Protocol - SAP v1.1 section 5.2.9 */
enum sap_transport_protocol {
/* Event indication. Implemented by server.c*/
int sap_status_ind(void *sap_device, uint8_t status_change);
+int sap_disconnect_ind(void *sap_device, uint8_t disc_type);
#define SAP_SERVER_INTERFACE "org.bluez.SimAccess"
#define SAP_UUID "0000112D-0000-1000-8000-00805F9B34FB"
#define SAP_SERVER_CHANNEL 8
-#define SAP_BUF_SIZE 512
+
+#define PADDING4(x) ((4 - (x & 0x03)) & 0x03)
+#define PARAMETER_SIZE(x) (sizeof(struct sap_parameter) + x + PADDING4(x))
+
+#define SAP_NO_REQ 0xFF
+
+#define SAP_TIMER_GRACEFUL_DISCONNECT 30
+#define SAP_TIMER_NO_ACTIVITY 30
enum {
SAP_STATE_DISCONNECTED,
+ SAP_STATE_CONNECT_IN_PROGRESS,
+ SAP_STATE_CONNECT_MODEM_BUSY,
SAP_STATE_CONNECTED,
+ SAP_STATE_GRACEFUL_DISCONNECT,
+ SAP_STATE_IMMEDIATE_DISCONNECT,
+ SAP_STATE_CLIENT_DISCONNECT
};
struct sap_connection {
GIOChannel *io;
uint32_t state;
+ uint8_t processing_req;
+ guint timer_id;
};
struct sap_server {
static DBusConnection *connection;
static struct sap_server *server;
+static void start_guard_timer(struct sap_connection *conn, guint interval);
+static void stop_guard_timer(struct sap_connection *conn);
+static gboolean guard_timeout(gpointer data);
+
+static size_t add_result_parameter(uint8_t result,
+ struct sap_parameter *param)
+{
+ param->id = SAP_PARAM_ID_RESULT_CODE;
+ param->len = htons(SAP_PARAM_ID_RESULT_CODE_LEN);
+ *param->val = result;
+
+ return PARAMETER_SIZE(SAP_PARAM_ID_RESULT_CODE_LEN);
+}
+
+static int is_power_sim_off_req_allowed(uint8_t processing_req)
+{
+ switch (processing_req) {
+ case SAP_NO_REQ:
+ case SAP_TRANSFER_APDU_REQ:
+ case SAP_TRANSFER_ATR_REQ:
+ case SAP_POWER_SIM_ON_REQ:
+ case SAP_RESET_SIM_REQ:
+ case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int is_reset_sim_req_allowed(uint8_t processing_req)
+{
+ switch (processing_req) {
+ case SAP_NO_REQ:
+ case SAP_TRANSFER_APDU_REQ:
+ case SAP_TRANSFER_ATR_REQ:
+ case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int check_msg(struct sap_message *msg)
+{
+ if (!msg)
+ return -EINVAL;
+
+ switch (msg->id) {
+ case SAP_CONNECT_REQ:
+ if (msg->nparam != 0x01)
+ return -EBADMSG;
+
+ if (msg->param->id != SAP_PARAM_ID_MAX_MSG_SIZE)
+ return -EBADMSG;
+
+ if (ntohs(msg->param->len) != SAP_PARAM_ID_MAX_MSG_SIZE_LEN)
+ return -EBADMSG;
+
+ break;
+
+ case SAP_TRANSFER_APDU_REQ:
+ if (msg->nparam != 0x01)
+ return -EBADMSG;
+
+ if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU)
+ if ( msg->param->id != SAP_PARAM_ID_COMMAND_APDU7816)
+ return -EBADMSG;
+
+ if (msg->param->len == 0x00)
+ return -EBADMSG;
+
+ break;
+
+ case SAP_SET_TRANSPORT_PROTOCOL_REQ:
+ if (msg->nparam != 0x01)
+ return -EBADMSG;
+
+ if (msg->param->id != SAP_PARAM_ID_TRANSPORT_PROTOCOL)
+ return -EBADMSG;
+
+ if (ntohs(msg->param->len) != SAP_PARAM_ID_TRANSPORT_PROTO_LEN)
+ return -EBADMSG;
+
+ if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T0)
+ if (*msg->param->val != SAP_TRANSPORT_PROTOCOL_T1)
+ return -EBADMSG;
+
+ break;
+
+ case SAP_DISCONNECT_REQ:
+ case SAP_TRANSFER_ATR_REQ:
+ case SAP_POWER_SIM_OFF_REQ:
+ case SAP_POWER_SIM_ON_REQ:
+ case SAP_RESET_SIM_REQ:
+ case SAP_TRANSFER_CARD_READER_STATUS_REQ:
+ if (msg->nparam != 0x00)
+ return -EBADMSG;
+
+ break;
+ }
+
+ return 0;
+}
+
static sdp_record_t *create_sap_record(uint8_t channel)
{
sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
if (!record)
return NULL;
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
root = sdp_list_append(NULL, &root_uuid);
sdp_set_browse_groups(record, root);
sdp_list_free(root, NULL);
return record;
}
+static int send_message(struct sap_connection *conn, void *buf, size_t size)
+{
+ size_t written = 0;
+ GError *gerr = NULL;
+ GIOStatus gstatus;
+
+ if (!conn || !buf)
+ return -EINVAL;
+
+ DBG("conn %p, size %zu", conn, size);
+
+ gstatus = g_io_channel_write_chars(conn->io, buf, size, &written,
+ &gerr);
+ if (gstatus != G_IO_STATUS_NORMAL) {
+ if (gerr)
+ g_error_free(gerr);
+
+ error("write error (0x%02x).", gstatus);
+ return -EIO;
+ }
+
+ if (written != size) {
+ error("written %zu bytes out of %zu", written, size);
+ return -EIO;
+ }
+
+ return written;
+}
+
+static int disconnect_ind(void *sap_device, uint8_t disc_type)
+{
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("data %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
+
+ if (conn->state != SAP_STATE_GRACEFUL_DISCONNECT &&
+ conn->state != SAP_STATE_IMMEDIATE_DISCONNECT) {
+ error("Processing error (state %d pr 0x%02x)", conn->state,
+ conn->processing_req);
+ return -EPERM;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_DISCONNECT_IND;
+ msg->nparam = 0x01;
+
+ /* Add disconnection type param. */
+ param->id = SAP_PARAM_ID_DISCONNECT_IND;
+ param->len = htons(SAP_PARAM_ID_DISCONNECT_IND_LEN);
+ *param->val = disc_type;
+ size += PARAMETER_SIZE(SAP_PARAM_ID_DISCONNECT_IND_LEN);
+
+ return send_message(sap_device, buf, size);
+}
+
static void connect_req(struct sap_connection *conn,
- struct sap_parameter *param)
+ struct sap_parameter *param)
{
- DBG("SAP_CONNECT_REQUEST");
+ uint16_t maxmsgsize, *val;
+
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (!param)
+ goto error_rsp;
+
+ if (conn->state != SAP_STATE_DISCONNECTED)
+ goto error_rsp;
+
+ stop_guard_timer(conn);
+
+ val = (uint16_t *) ¶m->val;
+ maxmsgsize = ntohs(*val);
+
+ DBG("Connect MaxMsgSize: 0x%04x", maxmsgsize);
+
+ conn->state = SAP_STATE_CONNECT_IN_PROGRESS;
+
+ if (maxmsgsize <= SAP_BUF_SIZE) {
+ conn->processing_req = SAP_CONNECT_REQ;
+ sap_connect_req(conn, maxmsgsize);
+ } else {
+ sap_connect_rsp(conn, SAP_STATUS_MAX_MSG_SIZE_NOT_SUPPORTED,
+ SAP_BUF_SIZE);
+ }
+
+ return;
+
+error_rsp:
+ sap_error_rsp(conn);
}
static int disconnect_req(struct sap_connection *conn, uint8_t disc_type)
{
- DBG("SAP_DISCONNECT_REQUEST");
- return 0;
+ DBG("conn %p state %d disc_type 0x%02x", conn, conn->state, disc_type);
+
+ switch (disc_type) {
+ case SAP_DISCONNECTION_TYPE_GRACEFUL:
+ if (conn->state == SAP_STATE_DISCONNECTED ||
+ conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+ conn->state == SAP_STATE_CONNECT_MODEM_BUSY)
+ return -EPERM;
+
+ if (conn->state == SAP_STATE_CONNECTED) {
+ conn->state = SAP_STATE_GRACEFUL_DISCONNECT;
+ conn->processing_req = SAP_NO_REQ;
+
+ disconnect_ind(conn, disc_type);
+ /* Timer will disconnect if client won't do.*/
+ start_guard_timer(conn, SAP_TIMER_GRACEFUL_DISCONNECT);
+ }
+
+ return 0;
+
+ case SAP_DISCONNECTION_TYPE_IMMEDIATE:
+ if (conn->state == SAP_STATE_DISCONNECTED ||
+ conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+ conn->state == SAP_STATE_CONNECT_MODEM_BUSY)
+ return -EPERM;
+
+ if (conn->state == SAP_STATE_CONNECTED ||
+ conn->state == SAP_STATE_GRACEFUL_DISCONNECT) {
+ conn->state = SAP_STATE_IMMEDIATE_DISCONNECT;
+ conn->processing_req = SAP_NO_REQ;
+
+ stop_guard_timer(conn);
+ disconnect_ind(conn, disc_type);
+ sap_disconnect_req(conn, 0);
+ }
+
+ return 0;
+
+ case SAP_DISCONNECTION_TYPE_CLIENT:
+ if (conn->state != SAP_STATE_CONNECTED &&
+ conn->state != SAP_STATE_GRACEFUL_DISCONNECT) {
+ sap_error_rsp(conn);
+ return -EPERM;
+ }
+
+ conn->state = SAP_STATE_CLIENT_DISCONNECT;
+ conn->processing_req = SAP_NO_REQ;
+
+ stop_guard_timer(conn);
+ sap_disconnect_req(conn, 0);
+
+ return 0;
+
+ default:
+ error("Unknown disconnection type (0x%02x).", disc_type);
+ return -EINVAL;
+ }
}
static void transfer_apdu_req(struct sap_connection *conn,
struct sap_parameter *param)
{
- DBG("SAP_APDU_REQUEST");
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (!param)
+ goto error_rsp;
+
+ param->len = ntohs(param->len);
+
+ if (conn->state != SAP_STATE_CONNECTED &&
+ conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+ goto error_rsp;
+
+ if (conn->processing_req != SAP_NO_REQ)
+ goto error_rsp;
+
+ conn->processing_req = SAP_TRANSFER_APDU_REQ;
+ sap_transfer_apdu_req(conn, param);
+
+ return;
+
+error_rsp:
+ sap_error_rsp(conn);
}
static void transfer_atr_req(struct sap_connection *conn)
{
- DBG("SAP_ATR_REQUEST");
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (conn->processing_req != SAP_NO_REQ)
+ goto error_rsp;
+
+ conn->processing_req = SAP_TRANSFER_ATR_REQ;
+ sap_transfer_atr_req(conn);
+
+ return;
+
+error_rsp:
+ sap_error_rsp(conn);
}
static void power_sim_off_req(struct sap_connection *conn)
{
- DBG("SAP_SIM_OFF_REQUEST");
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (!is_power_sim_off_req_allowed(conn->processing_req))
+ goto error_rsp;
+
+ conn->processing_req = SAP_POWER_SIM_OFF_REQ;
+ sap_power_sim_off_req(conn);
+
+ return;
+
+error_rsp:
+ sap_error_rsp(conn);
}
static void power_sim_on_req(struct sap_connection *conn)
{
- DBG("SAP_SIM_ON_REQUEST");
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (conn->processing_req != SAP_NO_REQ)
+ goto error_rsp;
+
+ conn->processing_req = SAP_POWER_SIM_ON_REQ;
+ sap_power_sim_on_req(conn);
+
+ return;
+
+error_rsp:
+ sap_error_rsp(conn);
}
static void reset_sim_req(struct sap_connection *conn)
{
- DBG("SAP_RESET_SIM_REQUEST");
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (!is_reset_sim_req_allowed(conn->processing_req))
+ goto error_rsp;
+
+ conn->processing_req = SAP_RESET_SIM_REQ;
+ sap_reset_sim_req(conn);
+
+ return;
+
+error_rsp:
+ sap_error_rsp(conn);
}
static void transfer_card_reader_status_req(struct sap_connection *conn)
{
- DBG("SAP_TRANSFER_CARD_READER_STATUS_REQUEST");
+ DBG("conn %p state %d", conn, conn->state);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (conn->processing_req != SAP_NO_REQ)
+ goto error_rsp;
+
+ conn->processing_req = SAP_TRANSFER_CARD_READER_STATUS_REQ;
+ sap_transfer_card_reader_status_req(conn);
+
+ return;
+
+error_rsp:
+ sap_error_rsp(conn);
}
static void set_transport_protocol_req(struct sap_connection *conn,
struct sap_parameter *param)
{
- DBG("SAP_SET_TRANSPORT_PROTOCOL_REQUEST");
+ if (!param)
+ goto error_rsp;
+
+ DBG("conn %p state %d param %p", conn, conn->state, param);
+
+ if (conn->state != SAP_STATE_CONNECTED)
+ goto error_rsp;
+
+ if (conn->processing_req != SAP_NO_REQ)
+ goto error_rsp;
+
+ conn->processing_req = SAP_SET_TRANSPORT_PROTOCOL_REQ;
+ sap_set_transport_protocol_req(conn, param);
+
+ return;
+
+error_rsp:
+ sap_error_rsp(conn);
+}
+
+static void start_guard_timer(struct sap_connection *conn, guint interval)
+{
+ if (!conn)
+ return;
+
+ if (!conn->timer_id)
+ conn->timer_id = g_timeout_add_seconds(interval, guard_timeout,
+ conn);
+ else
+ error("Timer is already active.");
+}
+
+static void stop_guard_timer(struct sap_connection *conn)
+{
+ if (conn && conn->timer_id) {
+ g_source_remove(conn->timer_id);
+ conn->timer_id = 0;
+ }
+}
+
+static gboolean guard_timeout(gpointer data)
+{
+ struct sap_connection *conn = data;
+
+ if (!conn)
+ return FALSE;
+
+ DBG("conn %p state %d pr 0x%02x", conn, conn->state,
+ conn->processing_req);
+
+ conn->timer_id = 0;
+
+ switch (conn->state) {
+ case SAP_STATE_DISCONNECTED:
+ /* Client opened RFCOMM channel but didn't send CONNECT_REQ,
+ * in fixed time or client disconnected SAP connection but
+ * didn't closed RFCOMM channel in fixed time.*/
+ if (conn->io) {
+ g_io_channel_shutdown(conn->io, TRUE, NULL);
+ g_io_channel_unref(conn->io);
+ }
+ break;
+
+ case SAP_STATE_GRACEFUL_DISCONNECT:
+ /* Client didn't disconnect SAP connection in fixed time,
+ * so close SAP connection immediately. */
+ disconnect_req(conn, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+ break;
+
+ default:
+ error("Unexpected state (%d).", conn->state);
+ break;
+ }
+
+ return FALSE;
+}
+
+static void sap_set_connected(struct sap_connection *conn)
+{
+ gboolean connected = TRUE;
+
+ emit_property_changed(connection, server->path,
+ SAP_SERVER_INTERFACE,
+ "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+ conn->state = SAP_STATE_CONNECTED;
}
int sap_connect_rsp(void *sap_device, uint8_t status, uint16_t maxmsgsize)
{
- return 0;
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x status 0x%02x", conn->state,
+ conn->processing_req, status);
+
+ if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS)
+ return -EPERM;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_CONNECT_RESP;
+ msg->nparam = 0x01;
+
+ /* Add connection status */
+ param->id = SAP_PARAM_ID_CONN_STATUS;
+ param->len = htons(SAP_PARAM_ID_CONN_STATUS_LEN);
+ *param->val = status;
+ size += PARAMETER_SIZE(SAP_PARAM_ID_CONN_STATUS_LEN);
+
+ /* Add MaxMsgSize */
+ if (maxmsgsize) {
+ uint16_t *len;
+
+ msg->nparam++;
+ param = (struct sap_parameter *) &buf[size];
+ param->id = SAP_PARAM_ID_MAX_MSG_SIZE;
+ param->len = htons(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
+ len = (uint16_t *) ¶m->val;
+ *len = htons(maxmsgsize);
+ size += PARAMETER_SIZE(SAP_PARAM_ID_MAX_MSG_SIZE_LEN);
+ }
+
+ if (status == SAP_STATUS_OK) {
+ sap_set_connected(conn);
+ } else if (status == SAP_STATUS_OK_ONGOING_CALL) {
+ DBG("ongoing call. Wait for reset indication!");
+ conn->state = SAP_STATE_CONNECT_MODEM_BUSY;
+ } else {
+ conn->state = SAP_STATE_DISCONNECTED;
+
+ /* Timer will shutdown channel if client doesn't send
+ * CONNECT_REQ or doesn't shutdown channel itself.*/
+ start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+ }
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
}
int sap_disconnect_rsp(void *sap_device)
{
+ struct sap_connection *conn = sap_device;
+ struct sap_message msg;
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+ switch (conn->state) {
+ case SAP_STATE_CLIENT_DISCONNECT:
+ memset(&msg, 0, sizeof(msg));
+ msg.id = SAP_DISCONNECT_RESP;
+
+ conn->state = SAP_STATE_DISCONNECTED;
+ conn->processing_req = SAP_NO_REQ;
+
+ /* Timer will close channel if client doesn't do it.*/
+ start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+
+ return send_message(sap_device, &msg, sizeof(msg));
+
+ case SAP_STATE_IMMEDIATE_DISCONNECT:
+ conn->state = SAP_STATE_DISCONNECTED;
+ conn->processing_req = SAP_NO_REQ;
+
+ if (conn->io) {
+ g_io_channel_shutdown(conn->io, TRUE, NULL);
+ g_io_channel_unref(conn->io);
+ }
+
+ return 0;
+
+ default:
+ break;
+ }
+
return 0;
}
int sap_transfer_apdu_rsp(void *sap_device, uint8_t result, uint8_t *apdu,
uint16_t length)
{
- return 0;
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+ if (conn->processing_req != SAP_TRANSFER_APDU_REQ)
+ return 0;
+
+ if (result == SAP_RESULT_OK && (!apdu || (apdu && length == 0x00)))
+ return -EINVAL;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_TRANSFER_APDU_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, param);
+
+ /* Add APDU response. */
+ if (result == SAP_RESULT_OK) {
+ msg->nparam++;
+ param = (struct sap_parameter *) &buf[size];
+ param->id = SAP_PARAM_ID_RESPONSE_APDU;
+ param->len = htons(length);
+
+ size += PARAMETER_SIZE(length);
+
+ if (size > SAP_BUF_SIZE)
+ return -EOVERFLOW;
+
+ memcpy(param->val, apdu, length);
+ }
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
}
int sap_transfer_atr_rsp(void *sap_device, uint8_t result, uint8_t *atr,
uint16_t length)
{
- return 0;
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("result 0x%02x state %d pr 0x%02x len %d", result, conn->state,
+ conn->processing_req, length);
+
+ if (conn->processing_req != SAP_TRANSFER_ATR_REQ)
+ return 0;
+
+ if (result == SAP_RESULT_OK && (!atr || (atr && length == 0x00)))
+ return -EINVAL;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_TRANSFER_ATR_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, param);
+
+ /* Add ATR response */
+ if (result == SAP_RESULT_OK) {
+ msg->nparam++;
+ param = (struct sap_parameter *) &buf[size];
+ param->id = SAP_PARAM_ID_ATR;
+ param->len = htons(length);
+ size += PARAMETER_SIZE(length);
+
+ if (size > SAP_BUF_SIZE)
+ return -EOVERFLOW;
+
+ memcpy(param->val, atr, length);
+ }
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
}
int sap_power_sim_off_rsp(void *sap_device, uint8_t result)
{
- return 0;
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+ if (conn->processing_req != SAP_POWER_SIM_OFF_REQ)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_POWER_SIM_OFF_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, msg->param);
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
}
int sap_power_sim_on_rsp(void *sap_device, uint8_t result)
{
- return 0;
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x", conn->state, conn->processing_req);
+
+ if (conn->processing_req != SAP_POWER_SIM_ON_REQ)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_POWER_SIM_ON_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, msg->param);
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
}
int sap_reset_sim_rsp(void *sap_device, uint8_t result)
{
- return 0;
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+ conn->processing_req, result);
+
+ if (conn->processing_req != SAP_RESET_SIM_REQ)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_RESET_SIM_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, msg->param);
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
}
int sap_transfer_card_reader_status_rsp(void *sap_device, uint8_t result,
uint8_t status)
{
- return 0;
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+ conn->processing_req, result);
+
+ if (conn->processing_req != SAP_TRANSFER_CARD_READER_STATUS_REQ)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_TRANSFER_CARD_READER_STATUS_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, param);
+
+ /* Add card reader status. */
+ if (result == SAP_RESULT_OK) {
+ msg->nparam++;
+ param = (struct sap_parameter *) &buf[size];
+ param->id = SAP_PARAM_ID_CARD_READER_STATUS;
+ param->len = htons(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
+ *param->val = status;
+ size += PARAMETER_SIZE(SAP_PARAM_ID_CARD_READER_STATUS_LEN);
+ }
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
}
int sap_transport_protocol_rsp(void *sap_device, uint8_t result)
{
- return 0;
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x result 0x%02x", conn->state,
+ conn->processing_req, result);
+
+ if (conn->processing_req != SAP_SET_TRANSPORT_PROTOCOL_REQ)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_SET_TRANSPORT_PROTOCOL_RESP;
+ msg->nparam = 0x01;
+ size += add_result_parameter(result, msg->param);
+
+ conn->processing_req = SAP_NO_REQ;
+
+ return send_message(sap_device, buf, size);
}
int sap_error_rsp(void *sap_device)
{
- return 0;
+ struct sap_message msg;
+ struct sap_connection *conn = sap_device;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.id = SAP_ERROR_RESP;
+
+ error("SAP error (state %d pr 0x%02x).", conn->state,
+ conn->processing_req);
+
+ return send_message(conn, &msg, sizeof(msg));
}
int sap_status_ind(void *sap_device, uint8_t status_change)
{
- return 0;
+ struct sap_connection *conn = sap_device;
+ char buf[SAP_BUF_SIZE];
+ struct sap_message *msg = (struct sap_message *) buf;
+ struct sap_parameter *param = (struct sap_parameter *) msg->param;
+ size_t size = sizeof(struct sap_message);
+
+ if (!conn)
+ return -EINVAL;
+
+ DBG("state %d pr 0x%02x sc 0x%02x", conn->state, conn->processing_req,
+ status_change);
+
+ /* Might be need to change state to connected after ongoing call.*/
+ if (conn->state == SAP_STATE_CONNECT_MODEM_BUSY &&
+ status_change == SAP_STATUS_CHANGE_CARD_RESET)
+ sap_set_connected(conn);
+
+ if (conn->state != SAP_STATE_CONNECTED &&
+ conn->state != SAP_STATE_GRACEFUL_DISCONNECT)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ msg->id = SAP_STATUS_IND;
+ msg->nparam = 0x01;
+
+ /* Add status change. */
+ param->id = SAP_PARAM_ID_STATUS_CHANGE;
+ param->len = htons(SAP_PARAM_ID_STATUS_CHANGE_LEN);
+ *param->val = status_change;
+ size += PARAMETER_SIZE(SAP_PARAM_ID_STATUS_CHANGE_LEN);
+
+ return send_message(sap_device, buf, size);
}
-static int handle_cmd(void *data, void *buf, size_t size)
+int sap_disconnect_ind(void *sap_device, uint8_t disc_type)
+{
+ struct sap_connection *conn = sap_device;
+
+ return disconnect_req(conn, SAP_DISCONNECTION_TYPE_IMMEDIATE);
+}
+
+static int handle_cmd(struct sap_connection *conn, void *buf, size_t size)
{
struct sap_message *msg = buf;
- struct sap_connection *conn = data;
if (!conn)
return -EINVAL;
if (size < sizeof(struct sap_message))
- return -EINVAL;
+ goto error_rsp;
if (msg->nparam != 0 && size < (sizeof(struct sap_message) +
sizeof(struct sap_parameter) + 4))
- return -EBADMSG;
+ goto error_rsp;
+
+ if (check_msg(msg) < 0)
+ goto error_rsp;
switch (msg->id) {
case SAP_CONNECT_REQ:
set_transport_protocol_req(conn, msg->param);
return 0;
default:
- DBG("SAP unknown message.");
- return -ENOMSG;
+ DBG("Unknown SAP message id 0x%02x.", msg->id);
+ break;
}
- return -1;
+error_rsp:
+ DBG("Invalid SAP message format.");
+ sap_error_rsp(conn);
+ return -EBADMSG;
}
static void sap_conn_remove(struct sap_connection *conn)
static gboolean sap_io_cb(GIOChannel *io, GIOCondition cond, gpointer data)
{
+ struct sap_connection *conn = data;
+
char buf[SAP_BUF_SIZE];
size_t bytes_read = 0;
GError *gerr = NULL;
GIOStatus gstatus;
- DBG("io %p", io);
+ DBG("conn %p io %p", conn, io);
if (cond & G_IO_NVAL) {
DBG("ERR (G_IO_NVAL) on rfcomm socket.");
}
gstatus = g_io_channel_read_chars(io, buf, sizeof(buf) - 1,
- &bytes_read, &gerr);
+ &bytes_read, &gerr);
if (gstatus != G_IO_STATUS_NORMAL) {
if (gerr)
g_error_free(gerr);
return TRUE;
}
- if (handle_cmd(data, buf, bytes_read) < 0)
- error("Invalid SAP message.");
+ if (handle_cmd(conn, buf, bytes_read) < 0)
+ error("SAP protocol processing failure.");
return TRUE;
}
DBG("conn %p", conn);
if (conn && conn->io) {
- conn->io = NULL;
+ gboolean connected = FALSE;
+
+ stop_guard_timer(conn);
+
+ if (conn->state != SAP_STATE_CONNECT_IN_PROGRESS &&
+ conn->state != SAP_STATE_CONNECT_MODEM_BUSY)
+ emit_property_changed(connection, server->path,
+ SAP_SERVER_INTERFACE, "Connected",
+ DBUS_TYPE_BOOLEAN, &connected);
+
+ if (conn->state == SAP_STATE_CONNECT_IN_PROGRESS ||
+ conn->state == SAP_STATE_CONNECT_MODEM_BUSY ||
+ conn->state == SAP_STATE_CONNECTED ||
+ conn->state == SAP_STATE_GRACEFUL_DISCONNECT)
+ sap_disconnect_req(NULL, 1);
+
sap_conn_remove(conn);
}
}
{
struct sap_connection *conn = data;
- DBG("io %p gerr %p data %p ", io, gerr, data);
+ DBG("conn %p, io %p", conn, io);
if (!conn)
return;
+ /* Timer will shutdown the channel in case of lack of client
+ activity */
+ start_guard_timer(conn, SAP_TIMER_NO_ACTIVITY);
+
g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
sap_io_cb, conn, sap_io_destroy);
struct sap_connection *conn = data;
GError *gerr = NULL;
- DBG("derr %p data %p ", derr, data);
+ DBG("conn %p", conn);
if (!conn)
return;
if (derr && dbus_error_is_set(derr)) {
- error("Access denied: %s", derr->message);
+ error("Access has been denied (%s)", derr->message);
sap_conn_remove(conn);
return;
}
return;
}
- DBG("Client has been authorized.");
+ DBG("Access has been granted.");
}
static void connect_confirm_cb(GIOChannel *io, gpointer data)
struct sap_connection *conn = server->conn;
GError *gerr = NULL;
bdaddr_t src, dst;
+ char dstaddr[18];
int err;
- DBG("io %p data %p ", io, data);
+ DBG("conn %p io %p", conn, io);
if (!io)
return;
if (conn) {
+ DBG("Another SAP connection already exists.");
g_io_channel_shutdown(io, TRUE, NULL);
return;
}
return;
}
- err = btd_request_authorization(&src, &dst, SAP_UUID,
- connect_auth_cb, conn);
+ ba2str(&dst, dstaddr);
+
+ err = btd_request_authorization(&src, &dst, SAP_UUID, connect_auth_cb,
+ conn);
if (err < 0) {
- DBG("Authorization denied: %d %s", err, strerror(err));
+ error("Authorization failure (err %d)", err);
sap_conn_remove(conn);
return;
}
- DBG("SAP incoming connection (sock %d) authorization.",
- g_io_channel_unix_get_fd(io));
+ DBG("Authorizing incomming SAP connection from %s", dstaddr);
}
static inline DBusMessage *message_failed(DBusMessage *msg,
const char *description)
{
- return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
- "%s", description);
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "%s",
+ description);
}
static DBusMessage *disconnect(DBusConnection *conn, DBusMessage *msg,
{
struct sap_server *server = data;
- DBG("server %p", server);
-
if (!server)
return message_failed(msg, "Server internal error.");
if (!server->conn)
return message_failed(msg, "Client already disconnected");
+ if (disconnect_req(server->conn, SAP_DISCONNECTION_TYPE_GRACEFUL) < 0)
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
+ "There is no active connection");
+
return dbus_message_new_method_return(msg);
}
DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
- connected = (conn->state == SAP_STATE_CONNECTED);
+ connected = (conn->state == SAP_STATE_CONNECTED ||
+ conn->state == SAP_STATE_GRACEFUL_DISCONNECT);
dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
dbus_message_iter_close_container(&iter, &dict);
sap_conn_remove(server->conn);
g_free(server->path);
g_free(server);
+ server = NULL;
}
static void destroy_sap_interface(void *data)
{
struct sap_server *server = data;
- DBG("Unregistered interface %s on path %s",
- SAP_SERVER_INTERFACE, server->path);
+ DBG("Unregistered interface %s on path %s", SAP_SERVER_INTERFACE,
+ server->path);
server_free(server);
}
server->conn = NULL;
if (!g_dbus_register_interface(connection, path, SAP_SERVER_INTERFACE,
- server_methods, server_signals, NULL,
- server, destroy_sap_interface)) {
+ server_methods, server_signals, NULL,
+ server, destroy_sap_interface)) {
error("D-Bus failed to register %s interface",
- SAP_SERVER_INTERFACE);
+ SAP_SERVER_INTERFACE);
goto server_err;
}
remove_record_from_server(server->record_id);
sdp_err:
server_free(server);
- server = NULL;
sap_exit();
return -1;
g_dbus_unregister_interface(connection, path, SAP_SERVER_INTERFACE);
- server_free(server);
- server = NULL;
sap_exit();
return 0;
int crc_pos = 0;
int32_t temp;
- int audio_sample;
+ uint32_t audio_sample;
int ch, sb, blk, bit; /* channel, subband, block and bit standard
counters */
int bits[2][8]; /* bits distribution */
for (blk = 0; blk < frame->blocks; blk++) {
for (ch = 0; ch < frame->channels; ch++) {
for (sb = 0; sb < frame->subbands; sb++) {
- if (levels[ch][sb] > 0) {
- audio_sample = 0;
- for (bit = 0; bit < bits[ch][sb]; bit++) {
- if (consumed > len * 8)
- return -1;
+ uint32_t shift;
- if ((data[consumed >> 3] >> (7 - (consumed & 0x7))) & 0x01)
- audio_sample |= 1 << (bits[ch][sb] - bit - 1);
+ if (levels[ch][sb] == 0) {
+ frame->sb_sample[blk][ch][sb] = 0;
+ continue;
+ }
- consumed++;
- }
+ shift = frame->scale_factor[ch][sb] +
+ 1 + SBCDEC_FIXED_EXTRA_BITS;
- frame->sb_sample[blk][ch][sb] =
- (((audio_sample << 1) | 1) << frame->scale_factor[ch][sb]) /
- levels[ch][sb] - (1 << frame->scale_factor[ch][sb]);
- } else
- frame->sb_sample[blk][ch][sb] = 0;
+ audio_sample = 0;
+ for (bit = 0; bit < bits[ch][sb]; bit++) {
+ if (consumed > len * 8)
+ return -1;
+
+ if ((data[consumed >> 3] >> (7 - (consumed & 0x7))) & 0x01)
+ audio_sample |= 1 << (bits[ch][sb] - bit - 1);
+
+ consumed++;
+ }
+
+ frame->sb_sample[blk][ch][sb] = (int32_t)
+ (((((uint64_t) audio_sample << 1) | 1) << shift) /
+ levels[ch][sb]) - (1 << shift);
}
}
}
#define SBC_FIXED_0(val) { val = 0; }
#define MUL(a, b) ((a) * (b))
-#ifdef __arm__
+#if defined(__arm__) && (!defined(__thumb__) || defined(__thumb2__))
#define MULA(a, b, res) ({ \
int tmp = res; \
__asm__( \
static void __attribute__((naked)) sbc_analyze_four_armv6()
{
/* r0 = in, r1 = out, r2 = consts */
- asm volatile (
+ __asm__ volatile (
"push {r1, r4-r7, lr}\n"
"push {r8-r11}\n"
"ldrd r4, r5, [r0, #0]\n"
static void __attribute__((naked)) sbc_analyze_eight_armv6()
{
/* r0 = in, r1 = out, r2 = consts */
- asm volatile (
+ __asm__ volatile (
"push {r1, r4-r7, lr}\n"
"push {r8-r11}\n"
"ldrd r4, r5, [r0, #24]\n"
#if !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15) && \
defined(__GNUC__) && defined(SBC_HAVE_ARMV6) && \
- defined(__ARM_EABI__) && !defined(__thumb__) && \
- !defined(__ARM_NEON__)
+ defined(__ARM_EABI__) && !defined(__ARM_NEON__) && \
+ (!defined(__thumb__) || defined(__thumb2__))
#define SBC_BUILD_WITH_ARMV6_SUPPORT
static inline void sbc_analyze_four_iwmmxt(const int16_t *in, int32_t *out,
const FIXED_T *consts)
{
- asm volatile (
+ __asm__ volatile (
"wldrd wr0, [%0]\n"
"tbcstw wr4, %2\n"
"wldrd wr2, [%1]\n"
static inline void sbc_analyze_eight_iwmmxt(const int16_t *in, int32_t *out,
const FIXED_T *consts)
{
- asm volatile (
+ __asm__ volatile (
"wldrd wr0, [%0]\n"
"tbcstw wr15, %2\n"
"wldrd wr1, [%0, #8]\n"
1 << (SBC_PROTO_FIXED4_SCALE - 1),
1 << (SBC_PROTO_FIXED4_SCALE - 1),
};
- asm volatile (
+ __asm__ volatile (
"movq (%0), %%mm0\n"
"movq 8(%0), %%mm1\n"
"pmaddwd (%1), %%mm0\n"
1 << (SBC_PROTO_FIXED8_SCALE - 1),
1 << (SBC_PROTO_FIXED8_SCALE - 1),
};
- asm volatile (
+ __asm__ volatile (
"movq (%0), %%mm0\n"
"movq 8(%0), %%mm1\n"
"movq 16(%0), %%mm2\n"
out += out_stride;
sbc_analyze_four_mmx(x + 0, out, analysis_consts_fixed4_simd_even);
- asm volatile ("emms\n");
+ __asm__ volatile ("emms\n");
}
static inline void sbc_analyze_4b_8s_mmx(int16_t *x, int32_t *out,
out += out_stride;
sbc_analyze_eight_mmx(x + 0, out, analysis_consts_fixed8_simd_even);
- asm volatile ("emms\n");
+ __asm__ volatile ("emms\n");
}
static void sbc_calc_scalefactors_mmx(
for (sb = 0; sb < subbands; sb += 2) {
blk = (blocks - 1) * (((char *) &sb_sample_f[1][0][0] -
(char *) &sb_sample_f[0][0][0]));
- asm volatile (
+ __asm__ volatile (
"movq (%4), %%mm0\n"
"1:\n"
"movq (%1, %0), %%mm1\n"
: "cc", "memory");
}
}
- asm volatile ("emms\n");
+ __asm__ volatile ("emms\n");
}
static int check_mmx_support(void)
return 1; /* We assume that all 64-bit processors have MMX support */
#else
int cpuid_feature_information;
- asm volatile (
+ __asm__ volatile (
/* According to Intel manual, CPUID instruction is supported
* if the value of ID bit (bit 21) in EFLAGS can be modified */
"pushf\n"
/* TODO: merge even and odd cases (or even merge all four calls to this
* function) in order to have only aligned reads from 'in' array
* and reduce number of load instructions */
- asm volatile (
+ __asm__ volatile (
"vld1.16 {d4, d5}, [%0, :64]!\n"
"vld1.16 {d8, d9}, [%1, :128]!\n"
/* TODO: merge even and odd cases (or even merge all four calls to this
* function) in order to have only aligned reads from 'in' array
* and reduce number of load instructions */
- asm volatile (
+ __asm__ volatile (
"vld1.16 {d4, d5}, [%0, :64]!\n"
"vld1.16 {d8, d9}, [%1, :128]!\n"
for (sb = 0; sb < subbands; sb += 4) {
int blk = blocks;
int32_t *in = &sb_sample_f[0][ch][sb];
- asm volatile (
+ __asm__ volatile (
"vmov.s32 q0, #0\n"
"vmov.s32 q1, %[c1]\n"
"vmov.s32 q14, #1\n"
i = subbands;
- asm volatile (
+ __asm__ volatile (
/*
* constants: q13 = (31 - SCALE_OUT_BITS), q14 = 1
* input: q0 = ((1 << SCALE_OUT_BITS) + 1)
if (position < nsamples) {
int16_t *dst = &X[0][SBC_X_BUFFER_SIZE - 40];
int16_t *src = &X[0][position];
- asm volatile (
+ __asm__ volatile (
"vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
"vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
"vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
if (nchannels > 1) {
dst = &X[1][SBC_X_BUFFER_SIZE - 40];
src = &X[1][position];
- asm volatile (
+ __asm__ volatile (
"vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
"vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
"vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
/* poor 'pcm' alignment */
int16_t *x = &X[0][position];
int16_t *y = &X[1][position];
- asm volatile (
+ __asm__ volatile (
"vld1.8 {d0, d1}, [%[perm], :128]\n"
"1:\n"
"sub %[x], %[x], #16\n"
/* proper 'pcm' alignment */
int16_t *x = &X[0][position];
int16_t *y = &X[1][position];
- asm volatile (
+ __asm__ volatile (
"vld1.8 {d0, d1}, [%[perm], :128]\n"
"1:\n"
"sub %[x], %[x], #16\n"
"d20", "d21", "d22", "d23");
} else {
int16_t *x = &X[0][position];
- asm volatile (
+ __asm__ volatile (
"vld1.8 {d0, d1}, [%[perm], :128]\n"
"1:\n"
"sub %[x], %[x], #16\n"
if (position < nsamples) {
int16_t *dst = &X[0][SBC_X_BUFFER_SIZE - 72];
int16_t *src = &X[0][position];
- asm volatile (
+ __asm__ volatile (
"vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
"vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
"vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
if (nchannels > 1) {
dst = &X[1][SBC_X_BUFFER_SIZE - 72];
src = &X[1][position];
- asm volatile (
+ __asm__ volatile (
"vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
"vst1.16 {d0, d1, d2, d3}, [%[dst], :128]!\n"
"vld1.16 {d0, d1, d2, d3}, [%[src], :128]!\n"
/* poor 'pcm' alignment */
int16_t *x = &X[0][position];
int16_t *y = &X[1][position];
- asm volatile (
+ __asm__ volatile (
"vld1.8 {d0, d1, d2, d3}, [%[perm], :128]\n"
"1:\n"
"sub %[x], %[x], #32\n"
/* proper 'pcm' alignment */
int16_t *x = &X[0][position];
int16_t *y = &X[1][position];
- asm volatile (
+ __asm__ volatile (
"vld1.8 {d0, d1, d2, d3}, [%[perm], :128]\n"
"1:\n"
"sub %[x], %[x], #32\n"
"d20", "d21", "d22", "d23");
} else {
int16_t *x = &X[0][position];
- asm volatile (
+ __asm__ volatile (
"vld1.8 {d0, d1, d2, d3}, [%[perm], :128]\n"
"1:\n"
"sub %[x], %[x], #32\n"
{ -4, 0, 0, 0, 0, 0, 1, 2 }
};
+/* extra bits of precision for the synthesis filter input data */
+#define SBCDEC_FIXED_EXTRA_BITS 2
#define SS4(val) ASR(val, SCALE_SPROTO4_TBL)
#define SS8(val) ASR(val, SCALE_SPROTO8_TBL)
-#define SN4(val) ASR(val, SCALE_NPROTO4_TBL)
-#define SN8(val) ASR(val, SCALE_NPROTO8_TBL)
+#define SN4(val) ASR(val, SCALE_NPROTO4_TBL + 1 + SBCDEC_FIXED_EXTRA_BITS)
+#define SN8(val) ASR(val, SCALE_NPROTO8_TBL + 1 + SBCDEC_FIXED_EXTRA_BITS)
static const int32_t sbc_proto_4_40m0[] = {
SS4(0x00000000), SS4(0xffa6982f), SS4(0xfba93848), SS4(0x0456c7b8),
unsigned char buf[64];
double rate;
int bitpool[SIZE], frame_len[SIZE];
- int subbands, blocks, freq, mode, method;
+ int subbands, blocks, freq, method;
int n, p1, p2, fd, size, num;
ssize_t len;
unsigned int count;
subbands = (hdr.subbands + 1) * 4;
blocks = (hdr.blocks + 1) * 4;
freq = hdr.sampling_frequency;
- mode = hdr.channel_mode;
method = hdr.allocation_method;
count = calc_frame_len(&hdr);
return verdict;
}
-static void usage()
+static void usage(void)
{
printf("SBC conformance test ver %s\n", VERSION);
printf("Copyright (c) 2007-2010 Marcel Holtmann\n");
-# Variety of Dell Bluetooth devices
-#
-# it looks like a bit of an odd rule, because it is matching
-# on a mouse device that is self powered, but that is where
-# a HID report needs to be sent to switch modes.
-#
-# Known supported devices:
-# 413c:8154
-# 413c:8158
-# 413c:8162
-ACTION=="add", ENV{ID_VENDOR}=="413c", ENV{ID_CLASS}=="mouse", ATTRS{bmAttributes}=="e0", KERNEL=="mouse*", RUN+="/usr/sbin/hid2hci --method dell -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+# do not edit this file, it will be overwritten on update
+
+ACTION=="remove", GOTO="hid2hci_end"
+SUBSYSTEM!="usb", GOTO="hid2hci_end"
+
+# Variety of Dell Bluetooth devices - match on a mouse device that is
+# self powered and where a HID report needs to be sent to switch modes
+# Known supported devices: 413c:8154, 413c:8158, 413c:8162
+ATTR{bInterfaceClass}=="03", ATTR{bInterfaceSubClass}=="01", ATTR{bInterfaceProtocol}=="02", \
+ ATTRS{bDeviceClass}=="00", ATTRS{idVendor}=="413c", ATTRS{bmAttributes}=="e0", \
+ RUN+="hid2hci --method=dell --devpath=%p", ENV{HID2HCI_SWITCH}="1"
# Logitech devices
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c703" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c704" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c705" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70a" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70b" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70c" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c70e" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c713" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c714" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71b" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="046d", ENV{ID_MODEL}=="c71c" RUN+="/usr/sbin/hid2hci --method logitech -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+KERNEL=="hiddev*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c70[345abce]|c71[34bc]", \
+ RUN+="hid2hci --method=logitech-hid --devpath=%p"
+
+ENV{DEVTYPE}!="usb_device", GOTO="hid2hci_end"
+
+# When a Dell device recovers from S3, the mouse child needs to be repoked
+# Unfortunately the only event seen is the BT device disappearing, so the mouse
+# device needs to be chased down on the USB bus.
+ATTR{bDeviceClass}=="e0", ATTR{bDeviceSubClass}=="01", ATTR{bDeviceProtocol}=="01", ATTR{idVendor}=="413c", \
+ ENV{REMOVE_CMD}="/sbin/udevadm trigger --action=change --subsystem-match=usb --property-match=HID2HCI_SWITCH=1"
-# CSR devices (in HID mode)
-ACTION=="add", ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="1000" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="1000" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
-ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="1000" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hci"
+# CSR devices
+ATTR{idVendor}=="0a12|0458|05ac", ATTR{idProduct}=="1000", RUN+="hid2hci --method=csr --devpath=%p"
-# CSR devices (in HCI mode)
-#ACTION=="add", ENV{ID_VENDOR}=="0a12", ENV{ID_MODEL}=="0001" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
-#ACTION=="add", ENV{ID_VENDOR}=="0458", ENV{ID_MODEL}=="003f" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
-#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8203" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
-#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8204" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
-#ACTION=="add", ENV{ID_VENDOR}=="05ac", ENV{ID_MODEL}=="8207" RUN+="/usr/sbin/hid2hci --method csr -v $env{ID_VENDOR} -p $env{ID_MODEL} --mode hid"
+LABEL="hid2hci_end"
#include "device.h"
#include "log.h"
-#include "textfile.h"
#include "error.h"
#include "port.h"
}
adapter_get_address(adapter, &src);
- device_get_address(device, &dst);
+ device_get_address(device, &dst, NULL);
return port_register(connection, path, &src, &dst, uuid, ch);
}
#include "../src/dbus-common.h"
#include "log.h"
+#include "glib-compat.h"
#include "glib-helper.h"
+#include "sdp-client.h"
#include "btio.h"
#include "error.h"
#define MAX_OPEN_TRIES 5
#define OPEN_WAIT 300 /* ms. udev node creation retry wait */
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
struct serial_device {
DBusConnection *conn; /* for name listener handling */
bdaddr_t src; /* Source (local) address */
req.flags = (1 << RFCOMM_HANGUP_NOW);
if (ioctl(rfcomm_ctl, RFCOMMRELEASEDEV, &req) < 0) {
- err = errno;
+ err = -errno;
error("Can't release device %s: %s (%d)",
- port->dev, strerror(err), err);
+ port->dev, strerror(-err), -err);
}
g_free(port->dev);
port->dev = NULL;
port->id = -1;
close(rfcomm_ctl);
- return -err;
+ return err;
}
-static void serial_port_free(struct serial_port *port)
+static void serial_port_free(void *data)
{
+ struct serial_port *port = data;
struct serial_device *device = port->device;
if (device && port->listener_id > 0)
g_free(port);
}
-static void serial_device_free(struct serial_device *device)
+static void serial_device_free(void *data)
{
+ struct serial_device *device = data;
+
g_free(device->path);
if (device->conn)
dbus_connection_unref(device->conn);
void port_release_all(void)
{
- g_slist_foreach(devices, (GFunc) serial_device_free, NULL);
- g_slist_free(devices);
+ g_slist_free_full(devices, serial_device_free);
}
static void open_notify(int fd, int err, struct serial_port *port)
goto fail;
}
+ sk = g_io_channel_unix_get_fd(chan);
+
+ if (dbus_message_has_member(port->msg, "ConnectFD")) {
+ reply = g_dbus_create_reply(port->msg, DBUS_TYPE_UNIX_FD, &sk,
+ DBUS_TYPE_INVALID);
+ g_dbus_send_message(device->conn, reply);
+
+ close(sk);
+
+ g_dbus_remove_watch(device->conn, port->listener_id);
+ port->listener_id = 0;
+
+ return;
+ }
+
memset(&req, 0, sizeof(req));
req.dev_id = -1;
req.flags = (1 << RFCOMM_REUSE_DLC);
g_io_channel_unref(port->io);
port->io = NULL;
- sk = g_io_channel_unix_get_fd(chan);
port->id = ioctl(sk, RFCOMMCREATEDEV, &req);
if (port->id < 0) {
int err = -errno;
DBusMessage *reply;
GError *gerr = NULL;
- if (!port->listener_id) {
- reply = NULL;
- goto failed;
- }
+ if (!port->listener_id)
+ return;
if (err < 0) {
error("Unable to get service record: %s (%d)", strerror(-err),
BT_IO_OPT_DEST_BDADDR, &device->dst,
BT_IO_OPT_CHANNEL, port->channel,
BT_IO_OPT_INVALID);
- if (port->io)
- return 0;
+ if (port->io == NULL)
+ return -EIO;
- return -errno;
+ return 0;
}
static struct serial_port *create_port(struct serial_device *device,
const char *pattern;
int err;
+ if (dbus_message_has_member(msg, "ConnectFD") && DBUS_TYPE_UNIX_FD < 0)
+ return btd_error_not_supported(msg);
+
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
DBUS_TYPE_INVALID) == FALSE)
return NULL;
dbus_message_get_sender(msg),
port_owner_exited, port,
NULL);
+
port->msg = dbus_message_ref(msg);
err = connect_port(port);
static GDBusMethodTable port_methods[] = {
{ "Connect", "s", "s", port_connect, G_DBUS_METHOD_FLAG_ASYNC },
+ { "ConnectFD", "s", "h", port_connect, G_DBUS_METHOD_FLAG_ASYNC },
{ "Disconnect", "s", "", port_disconnect },
{ }
};
if (!device)
return -ENOENT;
- g_slist_foreach(device->ports, (GFunc) serial_port_free, NULL);
- g_slist_free(device->ports);
+ g_slist_free_full(device->ports, serial_port_free);
g_dbus_unregister_interface(device->conn, path, SERIAL_PORT_INTERFACE);
#include "../src/adapter.h"
#include "log.h"
-#include "textfile.h"
#include "error.h"
#include "sdpd.h"
fd = g_io_channel_unix_get_fd(chan);
- wbytes = written = 0;
+ wbytes = 0;
while (wbytes < size) {
written = write(fd, buf + wbytes, size - wbytes);
-
- if (written)
+ if (written < 0)
return -errno;
wbytes += written;
/* Unix socket */
sk = socket(AF_UNIX, SOCK_STREAM, 0);
if (sk < 0) {
- err = errno;
+ err = -errno;
error("Unix socket(%s) create failed: %s(%d)",
- address, strerror(err), err);
- return -err;
+ address, strerror(-err), -err);
+ return err;
}
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- err = errno;
+ err = -errno;
error("Unix socket(%s) connect failed: %s(%d)",
- address, strerror(err), err);
+ address, strerror(-err), -err);
close(sk);
- errno = err;
- return -err;
+ return err;
}
return sk;
static int tcp_socket_connect(const char *address)
{
struct sockaddr_in addr;
- int err, sk;
- unsigned short int port;
+ int err, sk, port;
memset(&addr, 0, sizeof(addr));
sk = socket(PF_INET, SOCK_STREAM, 0);
if (sk < 0) {
- err = errno;
+ err = -errno;
error("TCP socket(%s) create failed %s(%d)", address,
- strerror(err), err);
- return -err;
+ strerror(-err), -err);
+ return err;
}
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- err = errno;
+ err = -errno;
error("TCP socket(%s) connect failed: %s(%d)",
- address, strerror(err), err);
+ address, strerror(-err), -err);
close(sk);
- errno = err;
- return -err;
+ return err;
}
return sk;
}
sk = open(tty, O_RDWR | O_NOCTTY);
if (sk < 0) {
- err = errno;
- error("Can't open TTY %s: %s(%d)", tty, strerror(err), err);
- return -err;
+ err = -errno;
+ error("Can't open TTY %s: %s(%d)", tty, strerror(-err), -err);
+ return err;
}
if (ti && tcsetattr(sk, TCSANOW, ti) < 0) {
- err = errno;
+ err = -errno;
error("Can't change serial settings: %s(%d)",
- strerror(err), err);
+ strerror(-err), -err);
close(sk);
- errno = err;
- return -err;
+ return err;
}
return sk;
sk = open(address, O_RDONLY | O_NOCTTY);
if (sk < 0) {
- error("Cant open TTY: %s(%d)", strerror(errno), errno);
+ error("Can't open TTY: %s(%d)", strerror(errno), errno);
return -EINVAL;
}
static struct serial_adapter *find_adapter(GSList *list,
struct btd_adapter *btd_adapter)
{
- GSList *l;
-
- for (l = list; l; l = l->next) {
- struct serial_adapter *adapter = l->data;
+ for (; list; list = list->next) {
+ struct serial_adapter *adapter = list->data;
if (adapter->btd_adapter == btd_adapter)
return adapter;
#include "manager.h"
#include "device.h"
#include "dbus-common.h"
-#include "event.h"
#include "error.h"
+#include "glib-compat.h"
#include "glib-helper.h"
#include "agent.h"
#include "storage.h"
-#include "attrib-server.h"
+#include "gattrib.h"
#include "att.h"
+#include "attrib-server.h"
+#include "eir.h"
/* Flags Descriptions */
#define EIR_LIM_DISC 0x01 /* LE Limited Discoverable Mode */
#define EIR_SIM_HOST 0x10 /* Simultaneous LE and BR/EDR to Same
Device Capable (Host) */
-#define ADV_TYPE_IND 0x00
-#define ADV_TYPE_DIRECT_IND 0x01
-
#define IO_CAPABILITY_DISPLAYONLY 0x00
#define IO_CAPABILITY_DISPLAYYESNO 0x01
#define IO_CAPABILITY_KEYBOARDONLY 0x02
#define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03
#define IO_CAPABILITY_INVALID 0xFF
-/* Limited Discoverable bit mask in CoD */
-#define LIMITED_BIT 0x002000
#define check_address(address) bachk(address)
+#define OFF_TIMER 3
+
static DBusConnection *connection = NULL;
static GSList *adapter_drivers = NULL;
struct btd_adapter {
uint16_t dev_id;
- int up;
+ gboolean up;
char *path; /* adapter object path */
bdaddr_t bdaddr; /* adapter Bluetooth Address */
uint32_t dev_class; /* Class of Device */
+ char *name; /* adapter name */
+ gboolean allow_name_changes; /* whether the adapter name can be changed */
guint discov_timeout_id; /* discoverable timeout id */
guint stop_discov_id; /* stop inquiry/scanning id */
uint32_t discov_timeout; /* discoverable time(sec) */
uint32_t pairable_timeout; /* pairable time(sec) */
uint8_t scan_mode; /* scan mode: SCAN_DISABLED, SCAN_PAGE,
* SCAN_INQUIRY */
-#ifdef __TIZEN_PATCH__
- // Adding Limited state for setting limited discoverable mode
- gboolean limited; /* limited discoverable state */
-#endif
uint8_t mode; /* off, connectable, discoverable,
* limited */
uint8_t global_mode; /* last valid global mode */
GSList *devices; /* Devices structure pointers */
GSList *mode_sessions; /* Request Mode sessions */
GSList *disc_sessions; /* Discovery sessions */
- guint scheduler_id; /* Scheduler handle */
+ guint discov_id; /* Discovery timer */
+ gboolean discovering; /* Discovery active */
+ gboolean discov_suspended; /* Discovery suspended */
+ guint auto_timeout_id; /* Automatic connections timeout */
sdp_list_t *services; /* Services associated to adapter */
- struct hci_dev dev; /* hci info */
gboolean pairable; /* pairable state */
gboolean initialized;
gint ref;
- GSList *powered_callbacks;
+ guint off_timer;
- gboolean name_stored;
+ GSList *powered_callbacks;
+ GSList *pin_callbacks;
GSList *loaded_drivers;
};
-static void adapter_set_pairable_timeout(struct btd_adapter *adapter,
- guint interval);
-
-static int found_device_cmp(const struct remote_dev_info *d1,
- const struct remote_dev_info *d2)
+static void dev_info_free(void *data)
{
- int ret;
-
- if (bacmp(&d2->bdaddr, BDADDR_ANY)) {
- ret = bacmp(&d1->bdaddr, &d2->bdaddr);
- if (ret)
- return ret;
- }
-
- if (d2->name_status != NAME_ANY) {
- ret = (d1->name_status - d2->name_status);
- if (ret)
- return ret;
- }
-
- return 0;
-}
+ struct remote_dev_info *dev = data;
-static void dev_info_free(struct remote_dev_info *dev)
-{
g_free(dev->name);
g_free(dev->alias);
- g_slist_foreach(dev->services, (GFunc) g_free, NULL);
- g_slist_free(dev->services);
+ g_slist_free_full(dev->services, g_free);
g_strfreev(dev->uuids);
g_free(dev);
}
-/*
- * Device name expansion
- * %d - device id
- */
-static char *expand_name(char *dst, int size, char *str, int dev_id)
-{
- register int sp, np, olen;
- char *opt, buf[10];
-
- if (!str || !dst)
- return NULL;
-
- sp = np = 0;
- while (np < size - 1 && str[sp]) {
- switch (str[sp]) {
- case '%':
- opt = NULL;
-
- switch (str[sp+1]) {
- case 'd':
- sprintf(buf, "%d", dev_id);
- opt = buf;
- break;
-
- case 'h':
- opt = main_opts.host_name;
- break;
-
- case '%':
- dst[np++] = str[sp++];
- /* fall through */
- default:
- sp++;
- continue;
- }
-
- if (opt) {
- /* substitute */
- olen = strlen(opt);
- if (np + olen < size - 1)
- memcpy(dst + np, opt, olen);
- np += olen;
- }
- sp += 2;
- continue;
-
- case '\\':
- sp++;
- /* fall through */
- default:
- dst[np++] = str[sp++];
- break;
- }
- }
- dst[np] = '\0';
- return dst;
-}
-
int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
uint8_t minor)
{
return adapter_ops->set_dev_class(adapter->dev_id, major, minor);
}
-static int pending_remote_name_cancel(struct btd_adapter *adapter)
-{
- struct remote_dev_info *dev, match;
- int err;
-
- /* find the pending remote name request */
- memset(&match, 0, sizeof(struct remote_dev_info));
- bacpy(&match.bdaddr, BDADDR_ANY);
- match.name_status = NAME_REQUESTED;
-
- dev = adapter_search_found_devices(adapter, &match);
- if (!dev) /* no pending request */
- return -ENODATA;
-
- err = adapter_ops->cancel_resolve_name(adapter->dev_id, &dev->bdaddr);
- if (err < 0)
- error("Remote name cancel failed: %s(%d)",
- strerror(errno), errno);
- return err;
-}
-
-int adapter_resolve_names(struct btd_adapter *adapter)
-{
- struct remote_dev_info *dev, match;
- int err;
-
- /* Do not attempt to resolve more names if on suspended state */
- if (adapter->state & STATE_SUSPENDED)
- return 0;
-
- memset(&match, 0, sizeof(struct remote_dev_info));
- bacpy(&match.bdaddr, BDADDR_ANY);
- match.name_status = NAME_REQUIRED;
-
- dev = adapter_search_found_devices(adapter, &match);
- if (!dev)
- return -ENODATA;
-
- /* send at least one request or return failed if the list is empty */
- do {
- /* flag to indicate the current remote name requested */
- dev->name_status = NAME_REQUESTED;
-
- err = adapter_ops->resolve_name(adapter->dev_id, &dev->bdaddr);
-
- if (!err)
- break;
-
- error("Unable to send HCI remote name req: %s (%d)",
- strerror(errno), errno);
-
- /* if failed, request the next element */
- /* remove the element from the list */
- adapter_remove_found_device(adapter, &dev->bdaddr);
-
- /* get the next element */
- dev = adapter_search_found_devices(adapter, &match);
- } while (dev);
-
- return err;
-}
-
static const char *mode2str(uint8_t mode)
{
switch(mode) {
static struct session_req *find_session_by_msg(GSList *list, const DBusMessage *msg)
{
- GSList *l;
-
- for (l = list; l; l = l->next) {
- struct session_req *req = l->data;
+ for (; list; list = list->next) {
+ struct session_req *req = list->data;
if (req->msg == msg)
return req;
{
int err;
const char *modestr;
- gboolean discoverable;
if (adapter->pending_mode != NULL)
return -EALREADY;
- discoverable = new_mode == MODE_DISCOVERABLE;
-
if (!adapter->up && new_mode != MODE_OFF) {
err = adapter_ops->set_powered(adapter->dev_id, TRUE);
if (err < 0)
struct btd_adapter *adapter = data;
uint8_t mode;
int err;
-
-#ifdef __TIZEN_PATCH__
- if (powered)
- {
- mode = adapter->mode ? adapter->mode : get_mode(&adapter->bdaddr, "on");
-
- }
- else
- mode = MODE_OFF;
-#else
+#ifdef __SAMSUNG_PATCH__
mode = powered ? get_mode(&adapter->bdaddr, "on") : MODE_OFF;
-#endif
+#else
+ if (powered) {
+ mode = get_mode(&adapter->bdaddr, "on");
+ return set_discoverable(conn, msg, mode == MODE_DISCOVERABLE,
+ data);
+ }
+ mode = MODE_OFF;
+#endif
if (mode == adapter->mode) {
adapter->global_mode = mode;
return dbus_message_new_method_return(msg);
return NULL;
}
-void btd_adapter_pairable_changed(struct btd_adapter *adapter,
- gboolean pairable)
-{
- adapter->pairable = pairable;
-
- write_device_pairable(&adapter->bdaddr, pairable);
-
- emit_property_changed(connection, adapter->path,
- ADAPTER_INTERFACE, "Pairable",
- DBUS_TYPE_BOOLEAN, &pairable);
-
- if (pairable && adapter->pairable_timeout)
- adapter_set_pairable_timeout(adapter,
- adapter->pairable_timeout);
-}
-
static DBusMessage *set_pairable(DBusConnection *conn, DBusMessage *msg,
gboolean pairable, void *data)
{
if (!(adapter->scan_mode & SCAN_INQUIRY))
goto store;
-#ifndef __TIZEN_PATCH__
err = set_mode(adapter, MODE_DISCOVERABLE, NULL);
if (err < 0 && msg)
return btd_error_failed(msg, strerror(-err));
-#endif
store:
adapter_ops->set_pairable(adapter->dev_id, pairable);
adapter);
}
-static struct session_req *find_session(GSList *list, const char *sender)
+void btd_adapter_pairable_changed(struct btd_adapter *adapter,
+ gboolean pairable)
{
- GSList *l;
+ adapter->pairable = pairable;
- for (l = list; l; l = l->next) {
- struct session_req *req = l->data;
+ write_device_pairable(&adapter->bdaddr, pairable);
+
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Pairable",
+ DBUS_TYPE_BOOLEAN, &pairable);
+
+ if (pairable && adapter->pairable_timeout)
+ adapter_set_pairable_timeout(adapter,
+ adapter->pairable_timeout);
+}
+
+static struct session_req *find_session(GSList *list, const char *sender)
+{
+ for (; list; list = list->next) {
+ struct session_req *req = list->data;
if (g_str_equal(req->owner, sender))
return req;
for (l = all, le = NULL; l; l = l->next) {
struct remote_dev_info *dev = l->data;
- if (dev->le == FALSE) {
+ if (dev->type == ADDR_TYPE_BREDR) {
dev_info_free(dev);
continue;
}
return le;
}
-static void stop_discovery(struct btd_adapter *adapter, gboolean suspend)
+/* Called when a session gets removed or the adapter is stopped */
+static void stop_discovery(struct btd_adapter *adapter)
{
- pending_remote_name_cancel(adapter);
-
- if (suspend == FALSE)
- adapter->found_devices = remove_bredr(adapter->found_devices);
+ adapter->found_devices = remove_bredr(adapter->found_devices);
if (adapter->oor_devices) {
g_slist_free(adapter->oor_devices);
}
/* Reset if suspended, otherwise remove timer (software scheduler)
- or request inquiry to stop */
- if (adapter->state & STATE_SUSPENDED) {
- adapter->state &= ~STATE_SUSPENDED;
+ * or request inquiry to stop */
+ if (adapter->discov_suspended) {
+ adapter->discov_suspended = FALSE;
return;
}
- if (adapter->scheduler_id) {
- g_source_remove(adapter->scheduler_id);
- adapter->scheduler_id = 0;
+ if (adapter->discov_id > 0) {
+ g_source_remove(adapter->discov_id);
+ adapter->discov_id = 0;
return;
}
- if (adapter->state & STATE_LE_SCAN)
- adapter_ops->stop_scanning(adapter->dev_id);
- else
- adapter_ops->stop_inquiry(adapter->dev_id);
+ adapter_ops->stop_discovery(adapter->dev_id);
}
static void session_remove(struct session_req *req)
DBG("Stopping discovery");
- stop_discovery(adapter, FALSE);
+ stop_discovery(adapter);
}
}
-static void session_free(struct session_req *req)
+static void session_free(void *data)
{
+ struct session_req *req = data;
+
if (req->id)
g_dbus_remove_watch(req->conn, req->id);
- session_remove(req);
-
if (req->msg) {
dbus_message_unref(req->msg);
if (!req->got_reply && req->mode && req->adapter->agent)
req->id = 0;
+ session_remove(req);
session_free(req);
}
if (req->refcount)
return;
+ session_remove(req);
session_free(req);
}
if (main_opts.attrib_server) {
/* Removes service class */
class[1] = class[1] & 0x1f;
- attrib_gap_set(GATT_CHARAC_APPEARANCE, class, 2);
+ attrib_gap_set(adapter, GATT_CHARAC_APPEARANCE, class, 2);
}
emit_property_changed(connection, adapter->path,
DBUS_TYPE_UINT32, &new_class);
}
-void adapter_update_local_name(struct btd_adapter *adapter, const char *name)
+void adapter_name_changed(struct btd_adapter *adapter, const char *name)
{
- struct hci_dev *dev = &adapter->dev;
-
- if (strncmp(name, dev->name, MAX_NAME_LENGTH) == 0)
+ if (g_strcmp0(adapter->name, name) == 0)
return;
- strncpy(dev->name, name, MAX_NAME_LENGTH);
+ g_free(adapter->name);
+ adapter->name = g_strdup(name);
+
+ if (connection)
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Name",
+ DBUS_TYPE_STRING, &name);
if (main_opts.attrib_server)
- attrib_gap_set(GATT_CHARAC_DEVICE_NAME,
- (const uint8_t *) dev->name, strlen(dev->name));
+ attrib_gap_set(adapter, GATT_CHARAC_DEVICE_NAME,
+ (const uint8_t *) name, strlen(name));
+}
+
+int adapter_set_name(struct btd_adapter *adapter, const char *name)
+{
+ char maxname[MAX_NAME_LENGTH + 1];
- if (!adapter->name_stored) {
- char *name_ptr = dev->name;
+ if (g_strcmp0(adapter->name, name) == 0)
+ return 0;
- write_local_name(&adapter->bdaddr, dev->name);
+ memset(maxname, 0, sizeof(maxname));
+ strncpy(maxname, name, MAX_NAME_LENGTH);
+ if (!g_utf8_validate(maxname, -1, NULL)) {
+ error("Name change failed: supplied name isn't valid UTF-8");
+ return -EINVAL;
+ }
- if (connection)
- emit_property_changed(connection, adapter->path,
- ADAPTER_INTERFACE, "Name",
- DBUS_TYPE_STRING, &name_ptr);
+ if (adapter->up) {
+ int err = adapter_ops->set_name(adapter->dev_id, maxname);
+ if (err < 0)
+ return err;
}
- adapter->name_stored = FALSE;
+ write_local_name(&adapter->bdaddr, maxname);
+
+ return 0;
}
static DBusMessage *set_name(DBusConnection *conn, DBusMessage *msg,
const char *name, void *data)
{
struct btd_adapter *adapter = data;
- struct hci_dev *dev = &adapter->dev;
- char *name_ptr = dev->name;
-
- if (!g_utf8_validate(name, -1, NULL)) {
- error("Name change failed: supplied name isn't valid UTF-8");
- return btd_error_invalid_args(msg);
- }
-
- if (strncmp(name, dev->name, MAX_NAME_LENGTH) == 0)
- goto done;
-
- strncpy(dev->name, name, MAX_NAME_LENGTH);
- write_local_name(&adapter->bdaddr, name);
- emit_property_changed(connection, adapter->path,
- ADAPTER_INTERFACE, "Name",
- DBUS_TYPE_STRING, &name_ptr);
+ int ret;
- if (adapter->up) {
- int err = adapter_ops->set_name(adapter->dev_id, name);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
+ if (adapter->allow_name_changes == FALSE)
+ return btd_error_failed(msg, strerror(EPERM));
- adapter->name_stored = TRUE;
- }
+ ret = adapter_set_name(adapter, name);
+ if (ret == -EINVAL)
+ return btd_error_invalid_args(msg);
+ else if (ret < 0)
+ return btd_error_failed(msg, strerror(-ret));
-done:
return dbus_message_new_method_return(msg);
}
int i;
sdp_list_t *list;
+ if (!adapter->initialized)
+ return;
+
uuids = g_new0(char *, sdp_list_len(adapter->services) + 1);
for (i = 0, list = adapter->services; list; list = list->next) {
adapter_emit_uuids_updated(adapter);
}
-#ifdef __TIZEN_PATCH__
-sdp_list_t *adapter_get_services(struct btd_adapter *adapter)
-{
- return adapter->services;
-}
-#endif
static struct btd_device *adapter_create_device(DBusConnection *conn,
struct btd_adapter *adapter,
const char *address,
- device_type_t type)
+ addr_type_t type)
{
struct btd_device *device;
const char *path;
return device;
return adapter_create_device(conn, adapter, address,
- DEVICE_TYPE_BREDR);
+ ADDR_TYPE_BREDR);
}
-static gboolean stop_scanning(gpointer user_data)
+static gboolean discovery_cb(gpointer user_data)
{
struct btd_adapter *adapter = user_data;
+ int err;
- adapter_ops->stop_scanning(adapter->dev_id);
-
- return FALSE;
-}
-
-static int start_discovery(struct btd_adapter *adapter)
-{
- int err, type;
-
- /* Do not start if suspended */
- if (adapter->state & STATE_SUSPENDED)
- return 0;
-
- /* Postpone discovery if still resolving names */
- if (adapter->state & STATE_RESOLVNAME)
- return 1;
-
- pending_remote_name_cancel(adapter);
-
- type = adapter_get_discover_type(adapter) & ~DISC_RESOLVNAME;
+ adapter->discov_id = 0;
- switch (type) {
- case DISC_STDINQ:
- case DISC_INTERLEAVE:
- err = adapter_ops->start_inquiry(adapter->dev_id,
- 0x08, FALSE);
- break;
- case DISC_PINQ:
- err = adapter_ops->start_inquiry(adapter->dev_id,
- 0x08, TRUE);
- break;
- case DISC_LE:
- err = adapter_ops->start_scanning(adapter->dev_id);
- break;
- default:
- err = -EINVAL;
- }
+ err = adapter_ops->start_discovery(adapter->dev_id);
+ if (err < 0)
+ error("start_discovery: %s (%d)", strerror(-err), -err);
- return err;
+ return FALSE;
}
static DBusMessage *adapter_start_discovery(DBusConnection *conn,
struct btd_adapter *adapter = data;
const char *sender = dbus_message_get_sender(msg);
int err;
- info("adapter_start_discovery");
+
if (!adapter->up)
return btd_error_not_ready(msg);
if (adapter->disc_sessions)
goto done;
- g_slist_foreach(adapter->found_devices, (GFunc) dev_info_free, NULL);
- g_slist_free(adapter->found_devices);
+ g_slist_free_full(adapter->found_devices, dev_info_free);
adapter->found_devices = NULL;
g_slist_free(adapter->oor_devices);
adapter->oor_devices = NULL;
- err = start_discovery(adapter);
+ if (adapter->discov_suspended)
+ goto done;
+
+ err = adapter_ops->start_discovery(adapter->dev_id);
if (err < 0)
return btd_error_failed(msg, strerror(-err));
return dbus_message_new_method_return(msg);
}
-struct remote_device_list_t {
- GSList *list;
- time_t time;
-};
-
static DBusMessage *get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
DBusMessage *reply;
DBusMessageIter iter;
DBusMessageIter dict;
- char str[MAX_NAME_LENGTH + 1], srcaddr[18];
+ char srcaddr[18];
gboolean value;
char **devices, **uuids;
int i;
GSList *l;
sdp_list_t *list;
- info("Get properties 1\n");
+
ba2str(&adapter->bdaddr, srcaddr);
if (check_address(srcaddr) < 0)
dict_append_entry(&dict, "Address", DBUS_TYPE_STRING, &property);
/* Name */
- memset(str, 0, sizeof(str));
- strncpy(str, (char *) adapter->dev.name, MAX_NAME_LENGTH);
- property = str;
+ property = adapter->name ? : "";
dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &property);
/* Discoverable */
value = adapter->scan_mode & SCAN_INQUIRY ? TRUE : FALSE;
dict_append_entry(&dict, "Discoverable", DBUS_TYPE_BOOLEAN, &value);
-#ifdef __TIZEN_PATCH__
- // Adding limited property for setting limited discoverable mode
- /* Limited */
- dict_append_entry(&dict, "Limited", DBUS_TYPE_BOOLEAN,
- &adapter->limited);
-#endif
/* Pairable */
dict_append_entry(&dict, "Pairable", DBUS_TYPE_BOOLEAN,
DBUS_TYPE_UINT32, &adapter->pairable_timeout);
- if (adapter->state & (STATE_PINQ | STATE_STDINQ | STATE_LE_SCAN))
- value = TRUE;
- else
- value = FALSE;
-
/* Discovering */
- dict_append_entry(&dict, "Discovering", DBUS_TYPE_BOOLEAN, &value);
+ dict_append_entry(&dict, "Discovering", DBUS_TYPE_BOOLEAN,
+ &adapter->discovering);
/* Devices */
devices = g_new0(char *, g_slist_length(adapter->devices) + 1);
static DBusMessage *set_property(DBusConnection *conn,
DBusMessage *msg, void *data)
{
- info(" set_property 1 \n");
struct btd_adapter *adapter = data;
DBusMessageIter iter;
DBusMessageIter sub;
dbus_message_iter_get_basic(&sub, &discoverable);
return set_discoverable(conn, msg, discoverable, data);
-#ifdef __TIZEN_PATCH__
- // Adding limited property for setting limited discoverable mode
- } else if (g_str_equal("Limited", property)) {
- gboolean limited;
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_get_basic(&sub, &limited);
-
-/// return set_limited(conn, msg, limited, data);
- return btd_error_invalid_args(msg);
-#endif
} else if (g_str_equal("DiscoverableTimeout", property)) {
uint32_t timeout;
return dbus_message_new_method_return(msg);
}
-static device_type_t flags2type(uint8_t flags)
-{
- /* Inferring the remote type based on the EIR Flags field */
-
- /* For LE only and dual mode the following flags must be zero */
- if (flags & (EIR_SIM_CONTROLLER | EIR_SIM_HOST))
- return DEVICE_TYPE_UNKNOWN;
-
- /* Limited or General discoverable mode bit must be enabled */
- if (!(flags & (EIR_LIM_DISC | EIR_GEN_DISC)))
- return DEVICE_TYPE_UNKNOWN;
-
- if (flags & EIR_BREDR_UNSUP)
- return DEVICE_TYPE_LE;
- else
- return DEVICE_TYPE_DUALMODE;
-}
-
-static gboolean event_is_connectable(uint8_t type)
-{
- switch (type) {
- case ADV_TYPE_IND:
- case ADV_TYPE_DIRECT_IND:
- return TRUE;
- default:
- return FALSE;
- }
-}
-
static struct btd_device *create_device_internal(DBusConnection *conn,
struct btd_adapter *adapter,
- const gchar *address,
- gboolean force, int *err)
+ const char *address, int *err)
{
- struct remote_dev_info *dev, match;
+ struct remote_dev_info *dev;
struct btd_device *device;
- device_type_t type;
+ bdaddr_t addr;
+ addr_type_t type;
- memset(&match, 0, sizeof(struct remote_dev_info));
- str2ba(address, &match.bdaddr);
- match.name_status = NAME_ANY;
+ str2ba(address, &addr);
- dev = adapter_search_found_devices(adapter, &match);
- if (dev && dev->flags)
- type = flags2type(dev->flags);
+ dev = adapter_search_found_devices(adapter, &addr);
+ if (dev)
+ type = dev->type;
else
- type = DEVICE_TYPE_BREDR;
-
- if (!force && type == DEVICE_TYPE_LE &&
- !event_is_connectable(dev->evt_type)) {
- if (err)
- *err = -ENOTCONN;
-
- return NULL;
- }
+ type = ADDR_TYPE_BREDR;
device = adapter_create_device(conn, adapter, address, type);
if (!device && err)
DBG("%s", address);
- device = create_device_internal(conn, adapter, address, TRUE, &err);
+ device = create_device_internal(conn, adapter, address, &err);
if (!device)
goto failed;
- if (device_get_type(device) != DEVICE_TYPE_LE)
+ if (device_is_bredr(device))
err = device_browse_sdp(device, conn, msg, NULL, FALSE);
else
err = device_browse_primary(device, conn, msg, FALSE);
return IO_CAPABILITY_NOINPUTNOOUTPUT;
return IO_CAPABILITY_INVALID;
}
-#ifdef __TIZEN_PATCH__
-DBusMessage *adapter_encrypt_link(DBusConnection *conn,
- DBusMessage *msg,
- const char *address,
- dbus_bool_t encrypt,
- void* data)
+static DBusMessage *create_paired_device(DBusConnection *conn,
+ DBusMessage *msg, void *data)
{
struct btd_adapter *adapter = data;
struct btd_device *device;
- char filename[PATH_MAX + 1];
- bdaddr_t bdaddr;
- struct hci_conn_info_req *cr;
- int opt, dd;
- int dev_id = 0;
+ const gchar *address, *agent_path, *capability, *sender;
+ uint8_t cap;
+ int err;
- DBG("handle_authenticate_link_request1");
- device = adapter_get_device(conn, adapter, address);
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_STRING, &capability,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
- str2ba(address, &bdaddr);
- /* check if there is a pending discover: requested by D-Bus/non clients */
- if (0)//todo
+ if (check_address(address) < 0)
+ return btd_error_invalid_args(msg);
- return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", "Discover in progress");
+ if (!adapter->up)
+ return btd_error_not_ready(msg);
- pending_remote_name_cancel(adapter);
+ sender = dbus_message_get_sender(msg);
+ if (adapter->agent &&
+ agent_matches(adapter->agent, sender, agent_path)) {
+ error("Refusing adapter agent usage as device specific one");
+ return btd_error_invalid_args(msg);
+ }
- if (device_get_bonding(device))
- return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", "Bonding in progress");
+ cap = parse_io_capability(capability);
+ if (cap == IO_CAPABILITY_INVALID)
+ return btd_error_invalid_args(msg);
+ device = adapter_find_device(adapter, address);
+ if (!device) {
+ device = create_device_internal(conn, adapter, address, &err);
+ if (!device)
+ return btd_error_failed(msg, strerror(-err));
+ }
- /*if (adapter_find_auth_request(adapter, &bdaddr))
- return in_progress(msg, "Bonding in progress");*/
+ if (device_is_bredr(device))
+ return device_create_bonding(device, conn, msg,
+ agent_path, cap);
- /* check if a link key already exists */
- create_name(filename, PATH_MAX, STORAGEDIR,address,
- "linkkeys");
+ err = device_browse_primary(device, conn, msg, TRUE);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
- dd = hci_open_dev(dev_id);
- if (dd < 0) {
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".Failed",
- "Device open failed");
- }
+ return NULL;
+}
- cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
- if (!cr) {
- return NULL;
- }
+static gint device_path_cmp(struct btd_device *device, const gchar *path)
+{
+ const gchar *dev_path = device_get_path(device);
- bacpy(&cr->bdaddr, &bdaddr);
- cr->type = ACL_LINK;
- if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
- free(cr);
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".Failed",
- "Getting connection info failed");
- }
-
-#if 0
- if (hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000) < 0) {
- free(cr);
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".Failed",
- "Authentication request failed");
- }
-
-#endif
- info("Encrypt value %d",encrypt);
- if (hci_encrypt_link(dd, htobs(cr->conn_info->handle), encrypt, 25000) < 0) {
- free(cr);
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".Failed",
- "Encryption request failed");
- }
-
- DBG("handle_authenticate_link_request2");
- free(cr);
-
- hci_close_dev(dd);
-
- return dbus_message_new_method_return(msg);
-
-}
-#endif
-
-
-
-
-#ifdef __TIZEN_PATCH__
-static DBusMessage *authenticate_link(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct btd_adapter *adapter = data;
- struct btd_device *device;
- const gchar *address, *agent_path, *capability, *sender;
- uint8_t cap;
- info("authenticate_link 1\n");
- if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
- DBUS_TYPE_OBJECT_PATH, &agent_path,
- DBUS_TYPE_STRING, &capability,
- DBUS_TYPE_INVALID) == FALSE)
- return btd_error_invalid_args(msg);
- info("authenticate_link 2\n");
- if (check_address(address) < 0)
- return btd_error_invalid_args(msg);
- info("authenticate_link 3\n");
- sender = dbus_message_get_sender(msg);
- if (adapter->agent &&
- agent_matches(adapter->agent, sender, agent_path)) {
- error("Refusing adapter agent usage as device specific one");
- return btd_error_invalid_args(msg);
- }
- info("authenticate_link 4\n");
- cap = parse_io_capability(capability);
- if (cap == IO_CAPABILITY_INVALID)
- return btd_error_invalid_args(msg);
- info("authenticate_link 5\n");
- device = adapter_get_device(conn, adapter, address);
- if (!device)
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".Failed",
- "Unable to create a new device object");
- info("authenticate_link 5\n");
- return device_jsr82_authenticate_link(device, conn, msg, agent_path, cap);
-}
-#endif
-
-#ifdef __TIZEN_PATCH__
-static DBusMessage *encrypt_connection(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct btd_adapter *adapter = data;
- gboolean encrypt = FALSE;
- DBG("encrypt_link");
- info("encrypt_connection value 1 %d",encrypt);
- if (!adapter->up)
- return btd_error_not_ready(msg);
- DBusMessageIter iter;
- DBusMessageIter sub;
- const char *address;
-
-
- if (!dbus_message_iter_init(msg, &iter))
- return btd_error_invalid_args(msg);
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_get_basic(&iter, &address);
- dbus_message_iter_next(&iter);
-
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
- return btd_error_invalid_args(msg);
- dbus_message_iter_recurse(&iter, &sub);
-
- if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_get_basic(&sub, &encrypt);
-/* if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &address, DBUS_TYPE_BOOLEAN,&encrypt,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);*/
-
- info("encrypt_connection value 2 %d",encrypt);
- return adapter_encrypt_link(conn, msg, address, encrypt, data);
-
-}
-#endif
-static DBusMessage *create_paired_device(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct btd_adapter *adapter = data;
- struct btd_device *device;
- const gchar *address, *agent_path, *capability, *sender;
- uint8_t cap;
- int err;
-
- if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
- DBUS_TYPE_OBJECT_PATH, &agent_path,
- DBUS_TYPE_STRING, &capability,
- DBUS_TYPE_INVALID) == FALSE)
- return btd_error_invalid_args(msg);
-
- if (check_address(address) < 0)
- return btd_error_invalid_args(msg);
-
- if (!adapter->up)
- return btd_error_not_ready(msg);
-
- sender = dbus_message_get_sender(msg);
- if (adapter->agent &&
- agent_matches(adapter->agent, sender, agent_path)) {
- error("Refusing adapter agent usage as device specific one");
- return btd_error_invalid_args(msg);
- }
-
- cap = parse_io_capability(capability);
- if (cap == IO_CAPABILITY_INVALID)
- return btd_error_invalid_args(msg);
-
- device = adapter_find_device(adapter, address);
- if (!device) {
- device = create_device_internal(conn, adapter, address,
- FALSE, &err);
- if (!device)
- return btd_error_failed(msg, strerror(-err));
- }
-
- if (device_get_type(device) != DEVICE_TYPE_LE)
- return device_create_bonding(device, conn, msg,
- agent_path, cap);
-
- err = device_browse_primary(device, conn, msg, TRUE);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
-
- return NULL;
-}
-
-static gint device_path_cmp(struct btd_device *device, const gchar *path)
-{
- const gchar *dev_path = device_get_path(device);
-
- return strcasecmp(dev_path, path);
-}
+ return strcasecmp(dev_path, path);
+}
static DBusMessage *remove_device(DBusConnection *conn, DBusMessage *msg,
void *data)
{ "FindDevice", "s", "o", find_device },
{ "RegisterAgent", "os", "", register_agent },
{ "UnregisterAgent", "o", "", unregister_agent },
-
- #ifdef __TIZEN_PATCH__
- { "AuthenticateLink","sos", "o", authenticate_link, G_DBUS_METHOD_FLAG_ASYNC},
- { "EncryptLink","sv", "", encrypt_connection, G_DBUS_METHOD_FLAG_ASYNC},
- #endif
{ }
};
void *user_data)
{
struct btd_adapter *adapter = user_data;
- GSList *uuids = bt_string2list(value);
+ GSList *list, *uuids = bt_string2list(value);
struct btd_device *device;
if (g_slist_find_custom(adapter->devices,
key, (GCompareFunc) device_address_cmp))
return;
- device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
+ device = device_create(connection, adapter, key, ADDR_TYPE_BREDR);
if (!device)
return;
device_set_temporary(device, FALSE);
adapter->devices = g_slist_append(adapter->devices, device);
+ list = device_services_from_record(device, uuids);
+ if (list)
+ device_register_services(connection, device, list, ATT_PSM);
+
device_probe_drivers(device, uuids);
- g_slist_foreach(uuids, (GFunc) g_free, NULL);
- g_slist_free(uuids);
+ g_slist_free_full(uuids, g_free);
}
struct adapter_keys {
(GCompareFunc) device_address_cmp))
return;
- device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
+ device = device_create(connection, adapter, key, ADDR_TYPE_BREDR);
if (device) {
device_set_temporary(device, FALSE);
adapter->devices = g_slist_append(adapter->devices, device);
key, (GCompareFunc) device_address_cmp))
return;
- device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
- if (device) {
- device_set_temporary(device, FALSE);
- adapter->devices = g_slist_append(adapter->devices, device);
- }
-}
-
-static void create_stored_device_from_types(char *key, char *value,
- void *user_data)
-{
- GSList *l;
- struct btd_adapter *adapter = user_data;
- struct btd_device *device;
- uint8_t type;
-
- type = strtol(value, NULL, 16);
-
- l = g_slist_find_custom(adapter->devices,
- key, (GCompareFunc) device_address_cmp);
- if (l) {
- device = l->data;
- device_set_type(device, type);
- return;
- }
-
- device = device_create(connection, adapter, key, type);
+ device = device_create(connection, adapter, key, ADDR_TYPE_BREDR);
if (device) {
device_set_temporary(device, FALSE);
adapter->devices = g_slist_append(adapter->devices, device);
struct btd_device *device;
GSList *services, *uuids, *l;
- l = g_slist_find_custom(adapter->devices,
- key, (GCompareFunc) device_address_cmp);
- if (l)
- device = l->data;
- else {
- device = device_create(connection, adapter, key, DEVICE_TYPE_BREDR);
- if (!device)
- return;
+ if (g_slist_find_custom(adapter->devices,
+ key, (GCompareFunc) device_address_cmp))
+ return;
- device_set_temporary(device, FALSE);
- adapter->devices = g_slist_append(adapter->devices, device);
- }
+ /* FIXME: Get the correct LE addr type (public/random) */
+ device = device_create(connection, adapter, key, ADDR_TYPE_LE_PUBLIC);
+ if (!device)
+ return;
+
+ device_set_temporary(device, FALSE);
+ adapter->devices = g_slist_append(adapter->devices, device);
services = string_to_primary_list(value);
if (services == NULL)
for (l = services, uuids = NULL; l; l = l->next) {
struct att_primary *prim = l->data;
uuids = g_slist_append(uuids, prim->uuid);
-
- device_add_primary(device, prim);
}
+ device_register_services(connection, device, services, -1);
+
device_probe_drivers(device, uuids);
- g_slist_free(services);
g_slist_free(uuids);
}
if (err < 0) {
error("Unable to load keys to adapter_ops: %s (%d)",
strerror(-err), -err);
- g_slist_foreach(keys.keys, (GFunc) g_free, NULL);
- g_slist_free(keys.keys);
+ g_slist_free_full(keys.keys, g_free);
}
create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "blocked");
textfile_foreach(filename, create_stored_device_from_blocked, adapter);
-
- create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "types");
- textfile_foreach(filename, create_stored_device_from_types, adapter);
}
int btd_adapter_block_address(struct btd_adapter *adapter, bdaddr_t *bdaddr)
adapter_add_connection(adapter, device);
}
- g_slist_foreach(conns, (GFunc) g_free, NULL);
- g_slist_free(conns);
+ g_slist_free_full(conns, g_free);
}
static int get_discoverable_timeout(const char *src)
adapter->found_devices = g_slist_remove(adapter->found_devices, dev);
}
-static void update_oor_devices(struct btd_adapter *adapter)
-{
- g_slist_foreach(adapter->oor_devices, emit_device_disappeared, adapter);
- g_slist_foreach(adapter->oor_devices, (GFunc) dev_info_free, NULL);
- g_slist_free(adapter->oor_devices);
- adapter->oor_devices = g_slist_copy(adapter->found_devices);
-}
-
-static gboolean bredr_capable(struct btd_adapter *adapter)
-{
- struct hci_dev *dev = &adapter->dev;
-
- return (dev->features[4] & LMP_NO_BREDR) == 0 ? TRUE : FALSE;
-}
-
-static gboolean le_capable(struct btd_adapter *adapter)
-{
- struct hci_dev *dev = &adapter->dev;
-
- return (dev->features[4] & LMP_LE &&
- dev->extfeatures[0] & LMP_HOST_LE) ? TRUE : FALSE;
-}
-
-int adapter_get_discover_type(struct btd_adapter *adapter)
-{
- gboolean le, bredr;
- int type;
-
- le = le_capable(adapter);
- bredr = bredr_capable(adapter);
-
- if (le)
- type = bredr ? DISC_INTERLEAVE : DISC_LE;
- else
- type = main_opts.discov_interval ? DISC_STDINQ :
- DISC_PINQ;
-
- if (main_opts.name_resolv)
- type |= DISC_RESOLVNAME;
-
- return type;
-}
-
void btd_adapter_get_mode(struct btd_adapter *adapter, uint8_t *mode,
uint8_t *on_mode, gboolean *pairable)
{
if (mode) {
if (main_opts.remember_powered == FALSE)
*mode = main_opts.mode;
- else if (read_device_mode(address, str, sizeof(str)) < 0)
- *mode = main_opts.mode;
- else
+ else if (read_device_mode(address, str, sizeof(str)) == 0)
*mode = get_mode(&adapter->bdaddr, str);
- }
-
- if (on_mode) {
- if (main_opts.remember_powered == FALSE) {
- if (adapter->initialized)
- *on_mode = get_mode(&adapter->bdaddr, "on");
- else {
- *on_mode = main_opts.mode;
- adapter->initialized = TRUE;
- }
- } else if (read_on_mode(address, str, sizeof(str)) < 0)
- *on_mode = main_opts.mode;
else
- *on_mode = get_mode(&adapter->bdaddr, str);
+ *mode = main_opts.mode;
}
+ if (on_mode)
+ *on_mode = get_mode(&adapter->bdaddr, "on");
+
if (pairable)
*pairable = adapter->pairable;
}
adapter->up = TRUE;
adapter->discov_timeout = get_discoverable_timeout(address);
adapter->pairable_timeout = get_pairable_timeout(address);
- adapter->state = STATE_IDLE;
adapter->mode = MODE_CONNECTABLE;
+ adapter->off_timer = 0;
- if (main_opts.le)
- adapter_ops->enable_le(adapter->dev_id);
-
- adapter_ops->set_name(adapter->dev_id, adapter->dev.name);
+ /* Forcing: Name is lost when adapter is powered off */
+ if (adapter->name)
+ adapter_ops->set_name(adapter->dev_id, adapter->name);
if (read_local_class(&adapter->bdaddr, cls) < 0) {
uint32_t class = htobl(main_opts.class);
DBG("");
+ if (adapter->mode == MODE_OFF) {
+ g_slist_free_full(adapter->mode_sessions, session_free);
+ adapter->mode_sessions = NULL;
+ }
+
if (adapter->pending_mode == NULL)
return;
int btd_adapter_stop(struct btd_adapter *adapter)
{
- gboolean powered, discoverable, pairable;
-#ifdef __TIZEN_PATCH__
- gboolean limited;
-#endif
+ gboolean prop_false = FALSE;
/* cancel pending timeout */
if (adapter->discov_timeout_id) {
/* check pending requests */
reply_pending_requests(adapter);
- stop_discovery(adapter, FALSE);
+ stop_discovery(adapter);
if (adapter->disc_sessions) {
- g_slist_foreach(adapter->disc_sessions, (GFunc) session_free,
- NULL);
- g_slist_free(adapter->disc_sessions);
+ g_slist_free_full(adapter->disc_sessions, session_free);
adapter->disc_sessions = NULL;
}
adapter_remove_connection(adapter, device);
}
- if (adapter->scan_mode == (SCAN_PAGE | SCAN_INQUIRY)) {
- discoverable = FALSE;
+ if (adapter->scan_mode == (SCAN_PAGE | SCAN_INQUIRY))
emit_property_changed(connection, adapter->path,
ADAPTER_INTERFACE, "Discoverable",
- DBUS_TYPE_BOOLEAN, &discoverable);
- }
+ DBUS_TYPE_BOOLEAN, &prop_false);
- if ((adapter->scan_mode & SCAN_PAGE) && adapter->pairable == TRUE) {
- pairable = FALSE;
+ if ((adapter->scan_mode & SCAN_PAGE) && adapter->pairable == TRUE)
emit_property_changed(connection, adapter->path,
ADAPTER_INTERFACE, "Pairable",
- DBUS_TYPE_BOOLEAN, &pairable);
- }
+ DBUS_TYPE_BOOLEAN, &prop_false);
+
+ if (adapter->discovering)
+ emit_property_changed(connection, adapter->path,
+ ADAPTER_INTERFACE, "Discovering",
+ DBUS_TYPE_BOOLEAN, &prop_false);
- powered = FALSE;
emit_property_changed(connection, adapter->path, ADAPTER_INTERFACE,
- "Powered", DBUS_TYPE_BOOLEAN, &powered);
+ "Powered", DBUS_TYPE_BOOLEAN, &prop_false);
- adapter->up = 0;
+ adapter->up = FALSE;
adapter->scan_mode = SCAN_DISABLED;
adapter->mode = MODE_OFF;
- adapter->state = STATE_IDLE;
adapter->off_requested = FALSE;
- adapter->name_stored = FALSE;
call_adapter_powered_callbacks(adapter, FALSE);
return 0;
}
-int adapter_update_ssp_mode(struct btd_adapter *adapter, uint8_t mode)
+static void off_timer_remove(struct btd_adapter *adapter)
{
- struct hci_dev *dev = &adapter->dev;
-
- dev->ssp_mode = mode;
-
- return 0;
+ g_source_remove(adapter->off_timer);
+ adapter->off_timer = 0;
}
static void adapter_free(gpointer user_data)
if (adapter->auth_idle_id)
g_source_remove(adapter->auth_idle_id);
+ if (adapter->off_timer)
+ off_timer_remove(adapter);
+
sdp_list_free(adapter->services, NULL);
- g_slist_foreach(adapter->found_devices, (GFunc) dev_info_free, NULL);
- g_slist_free(adapter->found_devices);
+ g_slist_free_full(adapter->found_devices, dev_info_free);
g_slist_free(adapter->oor_devices);
g_free(adapter->path);
+ g_free(adapter->name);
g_free(adapter);
}
gboolean adapter_init(struct btd_adapter *adapter)
{
- struct hci_version ver;
- struct hci_dev *dev;
- int err;
-
/* adapter_ops makes sure that newly registered adapters always
* start off as powered */
adapter->up = TRUE;
+ adapter->allow_name_changes = TRUE;
+
adapter_ops->read_bdaddr(adapter->dev_id, &adapter->bdaddr);
if (bacmp(&adapter->bdaddr, BDADDR_ANY) == 0) {
return FALSE;
}
- err = adapter_ops->read_local_version(adapter->dev_id, &ver);
- if (err < 0) {
- error("Can't read version info for hci%d: %s (%d)",
- adapter->dev_id, strerror(-err), -err);
- return FALSE;
- }
-
- dev = &adapter->dev;
-
- dev->hci_rev = ver.hci_rev;
- dev->lmp_ver = ver.lmp_ver;
- dev->lmp_subver = ver.lmp_subver;
- dev->manufacturer = ver.manufacturer;
-
- err = adapter_ops->read_local_features(adapter->dev_id, dev->features);
- if (err < 0) {
- error("Can't read features for hci%d: %s (%d)",
- adapter->dev_id, strerror(-err), -err);
- return FALSE;
- }
-
- if (read_local_name(&adapter->bdaddr, adapter->dev.name) < 0)
- expand_name(adapter->dev.name, MAX_NAME_LENGTH, main_opts.name,
- adapter->dev_id);
+ sdp_init_services_list(&adapter->bdaddr);
if (main_opts.attrib_server)
- attrib_gap_set(GATT_CHARAC_DEVICE_NAME,
- (const uint8_t *) dev->name, strlen(dev->name));
+ btd_adapter_gatt_server_start(adapter);
- sdp_init_services_list(&adapter->bdaddr);
load_drivers(adapter);
clear_blocked(adapter);
load_devices(adapter);
* the are active connections before the daemon've started */
load_connections(adapter);
+ adapter->initialized = TRUE;
+
return TRUE;
}
g_slist_free(adapter->devices);
unload_drivers(adapter);
+ if (main_opts.attrib_server)
+ btd_adapter_gatt_server_stop(adapter);
+
+ g_slist_free(adapter->pin_callbacks);
/* Return adapter to down state if it was not up on init */
adapter_ops->restore_powered(adapter->dev_id);
-
- btd_adapter_unref(adapter);
}
uint16_t adapter_get_dev_id(struct btd_adapter *adapter)
bacpy(bdaddr, &adapter->bdaddr);
}
-void adapter_set_state(struct btd_adapter *adapter, int state)
+void adapter_set_allow_name_changes(struct btd_adapter *adapter,
+ gboolean allow_name_changes)
+{
+ adapter->allow_name_changes = allow_name_changes;
+}
+
+void adapter_set_discovering(struct btd_adapter *adapter,
+ gboolean discovering)
{
const char *path = adapter->path;
- gboolean discov_active;
- int previous, type;
- if (adapter->state == state)
+ adapter->discovering = discovering;
+
+ emit_property_changed(connection, path,
+ ADAPTER_INTERFACE, "Discovering",
+ DBUS_TYPE_BOOLEAN, &discovering);
+
+ if (discovering)
return;
- previous = adapter->state;
- adapter->state = state;
+ g_slist_foreach(adapter->oor_devices, emit_device_disappeared, adapter);
+ g_slist_free_full(adapter->oor_devices, dev_info_free);
+ adapter->oor_devices = g_slist_copy(adapter->found_devices);
- type = adapter_get_discover_type(adapter);
+ if (!adapter_has_discov_sessions(adapter) || adapter->discov_suspended)
+ return;
- switch (state) {
- case STATE_STDINQ:
- case STATE_PINQ:
- discov_active = TRUE;
+ DBG("hci%u enabling timer, disc_sessions %u", adapter->dev_id,
+ g_slist_length(adapter->disc_sessions));
- /* Started a new session while resolving names ? */
- if (previous & STATE_RESOLVNAME)
- return;
- break;
- case STATE_LE_SCAN:
- /* Scanning enabled */
- adapter->stop_discov_id = g_timeout_add(5120,
- stop_scanning, adapter);
+ adapter->discov_id = g_timeout_add_seconds(main_opts.discov_interval,
+ discovery_cb, adapter);
+}
- /* For dual mode: don't send "Discovering = TRUE" */
- if (bredr_capable(adapter) == TRUE)
- return;
+static void suspend_discovery(struct btd_adapter *adapter)
+{
+ if (adapter->disc_sessions == NULL || adapter->discov_suspended)
+ return;
- /* LE only */
- discov_active = TRUE;
+ DBG("Suspending discovery");
- break;
- case STATE_IDLE:
- /*
- * Interleave: from inquiry to scanning. Interleave is not
- * applicable to requests triggered by external applications.
- */
- if (adapter->disc_sessions && (type & DISC_INTERLEAVE) &&
- (previous & STATE_STDINQ)) {
- adapter_ops->start_scanning(adapter->dev_id);
- return;
- }
- /* BR/EDR only: inquiry finished */
- discov_active = FALSE;
- break;
- default:
- discov_active = FALSE;
- break;
+ if (adapter->oor_devices) {
+ g_slist_free(adapter->oor_devices);
+ adapter->oor_devices = NULL;
}
- if (discov_active == FALSE) {
- if (type & DISC_RESOLVNAME) {
- if (adapter_resolve_names(adapter) == 0) {
- adapter->state |= STATE_RESOLVNAME;
- return;
- }
- }
-
- update_oor_devices(adapter);
- } else if (adapter->disc_sessions && main_opts.discov_interval)
- adapter->scheduler_id = g_timeout_add_seconds(
- main_opts.discov_interval,
- (GSourceFunc) start_discovery,
- adapter);
+ adapter->discov_suspended = TRUE;
- emit_property_changed(connection, path,
- ADAPTER_INTERFACE, "Discovering",
- DBUS_TYPE_BOOLEAN, &discov_active);
+ if (adapter->discov_id > 0) {
+ g_source_remove(adapter->discov_id);
+ adapter->discov_id = 0;
+ } else
+ adapter_ops->stop_discovery(adapter->dev_id);
}
-int adapter_get_state(struct btd_adapter *adapter)
+static int found_device_cmp(gconstpointer a, gconstpointer b)
{
- return adapter->state;
+ const struct remote_dev_info *d = a;
+ const bdaddr_t *bdaddr = b;
+
+ if (bacmp(bdaddr, BDADDR_ANY) == 0)
+ return 0;
+
+ return bacmp(&d->bdaddr, bdaddr);
}
struct remote_dev_info *adapter_search_found_devices(struct btd_adapter *adapter,
- struct remote_dev_info *match)
+ bdaddr_t *bdaddr)
{
GSList *l;
- l = g_slist_find_custom(adapter->found_devices, match,
- (GCompareFunc) found_device_cmp);
+ l = g_slist_find_custom(adapter->found_devices, bdaddr,
+ found_device_cmp);
if (l)
return l->data;
static char **strlist2array(GSList *list)
{
- GSList *l;
unsigned int i, n;
char **array;
n = g_slist_length(list);
array = g_new0(char *, n + 1);
- for (l = list, i = 0; l; l = l->next, i++)
- array[i] = g_strdup((const gchar *) l->data);
+ for (i = 0; list; list = list->next, i++)
+ array[i] = g_strdup((const gchar *) list->data);
return array;
}
struct btd_device *device;
char peer_addr[18], local_addr[18];
const char *icon, *paddr = peer_addr;
- dbus_bool_t paired = FALSE;
+ dbus_bool_t paired = FALSE, trusted = FALSE;
dbus_int16_t rssi = dev->rssi;
char *alias;
size_t uuid_count;
ba2str(&adapter->bdaddr, local_addr);
device = adapter_find_device(adapter, paddr);
- if (device)
+ if (device) {
paired = device_is_paired(device);
+ trusted = device_is_trusted(device);
+ }
/* The uuids string array is updated only if necessary */
uuid_count = g_slist_length(dev->services);
dev->uuid_count = uuid_count;
}
- if (dev->le) {
+ if (!dev->alias) {
+ if (!dev->name) {
+ alias = g_strdup(peer_addr);
+ g_strdelimit(alias, ":", '-');
+ } else
+ alias = g_strdup(dev->name);
+ } else
+ alias = g_strdup(dev->alias);
+
+ if (dev->type != ADDR_TYPE_BREDR) {
gboolean broadcaster;
if (dev->flags & (EIR_LIM_DISC | EIR_GEN_DISC))
else
broadcaster = TRUE;
+ dev->legacy = FALSE;
+
emit_device_found(adapter->path, paddr,
"Address", DBUS_TYPE_STRING, &paddr,
"RSSI", DBUS_TYPE_INT16, &rssi,
"Name", DBUS_TYPE_STRING, &dev->name,
+ "Alias", DBUS_TYPE_STRING, &alias,
+ "LegacyPairing", DBUS_TYPE_BOOLEAN, &dev->legacy,
"Paired", DBUS_TYPE_BOOLEAN, &paired,
"Broadcaster", DBUS_TYPE_BOOLEAN, &broadcaster,
"UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
NULL);
- return;
- }
-
- icon = class_to_icon(dev->class);
-
- if (!dev->alias) {
- if (!dev->name) {
- alias = g_strdup(peer_addr);
- g_strdelimit(alias, ":", '-');
- } else
- alias = g_strdup(dev->name);
- } else
- alias = g_strdup(dev->alias);
+ } else {
+ icon = class_to_icon(dev->class);
- emit_device_found(adapter->path, paddr,
- "Address", DBUS_TYPE_STRING, &paddr,
- "Class", DBUS_TYPE_UINT32, &dev->class,
- "Icon", DBUS_TYPE_STRING, &icon,
- "RSSI", DBUS_TYPE_INT16, &rssi,
- "Name", DBUS_TYPE_STRING, &dev->name,
- "Alias", DBUS_TYPE_STRING, &alias,
- "LegacyPairing", DBUS_TYPE_BOOLEAN, &dev->legacy,
- "Paired", DBUS_TYPE_BOOLEAN, &paired,
- "UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
- NULL);
+ emit_device_found(adapter->path, paddr,
+ "Address", DBUS_TYPE_STRING, &paddr,
+ "Class", DBUS_TYPE_UINT32, &dev->class,
+ "Icon", DBUS_TYPE_STRING, &icon,
+ "RSSI", DBUS_TYPE_INT16, &rssi,
+ "Name", DBUS_TYPE_STRING, &dev->name,
+ "Alias", DBUS_TYPE_STRING, &alias,
+ "LegacyPairing", DBUS_TYPE_BOOLEAN, &dev->legacy,
+ "Paired", DBUS_TYPE_BOOLEAN, &paired,
+ "Trusted", DBUS_TYPE_BOOLEAN, &trusted,
+ "UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
+ NULL);
+ }
g_free(alias);
}
-static struct remote_dev_info *get_found_dev(struct btd_adapter *adapter,
- const bdaddr_t *bdaddr,
- gboolean *new_dev)
+static struct remote_dev_info *found_device_new(const bdaddr_t *bdaddr,
+ addr_type_t type, const char *name,
+ const char *alias, uint32_t class,
+ gboolean legacy, int flags)
{
- struct remote_dev_info *dev, match;
-
- memset(&match, 0, sizeof(struct remote_dev_info));
- bacpy(&match.bdaddr, bdaddr);
- match.name_status = NAME_ANY;
+ struct remote_dev_info *dev;
- dev = adapter_search_found_devices(adapter, &match);
- if (dev) {
- *new_dev = FALSE;
- /* Out of range list update */
- adapter->oor_devices = g_slist_remove(adapter->oor_devices,
- dev);
- } else {
- *new_dev = TRUE;
- dev = g_new0(struct remote_dev_info, 1);
- bacpy(&dev->bdaddr, bdaddr);
- adapter->found_devices = g_slist_prepend(adapter->found_devices,
- dev);
- }
+ dev = g_new0(struct remote_dev_info, 1);
+ bacpy(&dev->bdaddr, bdaddr);
+ dev->type = type;
+ dev->name = g_strdup(name);
+ dev->alias = g_strdup(alias);
+ dev->class = class;
+ dev->legacy = legacy;
+ if (flags >= 0)
+ dev->flags = flags;
return dev;
}
dev->services = g_slist_prepend(dev->services, g_strdup(new_uuid));
}
-void adapter_update_device_from_info(struct btd_adapter *adapter,
- bdaddr_t bdaddr, int8_t rssi,
- uint8_t evt_type, const char *name,
- GSList *services, int flags)
+static gboolean pairing_is_legacy(bdaddr_t *local, bdaddr_t *peer,
+ const uint8_t *eir, const char *name)
{
- struct remote_dev_info *dev;
- gboolean new_dev;
+ unsigned char features[8];
- dev = get_found_dev(adapter, &bdaddr, &new_dev);
+ if (eir)
+ return FALSE;
- if (new_dev) {
- dev->le = TRUE;
- dev->evt_type = evt_type;
- } else if (dev->rssi == rssi)
- return;
+ if (name == NULL)
+ return TRUE;
- dev->rssi = rssi;
+ if (read_remote_features(local, peer, NULL, features) < 0)
+ return TRUE;
- adapter->found_devices = g_slist_sort(adapter->found_devices,
- (GCompareFunc) dev_rssi_cmp);
+ if (features[0] & 0x01)
+ return FALSE;
+ else
+ return TRUE;
+}
- g_slist_foreach(services, remove_same_uuid, dev);
- g_slist_foreach(services, dev_prepend_uuid, dev);
+static char *read_stored_data(bdaddr_t *local, bdaddr_t *peer, const char *file)
+{
+ char local_addr[18], peer_addr[18], filename[PATH_MAX + 1];
- if (flags >= 0)
- dev->flags = flags;
+ ba2str(local, local_addr);
+ ba2str(peer, peer_addr);
- if (name) {
- g_free(dev->name);
- dev->name = g_strdup(name);
- }
+ create_name(filename, PATH_MAX, STORAGEDIR, local_addr, file);
- /* FIXME: check if other information was changed before emitting the
- * signal */
- adapter_emit_device_found(adapter, dev);
+ return textfile_get(filename, peer_addr);
}
-void adapter_update_found_devices(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- int8_t rssi, uint32_t class, const char *name,
- const char *alias, gboolean legacy,
- GSList *services, name_status_t name_status)
+void adapter_update_found_devices(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr, addr_type_t type,
+ uint32_t class, int8_t rssi,
+ uint8_t confirm_name,
+ uint8_t *data, uint8_t data_len)
{
struct remote_dev_info *dev;
- gboolean new_dev;
+ struct eir_data eir_data;
+ char *alias, *name;
+ gboolean legacy, name_known;
+ int err;
+
+ memset(&eir_data, 0, sizeof(eir_data));
+ err = eir_parse(&eir_data, data, data_len);
+ if (err < 0) {
+ error("Error parsing EIR data: %s (%d)", strerror(-err), -err);
+ return;
+ }
- dev = get_found_dev(adapter, bdaddr, &new_dev);
+ if (eir_data.name != NULL && eir_data.name_complete)
+ write_device_name(&adapter->bdaddr, bdaddr, eir_data.name);
- if (new_dev) {
- if (name)
- dev->name = g_strdup(name);
+ dev = adapter_search_found_devices(adapter, bdaddr);
+ if (dev) {
+ adapter->oor_devices = g_slist_remove(adapter->oor_devices,
+ dev);
+ if (dev->rssi != rssi)
+ goto done;
- if (alias)
- dev->alias = g_strdup(alias);
+ eir_data_free(&eir_data);
- dev->le = FALSE;
- dev->class = class;
- dev->legacy = legacy;
- dev->name_status = name_status;
- } else if (dev->rssi == rssi)
return;
+ }
- dev->rssi = rssi;
+ /* New device in the discovery session */
- adapter->found_devices = g_slist_sort(adapter->found_devices,
- (GCompareFunc) dev_rssi_cmp);
+ name = read_stored_data(&adapter->bdaddr, bdaddr, "names");
- g_slist_foreach(services, remove_same_uuid, dev);
- g_slist_foreach(services, dev_prepend_uuid, dev);
+ if (type == ADDR_TYPE_BREDR) {
+ legacy = pairing_is_legacy(&adapter->bdaddr, bdaddr, data,
+ name);
- adapter_emit_device_found(adapter, dev);
-}
+ if (!name && main_opts.name_resolv &&
+ adapter_has_discov_sessions(adapter))
+ name_known = FALSE;
+ else
+ name_known = TRUE;
+ } else {
+ legacy = FALSE;
+ name_known = TRUE;
+ }
-int adapter_remove_found_device(struct btd_adapter *adapter, bdaddr_t *bdaddr)
-{
- struct remote_dev_info *dev, match;
+ if (confirm_name)
+ adapter_ops->confirm_name(adapter->dev_id, bdaddr, name_known);
- memset(&match, 0, sizeof(struct remote_dev_info));
- bacpy(&match.bdaddr, bdaddr);
+ alias = read_stored_data(&adapter->bdaddr, bdaddr, "aliases");
- dev = adapter_search_found_devices(adapter, &match);
- if (!dev)
- return -1;
+ dev = found_device_new(bdaddr, type, name, alias, class, legacy,
+ eir_data.flags);
+ free(name);
+ free(alias);
- dev->name_status = NAME_NOT_REQUIRED;
+ adapter->found_devices = g_slist_prepend(adapter->found_devices, dev);
- return 0;
+done:
+ dev->rssi = rssi;
+
+ adapter->found_devices = g_slist_sort(adapter->found_devices,
+ (GCompareFunc) dev_rssi_cmp);
+
+ g_slist_foreach(eir_data.services, remove_same_uuid, dev);
+ g_slist_foreach(eir_data.services, dev_prepend_uuid, dev);
+
+ adapter_emit_device_found(adapter, dev);
+
+ eir_data_free(&eir_data);
}
void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode)
gboolean discoverable, pairable;
DBG("old 0x%02x new 0x%02x", adapter->scan_mode, scan_mode);
-#ifdef __TIZEN_PATCH__
- gboolean limited;
-#endif
if (adapter->scan_mode == scan_mode)
return;
adapter->mode = MODE_OFF;
discoverable = FALSE;
pairable = FALSE;
-#ifdef __TIZEN_PATCH__
- limited = FALSE;
-#endif
break;
case SCAN_PAGE:
adapter->mode = MODE_CONNECTABLE;
discoverable = FALSE;
pairable = adapter->pairable;
-#ifdef __TIZEN_PATCH__
- limited = FALSE;
-#endif
break;
case (SCAN_PAGE | SCAN_INQUIRY):
adapter->mode = MODE_DISCOVERABLE;
struct agent *adapter_get_agent(struct btd_adapter *adapter)
{
- if (!adapter || !adapter->agent)
+ if (!adapter)
return NULL;
return adapter->agent;
void adapter_remove_connection(struct btd_adapter *adapter,
struct btd_device *device)
{
- bdaddr_t bdaddr;
-
DBG("");
if (!g_slist_find(adapter->connections, device)) {
adapter->connections = g_slist_remove(adapter->connections, device);
- /* clean pending HCI cmds */
- device_get_address(device, &bdaddr);
-
if (device_is_authenticating(device))
device_cancel_authentication(device, TRUE);
return TRUE;
}
-void adapter_suspend_discovery(struct btd_adapter *adapter)
-{
- if (adapter->disc_sessions == NULL ||
- adapter->state & STATE_SUSPENDED)
- return;
-
- DBG("Suspending discovery");
-
- stop_discovery(adapter, TRUE);
- adapter->state |= STATE_SUSPENDED;
-}
-
-void adapter_resume_discovery(struct btd_adapter *adapter)
-{
- if (adapter->disc_sessions == NULL)
- return;
-
- DBG("Resuming discovery");
-
- adapter->state &= ~STATE_SUSPENDED;
- start_discovery(adapter);
-}
-
int btd_register_adapter_driver(struct btd_adapter_driver *driver)
{
adapter_drivers = g_slist_append(adapter_drivers, driver);
adapter_any_path = NULL;
}
-gboolean adapter_is_pairable(struct btd_adapter *adapter)
-{
- return adapter->pairable;
-}
-
gboolean adapter_powering_down(struct btd_adapter *adapter)
{
return adapter->off_requested;
int btd_adapter_restore_powered(struct btd_adapter *adapter)
{
char mode[14], address[18];
- gboolean discoverable;
if (!adapter_ops)
return -EINVAL;
g_str_equal(mode, "off"))
return 0;
- discoverable = get_mode(&adapter->bdaddr, mode) == MODE_DISCOVERABLE;
-
return adapter_ops->set_powered(adapter->dev_id, TRUE);
}
+static gboolean switch_off_timeout(gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ adapter_ops->set_powered(adapter->dev_id, FALSE);
+ adapter->off_timer = 0;
+
+ return FALSE;
+}
+
int btd_adapter_switch_online(struct btd_adapter *adapter)
{
if (!adapter_ops)
return -EINVAL;
if (adapter->up)
- return 0;
+ return -EALREADY;
+
+ if (adapter->off_timer)
+ off_timer_remove(adapter);
return adapter_ops->set_powered(adapter->dev_id, TRUE);
}
return -EINVAL;
if (!adapter->up)
+ return -EALREADY;
+
+ if (adapter->off_timer)
return 0;
- return adapter_ops->set_powered(adapter->dev_id, FALSE);
+ adapter->global_mode = MODE_OFF;
+
+ if (adapter->connections == NULL)
+ return adapter_ops->set_powered(adapter->dev_id, FALSE);
+
+ g_slist_foreach(adapter->connections,
+ (GFunc) device_request_disconnect, NULL);
+
+ adapter->off_timer = g_timeout_add_seconds(OFF_TIMER,
+ switch_off_timeout, adapter);
+
+ return 0;
+}
+
+static gboolean disable_auto(gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ GSList *l;
+
+ for (l = adapter->devices; l; l = l->next) {
+ struct btd_device *device = l->data;
+
+ device_set_auto_connect(device, FALSE);
+ }
+
+ adapter->auto_timeout_id = 0;
+
+ return FALSE;
+}
+
+static void set_auto_connect(gpointer data, gpointer user_data)
+{
+ struct btd_device *device = data;
+
+ device_set_auto_connect(device, TRUE);
+}
+
+void btd_adapter_enable_auto_connect(struct btd_adapter *adapter)
+{
+ if (!adapter->up)
+ return;
+
+ DBG("Enabling automatic connections");
+
+ if (adapter->auto_timeout_id)
+ return;
+
+ g_slist_foreach(adapter->devices, set_auto_connect, NULL);
+
+ adapter->auto_timeout_id = g_timeout_add_seconds(main_opts.autoto,
+ disable_auto, adapter);
+}
+
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+ btd_adapter_pin_cb_t cb)
+{
+ adapter->pin_callbacks = g_slist_prepend(adapter->pin_callbacks, cb);
+}
+
+void btd_adapter_unregister_pin_cb(struct btd_adapter *adapter,
+ btd_adapter_pin_cb_t cb)
+{
+ adapter->pin_callbacks = g_slist_remove(adapter->pin_callbacks, cb);
+}
+
+ssize_t btd_adapter_get_pin(struct btd_adapter *adapter, struct btd_device *dev,
+ char *pin_buf)
+{
+ GSList *l;
+ btd_adapter_pin_cb_t cb;
+ bdaddr_t sba, dba;
+ ssize_t ret;
+
+ for (l = adapter->pin_callbacks; l != NULL; l = g_slist_next(l)) {
+ cb = l->data;
+ ret = cb(adapter, dev, pin_buf);
+ if (ret > 0)
+ return ret;
+ }
+
+ adapter_get_address(adapter, &sba);
+ device_get_address(dev, &dba, NULL);
+
+ return read_pin_code(&sba, &dba, pin_buf);
}
int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority)
}
int btd_adapter_pincode_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- const char *pin)
+ const char *pin, size_t pin_len)
{
- return adapter_ops->pincode_reply(adapter->dev_id, bdaddr, pin);
+ return adapter_ops->pincode_reply(adapter->dev_id, bdaddr, pin,
+ pin_len);
}
int btd_adapter_confirm_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
return adapter_ops->passkey_reply(adapter->dev_id, bdaddr, passkey);
}
-void btd_adapter_update_local_ext_features(struct btd_adapter *adapter,
- const uint8_t *features)
-{
- struct hci_dev *dev = &adapter->dev;
-
- memcpy(dev->extfeatures, features, 8);
-}
-
int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
bt_hci_result_t cb, gpointer user_data)
{
int adapter_create_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
uint8_t io_cap)
{
+ suspend_discovery(adapter);
return adapter_ops->create_bonding(adapter->dev_id, bdaddr, io_cap);
}
{
return adapter_ops->cancel_bonding(adapter->dev_id, bdaddr);
}
+
+void adapter_bonding_complete(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint8_t status)
+{
+ struct btd_device *device;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ if (status == 0)
+ device = adapter_get_device(connection, adapter, addr);
+ else
+ device = adapter_find_device(adapter, addr);
+
+ if (device != NULL)
+ device_bonding_complete(device, status);
+
+ if (adapter->discov_suspended) {
+ adapter->discov_suspended = FALSE;
+ adapter_ops->start_discovery(adapter->dev_id);
+ }
+}
+
+int btd_adapter_read_local_oob_data(struct btd_adapter *adapter)
+{
+ return adapter_ops->read_local_oob_data(adapter->dev_id);
+}
+
+int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr, uint8_t *hash, uint8_t *randomizer)
+{
+ return adapter_ops->add_remote_oob_data(adapter->dev_id, bdaddr, hash,
+ randomizer);
+}
+
+int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr)
+{
+ return adapter_ops->remove_remote_oob_data(adapter->dev_id, bdaddr);
+}
#define MODE_DISCOVERABLE 0x02
#define MODE_UNKNOWN 0xff
-/* Discover states */
-#define STATE_IDLE 0x00
-#define STATE_LE_SCAN 0x01
-#define STATE_STDINQ 0x02
-#define STATE_PINQ 0x04
-#define STATE_RESOLVNAME 0x08
-#define STATE_SUSPENDED 0x10
-
-/* Supported host/controller discover type */
-#define DISC_LE 0x01
-#define DISC_STDINQ 0x02
-#define DISC_INTERLEAVE 0x04
-#define DISC_PINQ 0x08
-#define DISC_RESOLVNAME 0x10
-
#define MAX_NAME_LENGTH 248
/* Invalid SSP passkey value used to indicate negative replies */
#define INVALID_PASSKEY 0xffffffff
typedef enum {
- NAME_ANY,
- NAME_NOT_REQUIRED, /* used by get remote name without name resolving */
- NAME_REQUIRED, /* remote name needs be resolved */
- NAME_REQUESTED, /* HCI remote name request was sent */
-} name_status_t;
+ ADDR_TYPE_BREDR,
+ ADDR_TYPE_LE_PUBLIC,
+ ADDR_TYPE_LE_RANDOM,
+} addr_type_t;
struct btd_adapter;
struct remote_dev_info {
bdaddr_t bdaddr;
+ addr_type_t type;
int8_t rssi;
uint32_t class;
char *name;
char *alias;
dbus_bool_t legacy;
- name_status_t name_status;
- gboolean le;
char **uuids;
size_t uuid_count;
GSList *services;
- uint8_t evt_type;
uint8_t bdaddr_type;
uint8_t flags;
};
-struct hci_dev {
- uint8_t features[8];
- uint8_t extfeatures[8];
- uint8_t lmp_ver;
- uint16_t lmp_subver;
- uint16_t hci_rev;
- uint16_t manufacturer;
-
- uint8_t ssp_mode;
- char name[MAX_NAME_LENGTH + 1];
-};
-
-#ifdef __TIZEN_PATCH__
-#ifndef MAX_REMOTE_SERVICES
- #define MAX_REMOTE_SERVICES 0x14
-#endif
-typedef struct
-{
- DBusMessage *msg;
- DBusConnection* conn;
-#if 0 //Service search
-#else
- DBusMessageIter* array_iter;
-#endif
-}service_dbus_ctxt_t;
-
-#endif
void btd_adapter_start(struct btd_adapter *adapter);
int btd_adapter_stop(struct btd_adapter *adapter);
void btd_adapter_get_mode(struct btd_adapter *adapter, uint8_t *mode,
uint8_t *on_mode, gboolean *pairable);
-int adapter_update_ssp_mode(struct btd_adapter *adapter, uint8_t mode);
-
struct btd_device *adapter_get_device(DBusConnection *conn,
struct btd_adapter *adapter, const char *address);
struct btd_device *device,
gboolean remove_storage);
-int adapter_resolve_names(struct btd_adapter *adapter);
-
struct btd_adapter *adapter_create(DBusConnection *conn, int id);
gboolean adapter_init(struct btd_adapter *adapter);
void adapter_remove(struct btd_adapter *adapter);
+void adapter_set_allow_name_changes(struct btd_adapter *adapter,
+ gboolean allow_name_changes);
+void adapter_set_discovering(struct btd_adapter *adapter,
+ gboolean discovering);
uint16_t adapter_get_dev_id(struct btd_adapter *adapter);
const gchar *adapter_get_path(struct btd_adapter *adapter);
void adapter_get_address(struct btd_adapter *adapter, bdaddr_t *bdaddr);
void adapter_set_state(struct btd_adapter *adapter, int state);
int adapter_get_state(struct btd_adapter *adapter);
-int adapter_get_discover_type(struct btd_adapter *adapter);
struct remote_dev_info *adapter_search_found_devices(struct btd_adapter *adapter,
- struct remote_dev_info *match);
-void adapter_update_device_from_info(struct btd_adapter *adapter,
- bdaddr_t bdaddr, int8_t rssi,
- uint8_t evt_type, const char *name,
- GSList *services, int flags);
-void adapter_update_found_devices(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- int8_t rssi, uint32_t class, const char *name,
- const char *alias, gboolean legacy,
- GSList *services, name_status_t name_status);
-int adapter_remove_found_device(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+ bdaddr_t *bdaddr);
+void adapter_update_found_devices(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr, addr_type_t type,
+ uint32_t class, int8_t rssi,
+ uint8_t confirm_name,
+ uint8_t *data, uint8_t data_len);
void adapter_emit_device_found(struct btd_adapter *adapter,
struct remote_dev_info *dev);
void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode);
-void adapter_update_local_name(struct btd_adapter *adapter, const char *name);
+int adapter_set_name(struct btd_adapter *adapter, const char *name);
+void adapter_name_changed(struct btd_adapter *adapter, const char *name);
void adapter_service_insert(struct btd_adapter *adapter, void *rec);
void adapter_service_remove(struct btd_adapter *adapter, void *rec);
void btd_adapter_class_changed(struct btd_adapter *adapter,
void adapter_remove_connection(struct btd_adapter *adapter,
struct btd_device *device);
gboolean adapter_has_discov_sessions(struct btd_adapter *adapter);
-void adapter_suspend_discovery(struct btd_adapter *adapter);
-void adapter_resume_discovery(struct btd_adapter *adapter);
struct btd_adapter *btd_adapter_ref(struct btd_adapter *adapter);
void btd_adapter_unref(struct btd_adapter *adapter);
const char *btd_adapter_any_request_path(void);
void btd_adapter_any_release_path(void);
-gboolean adapter_is_pairable(struct btd_adapter *adapter);
gboolean adapter_powering_down(struct btd_adapter *adapter);
int btd_adapter_restore_powered(struct btd_adapter *adapter);
int btd_adapter_switch_online(struct btd_adapter *adapter);
int btd_adapter_switch_offline(struct btd_adapter *adapter);
+void btd_adapter_enable_auto_connect(struct btd_adapter *adapter);
+
+typedef ssize_t (*btd_adapter_pin_cb_t) (struct btd_adapter *adapter,
+ struct btd_device *dev, char *out);
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+ btd_adapter_pin_cb_t cb);
+void btd_adapter_unregister_pin_cb(struct btd_adapter *adapter,
+ btd_adapter_pin_cb_t cb);
+ssize_t btd_adapter_get_pin(struct btd_adapter *adapter, struct btd_device *dev,
+ char *pin_buf);
typedef void (*bt_hci_result_t) (uint8_t status, gpointer user_data);
int (*set_discoverable) (int index, gboolean discoverable);
int (*set_pairable) (int index, gboolean pairable);
int (*set_limited_discoverable) (int index, gboolean limited);
- int (*start_inquiry) (int index, uint8_t length, gboolean periodic);
- int (*stop_inquiry) (int index);
- int (*start_scanning) (int index);
- int (*stop_scanning) (int index);
+ int (*start_discovery) (int index);
+ int (*stop_discovery) (int index);
- int (*resolve_name) (int index, bdaddr_t *bdaddr);
- int (*cancel_resolve_name) (int index, bdaddr_t *bdaddr);
int (*set_name) (int index, const char *name);
int (*set_dev_class) (int index, uint8_t major, uint8_t minor);
int (*set_fast_connectable) (int index, gboolean enable);
int (*block_device) (int index, bdaddr_t *bdaddr);
int (*unblock_device) (int index, bdaddr_t *bdaddr);
int (*get_conn_list) (int index, GSList **conns);
- int (*read_local_version) (int index, struct hci_version *ver);
- int (*read_local_features) (int index, uint8_t *features);
int (*disconnect) (int index, bdaddr_t *bdaddr);
int (*remove_bonding) (int index, bdaddr_t *bdaddr);
- int (*pincode_reply) (int index, bdaddr_t *bdaddr, const char *pin);
+ int (*pincode_reply) (int index, bdaddr_t *bdaddr, const char *pin,
+ size_t pin_len);
int (*confirm_reply) (int index, bdaddr_t *bdaddr, gboolean success);
int (*passkey_reply) (int index, bdaddr_t *bdaddr, uint32_t passkey);
- int (*enable_le) (int index);
int (*encrypt_link) (int index, bdaddr_t *bdaddr, bt_hci_result_t cb,
gpointer user_data);
int (*set_did) (int index, uint16_t vendor, uint16_t product,
int (*set_io_capability) (int index, uint8_t io_capability);
int (*create_bonding) (int index, bdaddr_t *bdaddr, uint8_t io_cap);
int (*cancel_bonding) (int index, bdaddr_t *bdaddr);
+ int (*read_local_oob_data) (int index);
+ int (*add_remote_oob_data) (int index, bdaddr_t *bdaddr, uint8_t *hash,
+ uint8_t *randomizer);
+ int (*remove_remote_oob_data) (int index, bdaddr_t *bdaddr);
+ int (*confirm_name) (int index, bdaddr_t *bdaddr, gboolean name_known);
};
int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority);
int btd_adapter_remove_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr);
int btd_adapter_pincode_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- const char *pin);
+ const char *pin, size_t pin_len);
int btd_adapter_confirm_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
gboolean success);
int btd_adapter_passkey_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
uint32_t passkey);
-void btd_adapter_update_local_ext_features(struct btd_adapter *adapter,
- const uint8_t *features);
-
int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
bt_hci_result_t cb, gpointer user_data);
int adapter_cancel_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr);
-#ifdef __TIZEN_PATCH__
-sdp_list_t *adapter_get_services(struct btd_adapter *adapter);
-#endif
+void adapter_bonding_complete(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint8_t status);
+
+int btd_adapter_read_local_oob_data(struct btd_adapter *adapter);
+
+int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr, uint8_t *hash, uint8_t *randomizer);
+
+int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr);
+
+int btd_adapter_gatt_server_start(struct btd_adapter *adapter);
+void btd_adapter_gatt_server_stop(struct btd_adapter *adapter);
#include "log.h"
-#include "hcid.h"
#include "adapter.h"
#include "device.h"
#include "agent.h"
static DBusConnection *connection = NULL;
-static int request_fallback(struct agent_request *req,
- DBusPendingCallNotifyFunction function);
-
static void agent_release(struct agent *agent)
{
DBusMessage *message;
dbus_error_init(&err);
if (dbus_set_error_from_message(&err, message)) {
- if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
- g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
- request_fallback(req, simple_agent_reply) == 0) {
- dbus_error_free(&err);
- return;
- }
-
error("Agent replied with an error: %s, %s",
err.name, err.message);
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
if (strcmp(err.message, "CanceledbyUser") == 0)
{
set_cancel_from_authentication_req(req->user_data);
struct agent_request *req;
int err;
- info("agent_authorize");
if (agent->request)
return -EBUSY;
dbus_error_init(&err);
if (dbus_set_error_from_message(&err, message)) {
- if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
- g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
- request_fallback(req, pincode_reply) == 0) {
- dbus_error_free(&err);
- return;
- }
-
- error("Agent replied with an error: %s, %s",
- err.name, err.message);
+ error("Agent %s replied with an error: %s, %s",
+ agent->path, err.name, err.message);
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
if (strcmp(err.message, "CanceledbyUser") == 0)
{
set_cancel_from_authentication_req(req->user_data);
}
static int pincode_request_new(struct agent_request *req, const char *device_path,
- dbus_bool_t numeric)
+ dbus_bool_t secure)
{
struct agent *agent = req->agent;
+ /* TODO: Add a new method or a new param to Agent interface to request
+ secure pin. */
+
req->msg = dbus_message_new_method_call(agent->name, agent->path,
"org.bluez.Agent", "RequestPinCode");
if (req->msg == NULL) {
}
int agent_request_pincode(struct agent *agent, struct btd_device *device,
- agent_pincode_cb cb, void *user_data,
- GDestroyNotify destroy)
+ agent_pincode_cb cb, gboolean secure,
+ void *user_data, GDestroyNotify destroy)
{
struct agent_request *req;
const gchar *dev_path = device_get_path(device);
req = agent_request_new(agent, AGENT_REQUEST_PINCODE, cb,
user_data, destroy);
- err = pincode_request_new(req, dev_path, FALSE);
+ err = pincode_request_new(req, dev_path, secure);
if (err < 0)
goto failed;
dbus_error_init(&err);
if (dbus_set_error_from_message(&err, message)) {
- if ((g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err.name) ||
- g_str_equal(DBUS_ERROR_NO_REPLY, err.name)) &&
- request_fallback(req, passkey_reply) == 0) {
- dbus_error_free(&err);
- return;
- }
-
error("Agent replied with an error: %s, %s",
- err.name, err.message);
-#ifdef __TIZEN_PATCH__
+ err.name, err.message);
+#ifdef __SAMSUNG_PATCH__
if (strcmp(err.message, "CanceledbyUser") == 0)
{
set_cancel_from_authentication_req(req->user_data);
return err;
}
-static int request_fallback(struct agent_request *req,
- DBusPendingCallNotifyFunction function)
-{
- struct btd_adapter *adapter = req->agent->adapter;
- struct agent *adapter_agent = adapter_get_agent(adapter);
- DBusMessage *msg;
-
- if (req->agent == adapter_agent || adapter_agent == NULL)
- return -EINVAL;
-
- dbus_pending_call_cancel(req->call);
- dbus_pending_call_unref(req->call);
-
- msg = dbus_message_copy(req->msg);
-
- dbus_message_set_destination(msg, adapter_agent->name);
- dbus_message_set_path(msg, adapter_agent->path);
-
- if (dbus_connection_send_with_reply(connection, msg,
- &req->call, REQUEST_TIMEOUT) == FALSE) {
- error("D-Bus send failed");
- dbus_message_unref(msg);
- return -EIO;
- }
-
- req->agent->request = NULL;
- req->agent = adapter_agent;
- req->agent->request = req;
-
- dbus_message_unref(req->msg);
- req->msg = msg;
-
- dbus_pending_call_set_notify(req->call, function, req, NULL);
-
- return 0;
-}
-
int agent_display_passkey(struct agent *agent, struct btd_device *device,
uint32_t passkey)
{
GDestroyNotify destroy);
int agent_request_pincode(struct agent *agent, struct btd_device *device,
- agent_pincode_cb cb, void *user_data,
- GDestroyNotify destroy);
+ agent_pincode_cb cb, gboolean secure,
+ void *user_data, GDestroyNotify destroy);
int agent_confirm_mode_change(struct agent *agent, const char *new_mode,
agent_cb cb, void *user_data,
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+typedef void (*attio_connect_cb) (GAttrib *attrib, gpointer user_data);
+typedef void (*attio_disconnect_cb) (gpointer user_data);
+
+guint btd_device_add_attio_callback(struct btd_device *device,
+ attio_connect_cb cfunc,
+ attio_disconnect_cb dcfunc,
+ gpointer user_data);
+
+gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id);
#include <bluetooth/sdp_lib.h>
#include "log.h"
-#include "glib-helper.h"
+#include "gdbus.h"
+#include "glib-compat.h"
#include "btio.h"
#include "sdpd.h"
#include "hcid.h"
+#include "adapter.h"
+#include "device.h"
+#include "manager.h"
#include "att.h"
#include "gattrib.h"
+#include "storage.h"
#include "attrib-server.h"
-#define GATT_PSM 0x1f
-#define GATT_CID 4
-
-static GSList *database = NULL;
+static GSList *servers = NULL;
+
+struct gatt_server {
+ struct btd_adapter *adapter;
+ GIOChannel *l2cap_io;
+ GIOChannel *le_io;
+ uint32_t gatt_sdp_handle;
+ uint32_t gap_sdp_handle;
+ GSList *database;
+ GSList *clients;
+ uint16_t name_handle;
+ uint16_t appearance_handle;
+};
struct gatt_channel {
bdaddr_t src;
bdaddr_t dst;
- GSList *configs;
- GSList *notify;
- GSList *indicate;
GAttrib *attrib;
guint mtu;
gboolean le;
guint id;
gboolean encrypted;
+ struct gatt_server *server;
};
struct group_elem {
uint16_t len;
};
-static GIOChannel *l2cap_io = NULL;
-static GIOChannel *le_io = NULL;
-static GSList *clients = NULL;
-static uint32_t gatt_sdp_handle = 0;
-static uint32_t gap_sdp_handle = 0;
-
-/* GAP attribute handles */
-static uint16_t name_handle = 0x0000;
-static uint16_t appearance_handle = 0x0000;
-
static bt_uuid_t prim_uuid = {
.type = BT_UUID16,
.value.u16 = GATT_PRIM_SVC_UUID
.type = BT_UUID16,
.value.u16 = GATT_SND_SVC_UUID
};
+static bt_uuid_t ccc_uuid = {
+ .type = BT_UUID16,
+ .value.u16 = GATT_CLIENT_CHARAC_CFG_UUID
+};
+
+static void attrib_free(void *data)
+{
+ struct attribute *a = data;
+
+ g_free(a->data);
+ g_free(a);
+}
+
+static void channel_free(struct gatt_channel *channel)
+{
+ g_attrib_unref(channel->attrib);
+
+ g_free(channel);
+}
+
+static void gatt_server_free(struct gatt_server *server)
+{
+ g_slist_free_full(server->database, attrib_free);
+
+ if (server->l2cap_io != NULL) {
+ g_io_channel_unref(server->l2cap_io);
+ g_io_channel_shutdown(server->l2cap_io, FALSE, NULL);
+ }
+
+ if (server->le_io != NULL) {
+ g_io_channel_unref(server->le_io);
+ g_io_channel_shutdown(server->le_io, FALSE, NULL);
+ }
+
+ g_slist_free_full(server->clients, (GDestroyNotify) channel_free);
+
+ if (server->gatt_sdp_handle > 0)
+ remove_record_from_server(server->gatt_sdp_handle);
+
+ if (server->gap_sdp_handle > 0)
+ remove_record_from_server(server->gap_sdp_handle);
+
+ if (server->adapter != NULL)
+ btd_adapter_unref(server->adapter);
+
+ g_free(server);
+}
+
+static gint adapter_cmp_addr(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_server *server = a;
+ const bdaddr_t *bdaddr = b;
+ bdaddr_t src;
+
+ adapter_get_address(server->adapter, &src);
+
+ return bacmp(&src, bdaddr);
+}
+
+static gint adapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_server *server = a;
+ const struct btd_adapter *adapter = b;
+
+ if (server->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct gatt_server *find_gatt_server(const bdaddr_t *bdaddr)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(servers, bdaddr, adapter_cmp_addr);
+ if (l == NULL) {
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ error("Not GATT adapter found for address %s", addr);
+ return NULL;
+ }
+
+ return l->data;
+}
static sdp_record_t *server_record_new(uuid_t *uuid, uint16_t start, uint16_t end)
{
uuid_t root_uuid, proto_uuid, l2cap;
sdp_record_t *record;
sdp_data_t *psm, *sh, *eh;
- uint16_t lp = GATT_PSM;
+ uint16_t lp = ATT_PSM;
if (uuid == NULL)
return NULL;
return attrib1->handle - attrib2->handle;
}
-static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode,
- int reqs)
+static struct attribute *find_primary_range(struct gatt_server *server,
+ uint16_t start, uint16_t *end)
{
- /* FIXME: currently, it is assumed an encrypted link is enough for
- * authentication. This will allow to enable the SMP negotiation once
- * it is on upstream kernel. */
- if (!channel->encrypted)
- channel->encrypted = g_attrib_is_encrypted(channel->attrib);
- if (reqs == ATT_AUTHENTICATION && !channel->encrypted)
- return ATT_ECODE_INSUFF_ENC;
+ struct attribute *attrib;
+ guint h = start;
+ GSList *l;
- switch (opcode) {
- case ATT_OP_READ_BY_GROUP_REQ:
- case ATT_OP_READ_BY_TYPE_REQ:
- case ATT_OP_READ_REQ:
- case ATT_OP_READ_BLOB_REQ:
- case ATT_OP_READ_MULTI_REQ:
- if (reqs == ATT_NOT_PERMITTED)
- return ATT_ECODE_READ_NOT_PERM;
- break;
- case ATT_OP_PREP_WRITE_REQ:
- case ATT_OP_WRITE_REQ:
- case ATT_OP_WRITE_CMD:
- if (reqs == ATT_NOT_PERMITTED)
- return ATT_ECODE_WRITE_NOT_PERM;
- break;
- }
+ if (end == NULL)
+ return NULL;
- return 0;
-}
+ l = g_slist_find_custom(server->database, GUINT_TO_POINTER(h),
+ handle_cmp);
+ if (!l)
+ return NULL;
-static uint8_t client_set_notifications(struct attribute *attr,
- gpointer user_data)
-{
- struct gatt_channel *channel = user_data;
- struct attribute *last_chr_val = NULL;
- uint16_t cfg_val;
- uint8_t props;
- bt_uuid_t uuid;
- GSList *l;
+ attrib = l->data;
- cfg_val = att_get_u16(attr->data);
+ if (bt_uuid_cmp(&attrib->uuid, &prim_uuid) != 0)
+ return NULL;
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- for (l = database, props = 0; l != NULL; l = l->next) {
+ *end = start;
+
+ for (l = l->next; l; l = l->next) {
struct attribute *a = l->data;
- static uint16_t handle = 0;
- if (a->handle >= attr->handle)
+ if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+ bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)
break;
- if (bt_uuid_cmp(&a->uuid, &uuid) == 0) {
- props = att_get_u8(&a->data[0]);
- handle = att_get_u16(&a->data[1]);
- continue;
- }
-
- if (handle && a->handle == handle)
- last_chr_val = a;
+ *end = a->handle;
}
- if (last_chr_val == NULL)
- return 0;
+ return attrib;
+}
- if ((cfg_val & 0x0001) && !(props & ATT_CHAR_PROPER_NOTIFY))
- return ATT_ECODE_WRITE_NOT_PERM;
+static uint32_t attrib_create_sdp_new(struct gatt_server *server,
+ uint16_t handle, const char *name)
+{
+ sdp_record_t *record;
+ struct attribute *a;
+ uint16_t end = 0;
+ uuid_t svc, gap_uuid;
+ bdaddr_t addr;
- if ((cfg_val & 0x0002) && !(props & ATT_CHAR_PROPER_INDICATE))
- return ATT_ECODE_WRITE_NOT_PERM;
+ a = find_primary_range(server, handle, &end);
- if (cfg_val & 0x0001)
- channel->notify = g_slist_append(channel->notify, last_chr_val);
- else
- channel->notify = g_slist_remove(channel->notify, last_chr_val);
+ if (a == NULL)
+ return 0;
- if (cfg_val & 0x0002)
- channel->indicate = g_slist_append(channel->indicate,
- last_chr_val);
+ if (a->len == 2)
+ sdp_uuid16_create(&svc, att_get_u16(a->data));
+ else if (a->len == 16)
+ sdp_uuid128_create(&svc, a->data);
else
- channel->indicate = g_slist_remove(channel->indicate,
- last_chr_val);
+ return 0;
+
+ record = server_record_new(&svc, handle, end);
+ if (record == NULL)
+ return 0;
+
+ if (name != NULL)
+ sdp_set_info_attr(record, name, "BlueZ", NULL);
+ sdp_uuid16_create(&gap_uuid, GENERIC_ACCESS_PROFILE_ID);
+ if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
+ sdp_set_url_attr(record, "http://www.bluez.org/",
+ "http://www.bluez.org/",
+ "http://www.bluez.org/");
+ }
+
+ adapter_get_address(server->adapter, &addr);
+ if (add_record_to_server(&addr, record) == 0)
+ return record->handle;
+
+ sdp_record_free(record);
return 0;
}
-static struct attribute *client_cfg_attribute(struct gatt_channel *channel,
- struct attribute *orig_attr,
- const uint8_t *value, int vlen)
+static struct attribute *attrib_db_add_new(struct gatt_server *server,
+ uint16_t handle, bt_uuid_t *uuid, int read_reqs,
+ int write_reqs, const uint8_t *value, int len)
{
- guint handle = orig_attr->handle;
- bt_uuid_t uuid;
- GSList *l;
+ struct attribute *a;
+ guint h = handle;
+
+ DBG("handle=0x%04x", handle);
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
- if (bt_uuid_cmp(&orig_attr->uuid, &uuid) != 0)
+ if (g_slist_find_custom(server->database, GUINT_TO_POINTER(h),
+ handle_cmp))
return NULL;
- /* Value is unchanged, not need to create a private copy yet */
- if (vlen == orig_attr->len && memcmp(orig_attr->data, value, vlen) == 0)
- return orig_attr;
+ a = g_new0(struct attribute, 1);
+ a->len = len;
+ a->data = g_memdup(value, len);
+ a->handle = handle;
+ a->uuid = *uuid;
+ a->read_reqs = read_reqs;
+ a->write_reqs = write_reqs;
- l = g_slist_find_custom(channel->configs, GUINT_TO_POINTER(handle),
- handle_cmp);
- if (!l) {
- struct attribute *a;
-
- /* Create a private copy of the Client Characteristic
- * Configuration attribute */
- a = g_malloc0(sizeof(*a) + vlen);
- memcpy(a, orig_attr, sizeof(*a));
- memcpy(a->data, value, vlen);
- a->write_cb = client_set_notifications;
- a->cb_user_data = channel;
-
- channel->configs = g_slist_insert_sorted(channel->configs, a,
+ server->database = g_slist_insert_sorted(server->database, a,
attribute_cmp);
- return a;
+ return a;
+}
+
+static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode,
+ int reqs)
+{
+ /* FIXME: currently, it is assumed an encrypted link is enough for
+ * authentication. This will allow to enable the SMP negotiation once
+ * it is on upstream kernel. High security level should be mapped
+ * to authentication and medium to encryption permission. */
+ if (!channel->encrypted)
+ channel->encrypted = g_attrib_is_encrypted(channel->attrib);
+ if (reqs == ATT_AUTHENTICATION && !channel->encrypted)
+ return ATT_ECODE_AUTHENTICATION;
+ else if (reqs == ATT_AUTHORIZATION)
+ return ATT_ECODE_AUTHORIZATION;
+
+ switch (opcode) {
+ case ATT_OP_READ_BY_GROUP_REQ:
+ case ATT_OP_READ_BY_TYPE_REQ:
+ case ATT_OP_READ_REQ:
+ case ATT_OP_READ_BLOB_REQ:
+ case ATT_OP_READ_MULTI_REQ:
+ if (reqs == ATT_NOT_PERMITTED)
+ return ATT_ECODE_READ_NOT_PERM;
+ break;
+ case ATT_OP_PREP_WRITE_REQ:
+ case ATT_OP_WRITE_REQ:
+ case ATT_OP_WRITE_CMD:
+ if (reqs == ATT_NOT_PERMITTED)
+ return ATT_ECODE_WRITE_NOT_PERM;
+ break;
}
- return l->data;
+ return 0;
}
static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start,
struct att_data_list *adl;
struct attribute *a;
struct group_elem *cur, *old = NULL;
- GSList *l, *groups;
+ GSList *l, *groups, *database;
uint16_t length, last_handle, last_size = 0;
uint8_t status;
int i;
ATT_ECODE_UNSUPP_GRP_TYPE, pdu, len);
last_handle = end;
- for (l = database, groups = NULL; l; l = l->next) {
- struct attribute *client_attr;
+ database = channel->server->database;
+ for (l = database, groups = NULL, cur = NULL; l; l = l->next) {
a = l->data;
status = att_check_reqs(channel, ATT_OP_READ_BY_GROUP_REQ,
a->read_reqs);
- client_attr = client_cfg_attribute(channel, a, a->data, a->len);
- if (client_attr)
- a = client_attr;
-
if (status == 0x00 && a->read_cb)
status = a->read_cb(a, a->cb_user_data);
if (status) {
- g_slist_foreach(groups, (GFunc) g_free, NULL);
- g_slist_free(groups);
+ g_slist_free_full(groups, g_free);
return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ,
a->handle, status, pdu, len);
}
length = enc_read_by_grp_resp(adl, pdu, len);
att_data_list_free(adl);
- g_slist_foreach(groups, (GFunc) g_free, NULL);
- g_slist_free(groups);
+ g_slist_free_full(groups, g_free);
return length;
}
uint8_t *pdu, int len)
{
struct att_data_list *adl;
- GSList *l, *types;
+ GSList *l, *types, *database;
struct attribute *a;
uint16_t num, length;
uint8_t status;
return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ, start,
ATT_ECODE_INVALID_HANDLE, pdu, len);
+ database = channel->server->database;
for (l = database, length = 0, types = NULL; l; l = l->next) {
- struct attribute *client_attr;
a = l->data;
if (a->handle < start)
continue;
- if (a->handle >= end)
+ if (a->handle > end)
break;
if (bt_uuid_cmp(&a->uuid, uuid) != 0)
status = att_check_reqs(channel, ATT_OP_READ_BY_TYPE_REQ,
a->read_reqs);
- client_attr = client_cfg_attribute(channel, a, a->data, a->len);
- if (client_attr)
- a = client_attr;
-
if (status == 0x00 && a->read_cb)
status = a->read_cb(a, a->cb_user_data);
return length;
}
-static int find_info(uint16_t start, uint16_t end, uint8_t *pdu, int len)
+static int find_info(struct gatt_channel *channel, uint16_t start, uint16_t end,
+ uint8_t *pdu, int len)
{
struct attribute *a;
struct att_data_list *adl;
- GSList *l, *info;
+ GSList *l, *info, *database;
uint8_t format, last_type = BT_UUID_UNSPEC;
uint16_t length, num;
int i;
return enc_error_resp(ATT_OP_FIND_INFO_REQ, start,
ATT_ECODE_INVALID_HANDLE, pdu, len);
+ database = channel->server->database;
for (l = database, info = NULL, num = 0; l; l = l->next) {
a = l->data;
} else if (last_type == BT_UUID128) {
length = 16;
format = 0x02;
+ } else {
+ g_slist_free(info);
+ return 0;
}
adl = att_data_list_alloc(num, length + 2);
return length;
}
-static int find_by_type(uint16_t start, uint16_t end, bt_uuid_t *uuid,
- const uint8_t *value, int vlen, uint8_t *opdu, int mtu)
+static int find_by_type(struct gatt_channel *channel, uint16_t start,
+ uint16_t end, bt_uuid_t *uuid, const uint8_t *value,
+ int vlen, uint8_t *opdu, int mtu)
{
struct attribute *a;
struct att_range *range;
- GSList *l, *matches;
+ GSList *l, *matches, *database;
int len;
if (start > end || start == 0x0000)
ATT_ECODE_INVALID_HANDLE, opdu, mtu);
/* Searching first requested handle number */
+ database = channel->server->database;
for (l = database, matches = NULL, range = NULL; l; l = l->next) {
a = l->data;
len = enc_find_by_type_resp(matches, opdu, mtu);
- g_slist_foreach(matches, (GFunc) g_free, NULL);
- g_slist_free(matches);
+ g_slist_free_full(matches, g_free);
return len;
}
-static struct attribute *find_primary_range(uint16_t start, uint16_t *end)
-{
- struct attribute *attrib;
- guint h = start;
- GSList *l;
-
- if (end == NULL)
- return NULL;
-
- l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
- if (!l)
- return NULL;
-
- attrib = l->data;
-
- if (bt_uuid_cmp(&attrib->uuid, &prim_uuid) != 0)
- return NULL;
-
- *end = start;
-
- for (l = l->next; l; l = l->next) {
- struct attribute *a = l->data;
-
- if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
- bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)
- break;
-
- *end = a->handle;
- }
-
- return attrib;
-}
-
static uint16_t read_value(struct gatt_channel *channel, uint16_t handle,
uint8_t *pdu, int len)
{
- struct attribute *a, *client_attr;
+ struct attribute *a;
uint8_t status;
GSList *l;
+ uint16_t cccval;
guint h = handle;
- l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ l = g_slist_find_custom(channel->server->database,
+ GUINT_TO_POINTER(h), handle_cmp);
if (!l)
return enc_error_resp(ATT_OP_READ_REQ, handle,
ATT_ECODE_INVALID_HANDLE, pdu, len);
a = l->data;
- status = att_check_reqs(channel, ATT_OP_READ_REQ, a->read_reqs);
+ if (bt_uuid_cmp(&ccc_uuid, &a->uuid) == 0 &&
+ read_device_ccc(&channel->src, &channel->dst,
+ handle, &cccval) == 0) {
+ uint8_t config[2];
- client_attr = client_cfg_attribute(channel, a, a->data, a->len);
- if (client_attr)
- a = client_attr;
+ att_put_u16(cccval, config);
+ return enc_read_resp(config, sizeof(config), pdu, len);
+ }
+
+ status = att_check_reqs(channel, ATT_OP_READ_REQ, a->read_reqs);
if (status == 0x00 && a->read_cb)
status = a->read_cb(a, a->cb_user_data);
static uint16_t read_blob(struct gatt_channel *channel, uint16_t handle,
uint16_t offset, uint8_t *pdu, int len)
{
- struct attribute *a, *client_attr;
+ struct attribute *a;
uint8_t status;
GSList *l;
+ uint16_t cccval;
guint h = handle;
- l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ l = g_slist_find_custom(channel->server->database,
+ GUINT_TO_POINTER(h), handle_cmp);
if (!l)
return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
ATT_ECODE_INVALID_HANDLE, pdu, len);
return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
ATT_ECODE_INVALID_OFFSET, pdu, len);
- status = att_check_reqs(channel, ATT_OP_READ_BLOB_REQ, a->read_reqs);
+ if (bt_uuid_cmp(&ccc_uuid, &a->uuid) == 0 &&
+ read_device_ccc(&channel->src, &channel->dst,
+ handle, &cccval) == 0) {
+ uint8_t config[2];
+
+ att_put_u16(cccval, config);
+ return enc_read_blob_resp(config, sizeof(config), offset,
+ pdu, len);
+ }
- client_attr = client_cfg_attribute(channel, a, a->data, a->len);
- if (client_attr)
- a = client_attr;
+ status = att_check_reqs(channel, ATT_OP_READ_BLOB_REQ, a->read_reqs);
if (status == 0x00 && a->read_cb)
status = a->read_cb(a, a->cb_user_data);
const uint8_t *value, int vlen,
uint8_t *pdu, int len)
{
- struct attribute *a, *client_attr;
+ struct attribute *a;
uint8_t status;
GSList *l;
guint h = handle;
- l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ l = g_slist_find_custom(channel->server->database,
+ GUINT_TO_POINTER(h), handle_cmp);
if (!l)
return enc_error_resp(ATT_OP_WRITE_REQ, handle,
ATT_ECODE_INVALID_HANDLE, pdu, len);
return enc_error_resp(ATT_OP_WRITE_REQ, handle, status, pdu,
len);
- client_attr = client_cfg_attribute(channel, a, value, vlen);
- if (client_attr)
- a = client_attr;
- else
- attrib_db_update(a->handle, &a->uuid, value, vlen);
+ if (bt_uuid_cmp(&ccc_uuid, &a->uuid) != 0) {
- if (a->write_cb) {
- status = a->write_cb(a, a->cb_user_data);
- if (status)
- return enc_error_resp(ATT_OP_WRITE_REQ, handle, status,
- pdu, len);
- }
+ attrib_db_update(channel->server->adapter, handle, NULL,
+ value, vlen, NULL);
- DBG("Notifications: %d, indications: %d",
- g_slist_length(channel->notify),
- g_slist_length(channel->indicate));
+ if (a->write_cb) {
+ status = a->write_cb(a, a->cb_user_data);
+ if (status)
+ return enc_error_resp(ATT_OP_WRITE_REQ, handle,
+ status, pdu, len);
+ }
+ } else {
+ uint16_t cccval = att_get_u16(value);
+ write_device_ccc(&channel->src, &channel->dst, handle, cccval);
+ }
return enc_write_resp(pdu, len);
}
else
channel->mtu = MIN(mtu, channel->mtu);
- bt_io_set(le_io, BT_IO_L2CAP, NULL,
+ bt_io_set(channel->server->le_io, BT_IO_L2CAP, NULL,
BT_IO_OPT_OMTU, channel->mtu,
BT_IO_OPT_INVALID);
{
struct gatt_channel *channel = user_data;
- g_attrib_unref(channel->attrib);
- clients = g_slist_remove(clients, channel);
-
- g_slist_free(channel->notify);
- g_slist_free(channel->indicate);
- g_slist_foreach(channel->configs, (GFunc) g_free, NULL);
- g_slist_free(channel->configs);
-
- g_free(channel);
+ channel->server->clients = g_slist_remove(channel->server->clients,
+ channel);
+ channel_free(channel);
}
static void channel_handler(const uint8_t *ipdu, uint16_t len,
goto done;
}
- length = find_info(start, end, opdu, channel->mtu);
+ length = find_info(channel, start, end, opdu, channel->mtu);
break;
case ATT_OP_WRITE_REQ:
length = dec_write_req(ipdu, len, &start, value, &vlen);
goto done;
}
- length = find_by_type(start, end, &uuid, value, vlen,
+ length = find_by_type(channel, start, end, &uuid, value, vlen,
opdu, channel->mtu);
break;
case ATT_OP_HANDLE_CNF:
NULL, NULL, NULL);
}
-static void connect_event(GIOChannel *io, GError *err, void *user_data)
+guint attrib_channel_attach(GAttrib *attrib, gboolean out)
{
+ struct gatt_server *server;
+ struct btd_device *device;
struct gatt_channel *channel;
- uint16_t cid;
+ GIOChannel *io;
GError *gerr = NULL;
+ char addr[18];
+ uint16_t cid;
- if (err) {
- error("%s", err->message);
- return;
- }
+ io = g_attrib_get_channel(attrib);
channel = g_new0(struct gatt_channel, 1);
error("bt_io_get: %s", gerr->message);
g_error_free(gerr);
g_free(channel);
- g_io_channel_shutdown(io, TRUE, NULL);
- return;
+ return 0;
}
+ server = find_gatt_server(&channel->src);
+ if (server == NULL)
+ return 0;
+
+ channel->server = server;
+
+ ba2str(&channel->dst, addr);
+ device = adapter_find_device(server->adapter, addr);
+
+ if (device_is_bonded(device) == FALSE)
+ delete_device_ccc(&channel->src, &channel->dst);
+
if (channel->mtu > ATT_MAX_MTU)
channel->mtu = ATT_MAX_MTU;
- if (cid != GATT_CID)
+ if (cid != ATT_CID)
channel->le = FALSE;
else
channel->le = TRUE;
- channel->attrib = g_attrib_new(io);
- g_io_channel_unref(io);
- channel->id = g_attrib_register(channel->attrib, GATTRIB_ALL_EVENTS,
- channel_handler, channel, NULL);
+ channel->attrib = g_attrib_ref(attrib);
+ channel->id = g_attrib_register(channel->attrib, GATTRIB_ALL_REQS,
+ channel_handler, channel, NULL);
- g_attrib_set_disconnect_function(channel->attrib, channel_disconnect,
- channel);
+ if (out == FALSE)
+ g_attrib_set_disconnect_function(channel->attrib,
+ channel_disconnect, channel);
+
+ server->clients = g_slist_append(server->clients, channel);
- clients = g_slist_append(clients, channel);
+ return channel->id;
}
-static void confirm_event(GIOChannel *io, void *user_data)
+static gint channel_id_cmp(gconstpointer data, gconstpointer user_data)
+{
+ const struct gatt_channel *channel = data;
+ guint id = GPOINTER_TO_UINT(user_data);
+
+ return channel->id - id;
+}
+
+gboolean attrib_channel_detach(GAttrib *attrib, guint id)
{
+ struct gatt_server *server;
+ struct gatt_channel *channel;
GError *gerr = NULL;
+ GIOChannel *io;
+ bdaddr_t src;
+ GSList *l;
- if (bt_io_accept(io, connect_event, user_data, NULL, &gerr) == FALSE) {
- error("bt_io_accept: %s", gerr->message);
+ io = g_attrib_get_channel(attrib);
+
+ bt_io_get(io, BT_IO_L2CAP, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_INVALID);
+
+ if (gerr != NULL) {
+ error("bt_io_get: %s", gerr->message);
g_error_free(gerr);
- g_io_channel_unref(io);
+ return FALSE;
}
- return;
-}
+ server = find_gatt_server(&src);
+ if (server == NULL)
+ return FALSE;
-static void attrib_notify_clients(struct attribute *attr)
-{
- guint handle = attr->handle;
- GSList *l;
+ l = g_slist_find_custom(server->clients, GUINT_TO_POINTER(id),
+ channel_id_cmp);
+ if (!l)
+ return FALSE;
- for (l = clients; l; l = l->next) {
- struct gatt_channel *channel = l->data;
+ channel = l->data;
- /* Notification */
- if (g_slist_find_custom(channel->notify,
- GUINT_TO_POINTER(handle), handle_cmp)) {
- uint8_t pdu[ATT_MAX_MTU];
- uint16_t len;
+ g_attrib_unregister(channel->attrib, channel->id);
- len = enc_notification(attr, pdu, channel->mtu);
- if (len == 0)
- continue;
+ channel_disconnect(channel);
- g_attrib_send(channel->attrib, 0, pdu[0], pdu, len,
- NULL, NULL, NULL);
- }
+ return TRUE;
+}
+
+static void connect_event(GIOChannel *io, GError *gerr, void *user_data)
+{
+ GAttrib *attrib;
- /* Indication */
- if (g_slist_find_custom(channel->indicate,
- GUINT_TO_POINTER(handle), handle_cmp)) {
- uint8_t pdu[ATT_MAX_MTU];
- uint16_t len;
+ if (gerr) {
+ error("%s", gerr->message);
+ return;
+ }
- len = enc_indication(attr, pdu, channel->mtu);
- if (len == 0)
- return;
+ attrib = g_attrib_new(io);
+ attrib_channel_attach(attrib, FALSE);
+ g_io_channel_unref(io);
+ g_attrib_unref(attrib);
+}
- g_attrib_send(channel->attrib, 0, pdu[0], pdu, len,
- NULL, NULL, NULL);
- }
+static void confirm_event(GIOChannel *io, void *user_data)
+{
+ GError *gerr = NULL;
+
+ if (bt_io_accept(io, connect_event, user_data, NULL, &gerr) == FALSE) {
+ error("bt_io_accept: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
}
+
+ return;
}
-static gboolean register_core_services(void)
+static gboolean register_core_services(struct gatt_server *server)
{
uint8_t atval[256];
bt_uuid_t uuid;
/* GAP service: primary service definition */
bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
att_put_u16(GENERIC_ACCESS_PROFILE_ID, &atval[0]);
- attrib_db_add(0x0001, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+ attrib_db_add_new(server, 0x0001, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 2);
/* GAP service: device name characteristic */
- name_handle = 0x0006;
+ server->name_handle = 0x0006;
bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(name_handle, &atval[1]);
+ att_put_u16(server->name_handle, &atval[1]);
att_put_u16(GATT_CHARAC_DEVICE_NAME, &atval[3]);
- attrib_db_add(0x0004, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+ attrib_db_add_new(server, 0x0004, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
/* GAP service: device name attribute */
bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
- attrib_db_add(name_handle, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
- NULL, 0);
+ attrib_db_add_new(server, server->name_handle, &uuid, ATT_NONE,
+ ATT_NOT_PERMITTED, NULL, 0);
/* GAP service: device appearance characteristic */
- appearance_handle = 0x0008;
+ server->appearance_handle = 0x0008;
bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
atval[0] = ATT_CHAR_PROPER_READ;
- att_put_u16(appearance_handle, &atval[1]);
+ att_put_u16(server->appearance_handle, &atval[1]);
att_put_u16(GATT_CHARAC_APPEARANCE, &atval[3]);
- attrib_db_add(0x0007, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+ attrib_db_add_new(server, 0x0007, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
/* GAP service: device appearance attribute */
bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
att_put_u16(appearance, &atval[0]);
- attrib_db_add(appearance_handle, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
- atval, 2);
- gap_sdp_handle = attrib_create_sdp(0x0001, "Generic Access Profile");
- if (gap_sdp_handle == 0) {
+ attrib_db_add_new(server, server->appearance_handle, &uuid, ATT_NONE,
+ ATT_NOT_PERMITTED, atval, 2);
+ server->gap_sdp_handle = attrib_create_sdp_new(server, 0x0001,
+ "Generic Access Profile");
+ if (server->gap_sdp_handle == 0) {
error("Failed to register GAP service record");
return FALSE;
}
/* GATT service: primary service definition */
bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
att_put_u16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]);
- attrib_db_add(0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+ attrib_db_add_new(server, 0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 2);
- gatt_sdp_handle = attrib_create_sdp(0x0010,
+ server->gatt_sdp_handle = attrib_create_sdp_new(server, 0x0010,
"Generic Attribute Profile");
- if (gatt_sdp_handle == 0) {
+ if (server->gatt_sdp_handle == 0) {
error("Failed to register GATT service record");
- goto failed;
+ return FALSE;
}
return TRUE;
-
-failed:
- remove_record_from_server(gap_sdp_handle);
-
- return FALSE;
}
-int attrib_server_init(void)
+int btd_adapter_gatt_server_start(struct btd_adapter *adapter)
{
+ struct gatt_server *server;
GError *gerr = NULL;
+ bdaddr_t addr;
+
+ DBG("Start GATT server in hci%d", adapter_get_dev_id(adapter));
+
+ server = g_new0(struct gatt_server, 1);
+ server->adapter = btd_adapter_ref(adapter);
+
+ adapter_get_address(server->adapter, &addr);
/* BR/EDR socket */
- l2cap_io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event,
+ server->l2cap_io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event,
NULL, NULL, &gerr,
- BT_IO_OPT_SOURCE_BDADDR, BDADDR_ANY,
- BT_IO_OPT_PSM, GATT_PSM,
+ BT_IO_OPT_SOURCE_BDADDR, &addr,
+ BT_IO_OPT_PSM, ATT_PSM,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
BT_IO_OPT_INVALID);
- if (l2cap_io == NULL) {
+
+ if (server->l2cap_io == NULL) {
error("%s", gerr->message);
g_error_free(gerr);
+ gatt_server_free(server);
return -1;
}
- if (!register_core_services())
- goto failed;
-
- if (!main_opts.le)
- return 0;
+ if (!register_core_services(server)) {
+ gatt_server_free(server);
+ return -1;
+ }
/* LE socket */
- le_io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event,
- &le_io, NULL, &gerr,
- BT_IO_OPT_SOURCE_BDADDR, BDADDR_ANY,
- BT_IO_OPT_CID, GATT_CID,
+ server->le_io = bt_io_listen(BT_IO_L2CAP, NULL, confirm_event,
+ &server->le_io, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &addr,
+ BT_IO_OPT_CID, ATT_CID,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
BT_IO_OPT_INVALID);
- if (le_io == NULL) {
+
+ if (server->le_io == NULL) {
error("%s", gerr->message);
g_error_free(gerr);
/* Doesn't have LE support, continue */
}
+ servers = g_slist_prepend(servers, server);
return 0;
-
-failed:
- g_io_channel_unref(l2cap_io);
- l2cap_io = NULL;
-
- if (le_io) {
- g_io_channel_unref(le_io);
- le_io = NULL;
- }
-
- return -1;
}
-void attrib_server_exit(void)
+void btd_adapter_gatt_server_stop(struct btd_adapter *adapter)
{
+ struct gatt_server *server;
GSList *l;
- g_slist_foreach(database, (GFunc) g_free, NULL);
- g_slist_free(database);
-
- if (l2cap_io) {
- g_io_channel_unref(l2cap_io);
- g_io_channel_shutdown(l2cap_io, FALSE, NULL);
- }
-
- if (le_io) {
- g_io_channel_unref(le_io);
- g_io_channel_shutdown(le_io, FALSE, NULL);
- }
+ l = g_slist_find_custom(servers, adapter, adapter_cmp);
+ if (l == NULL)
+ return;
- for (l = clients; l; l = l->next) {
- struct gatt_channel *channel = l->data;
+ DBG("Stop GATT server in hci%d", adapter_get_dev_id(adapter));
- g_slist_free(channel->notify);
- g_slist_free(channel->indicate);
- g_slist_foreach(channel->configs, (GFunc) g_free, NULL);
- g_slist_free(channel->configs);
+ server = l->data;
+ servers = g_slist_remove(servers, server);
+ gatt_server_free(server);
+}
- g_attrib_unref(channel->attrib);
- g_free(channel);
- }
+uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle,
+ const char *name)
+{
+ GSList *l;
- g_slist_free(clients);
+ l = g_slist_find_custom(servers, adapter, adapter_cmp);
+ if (l == NULL)
+ return 0;
- if (gatt_sdp_handle)
- remove_record_from_server(gatt_sdp_handle);
+ return attrib_create_sdp_new(l->data, handle, name);
+}
- if (gap_sdp_handle)
- remove_record_from_server(gap_sdp_handle);
+void attrib_free_sdp(uint32_t sdp_handle)
+{
+ remove_record_from_server(sdp_handle);
}
-uint32_t attrib_create_sdp(uint16_t handle, const char *name)
+uint16_t attrib_db_find_avail(struct btd_adapter *adapter, uint16_t nitems)
{
- sdp_record_t *record;
- struct attribute *a;
- uint16_t end = 0;
- uuid_t svc, gap_uuid;
+ struct gatt_server *server;
+ uint16_t handle;
+ GSList *l;
- a = find_primary_range(handle, &end);
+ g_assert(nitems > 0);
- if (a == NULL)
+ l = g_slist_find_custom(servers, adapter, adapter_cmp);
+ if (l == NULL)
return 0;
- if (a->len == 2)
- sdp_uuid16_create(&svc, att_get_u16(a->data));
- else if (a->len == 16)
- sdp_uuid128_create(&svc, a->data);
- else
- return 0;
+ server = l->data;
- record = server_record_new(&svc, handle, end);
- if (record == NULL)
- return 0;
+ for (l = server->database, handle = 0; l; l = l->next) {
+ struct attribute *a = l->data;
- if (name)
- sdp_set_info_attr(record, name, "BlueZ", NULL);
+ if (handle && (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+ bt_uuid_cmp(&a->uuid, &snd_uuid) == 0) &&
+ a->handle - handle >= nitems)
+ /* Note: the range above excludes the current handle */
+ return handle;
- sdp_uuid16_create(&gap_uuid, GENERIC_ACCESS_PROFILE_ID);
- if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
- sdp_set_url_attr(record, "http://www.bluez.org/",
- "http://www.bluez.org/",
- "http://www.bluez.org/");
+ if (a->handle == 0xffff)
+ return 0;
+
+ handle = a->handle + 1;
}
- if (add_record_to_server(BDADDR_ANY, record) < 0)
- sdp_record_free(record);
- else
- return record->handle;
+ if (0xffff - handle + 1 >= nitems)
+ return handle;
return 0;
}
-void attrib_free_sdp(uint32_t sdp_handle)
+struct attribute *attrib_db_add(struct btd_adapter *adapter, uint16_t handle,
+ bt_uuid_t *uuid, int read_reqs, int write_reqs,
+ const uint8_t *value, int len)
{
- remove_record_from_server(sdp_handle);
-}
-
-struct attribute *attrib_db_add(uint16_t handle, bt_uuid_t *uuid, int read_reqs,
- int write_reqs, const uint8_t *value, int len)
-{
- struct attribute *a;
-
- /* FIXME: handle conflicts */
-
- a = g_malloc0(sizeof(struct attribute) + len);
- a->handle = handle;
- memcpy(&a->uuid, uuid, sizeof(bt_uuid_t));
- a->read_reqs = read_reqs;
- a->write_reqs = write_reqs;
- a->len = len;
- memcpy(a->data, value, len);
+ GSList *l;
- database = g_slist_insert_sorted(database, a, attribute_cmp);
+ l = g_slist_find_custom(servers, adapter, adapter_cmp);
+ if (l == NULL)
+ return NULL;
- return a;
+ return attrib_db_add_new(l->data, handle, uuid, read_reqs, write_reqs,
+ value, len);
}
-int attrib_db_update(uint16_t handle, bt_uuid_t *uuid, const uint8_t *value,
- int len)
+int attrib_db_update(struct btd_adapter *adapter, uint16_t handle,
+ bt_uuid_t *uuid, const uint8_t *value,
+ int len, struct attribute **attr)
{
+ struct gatt_server *server;
struct attribute *a;
GSList *l;
guint h = handle;
- l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ l = g_slist_find_custom(servers, adapter, adapter_cmp);
+ if (l == NULL)
+ return -ENOENT;
+
+ server = l->data;
+
+ DBG("handle=0x%04x", handle);
+
+ l = g_slist_find_custom(server->database, GUINT_TO_POINTER(h),
+ handle_cmp);
if (!l)
return -ENOENT;
- a = g_try_realloc(l->data, sizeof(struct attribute) + len);
- if (a == NULL)
+ a = l->data;
+
+ a->data = g_try_realloc(a->data, len);
+ if (a->data == NULL)
return -ENOMEM;
- l->data = a;
- a->handle = handle;
- if (uuid != &a->uuid)
- memcpy(&a->uuid, uuid, sizeof(bt_uuid_t));
a->len = len;
memcpy(a->data, value, len);
- attrib_notify_clients(a);
+ if (uuid != NULL)
+ a->uuid = *uuid;
+
+ if (attr)
+ *attr = a;
return 0;
}
-int attrib_db_del(uint16_t handle)
+int attrib_db_del(struct btd_adapter *adapter, uint16_t handle)
{
+ struct gatt_server *server;
struct attribute *a;
GSList *l;
guint h = handle;
- l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
+ l = g_slist_find_custom(servers, adapter, adapter_cmp);
+ if (l == NULL)
+ return -ENOENT;
+
+ server = l->data;
+
+ DBG("handle=0x%04x", handle);
+
+ l = g_slist_find_custom(server->database, GUINT_TO_POINTER(h),
+ handle_cmp);
if (!l)
return -ENOENT;
a = l->data;
- database = g_slist_remove(database, a);
+ server->database = g_slist_remove(server->database, a);
+ g_free(a->data);
g_free(a);
return 0;
}
-int attrib_gap_set(uint16_t uuid, const uint8_t *value, int len)
+int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid,
+ const uint8_t *value, int len)
{
- bt_uuid_t u16;
+ struct gatt_server *server;
uint16_t handle;
+ GSList *l;
- /* FIXME: Missing Privacy and Reconnection Address */
+ l = g_slist_find_custom(servers, adapter, adapter_cmp);
+ if (l == NULL)
+ return -ENOENT;
+
+ server = l->data;
- bt_uuid16_create(&u16, uuid);
+ /* FIXME: Missing Privacy and Reconnection Address */
switch (uuid) {
case GATT_CHARAC_DEVICE_NAME:
- handle = name_handle;
+ handle = server->name_handle;
break;
case GATT_CHARAC_APPEARANCE:
- handle = appearance_handle;
+ handle = server->appearance_handle;
break;
default:
return -ENOSYS;
}
- return attrib_db_update(handle, &u16, value, len);
+ return attrib_db_update(adapter, handle, NULL, value, len, NULL);
}
*
*/
-int attrib_server_init(void);
-void attrib_server_exit(void);
-
-struct attribute *attrib_db_add(uint16_t handle, bt_uuid_t *uuid, int read_reqs,
- int write_reqs, const uint8_t *value, int len);
-int attrib_db_update(uint16_t handle, bt_uuid_t *uuid, const uint8_t *value,
- int len);
-int attrib_db_del(uint16_t handle);
-int attrib_gap_set(uint16_t uuid, const uint8_t *value, int len);
-uint32_t attrib_create_sdp(uint16_t handle, const char *name);
+uint16_t attrib_db_find_avail(struct btd_adapter *adapter, uint16_t nitems);
+struct attribute *attrib_db_add(struct btd_adapter *adapter, uint16_t handle,
+ bt_uuid_t *uuid, int read_reqs, int write_reqs,
+ const uint8_t *value, int len);
+int attrib_db_update(struct btd_adapter *adapter, uint16_t handle,
+ bt_uuid_t *uuid, const uint8_t *value,
+ int len, struct attribute **attr);
+int attrib_db_del(struct btd_adapter *adapter, uint16_t handle);
+int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid,
+ const uint8_t *value, int len);
+uint32_t attrib_create_sdp(struct btd_adapter *adapter, uint16_t handle,
+ const char *name);
void attrib_free_sdp(uint32_t sdp_handle);
+guint attrib_channel_attach(GAttrib *attrib, gboolean out);
+gboolean attrib_channel_detach(GAttrib *attrib, guint id);
<?xml version="1.0"?><!--*-nxml-*-->
+
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
--- /dev/null
+[Unit]
+Description=Bluetooth service
+After=syslog.target
+
+[Service]
+Type=dbus
+BusName=org.bluez
+ExecStart=@prefix@/sbin/bluetoothd -n
+StandardOutput=syslog
+
+[Install]
+WantedBy=bluetooth.target
#endif
#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
-
-#include <bluetooth/bluetooth.h>
+#include <stdint.h>
#include <glib.h>
#include <dbus/dbus.h>
#include "log.h"
-#include "adapter.h"
-#include "manager.h"
-#include "event.h"
#include "dbus-common.h"
static DBusConnection *connection = NULL;
#include "att.h"
#include "hcid.h"
#include "adapter.h"
+#include "gattrib.h"
+#include "attio.h"
#include "device.h"
#include "dbus-common.h"
-#include "event.h"
#include "error.h"
+#include "glib-compat.h"
#include "glib-helper.h"
-#include "gattrib.h"
+#include "sdp-client.h"
#include "gatt.h"
#include "agent.h"
#include "sdp-xml.h"
#include "storage.h"
#include "btio.h"
+#include "attrib-server.h"
+#include "attrib/client.h"
#define DISCONNECT_TIMER 2
#define DISCOVERY_TIMER 2
+#define AUTO_CONNECTION_INTERVAL 5 /* Next connection attempt */
+
/* When all services should trust a remote device */
#define GLOBAL_TRUST "[all]"
-struct btd_driver_data {
- guint id;
- struct btd_device_driver *driver;
- void *priv;
-};
-
struct btd_disconnect_data {
guint id;
disconnect_watch watch;
DBusMessage *msg;
GIOChannel *io;
guint listener_id;
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
guint cancel_by_user;
#endif
struct btd_device *device;
void *cb;
struct agent *agent;
struct btd_device *device;
+ uint32_t passkey;
+ gboolean secure;
};
struct browse_req {
DBusConnection *conn;
DBusMessage *msg;
- GAttrib *attrib;
GIOChannel *io;
+ GAttrib *attrib;
struct btd_device *device;
GSList *match_uuids;
GSList *profiles_added;
guint listener_id;
};
+struct attio_data {
+ guint id;
+ attio_connect_cb cfunc;
+ attio_disconnect_cb dcfunc;
+ gpointer user_data;
+};
+
struct btd_device {
bdaddr_t bdaddr;
- device_type_t type;
+ addr_type_t type;
gchar *path;
char name[MAX_NAME_LENGTH + 1];
char *alias;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
struct btd_adapter *adapter;
GSList *uuids;
GSList *services; /* Primary services path */
GSList *primaries; /* List of primary services */
- GSList *drivers; /* List of driver_data */
+ GSList *drivers; /* List of device drivers */
GSList *watches; /* List of disconnect_data */
gboolean temporary;
struct agent *agent;
struct bonding_req *bonding;
struct authentication_req *authr; /* authentication request */
GSList *disconnects; /* disconnects message */
+ GAttrib *attrib;
+ GSList *attios;
+ GSList *attios_offline;
+ guint attachid; /* Attrib server attach */
+ guint auto_id; /* Auto connect source id */
gboolean connected;
- /* Whether were creating a security mode 3 connection */
- gboolean secmode3;
-
sdp_list_t *tmp_records;
gboolean trusted;
gboolean paired;
gboolean blocked;
+ gboolean bonded;
+ gboolean auto_connect;
gboolean authorizing;
gint ref;
static GSList *device_drivers = NULL;
-static void browse_request_free(struct browse_req *req)
+static void browse_request_free(struct browse_req *req, gboolean shutdown)
{
if (req->listener_id)
g_dbus_remove_watch(req->conn, req->listener_id);
+ if (req->attrib)
+ g_attrib_unref(req->attrib);
+ if (req->io) {
+ if (shutdown)
+ g_io_channel_shutdown(req->io, FALSE, NULL);
+ g_io_channel_unref(req->io);
+ }
if (req->msg)
dbus_message_unref(req->msg);
if (req->conn)
dbus_connection_unref(req->conn);
if (req->device)
btd_device_unref(req->device);
- g_slist_foreach(req->profiles_added, (GFunc) g_free, NULL);
- g_slist_free(req->profiles_added);
+ g_slist_free_full(req->profiles_added, g_free);
g_slist_free(req->profiles_removed);
if (req->records)
sdp_list_free(req->records, (sdp_free_func_t) sdp_record_free);
- if (req->io) {
- g_attrib_unref(req->attrib);
- g_io_channel_unref(req->io);
- g_io_channel_shutdown(req->io, FALSE, NULL);
- }
-
g_free(req);
}
bt_cancel_discovery(&src, &device->bdaddr);
device->browse = NULL;
- browse_request_free(req);
+ browse_request_free(req, TRUE);
}
static void device_free(gpointer user_data)
agent_is_busy(agent, device->authr)))
agent_cancel(agent);
- g_slist_foreach(device->services, (GFunc) g_free, NULL);
- g_slist_free(device->services);
-
- g_slist_foreach(device->uuids, (GFunc) g_free, NULL);
- g_slist_free(device->uuids);
+ g_slist_free_full(device->services, g_free);
+ g_slist_free_full(device->uuids, g_free);
+ g_slist_free_full(device->primaries, g_free);
+ g_slist_free_full(device->attios, g_free);
+ g_slist_free_full(device->attios_offline, g_free);
- g_slist_foreach(device->primaries, (GFunc) g_free, NULL);
- g_slist_free(device->primaries);
+ g_attrib_unref(device->attrib);
if (device->tmp_records)
sdp_list_free(device->tmp_records,
if (device->discov_timer)
g_source_remove(device->discov_timer);
+ if (device->auto_id)
+ g_source_remove(device->auto_id);
+
DBG("%p", device);
g_free(device->authr);
g_free(device);
}
-gboolean device_is_paired(struct btd_device *device)
+gboolean device_is_bredr(struct btd_device *device)
{
- return device->paired;
+ return (device->type == ADDR_TYPE_BREDR);
}
-gboolean device_is_trusted(struct btd_device *device)
+gboolean device_is_le(struct btd_device *device)
{
- return device->trusted;
+ return (device->type != ADDR_TYPE_BREDR);
}
-#ifdef __TIZEN_PATCH__
-#include "oui.h"
-uint32_t device_pin_length(struct btd_device *device)
+gboolean device_is_paired(struct btd_device *device)
{
- struct btd_adapter *adapter = device->adapter;
- bdaddr_t src;
- uint8_t length;
- int len;
-
- adapter_get_address(adapter, &src);
-
- len = read_pin_length(&src, &device->bdaddr);
- if (len < 0)
- len = 0;
-
- length = (uint32_t)len;
-
- return length;
+ return device->paired;
}
-int get_device_company(struct btd_device *device, char *company, size_t size)
+gboolean device_is_bonded(struct btd_device *device)
{
- char oui[9], *tmp;
- int err;
-
- ba2oui(&device->bdaddr, oui);
- tmp = (char*)ouitocomp(oui);
-
- err = snprintf(company, size, "%s", tmp);
-
- free(tmp);
-
- return err;
+ return device->bonded;
}
-int get_device_version_info(struct btd_device *device,
- char *manufacturer, size_t manufacturer_size,
- char *revision, size_t revision_size,
- char *version, size_t version_size)
+gboolean device_is_trusted(struct btd_device *device)
{
- struct btd_adapter *adapter = device->adapter;
- int err;
- bdaddr_t src;
- char dstaddr[18], edr[7], *tmp;
-
- uint16_t remote_manufacturer = 65534;
- uint8_t remote_lmp_ver = 0;
- uint16_t remote_lmp_subver = 0;
- uint8_t features[8] = {0};
-
- if (version_size < 14)
- return -ENOBUFS;
-
- adapter_get_address(adapter, &src);
- ba2str(&device->bdaddr, dstaddr);
-
- err = read_version_info(&src, dstaddr, &remote_manufacturer, &remote_lmp_ver, &remote_lmp_subver, features);
- if (err < 0)
- return err;
-
- tmp = bt_compidtostr(remote_manufacturer);
-
- err = snprintf(manufacturer, manufacturer_size, "%s", tmp);
-
- snprintf(revision, revision_size, "HCI 0x%X", remote_lmp_subver);
-
- if ((remote_lmp_ver == 0x03 || remote_lmp_ver == 0x04) &&
- (features[3] & (LMP_EDR_ACL_2M | LMP_EDR_ACL_3M)))
- sprintf(edr, " + EDR");
- else
- edr[0] = '\0';
-
- tmp = lmp_vertostr(remote_lmp_ver);
-
- if (strlen(tmp) == 0)
- err = snprintf(version, version_size, "not assigned");
- else
- err = snprintf(version, version_size, "Bluetooth %s%s", tmp, edr);
-
- free(tmp);
-
- return err;
+ return device->trusted;
}
-#endif
+
static DBusMessage *get_properties(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
int i;
GSList *l;
-#ifdef __TIZEN_PATCH__
- uint32_t pin_length;
-#endif
-
ba2str(&device->bdaddr, dstaddr);
reply = dbus_message_new_method_return(msg);
DBUS_TYPE_STRING, &icon);
}
+ /* Vendor */
+ if (device->vendor)
+ dict_append_entry(&dict, "Vendor", DBUS_TYPE_UINT16,
+ &device->vendor);
+
+ /* Product */
+ if (device->product)
+ dict_append_entry(&dict, "Product", DBUS_TYPE_UINT16,
+ &device->product);
+
+ /* Version */
+ if (device->product)
+ dict_append_entry(&dict, "Version", DBUS_TYPE_UINT16,
+ &device->version);
+
/* Paired */
boolean = device_is_paired(device);
dict_append_entry(&dict, "Paired", DBUS_TYPE_BOOLEAN, &boolean);
-#ifdef __TIZEN_PATCH__
- /* PinLength */
- pin_length = device_pin_length(device);
- dict_append_entry(&dict, "PinLength", DBUS_TYPE_UINT32, &pin_length);
-#endif
/* Trusted */
boolean = device_is_trusted(device);
dict_append_entry(&dict, "Trusted", DBUS_TYPE_BOOLEAN, &boolean);
return reply;
}
-#ifdef __TIZEN_PATCH__
-static DBusMessage *get_version_properties(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
-{
- struct btd_device *device = user_data;
- struct btd_adapter *adapter = device->adapter;
- DBusMessage *reply;
- DBusMessageIter iter;
- DBusMessageIter dict;
- bdaddr_t src;
- char name[MAX_NAME_LENGTH + 1], srcaddr[18], dstaddr[18];
- const char *ptr;
- uint32_t class;
-
- char company[MAX_NAME_LENGTH + 1];
- char manufacturer[MAX_NAME_LENGTH + 1];
- char revision[MAX_NAME_LENGTH + 1];
- char version[MAX_NAME_LENGTH + 1];
-
- ba2str(&device->bdaddr, dstaddr);
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return NULL;
-
- dbus_message_iter_init_append(reply, &iter);
-
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
- /* Address */
- ptr = dstaddr;
- dict_append_entry(&dict, "Address", DBUS_TYPE_STRING, &ptr);
-
- /* Name */
- ptr = NULL;
- memset(name, 0, sizeof(name));
- adapter_get_address(adapter, &src);
- ba2str(&src, srcaddr);
- if (g_str_equal("", device->name))
- {
- // use address itself instead name
- ptr = dstaddr;
- }
- else
- {
- ptr = device->name;
- }
- dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &ptr);
-
- /* Class */
- if (read_remote_class(&src, &device->bdaddr, &class) == 0) {
- dict_append_entry(&dict, "Class", DBUS_TYPE_UINT32, &class);
- }
-
- /* Company */
- memset(company, 0, sizeof(company));
- get_device_company(device, company, sizeof(company));
- ptr = company;
- dict_append_entry(&dict, "Company", DBUS_TYPE_STRING, &ptr);
-
- /* Manufacturer */
- memset(manufacturer, 0, sizeof(manufacturer));
- memset(manufacturer, 0, sizeof(revision));
- memset(manufacturer, 0, sizeof(version));
- get_device_version_info(device, manufacturer, sizeof(manufacturer), revision, sizeof(revision), version, sizeof(version));
-
- ptr = manufacturer;
- dict_append_entry(&dict, "Manufacturer", DBUS_TYPE_STRING, &ptr);
-
- ptr = revision;
- dict_append_entry(&dict, "Revision", DBUS_TYPE_STRING, &ptr);
-
- ptr = version;
- dict_append_entry(&dict, "Version", DBUS_TYPE_STRING, &ptr);
-
- dbus_message_iter_close_container(&iter, &dict);
-
- return reply;
-}
-#endif
static DBusMessage *set_alias(DBusConnection *conn, DBusMessage *msg,
const char *alias, void *data)
return dbus_message_new_method_return(msg);
}
-static void driver_remove(struct btd_driver_data *driver_data,
+static void driver_remove(struct btd_device_driver *driver,
struct btd_device *device)
{
- struct btd_device_driver *driver = driver_data->driver;
-
driver->remove(device);
- device->drivers = g_slist_remove(device->drivers, driver_data);
- g_free(driver_data);
+ device->drivers = g_slist_remove(device->drivers, driver);
}
static gboolean do_disconnect(gpointer user_data)
return FALSE;
}
-static int device_block(DBusConnection *conn, struct btd_device *device)
+int device_block(DBusConnection *conn, struct btd_device *device,
+ gboolean update_only)
{
- int err;
+ int err = 0;
bdaddr_t src;
if (device->blocked)
g_slist_foreach(device->drivers, (GFunc) driver_remove, device);
- err = btd_adapter_block_address(device->adapter, &device->bdaddr);
+ if (!update_only)
+ err = btd_adapter_block_address(device->adapter, &device->bdaddr);
+
if (err < 0)
return err;
return 0;
}
-static int device_unblock(DBusConnection *conn, struct btd_device *device,
- gboolean silent)
+int device_unblock(DBusConnection *conn, struct btd_device *device,
+ gboolean silent, gboolean update_only)
{
- int err;
+ int err = 0;
bdaddr_t src;
if (!device->blocked)
return 0;
- err = btd_adapter_unblock_address(device->adapter, &device->bdaddr);
+ if (!update_only)
+ err = btd_adapter_unblock_address(device->adapter, &device->bdaddr);
+
if (err < 0)
return err;
int err;
if (value)
- err = device_block(conn, device);
+ err = device_block(conn, device, FALSE);
else
- err = device_unblock(conn, device, FALSE);
+ err = device_unblock(conn, device, FALSE, FALSE);
switch (-err) {
case 0:
static GDBusMethodTable device_methods[] = {
{ "GetProperties", "", "a{sv}", get_properties },
-#ifdef __TIZEN_PATCH__
- { "GetVersionProperties", "", "a{sv}", get_version_properties },
-#endif
{ "SetProperty", "sv", "", set_property },
{ "DiscoverServices", "s", "a{us}", discover_services,
G_DBUS_METHOD_FLAG_ASYNC},
device->disconnects = g_slist_remove(device->disconnects, msg);
}
+ if (device_is_paired(device) && !device_is_bonded(device))
+ device_set_paired(device, FALSE);
+
emit_property_changed(conn, device->path,
DEVICE_INTERFACE, "Connected",
DBUS_TYPE_BOOLEAN, &device->connected);
}
}
+static void device_set_vendor(struct btd_device *device, uint16_t value)
+{
+ DBusConnection *conn = get_dbus_connection();
+
+ if (device->vendor == value)
+ return;
+
+ device->vendor = value;
+
+ emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Vendor",
+ DBUS_TYPE_UINT16, &value);
+}
+
+static void device_set_product(struct btd_device *device, uint16_t value)
+{
+ DBusConnection *conn = get_dbus_connection();
+
+ if (device->product == value)
+ return;
+
+ device->product = value;
+
+ emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Product",
+ DBUS_TYPE_UINT16, &value);
+}
+
+static void device_set_version(struct btd_device *device, uint16_t value)
+{
+ DBusConnection *conn = get_dbus_connection();
+
+ if (device->version == value)
+ return;
+
+ device->version = value;
+
+ emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Version",
+ DBUS_TYPE_UINT16, &value);
+}
+
struct btd_device *device_create(DBusConnection *conn,
struct btd_adapter *adapter,
- const gchar *address, device_type_t type)
+ const gchar *address, addr_type_t type)
{
gchar *address_up;
struct btd_device *device;
const gchar *adapter_path = adapter_get_path(adapter);
bdaddr_t src;
char srcaddr[18], alias[MAX_NAME_LENGTH + 1];
+ uint16_t vendor, product, version;
device = g_try_malloc0(sizeof(struct btd_device));
if (device == NULL)
device->trusted = read_trust(&src, address, GLOBAL_TRUST);
if (read_blocked(&src, &device->bdaddr))
- device_block(conn, device);
+ device_block(conn, device, FALSE);
+
+ if (read_link_key(&src, &device->bdaddr, NULL, NULL) == 0) {
+ device_set_paired(device, TRUE);
+ device_set_bonded(device, TRUE);
+ }
- if (read_link_key(&src, &device->bdaddr, NULL, NULL) == 0)
- device->paired = TRUE;
+ if (read_device_id(srcaddr, address, NULL, &vendor, &product, &version)
+ == 0) {
+ device_set_vendor(device, vendor);
+ device_set_product(device, product);
+ device_set_version(device, version);
+ }
return btd_device_ref(device);
}
strncpy(name, device->name, len);
}
-device_type_t device_get_type(struct btd_device *device)
+uint16_t btd_device_get_vendor(struct btd_device *device)
{
- return device->type;
+ return device->vendor;
}
-void device_remove_bonding(struct btd_device *device)
+uint16_t btd_device_get_product(struct btd_device *device)
{
- char filename[PATH_MAX + 1];
- char srcaddr[18], dstaddr[18];
- bdaddr_t bdaddr;
-
- adapter_get_address(device->adapter, &bdaddr);
- ba2str(&bdaddr, srcaddr);
- ba2str(&device->bdaddr, dstaddr);
-
- create_name(filename, PATH_MAX, STORAGEDIR, srcaddr,
- "linkkeys");
-
- /* Delete the link key from storage */
- textfile_casedel(filename, dstaddr);
+ return device->product;
+}
- btd_adapter_remove_bonding(device->adapter, &device->bdaddr);
+uint16_t btd_device_get_version(struct btd_device *device)
+{
+ return device->version;
}
static void device_remove_stored(struct btd_device *device)
adapter_get_address(device->adapter, &src);
ba2str(&device->bdaddr, addr);
- if (device->paired)
- device_remove_bonding(device);
+ if (device_is_bonded(device)) {
+ delete_entry(&src, "linkkeys", addr);
+ delete_entry(&src, "aliases", addr);
+ device_set_bonded(device, FALSE);
+ device->paired = FALSE;
+ btd_adapter_remove_bonding(device->adapter, &device->bdaddr);
+ }
delete_entry(&src, "profiles", addr);
delete_entry(&src, "trusts", addr);
- delete_entry(&src, "types", addr);
- delete_entry(&src, "primary", addr);
delete_all_records(&src, &device->bdaddr);
delete_device_service(&src, &device->bdaddr);
if (device->blocked)
- device_unblock(conn, device, TRUE);
+ device_unblock(conn, device, TRUE, FALSE);
}
void device_remove(struct btd_device *device, gboolean remove_stored)
g_slist_free(device->drivers);
device->drivers = NULL;
+ attrib_client_unregister(device->services);
+
btd_device_unref(device);
}
/* match pattern driver */
match = device_match_pattern(device, *uuid, profiles);
- for (; match; match = match->next)
- uuids = g_slist_append(uuids, match->data);
+ uuids = g_slist_concat(uuids, match);
}
return uuids;
for (list = device_drivers; list; list = list->next) {
struct btd_device_driver *driver = list->data;
GSList *probe_uuids;
- struct btd_driver_data *driver_data;
probe_uuids = device_match_driver(device, driver, profiles);
if (!probe_uuids)
continue;
- driver_data = g_new0(struct btd_driver_data, 1);
-
err = driver->probe(device, probe_uuids);
if (err < 0) {
error("%s driver probe failed for device %s",
driver->name, addr);
- g_free(driver_data);
g_slist_free(probe_uuids);
continue;
}
- driver_data->driver = driver;
- device->drivers = g_slist_append(device->drivers, driver_data);
+ device->drivers = g_slist_append(device->drivers, driver);
g_slist_free(probe_uuids);
}
DBG("Removing drivers for %s", dstaddr);
for (list = device->drivers; list; list = next) {
- struct btd_driver_data *driver_data = list->data;
- struct btd_device_driver *driver = driver_data->driver;
+ struct btd_device_driver *driver = list->data;
const char **uuid;
next = list->next;
driver->remove(device);
device->drivers = g_slist_remove(device->drivers,
- driver_data);
- g_free(driver_data);
-
+ driver);
break;
}
}
pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID);
vendor = pdlist ? pdlist->val.uint16 : 0x0000;
+ device_set_vendor(device, vendor);
+
pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID);
product = pdlist ? pdlist->val.uint16 : 0x0000;
+ device_set_product(device, product);
+
pdlist = sdp_data_get(rec, SDP_ATTR_VERSION);
version = pdlist ? pdlist->val.uint16 : 0x0000;
+ device_set_version(device, version);
+
if (source || vendor || product || version)
store_device_id(srcaddr, dstaddr, source,
vendor, product, version);
sdp_copy_record(rec));
l = g_slist_find_custom(device->uuids, profile_uuid,
- (GCompareFunc) strcmp);
+ (GCompareFunc) strcmp);
if (!l)
req->profiles_added =
- g_slist_append(req->profiles_added,
- profile_uuid);
+ g_slist_append(req->profiles_added,
+ profile_uuid);
else {
req->profiles_removed =
- g_slist_remove(req->profiles_removed,
- l->data);
+ g_slist_remove(req->profiles_removed,
+ l->data);
g_free(profile_uuid);
}
g_dbus_send_message(req->conn, reply);
}
+GSList *device_services_from_record(struct btd_device *device, GSList *profiles)
+{
+ GSList *l, *prim_list = NULL;
+ char *att_uuid;
+ uuid_t proto_uuid;
+
+ sdp_uuid16_create(&proto_uuid, ATT_UUID);
+ att_uuid = bt_uuid2string(&proto_uuid);
+
+ for (l = profiles; l; l = l->next) {
+ const char *profile_uuid = l->data;
+ const sdp_record_t *rec;
+ struct att_primary *prim;
+ uint16_t start = 0, end = 0, psm = 0;
+ uuid_t prim_uuid;
+
+ rec = btd_device_get_record(device, profile_uuid);
+ if (!rec)
+ continue;
+
+ if (!record_has_uuid(rec, att_uuid))
+ continue;
+
+ if (!gatt_parse_record(rec, &prim_uuid, &psm, &start, &end))
+ continue;
+
+ prim = g_new0(struct att_primary, 1);
+ prim->start = start;
+ prim->end = end;
+ sdp_uuid2strn(&prim_uuid, prim->uuid, sizeof(prim->uuid));
+
+ prim_list = g_slist_append(prim_list, prim);
+ }
+
+ g_free(att_uuid);
+
+ return prim_list;
+}
+
static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
{
struct browse_req *req = user_data;
}
/* Probe matching drivers for services added */
- if (req->profiles_added)
+ if (req->profiles_added) {
+ GSList *list;
+
+ list = device_services_from_record(device, req->profiles_added);
+ if (list)
+ device_register_services(req->conn, device, list,
+ ATT_PSM);
+
device_probe_drivers(device, req->profiles_added);
+ }
/* Remove drivers for services removed */
if (req->profiles_removed)
bdaddr_t sba, dba;
adapter_get_address(device->adapter, &sba);
- device_get_address(device, &dba);
+ device_get_address(device, &dba, NULL);
store_profiles(device);
- write_device_type(&sba, &dba, device->type);
}
device->browse = NULL;
- browse_request_free(req);
+ browse_request_free(req, FALSE);
}
static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
return g_string_free(services, FALSE);
}
+static void store_services(struct btd_device *device)
+{
+ struct btd_adapter *adapter = device->adapter;
+ bdaddr_t dba, sba;
+ char *str = primary_list_to_string(device->primaries);
+
+ adapter_get_address(adapter, &sba);
+ device_get_address(device, &dba, NULL);
+
+ write_device_services(&sba, &dba, str);
+
+ g_free(str);
+}
+
+static void attio_connected(gpointer data, gpointer user_data)
+{
+ struct attio_data *attio = data;
+ GAttrib *attrib = user_data;
+
+ if (attio->cfunc)
+ attio->cfunc(attrib, attio->user_data);
+}
+
+static void attio_disconnected(gpointer data, gpointer user_data)
+{
+ struct attio_data *attio = data;
+
+ if (attio->dcfunc)
+ attio->dcfunc(attio->user_data);
+}
+
+static void att_connect_dispatched(gpointer user_data)
+{
+ struct btd_device *device = user_data;
+
+ device->auto_id = 0;
+}
+
+static gboolean att_connect(gpointer user_data);
+
+static void attrib_disconnected(gpointer user_data)
+{
+ struct btd_device *device = user_data;
+ GIOChannel *io;
+ int sock, err = 0;
+ socklen_t len;
+
+ io = g_attrib_get_channel(device->attrib);
+ sock = g_io_channel_unix_get_fd(io);
+ len = sizeof(err);
+ getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len);
+
+ g_slist_foreach(device->attios, attio_disconnected, NULL);
+
+ attrib_channel_detach(device->attrib, device->attachid);
+ g_attrib_unref(device->attrib);
+ device->attrib = NULL;
+
+ if (device->auto_connect == FALSE)
+ return;
+
+ if (err != ETIMEDOUT)
+ return;
+
+ device->auto_id = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT_IDLE,
+ AUTO_CONNECTION_INTERVAL,
+ att_connect, device,
+ att_connect_dispatched);
+}
+
static void primary_cb(GSList *services, guint8 status, gpointer user_data)
{
struct browse_req *req = user_data;
struct btd_device *device = req->device;
- struct btd_adapter *adapter = device->adapter;
GSList *l, *uuids = NULL;
- bdaddr_t dba, sba;
- char *str;
+ gboolean shutdown;
if (status) {
DBusMessage *reply;
reply = btd_error_failed(req->msg, att_ecode2str(status));
g_dbus_send_message(req->conn, reply);
+ shutdown = TRUE;
goto done;
}
- services_changed(device);
device_set_temporary(device, FALSE);
for (l = services; l; l = l->next) {
struct att_primary *prim = l->data;
uuids = g_slist_append(uuids, prim->uuid);
- device_add_primary(device, prim);
}
+ /*
+ * Profiles may register attio callbacks in the probing callback.
+ * GAttrib reference can be released if the registered callbacks
+ * list is emtpy.
+ */
+ device->attrib = g_attrib_ref(req->attrib);
+
+ device_register_services(req->conn, device, g_slist_copy(services), -1);
device_probe_drivers(device, uuids);
+
+ if (device->attios == NULL && device->attios_offline == NULL) {
+ attrib_channel_detach(device->attrib, device->attachid);
+ g_attrib_unref(device->attrib);
+ device->attrib = NULL;
+ } else
+ g_attrib_set_disconnect_function(device->attrib,
+ attrib_disconnected, device);
+
g_slist_free(uuids);
+ services_changed(device);
create_device_reply(device, req);
- str = primary_list_to_string(services);
-
- adapter_get_address(adapter, &sba);
- device_get_address(device, &dba);
-
- write_device_type(&sba, &dba, device->type);
- write_device_services(&sba, &dba, str);
- g_free(str);
+ store_services(device);
+ shutdown = FALSE;
done:
device->browse = NULL;
- browse_request_free(req);
+ browse_request_free(req, shutdown);
}
-static void gatt_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
{
- struct browse_req *req = user_data;
- struct btd_device *device = req->device;
+ struct btd_device *device = user_data;
+ struct browse_req *req = device->browse;
+ GAttrib *attrib;
if (gerr) {
DBusMessage *reply;
DBG("%s", gerr->message);
- reply = btd_error_failed(req->msg, gerr->message);
- g_dbus_send_message(req->conn, reply);
+ if (req) {
+ reply = btd_error_failed(req->msg, gerr->message);
+ g_dbus_send_message(req->conn, reply);
- device->browse = NULL;
- browse_request_free(req);
+ device->browse = NULL;
+ browse_request_free(req, TRUE);
+ } else if (device->auto_connect)
+ device->auto_id = g_timeout_add_seconds_full(
+ G_PRIORITY_DEFAULT_IDLE,
+ AUTO_CONNECTION_INTERVAL,
+ att_connect, device,
+ att_connect_dispatched);
return;
}
- req->attrib = g_attrib_new(io);
- gatt_discover_primary(req->attrib, NULL, primary_cb, req);
+ attrib = g_attrib_new(io);
+ device->attachid = attrib_channel_attach(attrib, TRUE);
+ if (device->attachid == 0)
+ error("Attribute server attach failure!");
+
+ if (req) {
+ req->attrib = attrib;
+ gatt_discover_primary(req->attrib, NULL, primary_cb, req);
+ } else if (device->attios) {
+ device->attrib = attrib;
+ g_attrib_set_disconnect_function(device->attrib,
+ attrib_disconnected, device);
+ g_slist_foreach(device->attios, attio_connected,
+ device->attrib);
+ }
+}
+
+static gboolean att_connect(gpointer user_data)
+{
+ struct btd_device *device = user_data;
+ struct btd_adapter *adapter = device->adapter;
+ GIOChannel *io;
+ GError *gerr = NULL;
+ char addr[18];
+ bdaddr_t sba;
+
+ adapter_get_address(adapter, &sba);
+ ba2str(&device->bdaddr, addr);
+
+ DBG("Connection attempt to: %s", addr);
+
+ if (device_is_bredr(device)) {
+ io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
+ device, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &sba,
+ BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+ BT_IO_OPT_PSM, ATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ } else {
+ io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
+ device, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &sba,
+ BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+ BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+ }
+
+ if (io == NULL) {
+ error("ATT bt_io_connect(%s): %s", addr, gerr->message);
+ g_error_free(gerr);
+ return FALSE;
+ }
+
+ g_io_channel_unref(io);
+
+ return FALSE;
}
int device_browse_primary(struct btd_device *device, DBusConnection *conn,
sec_level = secure ? BT_IO_SEC_HIGH : BT_IO_SEC_LOW;
- req->io = bt_io_connect(BT_IO_L2CAP, gatt_connect_cb, req, NULL, NULL,
+ req->io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
+ device, NULL, NULL,
BT_IO_OPT_SOURCE_BDADDR, &src,
BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
- BT_IO_OPT_CID, GATT_CID,
+ BT_IO_OPT_CID, ATT_CID,
BT_IO_OPT_SEC_LEVEL, sec_level,
BT_IO_OPT_INVALID);
- if (req->io == NULL ) {
- browse_request_free(req);
+ if (req->io == NULL) {
+ browse_request_free(req, FALSE);
return -EIO;
}
- if (conn == NULL)
- conn = get_dbus_connection();
-
req->conn = dbus_connection_ref(conn);
device->browse = req;
err = bt_search_service(&src, &device->bdaddr, &uuid, cb, req, NULL);
if (err < 0) {
- browse_request_free(req);
+ browse_request_free(req, FALSE);
return err;
}
return err;
}
-#ifdef __TIZEN_PATCH__
-struct bonding_req *device_get_bonding(struct btd_device *device)
-{
- if (!device)
- return NULL;
- return device->bonding;
-}
-#endif
struct btd_adapter *device_get_adapter(struct btd_device *device)
{
if (!device)
return device->adapter;
}
-void device_get_address(struct btd_device *device, bdaddr_t *bdaddr)
+void device_get_address(struct btd_device *device, bdaddr_t *bdaddr,
+ addr_type_t *type)
{
bacpy(bdaddr, &device->bdaddr);
+ if (type != NULL)
+ *type = device->type;
}
const gchar *device_get_path(struct btd_device *device)
device->temporary = temporary;
}
-void device_set_type(struct btd_device *device, device_type_t type)
+void device_set_bonded(struct btd_device *device, gboolean bonded)
{
if (!device)
return;
- device->type = type;
+ DBG("bonded %d", bonded);
+
+ device->bonded = bonded;
+}
+
+void device_set_auto_connect(struct btd_device *device, gboolean enable)
+{
+ char addr[18];
+
+ if (!device)
+ return;
+
+ ba2str(&device->bdaddr, addr);
+
+ DBG("%s auto connect: %d", addr, enable);
+
+ device->auto_connect = enable;
+
+ /* Disabling auto connect */
+ if (enable == FALSE) {
+ if (device->auto_id)
+ g_source_remove(device->auto_id);
+ return;
+ }
+
+ /* Enabling auto connect */
+ if (device->auto_id != 0)
+ return;
+
+ if (device->attrib) {
+ DBG("Already connected");
+ return;
+ }
+
+ if (device->attios == NULL && device->attios_offline == NULL)
+ return;
+
+ device->auto_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
+ att_connect, device,
+ att_connect_dispatched);
}
static gboolean start_discovery(gpointer user_data)
ERROR_INTERFACE ".AuthenticationCanceled",
"Authentication Canceled");
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
case 0x2a: /* Cancel by agent */
return dbus_message_new_error(msg,
ERROR_INTERFACE ".CanceledbyUser",
device->bonding = NULL;
- adapter_resume_discovery(device->adapter);
-
if (!device->agent)
return;
agent_free(device->agent);
device->agent = NULL;
}
-#ifdef __TIZEN_PATCH__
-void device_send_reply(struct btd_device *device)
-{
- info("device_send_reply 1\n");
- DBusMessage *reply;
-
- device_set_temporary(device, FALSE);
-
- if(device->bonding !=NULL)
- {
- reply = dbus_message_new_method_return(device->bonding->msg);
- dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &device->path, DBUS_TYPE_INVALID);
- g_dbus_send_message(device->bonding->conn, reply);
- info("device_send_reply 2\n");
- bonding_request_free(device->bonding);
-
- }
- info("device_send_reply 3\n");
-}
-#endif
void device_set_paired(struct btd_device *device, gboolean value)
{
- info("device_set_paired +\n");
DBusConnection *conn = get_dbus_connection();
if (device->paired == value)
return;
+ if (!value)
+ btd_adapter_remove_bonding(device->adapter, &device->bdaddr);
+
device->paired = value;
emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Paired",
struct btd_device *device = user_data;
device->agent = NULL;
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
if (device->authr && (device->authr->agent == agent)) {
DBG("device->agent is the same with authr->agent");
device->authr->agent = NULL;
bonding->conn = dbus_connection_ref(conn);
bonding->msg = dbus_message_ref(msg);
- adapter_suspend_discovery(device->adapter);
-
return bonding;
}
-static int device_authentication_requested(struct btd_device *device,
- int handle)
-{
- struct hci_request rq;
- auth_requested_cp cp;
- evt_cmd_status rp;
- int dd;
-
- dd = hci_open_dev(adapter_get_dev_id(device->adapter));
- if (dd < 0) {
- int err = -errno;
- error("Unable to open adapter: %s(%d)", strerror(-err), -err);
- return err;
- }
-
- memset(&rp, 0, sizeof(rp));
-
- memset(&cp, 0, sizeof(cp));
- cp.handle = htobs(handle);
-
- memset(&rq, 0, sizeof(rq));
- rq.ogf = OGF_LINK_CTL;
- rq.ocf = OCF_AUTH_REQUESTED;
- rq.cparam = &cp;
- rq.clen = AUTH_REQUESTED_CP_SIZE;
- rq.rparam = &rp;
- rq.rlen = EVT_CMD_STATUS_SIZE;
- rq.event = EVT_CMD_STATUS;
-
- if (hci_send_req(dd, &rq, HCI_REQ_TIMEOUT) < 0) {
- int err = -errno;
- error("Unable to send HCI request: %s (%d)",
- strerror(-err), -err);
- hci_close_dev(dd);
- return err;
- }
-
- if (rp.status) {
- error("HCI_Authentication_Requested failed with status 0x%02x",
- rp.status);
- hci_close_dev(dd);
- return rp.status;
- }
-
- info("Authentication requested");
-
- hci_close_dev(dd);
- return 0;
-}
-
-static void bonding_connect_cb(GIOChannel *io, GError *err, gpointer user_data)
-{
- struct btd_device *device = user_data;
- uint16_t handle;
- int status;
-
- if (!device->bonding) {
- if (!err)
- g_io_channel_shutdown(io, TRUE, NULL);
- return;
- }
-
- if (err)
- /* Wait proper error to be propagated by bonding complete */
- return;
-
- if (!bt_io_get(io, BT_IO_L2RAW, &err,
- BT_IO_OPT_HANDLE, &handle,
- BT_IO_OPT_INVALID)) {
- error("Unable to get connection handle: %s", err->message);
- g_error_free(err);
- status = -errno;
- goto failed;
- }
-
- status = device_authentication_requested(device, handle);
- if (status != 0)
- goto failed;
-
- return;
-
-failed:
- g_io_channel_shutdown(io, TRUE, NULL);
- device_cancel_bonding(device, status);
-}
static void create_bond_req_exit(DBusConnection *conn, void *user_data)
{
struct btd_device *device = user_data;
device_request_disconnect(device, NULL);
}
}
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
void set_cancel_from_authentication_req(void *user_data)
{
struct authentication_req *auth = (struct authentication_req *)user_data;
}
}
#endif
-#ifdef __TIZEN_PATCH__
-DBusMessage *device_jsr82_authenticate_link(struct btd_device *device,
- DBusConnection *conn,
- DBusMessage *msg,
- const char *agent_path,
- uint8_t capability)
-
-{
- char filename[PATH_MAX + 1];
- char *str, srcaddr[18], dstaddr[18];
- struct btd_adapter *adapter = device->adapter;
- struct bonding_req *bonding;
- bdaddr_t src;
- GError *err = NULL;
- GIOChannel *io;
-
- adapter_get_address(adapter, &src);
- ba2str(&src, srcaddr);
- ba2str(&device->bdaddr, dstaddr);
-
- if (device->bonding)
- return btd_error_in_progress(msg);
-
- /* check if a link key already exists */
- create_name(filename, PATH_MAX, STORAGEDIR, srcaddr,
- "linkkeys");
-
- str = textfile_caseget(filename, dstaddr);
-/* if (str) {
- free(str);
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".AlreadyExists",
- "Bonding already exists");
- }*/
-
-
- io = bt_io_connect(BT_IO_L2RAW, bonding_connect_cb, device,
- NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, &src,
- BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
- BT_IO_OPT_INVALID);
- if (io == NULL) {
- DBusMessage *reply;
- reply = g_dbus_create_error(msg,
- ERROR_INTERFACE ".ConnectionAttemptFailed",
- err->message);
- error("bt_io_connect: %s", err->message);
- g_error_free(err);
- return reply;
- }
-
- bonding = bonding_request_new(conn, msg, device, agent_path,
- capability);
- if (!bonding) {
- g_io_channel_shutdown(io, TRUE, NULL);
- return NULL;
- }
-
- bonding->io = io;
-
- bonding->listener_id = g_dbus_add_disconnect_watch(conn,
- dbus_message_get_sender(msg),
- create_bond_req_exit, device,
- NULL);
-
- device->bonding = bonding;
- bonding->device = device;
-
- return NULL;
-
-}
-#endif
DBusMessage *device_create_bonding(struct btd_device *device,
DBusConnection *conn,
void device_bonding_complete(struct btd_device *device, uint8_t status)
{
- info("device_bonding_complete() +");
struct bonding_req *bonding = device->bonding;
struct authentication_req *auth = device->authr;
device_auth_req_free(device);
-#ifdef __TIZEN_PATCH__
- if (bonding) {
- if (dbus_message_is_method_call(bonding->msg, ADAPTER_INTERFACE,
- "AuthenticateLink") ) {
- DBusMessage *reply;
- reply = dbus_message_new_method_return(bonding->msg);
- if (!reply) {
- bonding_request_free(bonding);
- return;
- }
-
- const char *path = device_get_path(device);
-
- dbus_message_append_args(reply,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID);
-
- g_dbus_send_message(bonding->conn, reply);
- bonding_request_free(bonding);
- return;
- }
- }
-#endif
-
-
/* If we're already paired nothing more is needed */
if (device->paired)
return;
device);
}
}
- info("device_bonding_complete() -");
}
gboolean device_is_creating(struct btd_device *device, const char *sender)
if (device->authr)
device_cancel_authentication(device, FALSE);
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
if (bonding->cancel_by_user)
{
info("Bonding Cancel by user");
{
struct authentication_req *auth = data;
struct btd_device *device = auth->device;
+ struct btd_adapter *adapter = device_get_adapter(device);
+ struct agent *adapter_agent = adapter_get_agent(adapter);
+
+ if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
+
+ if (auth->agent == adapter_agent || adapter_agent == NULL)
+ goto done;
+
+ if (agent_request_pincode(adapter_agent, device, pincode_cb,
+ auth->secure, auth, NULL) < 0)
+ goto done;
+ auth->agent = adapter_agent;
+ return;
+ }
+
+done:
/* No need to reply anything if the authentication already failed */
if (auth->cb == NULL)
return;
{
struct authentication_req *auth = data;
struct btd_device *device = auth->device;
+ struct btd_adapter *adapter = device_get_adapter(device);
+ struct agent *adapter_agent = adapter_get_agent(adapter);
- /* No need to reply anything if the authentication already failed */
- if (auth->cb == NULL)
+ if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
+
+ if (auth->agent == adapter_agent || adapter_agent == NULL)
+ goto done;
+
+ if (agent_request_confirmation(adapter_agent, device,
+ auth->passkey, confirm_cb,
+ auth, NULL) < 0)
+ goto done;
+
+ auth->agent = adapter_agent;
return;
+ }
-#ifdef __TIZEN_PATCH__
- if (!device)
+done:
+ /* No need to reply anything if the authentication already failed */
+ if (auth->cb == NULL)
return;
-#endif
((agent_cb) auth->cb)(agent, err, device);
-#ifdef __TIZEN_PATCH__
- if (!device->authr)
- return;
-#endif
-
device->authr->cb = NULL;
device->authr->agent = NULL;
}
{
struct authentication_req *auth = data;
struct btd_device *device = auth->device;
+ struct btd_adapter *adapter = device_get_adapter(device);
+ struct agent *adapter_agent = adapter_get_agent(adapter);
+
+ if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
+
+ if (auth->agent == adapter_agent || adapter_agent == NULL)
+ goto done;
+ if (agent_request_passkey(adapter_agent, device, passkey_cb,
+ auth, NULL) < 0)
+ goto done;
+
+ auth->agent = adapter_agent;
+ return;
+ }
+
+done:
/* No need to reply anything if the authentication already failed */
if (auth->cb == NULL)
return;
}
int device_request_authentication(struct btd_device *device, auth_type_t type,
- uint32_t passkey, void *cb)
+ uint32_t passkey, gboolean secure, void *cb)
{
struct authentication_req *auth;
struct agent *agent;
auth->device = device;
auth->cb = cb;
auth->type = type;
+ auth->passkey = passkey;
+ auth->secure = secure;
device->authr = auth;
switch (type) {
case AUTH_TYPE_PINCODE:
- err = agent_request_pincode(agent, device, pincode_cb,
+ err = agent_request_pincode(agent, device, pincode_cb, secure,
auth, NULL);
break;
case AUTH_TYPE_PASSKEY:
device->authorizing = auth;
}
-void btd_device_add_service(struct btd_device *device, const char *path)
+void device_register_services(DBusConnection *conn, struct btd_device *device,
+ GSList *prim_list, int psm)
{
- if (g_slist_find_custom(device->services, path, (GCompareFunc) strcmp))
- return;
-
- device->services = g_slist_append(device->services, g_strdup(path));
-}
-
-void device_add_primary(struct btd_device *device, struct att_primary *prim)
-{
- device->primaries = g_slist_append(device->primaries, prim);
+ device->primaries = g_slist_concat(device->primaries, prim_list);
+ device->services = attrib_client_register(conn, device, psm, NULL,
+ prim_list);
}
GSList *btd_device_get_primaries(struct btd_device *device)
g_free(path);
}
+
+void device_set_class(struct btd_device *device, uint32_t value)
+{
+ DBusConnection *conn = get_dbus_connection();
+
+ emit_property_changed(conn, device->path, DEVICE_INTERFACE, "Class",
+ DBUS_TYPE_UINT32, &value);
+}
+
+static gboolean notify_attios(gpointer user_data)
+{
+ struct btd_device *device = user_data;
+
+ if (device->attrib == NULL)
+ return FALSE;
+
+ g_slist_foreach(device->attios_offline, attio_connected, device->attrib);
+ device->attios = g_slist_concat(device->attios, device->attios_offline);
+ device->attios_offline = NULL;
+
+ return FALSE;
+}
+
+guint btd_device_add_attio_callback(struct btd_device *device,
+ attio_connect_cb cfunc,
+ attio_disconnect_cb dcfunc,
+ gpointer user_data)
+{
+ struct attio_data *attio;
+ static guint attio_id = 0;
+
+ DBG("%p registered ATT connection callback", device);
+
+ attio = g_new0(struct attio_data, 1);
+ attio->id = ++attio_id;
+ attio->cfunc = cfunc;
+ attio->dcfunc = dcfunc;
+ attio->user_data = user_data;
+
+ if (device->attrib && cfunc) {
+ device->attios_offline = g_slist_append(device->attios_offline,
+ attio);
+ g_idle_add(notify_attios, device);
+ } else {
+ device->auto_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
+ att_connect, device,
+ att_connect_dispatched);
+
+ device->attios = g_slist_append(device->attios, attio);
+ }
+
+ return attio->id;
+}
+
+static int attio_id_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct attio_data *attio = a;
+ guint id = GPOINTER_TO_UINT(b);
+
+ return attio->id - id;
+}
+
+gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id)
+{
+ struct attio_data *attio;
+ GSList *l;
+
+ l = g_slist_find_custom(device->attios, GUINT_TO_POINTER(id),
+ attio_id_cmp);
+ if (l) {
+ attio = l->data;
+ device->attios = g_slist_remove(device->attios, attio);
+ } else {
+ l = g_slist_find_custom(device->attios_offline,
+ GUINT_TO_POINTER(id), attio_id_cmp);
+ if (!l)
+ return FALSE;
+
+ attio = l->data;
+ device->attios_offline = g_slist_remove(device->attios_offline,
+ attio);
+ }
+
+ g_free(attio);
+
+ if (device->attios != NULL || device->attios_offline != NULL)
+ return TRUE;
+
+ if (device->attachid) {
+ attrib_channel_detach(device->attrib, device->attachid);
+ device->attachid = 0;
+ }
+
+ if (device->attrib) {
+ g_attrib_unref(device->attrib);
+ device->attrib = NULL;
+ }
+
+ return TRUE;
+}
#define DEVICE_INTERFACE "org.bluez.Device"
struct btd_device;
-struct att_primary;
typedef enum {
AUTH_TYPE_PINCODE,
AUTH_TYPE_NOTIFY,
} auth_type_t;
-typedef enum {
- DEVICE_TYPE_UNKNOWN,
- DEVICE_TYPE_BREDR,
- DEVICE_TYPE_LE,
- DEVICE_TYPE_DUALMODE
-} device_type_t;
-
struct btd_device *device_create(DBusConnection *conn,
- struct btd_adapter *adapter,
- const gchar *address, device_type_t type);
+ struct btd_adapter *adapter,
+ const char *address, addr_type_t type);
void device_set_name(struct btd_device *device, const char *name);
void device_get_name(struct btd_device *device, char *name, size_t len);
-device_type_t device_get_type(struct btd_device *device);
+uint16_t btd_device_get_vendor(struct btd_device *device);
+uint16_t btd_device_get_product(struct btd_device *device);
+uint16_t btd_device_get_version(struct btd_device *device);
void device_remove(struct btd_device *device, gboolean remove_stored);
gint device_address_cmp(struct btd_device *device, const gchar *address);
int device_browse_primary(struct btd_device *device, DBusConnection *conn,
const sdp_record_t *btd_device_get_record(struct btd_device *device,
const char *uuid);
GSList *btd_device_get_primaries(struct btd_device *device);
-void btd_device_add_service(struct btd_device *device, const char *path);
-void device_add_primary(struct btd_device *device, struct att_primary *prim);
+void device_register_services(DBusConnection *conn, struct btd_device *device,
+ GSList *prim_list, int psm);
+GSList *device_services_from_record(struct btd_device *device,
+ GSList *profiles);
void btd_device_add_uuid(struct btd_device *device, const char *uuid);
struct btd_adapter *device_get_adapter(struct btd_device *device);
-void device_get_address(struct btd_device *device, bdaddr_t *bdaddr);
+void device_get_address(struct btd_device *device, bdaddr_t *bdaddr,
+ addr_type_t *type);
const gchar *device_get_path(struct btd_device *device);
struct agent *device_get_agent(struct btd_device *device);
+gboolean device_is_bredr(struct btd_device *device);
+gboolean device_is_le(struct btd_device *device);
gboolean device_is_busy(struct btd_device *device);
gboolean device_is_temporary(struct btd_device *device);
gboolean device_is_paired(struct btd_device *device);
+gboolean device_is_bonded(struct btd_device *device);
gboolean device_is_trusted(struct btd_device *device);
void device_set_paired(struct btd_device *device, gboolean paired);
void device_set_temporary(struct btd_device *device, gboolean temporary);
-void device_set_cap(struct btd_device *device, uint8_t cap);
-void device_set_type(struct btd_device *device, device_type_t type);
-uint8_t device_get_cap(struct btd_device *device);
-void device_set_auth(struct btd_device *device, uint8_t auth);
-uint8_t device_get_auth(struct btd_device *device);
+void device_set_bonded(struct btd_device *device, gboolean bonded);
+void device_set_auto_connect(struct btd_device *device, gboolean enable);
gboolean device_is_connected(struct btd_device *device);
DBusMessage *device_create_bonding(struct btd_device *device,
DBusConnection *conn, DBusMessage *msg,
const char *agent_path, uint8_t capability);
-void device_remove_bonding(struct btd_device *device);
void device_bonding_complete(struct btd_device *device, uint8_t status);
void device_simple_pairing_complete(struct btd_device *device, uint8_t status);
gboolean device_is_creating(struct btd_device *device, const char *sender);
gboolean device_is_bonding(struct btd_device *device, const char *sender);
void device_cancel_bonding(struct btd_device *device, uint8_t status);
int device_request_authentication(struct btd_device *device, auth_type_t type,
- uint32_t passkey, void *cb);
+ uint32_t passkey, gboolean secure, void *cb);
void device_cancel_authentication(struct btd_device *device, gboolean aborted);
gboolean device_is_authenticating(struct btd_device *device);
gboolean device_is_authorizing(struct btd_device *device);
disconnect_watch watch, void *user_data,
GDestroyNotify destroy);
void device_remove_disconnect_watch(struct btd_device *device, guint id);
+void device_set_class(struct btd_device *device, uint32_t value);
#define BTD_UUIDS(args...) ((const char *[]) { args, NULL } )
struct btd_device *btd_device_ref(struct btd_device *device);
void btd_device_unref(struct btd_device *device);
-#ifdef __TIZEN_PATCH__
-DBusMessage *device_jsr82_authenticate_link(struct btd_device *device,
- DBusConnection *conn,
- DBusMessage *msg,
- const char *agent_path,
- uint8_t capability);
-#endif
+int device_block(DBusConnection *conn, struct btd_device *device,
+ gboolean update_only);
+int device_unblock(DBusConnection *conn, struct btd_device *device,
+ gboolean silent, gboolean update_only);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "glib-compat.h"
+#include "glib-helper.h"
+#include "eir.h"
+
+#define EIR_FLAGS 0x01 /* flags */
+#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
+#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
+#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
+#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
+#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
+#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
+#define EIR_NAME_SHORT 0x08 /* shortened local name */
+#define EIR_NAME_COMPLETE 0x09 /* complete local name */
+#define EIR_TX_POWER 0x0A /* transmit power level */
+#define EIR_DEVICE_ID 0x10 /* device ID */
+
+void eir_data_free(struct eir_data *eir)
+{
+ g_slist_free_full(eir->services, g_free);
+ eir->services = NULL;
+ g_free(eir->name);
+ eir->name = NULL;
+}
+
+static void eir_parse_uuid16(struct eir_data *eir, void *data, uint8_t len)
+{
+ uint16_t *uuid16 = data;
+ uuid_t service;
+ char *uuid_str;
+ unsigned int i;
+
+ service.type = SDP_UUID16;
+ for (i = 0; i < len / 2; i++, uuid16++) {
+ service.value.uuid16 = btohs(bt_get_unaligned(uuid16));
+ uuid_str = bt_uuid2string(&service);
+ eir->services = g_slist_append(eir->services, uuid_str);
+ }
+}
+
+static void eir_parse_uuid32(struct eir_data *eir, void *data, uint8_t len)
+{
+ uint32_t *uuid32 = data;
+ uuid_t service;
+ char *uuid_str;
+ unsigned int i;
+
+ service.type = SDP_UUID32;
+ for (i = 0; i < len / 4; i++, uuid32++) {
+ service.value.uuid32 = btohl(bt_get_unaligned(uuid32));
+ uuid_str = bt_uuid2string(&service);
+ eir->services = g_slist_append(eir->services, uuid_str);
+ }
+}
+
+static void eir_parse_uuid128(struct eir_data *eir, uint8_t *data, uint8_t len)
+{
+ uint8_t *uuid_ptr = data;
+ uuid_t service;
+ char *uuid_str;
+ unsigned int i;
+ int k;
+
+ service.type = SDP_UUID128;
+ for (i = 0; i < len / 16; i++) {
+ for (k = 0; k < 16; k++)
+ service.value.uuid128.data[k] = uuid_ptr[16 - k - 1];
+ uuid_str = bt_uuid2string(&service);
+ eir->services = g_slist_append(eir->services, uuid_str);
+ uuid_ptr += 16;
+ }
+}
+
+int eir_parse(struct eir_data *eir, uint8_t *eir_data, uint8_t eir_len)
+{
+ uint16_t len = 0;
+
+ eir->flags = -1;
+
+ /* No EIR data to parse */
+ if (eir_data == NULL)
+ return 0;
+
+ while (len < eir_len - 1) {
+ uint8_t field_len = eir_data[0];
+ uint8_t name_len;
+
+ /* Check for the end of EIR */
+ if (field_len == 0)
+ break;
+
+ len += field_len + 1;
+
+ /* Bail out if got incorrect length */
+ if (len > eir_len) {
+ eir_data_free(eir);
+ return -EINVAL;
+ }
+
+ switch (eir_data[1]) {
+ case EIR_UUID16_SOME:
+ case EIR_UUID16_ALL:
+ eir_parse_uuid16(eir, &eir_data[2], field_len);
+ break;
+
+ case EIR_UUID32_SOME:
+ case EIR_UUID32_ALL:
+ eir_parse_uuid32(eir, &eir_data[2], field_len);
+ break;
+
+ case EIR_UUID128_SOME:
+ case EIR_UUID128_ALL:
+ eir_parse_uuid128(eir, &eir_data[2], field_len);
+ break;
+
+ case EIR_FLAGS:
+ eir->flags = eir_data[2];
+ break;
+
+ case EIR_NAME_SHORT:
+ case EIR_NAME_COMPLETE:
+ /* Some vendors put a NUL byte terminator into
+ * the name */
+ name_len = field_len - 1;
+
+ while (name_len > 0 && eir_data[name_len - 1] == '\0')
+ name_len--;
+
+ if (!g_utf8_validate((char *) &eir_data[2],
+ name_len, NULL))
+ break;
+
+ g_free(eir->name);
+
+ eir->name = g_strndup((char *) &eir_data[2],
+ field_len - 1);
+ eir->name_complete = eir_data[1] == EIR_NAME_COMPLETE;
+ break;
+ }
+
+ eir_data += field_len + 1;
+ }
+
+ return 0;
+}
+
+#define SIZEOF_UUID128 16
+
+static void eir_generate_uuid128(GSList *list, uint8_t *ptr, uint16_t *eir_len)
+{
+ int i, k, uuid_count = 0;
+ uint16_t len = *eir_len;
+ uint8_t *uuid128;
+ gboolean truncated = FALSE;
+
+ /* Store UUIDs in place, skip 2 bytes to write type and length later */
+ uuid128 = ptr + 2;
+
+ for (; list; list = list->next) {
+ struct uuid_info *uuid = list->data;
+ uint8_t *uuid128_data = uuid->uuid.value.uuid128.data;
+
+ if (uuid->uuid.type != SDP_UUID128)
+ continue;
+
+ /* Stop if not enough space to put next UUID128 */
+ if ((len + 2 + SIZEOF_UUID128) > HCI_MAX_EIR_LENGTH) {
+ truncated = TRUE;
+ break;
+ }
+
+ /* Check for duplicates, EIR data is Little Endian */
+ for (i = 0; i < uuid_count; i++) {
+ for (k = 0; k < SIZEOF_UUID128; k++) {
+ if (uuid128[i * SIZEOF_UUID128 + k] !=
+ uuid128_data[SIZEOF_UUID128 - 1 - k])
+ break;
+ }
+ if (k == SIZEOF_UUID128)
+ break;
+ }
+
+ if (i < uuid_count)
+ continue;
+
+ /* EIR data is Little Endian */
+ for (k = 0; k < SIZEOF_UUID128; k++)
+ uuid128[uuid_count * SIZEOF_UUID128 + k] =
+ uuid128_data[SIZEOF_UUID128 - 1 - k];
+
+ len += SIZEOF_UUID128;
+ uuid_count++;
+ }
+
+ if (uuid_count > 0 || truncated) {
+ /* EIR Data length */
+ ptr[0] = (uuid_count * SIZEOF_UUID128) + 1;
+ /* EIR Data type */
+ ptr[1] = truncated ? EIR_UUID128_SOME : EIR_UUID128_ALL;
+ len += 2;
+ *eir_len = len;
+ }
+}
+
+void eir_create(const char *name, int8_t tx_power, uint16_t did_vendor,
+ uint16_t did_product, uint16_t did_version,
+ GSList *uuids, uint8_t *data)
+{
+ GSList *l;
+ uint8_t *ptr = data;
+ uint16_t eir_len = 0;
+ uint16_t uuid16[HCI_MAX_EIR_LENGTH / 2];
+ int i, uuid_count = 0;
+ gboolean truncated = FALSE;
+ size_t name_len;
+
+ name_len = strlen(name);
+
+ if (name_len > 0) {
+ /* EIR Data type */
+ if (name_len > 48) {
+ name_len = 48;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ /* EIR Data length */
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, name, name_len);
+
+ eir_len += (name_len + 2);
+ ptr += (name_len + 2);
+ }
+
+ if (tx_power != 0) {
+ *ptr++ = 2;
+ *ptr++ = EIR_TX_POWER;
+ *ptr++ = (uint8_t) tx_power;
+ eir_len += 3;
+ }
+
+ if (did_vendor != 0x0000) {
+ uint16_t source = 0x0002;
+ *ptr++ = 9;
+ *ptr++ = EIR_DEVICE_ID;
+ *ptr++ = (source & 0x00ff);
+ *ptr++ = (source & 0xff00) >> 8;
+ *ptr++ = (did_vendor & 0x00ff);
+ *ptr++ = (did_vendor & 0xff00) >> 8;
+ *ptr++ = (did_product & 0x00ff);
+ *ptr++ = (did_product & 0xff00) >> 8;
+ *ptr++ = (did_version & 0x00ff);
+ *ptr++ = (did_version & 0xff00) >> 8;
+ eir_len += 10;
+ }
+
+ /* Group all UUID16 types */
+ for (l = uuids; l != NULL; l = g_slist_next(l)) {
+ struct uuid_info *uuid = l->data;
+
+ if (uuid->uuid.type != SDP_UUID16)
+ continue;
+
+ if (uuid->uuid.value.uuid16 < 0x1100)
+ continue;
+
+ if (uuid->uuid.value.uuid16 == PNP_INFO_SVCLASS_ID)
+ continue;
+
+ /* Stop if not enough space to put next UUID16 */
+ if ((eir_len + 2 + sizeof(uint16_t)) > HCI_MAX_EIR_LENGTH) {
+ truncated = TRUE;
+ break;
+ }
+
+ /* Check for duplicates */
+ for (i = 0; i < uuid_count; i++)
+ if (uuid16[i] == uuid->uuid.value.uuid16)
+ break;
+
+ if (i < uuid_count)
+ continue;
+
+ uuid16[uuid_count++] = uuid->uuid.value.uuid16;
+ eir_len += sizeof(uint16_t);
+ }
+
+ if (uuid_count > 0) {
+ /* EIR Data length */
+ ptr[0] = (uuid_count * sizeof(uint16_t)) + 1;
+ /* EIR Data type */
+ ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
+
+ ptr += 2;
+ eir_len += 2;
+
+ for (i = 0; i < uuid_count; i++) {
+ *ptr++ = (uuid16[i] & 0x00ff);
+ *ptr++ = (uuid16[i] & 0xff00) >> 8;
+ }
+ }
+
+ /* Group all UUID128 types */
+ if (eir_len <= HCI_MAX_EIR_LENGTH - 2)
+ eir_generate_uuid128(uuids, ptr, &eir_len);
+}
+
+gboolean eir_has_complete_name(uint8_t *data, size_t len)
+{
+ uint8_t field_len;
+ size_t parsed;
+
+ for (parsed = 0; parsed < len - 1; parsed += field_len) {
+ field_len = data[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed += field_len + 1;
+
+ if (parsed > len)
+ break;
+
+ if (data[1] == EIR_NAME_COMPLETE)
+ return TRUE;
+
+ data += field_len + 1;
+ }
+
+ return FALSE;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct uuid_info {
+ uuid_t uuid;
+ uint8_t svc_hint;
+};
+
+struct eir_data {
+ GSList *services;
+ int flags;
+ char *name;
+ gboolean name_complete;
+};
+
+void eir_data_free(struct eir_data *eir);
+int eir_parse(struct eir_data *eir, uint8_t *eir_data, uint8_t eir_len);
+void eir_create(const char *name, int8_t tx_power, uint16_t did_vendor,
+ uint16_t did_product, uint16_t did_version,
+ GSList *uuids, uint8_t *data);
+
+gboolean eir_has_complete_name(uint8_t *data, size_t len);
#define _GNU_SOURCE
#include <stdio.h>
+#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/param.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus.h>
#include "log.h"
-#include "textfile.h"
-#include "hcid.h"
#include "adapter.h"
#include "manager.h"
#include "device.h"
#include "error.h"
-#include "glib-helper.h"
#include "dbus-common.h"
#include "agent.h"
#include "storage.h"
#include "event.h"
-#include "sdpd.h"
-
-struct eir_data {
- GSList *services;
- int flags;
- char *name;
- gboolean name_complete;
-};
static gboolean get_adapter_and_device(bdaddr_t *src, bdaddr_t *dst,
struct btd_adapter **adapter,
const char *pincode, struct btd_device *device)
{
struct btd_adapter *adapter = device_get_adapter(device);
- bdaddr_t sba, dba;
+ bdaddr_t dba;
int err;
- device_get_address(device, &dba);
+ device_get_address(device, &dba, NULL);
if (derr) {
- err = btd_adapter_pincode_reply(adapter, &dba, NULL);
+ err = btd_adapter_pincode_reply(adapter, &dba, NULL, 0);
if (err < 0)
goto fail;
return;
}
- err = btd_adapter_pincode_reply(adapter, &dba, pincode);
+ err = btd_adapter_pincode_reply(adapter, &dba, pincode,
+ pincode ? strlen(pincode) : 0);
if (err < 0)
goto fail;
- adapter_get_address(adapter, &sba);
-
return;
fail:
error("Sending PIN code reply failed: %s (%d)", strerror(-err), -err);
}
-int btd_event_request_pin(bdaddr_t *sba, bdaddr_t *dba)
+int btd_event_request_pin(bdaddr_t *sba, bdaddr_t *dba, gboolean secure)
{
struct btd_adapter *adapter;
struct btd_device *device;
char pin[17];
- int pinlen;
+ ssize_t pinlen;
if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
return -ENODEV;
memset(pin, 0, sizeof(pin));
- pinlen = read_pin_code(sba, dba, pin);
- if (pinlen > 0) {
- btd_adapter_pincode_reply(adapter, dba, pin);
+ pinlen = btd_adapter_get_pin(adapter, device, pin);
+ if (pinlen > 0 && (!secure || pinlen == 16)) {
+ btd_adapter_pincode_reply(adapter, dba, pin, pinlen);
return 0;
}
return device_request_authentication(device, AUTH_TYPE_PINCODE, 0,
- pincode_cb);
+ secure, pincode_cb);
}
static int confirm_reply(struct btd_adapter *adapter,
{
bdaddr_t bdaddr;
- device_get_address(device, &bdaddr);
+ device_get_address(device, &bdaddr, NULL);
return btd_adapter_confirm_reply(adapter, &bdaddr, success);
}
struct btd_adapter *adapter = device_get_adapter(device);
bdaddr_t bdaddr;
- device_get_address(device, &bdaddr);
+ device_get_address(device, &bdaddr, NULL);
if (err)
passkey = INVALID_PASSKEY;
return -ENODEV;
return device_request_authentication(device, AUTH_TYPE_CONFIRM,
- passkey, confirm_cb);
+ passkey, FALSE, confirm_cb);
}
int btd_event_user_passkey(bdaddr_t *sba, bdaddr_t *dba)
return -ENODEV;
return device_request_authentication(device, AUTH_TYPE_PASSKEY, 0,
- passkey_cb);
+ FALSE, passkey_cb);
}
int btd_event_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey)
if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
return -ENODEV;
- return device_request_authentication(device, AUTH_TYPE_NOTIFY,
- passkey, NULL);
-}
-
-void btd_event_bonding_complete(bdaddr_t *local, bdaddr_t *peer,
- uint8_t status)
-{
- struct btd_adapter *adapter;
- struct btd_device *device;
- gboolean create;
-
- DBG("status 0x%02x", status);
-
- create = status ? FALSE : TRUE;
-
- if (!get_adapter_and_device(local, peer, &adapter, &device, create))
- return;
-
- if (device)
- device_bonding_complete(device, status);
+ return device_request_authentication(device, AUTH_TYPE_NOTIFY, passkey,
+ FALSE, NULL);
}
void btd_event_simple_pairing_complete(bdaddr_t *local, bdaddr_t *peer,
device_simple_pairing_complete(device, status);
}
-static int parse_eir_data(struct eir_data *eir, uint8_t *eir_data,
- size_t eir_length)
-{
- uint16_t len = 0;
- size_t total;
- size_t uuid16_count = 0;
- size_t uuid32_count = 0;
- size_t uuid128_count = 0;
- uint8_t *uuid16 = NULL;
- uint8_t *uuid32 = NULL;
- uint8_t *uuid128 = NULL;
- uuid_t service;
- char *uuid_str;
- unsigned int i;
-
- eir->flags = -1;
-
- /* No EIR data to parse */
- if (eir_data == NULL || eir_length == 0)
- return 0;
-
- while (len < eir_length - 1) {
- uint8_t field_len = eir_data[0];
-
- /* Check for the end of EIR */
- if (field_len == 0)
- break;
-
- switch (eir_data[1]) {
- case EIR_UUID16_SOME:
- case EIR_UUID16_ALL:
- uuid16_count = field_len / 2;
- uuid16 = &eir_data[2];
- break;
- case EIR_UUID32_SOME:
- case EIR_UUID32_ALL:
- uuid32_count = field_len / 4;
- uuid32 = &eir_data[2];
- break;
- case EIR_UUID128_SOME:
- case EIR_UUID128_ALL:
- uuid128_count = field_len / 16;
- uuid128 = &eir_data[2];
- break;
- case EIR_FLAGS:
- eir->flags = eir_data[2];
- break;
- case EIR_NAME_SHORT:
- case EIR_NAME_COMPLETE:
- if (g_utf8_validate((char *) &eir_data[2],
- field_len - 1, NULL))
- eir->name = g_strndup((char *) &eir_data[2],
- field_len - 1);
- else
- eir->name = g_strdup("");
- eir->name_complete = eir_data[1] == EIR_NAME_COMPLETE;
- break;
- }
-
- len += field_len + 1;
- eir_data += field_len + 1;
- }
-
- /* Bail out if got incorrect length */
- if (len > eir_length)
- return -EINVAL;
-
- total = uuid16_count + uuid32_count + uuid128_count;
-
- /* No UUIDs were parsed, so skip code below */
- if (!total)
- return 0;
-
- /* Generate uuids in SDP format (EIR data is Little Endian) */
- service.type = SDP_UUID16;
- for (i = 0; i < uuid16_count; i++) {
- uint16_t val16 = uuid16[1];
-
- val16 = (val16 << 8) + uuid16[0];
- service.value.uuid16 = val16;
- uuid_str = bt_uuid2string(&service);
- eir->services = g_slist_append(eir->services, uuid_str);
- uuid16 += 2;
- }
-
- service.type = SDP_UUID32;
- for (i = uuid16_count; i < uuid32_count + uuid16_count; i++) {
- uint32_t val32 = uuid32[3];
- int k;
-
- for (k = 2; k >= 0; k--)
- val32 = (val32 << 8) + uuid32[k];
-
- service.value.uuid32 = val32;
- uuid_str = bt_uuid2string(&service);
- eir->services = g_slist_append(eir->services, uuid_str);
- uuid32 += 4;
- }
-
- service.type = SDP_UUID128;
- for (i = uuid32_count + uuid16_count; i < total; i++) {
- int k;
-
- for (k = 0; k < 16; k++)
- service.value.uuid128.data[k] = uuid128[16 - k - 1];
-
- uuid_str = bt_uuid2string(&service);
- eir->services = g_slist_append(eir->services, uuid_str);
- uuid128 += 16;
- }
-
- return 0;
-}
-
-static void free_eir_data(struct eir_data *eir)
-{
- g_slist_foreach(eir->services, (GFunc) g_free, NULL);
- g_slist_free(eir->services);
- g_free(eir->name);
-}
-
-void btd_event_advertising_report(bdaddr_t *local, le_advertising_info *info)
-{
- struct btd_adapter *adapter;
- struct eir_data eir_data;
- int8_t rssi;
- int err;
-
- adapter = manager_find_adapter(local);
- if (adapter == NULL) {
- error("No matching adapter found");
- return;
- }
-
- memset(&eir_data, 0, sizeof(eir_data));
- err = parse_eir_data(&eir_data, info->data, info->length);
- if (err < 0)
- error("Error parsing advertising data: %s (%d)",
- strerror(-err), -err);
-
- rssi = *(info->data + info->length);
-
- adapter_update_device_from_info(adapter, info->bdaddr, rssi,
- info->evt_type, eir_data.name,
- eir_data.services, eir_data.flags);
-
- free_eir_data(&eir_data);
-}
-
static void update_lastseen(bdaddr_t *sba, bdaddr_t *dba)
{
time_t t;
write_lastused_info(sba, dba, tm);
}
-void btd_event_device_found(bdaddr_t *local, bdaddr_t *peer, uint32_t class,
- int8_t rssi, uint8_t *data)
+void btd_event_device_found(bdaddr_t *local, bdaddr_t *peer, addr_type_t type,
+ uint32_t class, int8_t rssi,
+ uint8_t confirm_name, uint8_t *data,
+ uint8_t data_len)
{
- char filename[PATH_MAX + 1];
struct btd_adapter *adapter;
- char local_addr[18], peer_addr[18], *alias, *name;
- name_status_t name_status;
- struct eir_data eir_data;
- int state, err;
- dbus_bool_t legacy;
- unsigned char features[8];
- const char *dev_name;
-
- ba2str(local, local_addr);
- ba2str(peer, peer_addr);
adapter = manager_find_adapter(local);
if (!adapter) {
if (data)
write_remote_eir(local, peer, data);
- /*
- * Workaround to identify periodic inquiry: inquiry complete event is
- * sent after each window, however there isn't an event to indicate the
- * beginning of a new periodic inquiry window.
- */
- state = adapter_get_state(adapter);
- if (!(state & (STATE_STDINQ | STATE_LE_SCAN | STATE_PINQ))) {
- state |= STATE_PINQ;
- adapter_set_state(adapter, state);
- }
-
- /* the inquiry result can be triggered by NON D-Bus client */
- if (adapter_get_discover_type(adapter) & DISC_RESOLVNAME &&
- adapter_has_discov_sessions(adapter))
- name_status = NAME_REQUIRED;
- else
- name_status = NAME_NOT_REQUIRED;
-
- create_name(filename, PATH_MAX, STORAGEDIR, local_addr, "aliases");
- alias = textfile_get(filename, peer_addr);
-
- create_name(filename, PATH_MAX, STORAGEDIR, local_addr, "names");
- name = textfile_get(filename, peer_addr);
-
- if (data)
- legacy = FALSE;
- else if (name == NULL)
- legacy = TRUE;
- else if (read_remote_features(local, peer, NULL, features) == 0) {
- if (features[0] & 0x01)
- legacy = FALSE;
- else
- legacy = TRUE;
- } else
- legacy = TRUE;
-
- memset(&eir_data, 0, sizeof(eir_data));
- err = parse_eir_data(&eir_data, data, EIR_DATA_LENGTH);
- if (err < 0)
- error("Error parsing EIR data: %s (%d)", strerror(-err), -err);
-
- /* Complete EIR names are always used. Shortened EIR names are only
- * used if there is no name already in storage. */
- dev_name = name;
- if (eir_data.name != NULL) {
- if (eir_data.name_complete) {
- write_device_name(local, peer, eir_data.name);
- name_status = NAME_NOT_REQUIRED;
- dev_name = eir_data.name;
- } else if (name == NULL)
- dev_name = eir_data.name;
- }
-
- adapter_update_found_devices(adapter, peer, rssi, class, dev_name,
- alias, legacy, eir_data.services,
- name_status);
-
- free_eir_data(&eir_data);
- free(name);
- free(alias);
+ adapter_update_found_devices(adapter, peer, type, class, rssi,
+ confirm_name, data, data_len);
}
void btd_event_set_legacy_pairing(bdaddr_t *local, bdaddr_t *peer,
gboolean legacy)
{
struct btd_adapter *adapter;
- struct remote_dev_info *dev, match;
+ struct remote_dev_info *dev;
adapter = manager_find_adapter(local);
if (!adapter) {
return;
}
- memset(&match, 0, sizeof(struct remote_dev_info));
- bacpy(&match.bdaddr, peer);
- match.name_status = NAME_ANY;
-
- dev = adapter_search_found_devices(adapter, &match);
+ dev = adapter_search_found_devices(adapter, peer);
if (dev)
dev->legacy = legacy;
}
void btd_event_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class)
{
- uint32_t old_class = 0;
struct btd_adapter *adapter;
struct btd_device *device;
- const gchar *dev_path;
- DBusConnection *conn = get_dbus_connection();
+ uint32_t old_class = 0;
read_remote_class(local, peer, &old_class);
if (!device)
return;
- dev_path = device_get_path(device);
-
- emit_property_changed(conn, dev_path, DEVICE_INTERFACE, "Class",
- DBUS_TYPE_UINT32, &class);
+ device_set_class(device, class);
}
-void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status,
- char *name)
+void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, char *name)
{
struct btd_adapter *adapter;
- char srcaddr[18], dstaddr[18];
- int state;
+ char srcaddr[18];
struct btd_device *device;
- struct remote_dev_info match, *dev_info;
-
- if (status == 0) {
- char *end;
+ struct remote_dev_info *dev_info;
- /* It's ok to cast end between const and non-const since
- * we know it points to inside of name which is non-const */
- if (!g_utf8_validate(name, -1, (const char **) &end))
- *end = '\0';
+ if (!g_utf8_validate(name, -1, NULL)) {
+ int i;
- write_device_name(local, peer, name);
+ /* Assume ASCII, and replace all non-ASCII with spaces */
+ for (i = 0; name[i] != '\0'; i++) {
+ if (!isascii(name[i]))
+ name[i] = ' ';
+ }
+ /* Remove leading and trailing whitespace characters */
+ g_strstrip(name);
}
+ write_device_name(local, peer, name);
+
if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
return;
ba2str(local, srcaddr);
- ba2str(peer, dstaddr);
-
- if (status != 0)
- goto proceed;
-
- bacpy(&match.bdaddr, peer);
- match.name_status = NAME_ANY;
- dev_info = adapter_search_found_devices(adapter, &match);
+ dev_info = adapter_search_found_devices(adapter, peer);
if (dev_info) {
g_free(dev_info->name);
dev_info->name = g_strdup(name);
if (device)
device_set_name(device, name);
-
-proceed:
- /* remove from remote name request list */
- adapter_remove_found_device(adapter, peer);
-
- /* check if there is more devices to request names */
- if (adapter_resolve_names(adapter) == 0)
- return;
-
- state = adapter_get_state(adapter);
- state &= ~STATE_RESOLVNAME;
- adapter_set_state(adapter, state);
}
int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer,
ret = write_link_key(local, peer, key, key_type, pin_length);
- if (ret == 0 && device_is_temporary(device))
- device_set_temporary(device, FALSE);
+ if (ret == 0) {
+ device_set_bonded(device, TRUE);
+
+ if (device_is_temporary(device))
+ device_set_temporary(device, FALSE);
+ }
return ret;
}
if (!device)
return;
+ if (device_is_bonding(device, NULL))
+ device_cancel_bonding(device, status);
+
if (device_is_temporary(device))
adapter_remove_device(conn, adapter, device, TRUE);
}
adapter_remove_connection(adapter, device);
}
-/* Section reserved to device HCI callbacks */
-
-void btd_event_le_set_scan_enable_complete(bdaddr_t *local, uint8_t status)
+void btd_event_device_blocked(bdaddr_t *local, bdaddr_t *peer)
{
struct btd_adapter *adapter;
- int state;
+ struct btd_device *device;
- adapter = manager_find_adapter(local);
- if (!adapter) {
- error("No matching adapter found");
- return;
- }
+ DBusConnection *conn = get_dbus_connection();
- if (status) {
- error("Can't enable/disable LE scan");
+ if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
return;
- }
- state = adapter_get_state(adapter);
+ device_block(conn, device, TRUE);
+}
- /* Enabling or disabling ? */
- if (state & STATE_LE_SCAN)
- state &= ~STATE_LE_SCAN;
- else
- state |= STATE_LE_SCAN;
+void btd_event_device_unblocked(bdaddr_t *local, bdaddr_t *peer)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
- adapter_set_state(adapter, state);
+ DBusConnection *conn = get_dbus_connection();
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+ return;
+
+ device_unblock(conn, device, FALSE, TRUE);
}
+/* Section reserved to device HCI callbacks */
+
void btd_event_returned_link_key(bdaddr_t *local, bdaddr_t *peer)
{
struct btd_adapter *adapter;
*
*/
-int btd_event_request_pin(bdaddr_t *sba, bdaddr_t *dba);
-void btd_event_advertising_report(bdaddr_t *local, le_advertising_info *info);
-void btd_event_device_found(bdaddr_t *local, bdaddr_t *peer, uint32_t class,
- int8_t rssi, uint8_t *data);
+int btd_event_request_pin(bdaddr_t *sba, bdaddr_t *dba, gboolean secure);
+void btd_event_device_found(bdaddr_t *local, bdaddr_t *peer, addr_type_t type,
+ uint32_t cls, int8_t rssi, uint8_t confirm_name,
+ uint8_t *data, uint8_t data_len);
void btd_event_set_legacy_pairing(bdaddr_t *local, bdaddr_t *peer, gboolean legacy);
void btd_event_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class);
-void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, uint8_t status, char *name);
+void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, char *name);
void btd_event_conn_complete(bdaddr_t *local, bdaddr_t *peer);
void btd_event_conn_failed(bdaddr_t *local, bdaddr_t *peer, uint8_t status);
void btd_event_disconn_complete(bdaddr_t *local, bdaddr_t *peer);
-void btd_event_bonding_complete(bdaddr_t *local, bdaddr_t *peer,
- uint8_t status);
void btd_event_simple_pairing_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t status);
-void btd_event_le_set_scan_enable_complete(bdaddr_t *local, uint8_t status);
void btd_event_returned_link_key(bdaddr_t *local, bdaddr_t *peer);
int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey);
int btd_event_user_passkey(bdaddr_t *sba, bdaddr_t *dba);
int btd_event_user_notify(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey);
+void btd_event_device_blocked(bdaddr_t *local, bdaddr_t *peer);
+void btd_event_device_unblocked(bdaddr_t *local, bdaddr_t *peer);
int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer, uint8_t *key,
uint8_t key_type, uint8_t pin_length);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef NEED_G_SLIST_FREE_FULL
+static inline void g_slist_free_full(GSList *list, GDestroyNotify free_func)
+{
+ g_slist_foreach(list, (GFunc) free_func, NULL);
+ g_slist_free(list);
+}
+#endif
#include <stdlib.h>
#include <errno.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <glib.h>
#include "btio.h"
-#include "sdpd.h"
+#include "glib-compat.h"
#include "glib-helper.h"
-/* Number of seconds to keep a sdp_session_t in the cache */
-#define CACHE_TIMEOUT 2
-
-struct cached_sdp_session {
- bdaddr_t src;
- bdaddr_t dst;
- sdp_session_t *session;
- guint timer;
-};
-
-static GSList *cached_sdp_sessions = NULL;
-
-static gboolean cached_session_expired(gpointer user_data)
-{
- struct cached_sdp_session *cached = user_data;
-
- cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, cached);
-
- sdp_close(cached->session);
-
- g_free(cached);
-
- return FALSE;
-}
-
-static sdp_session_t *get_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
-{
- GSList *l;
-
- for (l = cached_sdp_sessions; l != NULL; l = l->next) {
- struct cached_sdp_session *c = l->data;
- sdp_session_t *session;
-
- if (bacmp(&c->src, src) || bacmp(&c->dst, dst))
- continue;
-
- g_source_remove(c->timer);
-
- session = c->session;
-
- cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, c);
- g_free(c);
-
- return session;
- }
-
- return sdp_connect(src, dst, SDP_NON_BLOCKING);
-}
-
-static void cache_sdp_session(bdaddr_t *src, bdaddr_t *dst,
- sdp_session_t *session)
-{
- struct cached_sdp_session *cached;
-
- cached = g_new0(struct cached_sdp_session, 1);
-
- bacpy(&cached->src, src);
- bacpy(&cached->dst, dst);
-
- cached->session = session;
-
- cached_sdp_sessions = g_slist_append(cached_sdp_sessions, cached);
-
- cached->timer = g_timeout_add_seconds(CACHE_TIMEOUT,
- cached_session_expired,
- cached);
-}
-
-struct search_context {
- bdaddr_t src;
- bdaddr_t dst;
- sdp_session_t *session;
- bt_callback_t cb;
- bt_destroy_t destroy;
- gpointer user_data;
- uuid_t uuid;
- guint io_id;
-};
-
-static GSList *context_list = NULL;
-
-static void search_context_cleanup(struct search_context *ctxt)
-{
- context_list = g_slist_remove(context_list, ctxt);
-
- if (ctxt->destroy)
- ctxt->destroy(ctxt->user_data);
-
- g_free(ctxt);
-}
-
-static void search_completed_cb(uint8_t type, uint16_t status,
- uint8_t *rsp, size_t size, void *user_data)
-{
- struct search_context *ctxt = user_data;
- sdp_list_t *recs = NULL;
- int scanned, seqlen = 0, bytesleft = size;
- uint8_t dataType;
- int err = 0;
-
- if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
- err = -EPROTO;
- goto done;
- }
-
- scanned = sdp_extract_seqtype(rsp, bytesleft, &dataType, &seqlen);
- if (!scanned || !seqlen)
- goto done;
-
- rsp += scanned;
- bytesleft -= scanned;
- do {
- sdp_record_t *rec;
- int recsize;
-
- recsize = 0;
- rec = sdp_extract_pdu(rsp, bytesleft, &recsize);
- if (!rec)
- break;
-
- if (!recsize) {
- sdp_record_free(rec);
- break;
- }
-
- scanned += recsize;
- rsp += recsize;
- bytesleft -= recsize;
-
- recs = sdp_list_append(recs, rec);
- } while (scanned < (ssize_t) size && bytesleft > 0);
-
-done:
- cache_sdp_session(&ctxt->src, &ctxt->dst, ctxt->session);
-
- if (ctxt->cb)
- ctxt->cb(recs, err, ctxt->user_data);
-
- if (recs)
- sdp_list_free(recs, (sdp_free_func_t) sdp_record_free);
-
- search_context_cleanup(ctxt);
-}
-
-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);
-
- search_context_cleanup(ctxt);
- }
-
- return FALSE;
-}
-
-static gboolean connect_watch(GIOChannel *chan, GIOCondition cond,
- gpointer user_data)
-{
- struct search_context *ctxt = user_data;
- sdp_list_t *search, *attrids;
- uint32_t range = 0x0000ffff;
- socklen_t len;
- int sk, err = 0;
-
- sk = g_io_channel_unix_get_fd(chan);
- ctxt->io_id = 0;
-
- len = sizeof(err);
- if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
- err = errno;
- goto failed;
- }
-
- if (err != 0)
- goto failed;
-
- if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) {
- err = EIO;
- goto failed;
- }
-
- search = sdp_list_append(NULL, &ctxt->uuid);
- attrids = sdp_list_append(NULL, &range);
- if (sdp_service_search_attr_async(ctxt->session,
- search, SDP_ATTR_REQ_RANGE, attrids) < 0) {
- sdp_list_free(attrids, NULL);
- sdp_list_free(search, NULL);
- err = EIO;
- goto failed;
- }
-
- sdp_list_free(attrids, NULL);
- sdp_list_free(search, NULL);
-
- /* Set callback responsible for update the internal SDP transaction */
- ctxt->io_id = g_io_add_watch(chan,
- G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- search_process_cb, ctxt);
- return FALSE;
-
-failed:
- sdp_close(ctxt->session);
- ctxt->session = NULL;
-
- if (ctxt->cb)
- ctxt->cb(NULL, -err, ctxt->user_data);
-
- search_context_cleanup(ctxt);
-
- return FALSE;
-}
-
-static int create_search_context(struct search_context **ctxt,
- const bdaddr_t *src,
- const bdaddr_t *dst,
- uuid_t *uuid)
-{
- sdp_session_t *s;
- GIOChannel *chan;
-
- if (!ctxt)
- return -EINVAL;
-
- s = get_sdp_session(src, dst);
- if (!s)
- return -errno;
-
- *ctxt = g_try_malloc0(sizeof(struct search_context));
- if (!*ctxt) {
- sdp_close(s);
- return -ENOMEM;
- }
-
- bacpy(&(*ctxt)->src, src);
- bacpy(&(*ctxt)->dst, dst);
- (*ctxt)->session = s;
- (*ctxt)->uuid = *uuid;
-
- chan = g_io_channel_unix_new(sdp_get_socket(s));
- (*ctxt)->io_id = g_io_add_watch(chan,
- G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
- connect_watch, *ctxt);
- g_io_channel_unref(chan);
-
- return 0;
-}
-
-int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
- uuid_t *uuid, bt_callback_t cb, void *user_data,
- bt_destroy_t destroy)
-{
- struct search_context *ctxt = NULL;
- int err;
-
- if (!cb)
- return -EINVAL;
-
- err = create_search_context(&ctxt, src, dst, uuid);
- if (err < 0)
- return err;
-
- ctxt->cb = cb;
- ctxt->destroy = destroy;
- ctxt->user_data = user_data;
-
- context_list = g_slist_append(context_list, ctxt);
-
- return 0;
-}
-
-static gint find_by_bdaddr(gconstpointer data, gconstpointer user_data)
-{
- const struct search_context *ctxt = data, *search = user_data;
-
- return (bacmp(&ctxt->dst, &search->dst) &&
- bacmp(&ctxt->src, &search->src));
-}
-
-int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst)
-{
- struct search_context match, *ctxt;
- GSList *l;
-
- memset(&match, 0, sizeof(match));
- bacpy(&match.src, src);
- bacpy(&match.dst, dst);
-
- /* Ongoing SDP Discovery */
- l = g_slist_find_custom(context_list, &match, find_by_bdaddr);
- if (l == NULL)
- return -ENOENT;
-
- ctxt = l->data;
-
- if (!ctxt->session)
- return -ENOTCONN;
-
- if (ctxt->io_id)
- g_source_remove(ctxt->io_id);
-
- if (ctxt->session)
- sdp_close(ctxt->session);
-
- search_context_cleanup(ctxt);
-
- return 0;
-}
-
char *bt_uuid2string(uuid_t *uuid)
{
gchar *str;
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
-#include <bluetooth/bluetooth.h>
-typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data);
-typedef void (*bt_destroy_t) (gpointer user_data);
-
-int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
- uuid_t *uuid, bt_callback_t cb, void *user_data,
- bt_destroy_t destroy);
-int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst);
gchar *bt_uuid2string(uuid_t *uuid);
char *bt_name2string(const char *string);
int bt_string2uuid(uuid_t *uuid, const char *string);
gchar *bt_list2string(GSList *list);
GSList *bt_string2list(const gchar *str);
-
-#ifdef NEED_G_SLIST_FREE_FULL
-static inline void g_slist_free_full(GSList *list, GDestroyNotify free_func)
-{
- g_slist_foreach(list, (GFunc) free_func, NULL);
- g_slist_free(list);
-}
-#endif
*
*/
-#define HCID_DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */
-
-/* Timeout for hci_send_req (milliseconds) */
-#define HCI_REQ_TIMEOUT 5000
struct main_opts {
char host_name[40];
unsigned long flags;
char *name;
uint32_t class;
uint16_t pageto;
+ uint16_t autoto;
uint32_t discovto;
uint32_t pairto;
uint16_t link_mode;
gboolean name_resolv;
gboolean debug_keys;
gboolean attrib_server;
- gboolean le;
- uint8_t scan;
uint8_t mode;
uint8_t discov_interval;
char deviceid[15]; /* FIXME: */
if (enabled == NULL)
return 0;
- for (i = 0; enabled[i] != NULL; i++) {
- if (desc->name != NULL && g_pattern_match_simple(enabled[i],
- desc->name) == TRUE)
- return 1;
+ for (i = 0; enabled[i] != NULL; i++)
if (desc->file != NULL && g_pattern_match_simple(enabled[i],
desc->file) == TRUE)
return 1;
- }
return 0;
}
-void __btd_toggle_debug()
+void __btd_enable_debug(struct btd_debug_desc *start,
+ struct btd_debug_desc *stop)
+{
+ struct btd_debug_desc *desc;
+
+ if (start == NULL || stop == NULL)
+ return;
+
+ for (desc = start; desc < stop; desc++) {
+ if (is_enabled(desc))
+ desc->flags |= BTD_DEBUG_FLAG_PRINT;
+ }
+}
+
+void __btd_toggle_debug(void)
{
struct btd_debug_desc *desc;
void __btd_log_init(const char *debug, int detach)
{
int option = LOG_NDELAY | LOG_PID;
- struct btd_debug_desc *desc;
- const char *name = NULL, *file = NULL;
if (debug != NULL)
enabled = g_strsplit_set(debug, ":, ", 0);
- for (desc = __start___debug; desc < __stop___debug; desc++) {
- if (file != NULL || name != NULL) {
- if (g_strcmp0(desc->file, file) == 0) {
- if (desc->name == NULL)
- desc->name = name;
- } else
- file = NULL;
- }
-
- if (is_enabled(desc))
- desc->flags |= BTD_DEBUG_FLAG_PRINT;
- }
+ __btd_enable_debug(__start___debug, __stop___debug);
if (!detach)
option |= LOG_PERROR;
openlog("bluetoothd", option, LOG_DAEMON);
- syslog(LOG_INFO, "Bluetooth deamon %s", VERSION);
+ syslog(LOG_INFO, "Bluetooth daemon %s", VERSION);
}
void __btd_log_cleanup(void)
void __btd_log_init(const char *debug, int detach);
void __btd_log_cleanup(void);
-void __btd_toggle_debug();
+void __btd_toggle_debug(void);
struct btd_debug_desc {
- const char *name;
const char *file;
#define BTD_DEBUG_FLAG_DEFAULT (0)
#define BTD_DEBUG_FLAG_PRINT (1 << 0)
unsigned int flags;
} __attribute__((aligned(8)));
+void __btd_enable_debug(struct btd_debug_desc *start,
+ struct btd_debug_desc *stop);
+
/**
* DBG:
* @fmt: format string
#include <stdlib.h>
#include <string.h>
#include <signal.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
+#include <sys/signalfd.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/uuid.h>
#include "hcid.h"
#include "sdpd.h"
-#include "attrib-server.h"
#include "adapter.h"
-#include "event.h"
#include "dbus-common.h"
#include "agent.h"
#include "manager.h"
#ifdef HAVE_CAPNG
#include <cap-ng.h>
#endif
-#include <bluetooth/sdp_lib.h>
+
#define BLUEZ_NAME "org.bluez"
#define LAST_ADAPTER_EXIT_TIMEOUT 30
#define DEFAULT_DISCOVERABLE_TIMEOUT 180 /* 3 minutes */
+#define DEFAULT_AUTO_CONNECT_TIMEOUT 60 /* 60 seconds */
struct main_opts main_opts;
-sdp_session_t *g_cached_session = NULL;
static GKeyFile *load_config(const char *file)
{
main_opts.flags |= 1 << HCID_SET_PAGETO;
}
+ val = g_key_file_get_integer(config, "General", "AutoConnectTimeout",
+ &err);
+ if (err) {
+ DBG("%s", err->message);
+ g_clear_error(&err);
+ } else {
+ DBG("auto_to=%d", val);
+ main_opts.autoto = val;
+ }
+
str = g_key_file_get_string(config, "General", "Name", &err);
if (err) {
DBG("%s", err->message);
else
main_opts.attrib_server = boolean;
- boolean = g_key_file_get_boolean(config, "General",
- "EnableLE", &err);
- if (err)
- g_clear_error(&err);
- else
- main_opts.le = boolean;
-
main_opts.link_mode = HCI_LM_ACCEPT;
main_opts.link_policy = HCI_LP_RSWITCH | HCI_LP_SNIFF |
{
/* Default HCId settings */
memset(&main_opts, 0, sizeof(main_opts));
- main_opts.scan = SCAN_PAGE;
-#ifdef __TIZEN_PATCH__
+#ifdef __SAMSUNG_PATCH__
main_opts.mode = MODE_DISCOVERABLE;
#else
main_opts.mode = MODE_CONNECTABLE;
#endif
main_opts.name = g_strdup("BlueZ");
main_opts.discovto = DEFAULT_DISCOVERABLE_TIMEOUT;
+ main_opts.autoto = DEFAULT_AUTO_CONNECT_TIMEOUT;
main_opts.remember_powered = TRUE;
main_opts.reverse_sdp = TRUE;
main_opts.name_resolv = TRUE;
static GMainLoop *event_loop;
-static void sig_term(int sig)
+static unsigned int __terminated = 0;
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+ gpointer user_data)
{
- g_main_loop_quit(event_loop);
+ 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;
+
+ switch (si.ssi_signo) {
+ case SIGINT:
+ case SIGTERM:
+ if (__terminated == 0) {
+ info("Terminating");
+ g_main_loop_quit(event_loop);
+ }
+
+ __terminated = 1;
+ break;
+ case SIGUSR2:
+ __btd_toggle_debug();
+ break;
+ case SIGPIPE:
+ /* ignore */
+ break;
+ }
+
+ return TRUE;
}
-static void sig_debug(int sig)
+static guint setup_signalfd(void)
{
- __btd_toggle_debug();
+ GIOChannel *channel;
+ guint source;
+ sigset_t mask;
+ int fd;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGUSR2);
+ sigaddset(&mask, SIGPIPE);
+
+ 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;
+ }
+
+ 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 gchar *option_debug = NULL;
conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, BLUEZ_NAME, &err);
if (!conn) {
if (dbus_error_is_set(&err)) {
+ g_printerr("D-Bus setup failed: %s\n", err.message);
dbus_error_free(&err);
return -EIO;
}
{
GOptionContext *context;
GError *err = NULL;
- struct sigaction sa;
uint16_t mtu = 0;
GKeyFile *config;
- info("(info)Bluetoothd main starting .......\n");
+ guint signal;
init_defaults();
umask(0077);
- __btd_log_init(option_debug, option_detach);
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_flags = SA_NOCLDSTOP;
- sa.sa_handler = sig_term;
- sigaction(SIGTERM, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
+ event_loop = g_main_loop_new(NULL, FALSE);
- sa.sa_handler = sig_debug;
- sigaction(SIGUSR2, &sa, NULL);
+ signal = setup_signalfd();
- sa.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sa, NULL);
+ __btd_log_init(option_debug, option_detach);
config = load_config(CONFIGDIR "/main.conf");
start_sdp_server(mtu, main_opts.deviceid, SDP_SERVER_COMPAT);
- if (main_opts.attrib_server) {
- if (attrib_server_init() < 0)
- error("Can't initialize attribute server");
- }
-
/* Loading plugins has to be done after D-Bus has been setup since
* the plugins might wanna expose some paths on the bus. However the
* best order of how to init various subsystems of the Bluetooth
* daemon needs to be re-worked. */
plugin_init(config, option_plugin, option_noplugin);
- event_loop = g_main_loop_new(NULL, FALSE);
-
if (adapter_ops_setup() < 0) {
error("adapter_ops_setup failed");
exit(1);
g_main_loop_run(event_loop);
+ g_source_remove(signal);
+
disconnect_dbus();
rfkill_exit();
plugin_cleanup();
- if (main_opts.attrib_server)
- attrib_server_exit();
-
stop_sdp_server();
agent_exit();
# Default device class. Only the major and minor device class bits are
# considered.
-#Class = 0x000100 # Computer
-Class = 0x5A020C # Smart phone
+#ifdef __SAMSUNG_PATCH__
+Class = 0x00020C # Smart phone
+#else
+#Class = 0x000100
+#endif
# How long to stay in discoverable mode before going back to non-discoverable
# The value is in seconds. Default is 180, i.e. 3 minutes.
PageTimeout = 8192
# Discover scheduler interval used in Adapter.DiscoverDevices
-# The value is in seconds. Defaults is 0 to use controller scheduler.
-DiscoverSchedulerInterval = 0
+# The value is in seconds. Defaults is 30.
+DiscoverSchedulerInterval = 30
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels.
+AutoConnectTimeout = 60
# What value should be assumed for the adapter Powered property when
# SetProperty(Powered, ...) hasn't been called yet. Defaults to true
# that they were created for.
DebugKeys = false
-# Enable Low Energy support if the dongle supports. Default is false.
-# Enable/Disable interleave discovery and attribute server over LE.
-EnableLE = false
-
# Enable the GATT Attribute Server. Default is false, because it is only
-# useful for testing. Attribute server is not enabled over LE if EnableLE
-# is false.
+# useful for testing.
AttributeServer = false
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
#include <glib.h>
#include <gdbus.h>
+#include "glib-helper.h"
#include "hcid.h"
#include "dbus-common.h"
#include "log.h"
DBUS_TYPE_INVALID);
}
+struct btd_adapter *manager_get_default_adapter(void)
+{
+ return manager_find_adapter_by_id(default_adapter_id);
+}
+
static void manager_remove_adapter(struct btd_adapter *adapter)
{
uint16_t dev_id = adapter_get_dev_id(adapter);
DBUS_TYPE_INVALID);
adapter_remove(adapter);
+ btd_adapter_unref(adapter);
if (adapters == NULL)
btd_start_exit_timer();
void manager_cleanup(DBusConnection *conn, const char *path)
{
- g_slist_foreach(adapters, (GFunc) manager_remove_adapter, NULL);
- g_slist_free(adapters);
+ while (adapters) {
+ struct btd_adapter *adapter = adapters->data;
+
+ adapters = g_slist_remove(adapters, adapter);
+ adapter_remove(adapter);
+ btd_adapter_unref(adapter);
+ }
+
+ btd_start_exit_timer();
g_dbus_unregister_interface(conn, "/", MANAGER_INTERFACE);
}
adapters = g_slist_append(adapters, adapter);
if (!adapter_init(adapter)) {
+ adapters = g_slist_remove(adapters, adapter);
btd_adapter_unref(adapter);
return NULL;
}
const char *manager_get_base_path(void);
struct btd_adapter *manager_find_adapter(const bdaddr_t *sba);
struct btd_adapter *manager_find_adapter_by_id(int id);
+struct btd_adapter *manager_get_default_adapter(void);
void manager_foreach_adapter(adapter_cb func, gpointer user_data);
GSList *manager_get_adapters(void);
struct btd_adapter *btd_manager_register_adapter(int id);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * 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
+ *
+ */
+
+#include "adapter.h"
+#include "oob.h"
+
+static oob_read_cb_t local_oob_read_cb = NULL;
+
+void oob_register_cb(oob_read_cb_t cb)
+{
+ local_oob_read_cb = cb;
+}
+
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer)
+{
+ if (local_oob_read_cb)
+ local_oob_read_cb(adapter, hash, randomizer);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * 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 (*oob_read_cb_t) (struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer);
+
+void oob_register_cb(oob_read_cb_t cb);
+
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer);
plugin->active = FALSE;
plugin->desc = desc;
+ __btd_enable_debug(desc->debug_start, desc->debug_stop);
+
plugins = g_slist_insert_sorted(plugins, plugin, compare_priority);
return TRUE;
int priority;
int (*init) (void);
void (*exit) (void);
+ void *debug_start;
+ void *debug_stop;
};
#ifdef BLUETOOTH_PLUGIN_BUILTIN
};
#else
#define BLUETOOTH_PLUGIN_DEFINE(name, version, priority, init, exit) \
+ extern struct btd_debug_desc __start___debug[] \
+ __attribute__ ((weak, visibility("hidden"))); \
+ extern struct btd_debug_desc __stop___debug[] \
+ __attribute__ ((weak, visibility("hidden"))); \
extern struct bluetooth_plugin_desc bluetooth_plugin_desc \
__attribute__ ((visibility("default"))); \
struct bluetooth_plugin_desc bluetooth_plugin_desc = { \
- #name, version, priority, init, exit \
+ #name, version, priority, init, exit, \
+ __start___debug, __stop___debug \
};
#endif
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <glib.h>
+
+#include "btio.h"
+#include "sdp-client.h"
+
+/* Number of seconds to keep a sdp_session_t in the cache */
+#define CACHE_TIMEOUT 2
+
+struct cached_sdp_session {
+ bdaddr_t src;
+ bdaddr_t dst;
+ sdp_session_t *session;
+ guint timer;
+};
+
+static GSList *cached_sdp_sessions = NULL;
+
+static gboolean cached_session_expired(gpointer user_data)
+{
+ struct cached_sdp_session *cached = user_data;
+
+ cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, cached);
+
+ sdp_close(cached->session);
+
+ g_free(cached);
+
+ return FALSE;
+}
+
+static sdp_session_t *get_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ GSList *l;
+
+ for (l = cached_sdp_sessions; l != NULL; l = l->next) {
+ struct cached_sdp_session *c = l->data;
+ sdp_session_t *session;
+
+ if (bacmp(&c->src, src) || bacmp(&c->dst, dst))
+ continue;
+
+ g_source_remove(c->timer);
+
+ session = c->session;
+
+ cached_sdp_sessions = g_slist_remove(cached_sdp_sessions, c);
+ g_free(c);
+
+ return session;
+ }
+
+ return sdp_connect(src, dst, SDP_NON_BLOCKING);
+}
+
+static void cache_sdp_session(bdaddr_t *src, bdaddr_t *dst,
+ sdp_session_t *session)
+{
+ struct cached_sdp_session *cached;
+
+ cached = g_new0(struct cached_sdp_session, 1);
+
+ bacpy(&cached->src, src);
+ bacpy(&cached->dst, dst);
+
+ cached->session = session;
+
+ cached_sdp_sessions = g_slist_append(cached_sdp_sessions, cached);
+
+ cached->timer = g_timeout_add_seconds(CACHE_TIMEOUT,
+ cached_session_expired,
+ cached);
+}
+
+struct search_context {
+ bdaddr_t src;
+ bdaddr_t dst;
+ sdp_session_t *session;
+ bt_callback_t cb;
+ bt_destroy_t destroy;
+ gpointer user_data;
+ uuid_t uuid;
+ guint io_id;
+};
+
+static GSList *context_list = NULL;
+
+static void search_context_cleanup(struct search_context *ctxt)
+{
+ context_list = g_slist_remove(context_list, ctxt);
+
+ if (ctxt->destroy)
+ ctxt->destroy(ctxt->user_data);
+
+ g_free(ctxt);
+}
+
+static void search_completed_cb(uint8_t type, uint16_t status,
+ uint8_t *rsp, size_t size, void *user_data)
+{
+ struct search_context *ctxt = user_data;
+ sdp_list_t *recs = NULL;
+ int scanned, seqlen = 0, bytesleft = size;
+ uint8_t dataType;
+ int err = 0;
+
+ if (status || type != SDP_SVC_SEARCH_ATTR_RSP) {
+ err = -EPROTO;
+ goto done;
+ }
+
+ scanned = sdp_extract_seqtype(rsp, bytesleft, &dataType, &seqlen);
+ if (!scanned || !seqlen)
+ goto done;
+
+ rsp += scanned;
+ bytesleft -= scanned;
+ do {
+ sdp_record_t *rec;
+ int recsize;
+
+ recsize = 0;
+ rec = sdp_extract_pdu(rsp, bytesleft, &recsize);
+ if (!rec)
+ break;
+
+ if (!recsize) {
+ sdp_record_free(rec);
+ break;
+ }
+
+ scanned += recsize;
+ rsp += recsize;
+ bytesleft -= recsize;
+
+ recs = sdp_list_append(recs, rec);
+ } while (scanned < (ssize_t) size && bytesleft > 0);
+
+done:
+ cache_sdp_session(&ctxt->src, &ctxt->dst, ctxt->session);
+
+ if (ctxt->cb)
+ ctxt->cb(recs, err, ctxt->user_data);
+
+ if (recs)
+ sdp_list_free(recs, (sdp_free_func_t) sdp_record_free);
+
+ search_context_cleanup(ctxt);
+}
+
+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);
+
+ search_context_cleanup(ctxt);
+ }
+
+ return FALSE;
+}
+
+static gboolean connect_watch(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ struct search_context *ctxt = user_data;
+ sdp_list_t *search, *attrids;
+ uint32_t range = 0x0000ffff;
+ socklen_t len;
+ int sk, err, sk_err = 0;
+
+ sk = g_io_channel_unix_get_fd(chan);
+ ctxt->io_id = 0;
+
+ len = sizeof(sk_err);
+ if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0)
+ err = -errno;
+ else
+ err = -sk_err;
+
+ if (err != 0)
+ goto failed;
+
+ if (sdp_set_notify(ctxt->session, search_completed_cb, ctxt) < 0) {
+ err = -EIO;
+ goto failed;
+ }
+
+ search = sdp_list_append(NULL, &ctxt->uuid);
+ attrids = sdp_list_append(NULL, &range);
+ if (sdp_service_search_attr_async(ctxt->session,
+ search, SDP_ATTR_REQ_RANGE, attrids) < 0) {
+ sdp_list_free(attrids, NULL);
+ sdp_list_free(search, NULL);
+ err = -EIO;
+ goto failed;
+ }
+
+ sdp_list_free(attrids, NULL);
+ sdp_list_free(search, NULL);
+
+ /* Set callback responsible for update the internal SDP transaction */
+ ctxt->io_id = g_io_add_watch(chan,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ search_process_cb, ctxt);
+ return FALSE;
+
+failed:
+ sdp_close(ctxt->session);
+ ctxt->session = NULL;
+
+ if (ctxt->cb)
+ ctxt->cb(NULL, err, ctxt->user_data);
+
+ search_context_cleanup(ctxt);
+
+ return FALSE;
+}
+
+static int create_search_context(struct search_context **ctxt,
+ const bdaddr_t *src,
+ const bdaddr_t *dst,
+ uuid_t *uuid)
+{
+ sdp_session_t *s;
+ GIOChannel *chan;
+
+ if (!ctxt)
+ return -EINVAL;
+
+ s = get_sdp_session(src, dst);
+ if (!s)
+ return -errno;
+
+ *ctxt = g_try_malloc0(sizeof(struct search_context));
+ if (!*ctxt) {
+ sdp_close(s);
+ return -ENOMEM;
+ }
+
+ bacpy(&(*ctxt)->src, src);
+ bacpy(&(*ctxt)->dst, dst);
+ (*ctxt)->session = s;
+ (*ctxt)->uuid = *uuid;
+
+ chan = g_io_channel_unix_new(sdp_get_socket(s));
+ (*ctxt)->io_id = g_io_add_watch(chan,
+ G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ connect_watch, *ctxt);
+ g_io_channel_unref(chan);
+
+ return 0;
+}
+
+int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
+ uuid_t *uuid, bt_callback_t cb, void *user_data,
+ bt_destroy_t destroy)
+{
+ struct search_context *ctxt = NULL;
+ int err;
+
+ if (!cb)
+ return -EINVAL;
+
+ err = create_search_context(&ctxt, src, dst, uuid);
+ if (err < 0)
+ return err;
+
+ ctxt->cb = cb;
+ ctxt->destroy = destroy;
+ ctxt->user_data = user_data;
+
+ context_list = g_slist_append(context_list, ctxt);
+
+ return 0;
+}
+
+static gint find_by_bdaddr(gconstpointer data, gconstpointer user_data)
+{
+ const struct search_context *ctxt = data, *search = user_data;
+ int ret;
+
+ ret = bacmp(&ctxt->src, &search->src);
+ if (ret != 0)
+ return ret;
+
+ return bacmp(&ctxt->dst, &search->dst);
+}
+
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ struct search_context match, *ctxt;
+ GSList *l;
+
+ memset(&match, 0, sizeof(match));
+ bacpy(&match.src, src);
+ bacpy(&match.dst, dst);
+
+ /* Ongoing SDP Discovery */
+ l = g_slist_find_custom(context_list, &match, find_by_bdaddr);
+ if (l == NULL)
+ return -ENOENT;
+
+ ctxt = l->data;
+
+ if (!ctxt->session)
+ return -ENOTCONN;
+
+ if (ctxt->io_id)
+ g_source_remove(ctxt->io_id);
+
+ if (ctxt->session)
+ sdp_close(ctxt->session);
+
+ search_context_cleanup(ctxt);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+typedef void (*bt_callback_t) (sdp_list_t *recs, int err, gpointer user_data);
+typedef void (*bt_destroy_t) (gpointer user_data);
+
+int bt_search_service(const bdaddr_t *src, const bdaddr_t *dst,
+ uuid_t *uuid, bt_callback_t cb, void *user_data,
+ bt_destroy_t destroy);
+int bt_cancel_discovery(const bdaddr_t *src, const bdaddr_t *dst);
int i, hex;
char buf[STRBUFSIZE];
char indent[MAXINDENT];
- char next_indent[MAXINDENT];
if (!value)
return;
if (indent_level >= MAXINDENT)
indent_level = MAXINDENT - 2;
- for (i = 0; i < indent_level; i++) {
+ for (i = 0; i < indent_level; i++)
indent[i] = '\t';
- next_indent[i] = '\t';
- }
indent[i] = '\0';
- next_indent[i] = '\t';
- next_indent[i + 1] = '\0';
-
buf[STRBUFSIZE - 1] = '\0';
switch (value->dtd) {
#define DEFAULT_XML_DATA_SIZE 1024
-struct sdp_xml_data *sdp_xml_data_alloc()
+struct sdp_xml_data *sdp_xml_data_alloc(void)
{
struct sdp_xml_data *elem;
/* TODO: What is it used for? */
};
-struct sdp_xml_data *sdp_xml_data_alloc();
+struct sdp_xml_data *sdp_xml_data_alloc(void);
void sdp_xml_data_free(struct sdp_xml_data *elem);
struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem);
/*
* Reset the service repository by deleting its contents
*/
-void sdp_svcdb_reset()
+void sdp_svcdb_reset(void)
{
sdp_list_free(service_db, (sdp_free_func_t) sdp_record_free);
sdp_list_free(access_db, access_free);
static sdp_cstate_list_t *cstates;
-// FIXME: should probably remove it when it's found
+/* FIXME: should probably remove it when it's found */
static sdp_buf_t *sdp_get_cached_rsp(sdp_cont_state_t *cstate)
{
sdp_cstate_list_t *p;
if (cstate) {
SDPDBG("Non null sdp_cstate_t id : 0x%x", cstate->timestamp);
- *(uint8_t *)pdata = sizeof(sdp_cont_state_t);
+ *pdata = sizeof(sdp_cont_state_t);
pdata += sizeof(uint8_t);
length += sizeof(uint8_t);
memcpy(pdata, cstate, sizeof(sdp_cont_state_t));
length += sizeof(sdp_cont_state_t);
} else {
- // set "null" continuation state
- *(uint8_t *)pdata = 0;
- pdata += sizeof(uint8_t);
+ /* set "null" continuation state */
+ *pdata = 0;
length += sizeof(uint8_t);
}
buf->data_size += length;
if (data == NULL)
return -1;
- // create 128-bit form of the search UUID
+ /* create 128-bit form of the search UUID */
uuid128 = sdp_uuid_to_uuid128((uuid_t *)data);
list = sdp_list_find(pattern, uuid128, sdp_uuid128_cmp);
bt_free(uuid128);
plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
mlen = scanned + sizeof(uint16_t) + 1;
- // ensure we don't read past buffer
+ /* ensure we don't read past buffer */
if (plen < mlen || plen != mlen + *(uint8_t *)(pdata+sizeof(uint16_t))) {
status = SDP_INVALID_SYNTAX;
goto done;
plen = ntohs(((sdp_pdu_hdr_t *)(req->buf))->plen);
mlen = scanned + sizeof(uint32_t) + sizeof(uint16_t) + 1;
- // ensure we don't read past buffer
+ /* ensure we don't read past buffer */
if (plen < mlen || plen != mlen + *(uint8_t *)pdata) {
status = SDP_INVALID_PDU_SIZE;
goto done;
}
/*
- * Calculate Attribute size acording to MTU
+ * Calculate Attribute size according to MTU
* We can send only (MTU - sizeof(sdp_pdu_hdr_t) - sizeof(sdp_cont_state_t))
*/
max_rsp_size = MIN(max_rsp_size, req->mtu - sizeof(sdp_pdu_hdr_t) -
}
}
- // push header
+ /* push header */
buf->data -= sizeof(uint16_t);
buf->buf_size += sizeof(uint16_t);
short cstate_size = 0;
uint8_t dtd = 0;
sdp_buf_t tmpbuf;
- size_t data_left = req->len;
+ size_t data_left;
tmpbuf.data = NULL;
pdata = req->buf + sizeof(sdp_pdu_hdr_t);
memset(tmpbuf.data, 0, USHRT_MAX);
/*
- * Calculate Attribute size acording to MTU
+ * Calculate Attribute size according to MTU
* We can send only (MTU - sizeof(sdp_pdu_hdr_t) - sizeof(sdp_cont_state_t))
*/
max = MIN(max, req->mtu - sizeof(sdp_pdu_hdr_t) - SDP_CONT_STATE_SIZE - sizeof(uint16_t));
break;
}
if (buf->data_size + tmpbuf.data_size < buf->buf_size) {
- // to be sure no relocations
+ /* to be sure no relocations */
sdp_append_to_buf(buf, tmpbuf.data, tmpbuf.data_size);
tmpbuf.data_size = 0;
memset(tmpbuf.data, 0, USHRT_MAX);
}
if (!rsp_count && !cstate) {
- // found nothing
+ /* found nothing */
buf->data_size = 0;
sdp_append_to_buf(buf, tmpbuf.data, tmpbuf.data_size);
sdp_set_cstate_pdu(buf, NULL);
}
- // push header
+ /* push header */
buf->data -= sizeof(uint16_t);
buf->buf_size += sizeof(uint16_t);
sdp_pdu_hdr_t *rsphdr;
sdp_buf_t rsp;
uint8_t *buf = malloc(USHRT_MAX);
- int sent = 0;
int status = SDP_INVALID_SYNTAX;
memset(buf, 0, USHRT_MAX);
rsp.data = buf;
/* stream the rsp PDU */
- sent = send(req->sock, rsp.data, rsp.data_size, 0);
+ if (send(req->sock, rsp.data, rsp.data_size, 0) < 0)
+ error("send: %s (%d)", strerror(errno), errno);
SDPDBG("Bytes Sent : %d", sent);
* seconds. Used for updating the service db state
* attribute of the service record of the SDP server
*/
-uint32_t sdp_get_time()
+uint32_t sdp_get_time(void)
{
/*
* To handle failure in gettimeofday, so an old
* Set the SDP server DB. Simply a timestamp which is the marker
* when the DB was modified.
*/
-void update_db_timestamp(void)
+static void update_db_timestamp(void)
{
uint32_t dbts = sdp_get_time();
sdp_data_t *d = sdp_data_alloc(SDP_UINT32, &dbts);
sdp_attr_replace(server, SDP_ATTR_SVCDB_STATE, d);
}
-#ifdef __TIZEN_PATCH__
-static void update_adapter_svclass_list(struct btd_adapter *adapter)
-{
- sdp_list_t *list = adapter_get_services(adapter);
- uint8_t val = 0;
-
- for (; list; list = list->next) {
- sdp_record_t *rec = (sdp_record_t *) list->data;
-
- if (rec->svclass.type != SDP_UUID16)
- continue;
-
- switch (rec->svclass.value.uuid16) {
- case DIALUP_NET_SVCLASS_ID:
- case CIP_SVCLASS_ID:
- val |= 0x42; /* Telephony & Networking */
- break;
- case IRMC_SYNC_SVCLASS_ID:
- case OBEX_OBJPUSH_SVCLASS_ID:
- case OBEX_FILETRANS_SVCLASS_ID:
- case IRMC_SYNC_CMD_SVCLASS_ID:
- case PBAP_PSE_SVCLASS_ID:
- val |= 0x10; /* Object Transfer */
- break;
- case HEADSET_SVCLASS_ID:
- case HANDSFREE_SVCLASS_ID:
- val |= 0x20; /* Audio */
- break;
- case CORDLESS_TELEPHONY_SVCLASS_ID:
- case INTERCOM_SVCLASS_ID:
- case FAX_SVCLASS_ID:
- case SAP_SVCLASS_ID:
- /*
- * Setting the telephony bit for the handsfree audio gateway
- * role is not required by the HFP specification, but the
- * Nokia 616 carkit is just plain broken! It will refuse
- * pairing without this bit set.
- */
- case HANDSFREE_AGW_SVCLASS_ID:
- val |= 0x40; /* Telephony */
- break;
- case AUDIO_SOURCE_SVCLASS_ID:
- case VIDEO_SOURCE_SVCLASS_ID:
- val |= 0x08; /* Capturing */
- break;
- case AUDIO_SINK_SVCLASS_ID:
- case VIDEO_SINK_SVCLASS_ID:
- val |= 0x04; /* Rendering */
- break;
- case PANU_SVCLASS_ID:
- case NAP_SVCLASS_ID:
- case GN_SVCLASS_ID:
- val |= 0x02; /* Networking */
- break;
- }
- }
-
- SDPDBG("Service classes 0x%02x", val);
-
-// manager_update_svc(adapter, val);
-}
-void update_svclass_list(const bdaddr_t *src)
-{
- GSList *adapters = manager_get_adapters();
-
- for (; adapters; adapters = adapters->next) {
- struct btd_adapter *adapter = adapters->data;
- bdaddr_t bdaddr;
-
- adapter_get_address(adapter, &bdaddr);
-
- if (bacmp(src, BDADDR_ANY) == 0 || bacmp(src, &bdaddr) == 0)
- update_adapter_svclass_list(adapter);
- }
-
-}
-#endif
void register_public_browse_group(void)
{
sdp_list_t *browselist;
bufsize -= sizeof(bdaddr_t);
}
- // save image of PDU: we need it when clients request this attribute
+ /* save image of PDU: we need it when clients request this attribute */
rec = extract_pdu_server(&req->device, p, bufsize, 0xffffffff, &scanned);
if (!rec)
goto invalid;
int status = 0;
/* extract service record handle */
- p += sizeof(uint32_t);
rec = sdp_record_find(handle);
if (rec) {
#define SDPDBG(fmt...)
#endif
-#define EIR_DATA_LENGTH 240
-
-#define EIR_FLAGS 0x01 /* flags */
-#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
-#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
-#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
-#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
-#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
-#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
-#define EIR_NAME_SHORT 0x08 /* shortened local name */
-#define EIR_NAME_COMPLETE 0x09 /* complete local name */
-#define EIR_TX_POWER 0x0A /* transmit power level */
-#define EIR_DEVICE_ID 0x10 /* device ID */
-
typedef struct request {
bdaddr_t device;
bdaddr_t bdaddr;
int sdp_check_access(uint32_t handle, bdaddr_t *device);
uint32_t sdp_next_handle(void);
-uint32_t sdp_get_time();
+uint32_t sdp_get_time(void);
#define SDP_SERVER_COMPAT (1 << 0)
#define SDP_SERVER_MASTER (1 << 1)
int remove_record_from_server(uint32_t handle);
void sdp_init_services_list(bdaddr_t *device);
-#ifdef __TIZEN_PATCH__
-sdp_record_t *sdp_extract_pdu_safe(const uint8_t *buf, int bufsize, int *scanned);
-#endif
-
#include <time.h>
#include <sys/file.h>
#include <sys/stat.h>
-#include <sys/param.h>
-#include <sys/socket.h>
#include <glib.h>
#include <bluetooth/sdp_lib.h>
#include "textfile.h"
-#include "adapter.h"
-#include "device.h"
+#include "glib-compat.h"
#include "glib-helper.h"
#include "storage.h"
return -ENOENT;
len = strlen(str);
- if (len > 248)
- str[248] = '\0';
+ if (len > HCI_MAX_NAME_LENGTH)
+ str[HCI_MAX_NAME_LENGTH] = '\0';
strcpy(name, str);
free(str);
int write_device_name(bdaddr_t *local, bdaddr_t *peer, char *name)
{
- char filename[PATH_MAX + 1], addr[18], str[249];
+ char filename[PATH_MAX + 1], addr[18], str[HCI_MAX_NAME_LENGTH + 1];
int i;
memset(str, 0, sizeof(str));
- for (i = 0; i < 248 && name[i]; i++)
+ for (i = 0; i < HCI_MAX_NAME_LENGTH && name[i]; i++)
if ((unsigned char) name[i] < 32 || name[i] == 127)
str[i] = '.';
else
return -ENOENT;
len = strlen(str);
- if (len > 248)
- str[248] = '\0';
+ if (len > HCI_MAX_NAME_LENGTH)
+ str[HCI_MAX_NAME_LENGTH] = '\0';
strcpy(name, str);
free(str);
int i;
memset(str, 0, sizeof(str));
- for (i = 0; i < 240; i++)
+ for (i = 0; i < HCI_MAX_EIR_LENGTH; i++)
sprintf(str + (i * 2), "%2.2X", data[i]);
create_filename(filename, PATH_MAX, local, "eir");
return -EIO;
}
- for (i = 0; i < 240; i++)
+ for (i = 0; i < HCI_MAX_EIR_LENGTH; i++)
sscanf(str + (i * 2), "%02hhX", &data[i]);
free(str);
return 0;
}
-#ifdef __TIZEN_PATCH__
-int read_pin_length(bdaddr_t *local, bdaddr_t *peer)
-{
- char filename[PATH_MAX + 1], addr[18], *str;
- int len;
- create_filename(filename, PATH_MAX, local, "linkkeys");
-
- ba2str(peer, addr);
- str = textfile_get(filename, addr);
- if (!str)
- return -ENOENT;
-
- if (strlen(str) < 36) {
- free(str);
- return -ENOENT;
- }
-
- len = atoi(str + 35);
-
- free(str);
-
- return len;
-}
-#endif
-int read_pin_code(bdaddr_t *local, bdaddr_t *peer, char *pin)
+ssize_t read_pin_code(bdaddr_t *local, bdaddr_t *peer, char *pin)
{
char filename[PATH_MAX + 1], addr[18], *str;
- int len;
+ ssize_t len;
create_filename(filename, PATH_MAX, local, "pincodes");
return ret;
}
-struct trust_list {
- GSList *trusts;
- const char *service;
-};
-
-static void append_trust(char *key, char *value, void *data)
-{
- struct trust_list *list = data;
-
- if (strstr(value, list->service))
- list->trusts = g_slist_append(list->trusts, g_strdup(key));
-}
-
-GSList *list_trusts(bdaddr_t *local, const char *service)
-{
- char filename[PATH_MAX + 1];
- struct trust_list list;
-
- create_filename(filename, PATH_MAX, local, "trusts");
-
- list.trusts = NULL;
- list.service = service;
-
- if (textfile_foreach(filename, append_trust, &list) < 0)
- return NULL;
-
- return list.trusts;
-}
-
int write_device_profiles(bdaddr_t *src, bdaddr_t *dst, const char *profiles)
{
char filename[PATH_MAX + 1], addr[18];
create_filename(filename, PATH_MAX, bdaddr, "config");
- create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
str = textfile_get(filename, "pairable");
if (!str)
return -ENOENT;
return 0;
}
-#ifdef __TIZEN_PATCH__
-// Storing limited property
-int write_device_limited(bdaddr_t *bdaddr, gboolean mode)
-{
- char filename[PATH_MAX + 1];
-
- create_filename(filename, PATH_MAX, bdaddr, "config");
-
- create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
- return textfile_put(filename, "limited", mode ? "yes" : "no");
-}
-int read_device_limited(bdaddr_t *bdaddr, gboolean *mode)
-{
- char filename[PATH_MAX + 1], *str;
-
- create_filename(filename, PATH_MAX, bdaddr, "config");
-
- create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
- str = textfile_get(filename, "limited");
- if (!str)
- return -ENOENT;
-
- *mode = strcmp(str, "yes") == 0 ? TRUE : FALSE;
-
- free(str);
-
- return 0;
-}
-
-int read_version_info(const bdaddr_t *local, const char *addr,
- uint16_t *manufacturer, uint8_t *lmp_ver, uint16_t *lmp_subver,
- uint8_t *features)
-{
- char filename[PATH_MAX + 1], *str, feature_value[5];
- int i;
-
- create_filename(filename, PATH_MAX, local, "manufacturers");
-
- str = textfile_caseget(filename, addr);
-
- if (!str)
- return -ENOENT;
-
- if (sscanf(str, "%d %d %d", manufacturer, lmp_ver, lmp_subver) != 3) {
- free(str);
- return -ENOENT;
- }
-
- free(str);
-
- create_filename(filename, PATH_MAX, local, "features");
-
- str = textfile_caseget(filename, addr);
-
- if (!str)
- return -ENOENT;
-
- if (strlen(str) != 16)
- {
- free(str);
- return -ENOENT;
- }
-
- memset(feature_value, 0x00, sizeof(feature_value));
-
- for (i = 0; i < 8; i++)
- {
- strncpy(feature_value, str + (i * 2), 4);
- if (sscanf(feature_value, "%x", &features[i]) != 1) {
- free(str);
- return -ENOENT;
- }
- }
-
- free(str);
-
- return 0;
-}
-int read_aptx_preference(bdaddr_t *bdaddr, gboolean *aptx_preference)
-{
- char filename[PATH_MAX + 1], *str;
- int len;
-
- create_filename(filename, PATH_MAX, bdaddr, "config");
-
- str = textfile_get(filename, "Codec_preference");
- if (!str)
- return -ENOENT;
-
- len = strlen(str);
- if (len > 248)
- str[248] = '\0';
-
- if(!aptx_preference)
- return -EINVAL;
-
- if(!strncmp(str,"aptx",4))
- *aptx_preference=TRUE;
- else
- *aptx_preference=FALSE;
-
- free(str);
-
- return 0;
-}
-
-int write_aptx_preference(bdaddr_t *bdaddr, gboolean aptx_preference)
-{
- char filename[PATH_MAX + 1], str[249];
- int i;
-
- memset(str, 0, sizeof(str));
- if(aptx_preference)
- memcpy(str,"aptx",5);
- else
- memcpy(str,"sbc",4);
-
- create_filename(filename, PATH_MAX, bdaddr, "config");
-
- create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
- return textfile_put(filename, "Codec_preference", str);
-}
-
-#endif
gboolean read_blocked(const bdaddr_t *local, const bdaddr_t *remote)
{
char filename[PATH_MAX + 1], *str, addr[18];
static void filter_keys(char *key, char *value, void *data)
{
struct match *match = data;
- const char *address = match->pattern;
- /* Each key contains: MAC#handle*/
- if (strncasecmp(key, address, 17) == 0)
+ if (strncasecmp(key, match->pattern, strlen(match->pattern)) == 0)
match->keys = g_slist_append(match->keys, g_strdup(key));
}
-int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba)
+static void delete_by_pattern(const char *filename, char *pattern)
{
- GSList *l;
struct match match;
- char filename[PATH_MAX + 1], address[18];
+ GSList *l;
int err;
- create_filename(filename, PATH_MAX, sba, "primary");
-
- memset(address, 0, sizeof(address));
- ba2str(dba, address);
-
- err = textfile_del(filename, address);
- if (err < 0)
- return err;
-
- /* Deleting all characteristics of a given address */
memset(&match, 0, sizeof(match));
- match.pattern = address;
+ match.pattern = pattern;
- create_filename(filename, PATH_MAX, sba, "characteristic");
err = textfile_foreach(filename, filter_keys, &match);
if (err < 0)
- return err;
+ goto done;
for (l = match.keys; l; l = l->next) {
const char *key = l->data;
textfile_del(filename, key);
}
- g_slist_foreach(match.keys, (GFunc) g_free, NULL);
- g_slist_free(match.keys);
+done:
+ g_slist_free_full(match.keys, g_free);
+}
- /* Deleting all attributes values of a given address */
- memset(&match, 0, sizeof(match));
- match.pattern = address;
+int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba)
+{
+ char filename[PATH_MAX + 1], address[18];
- create_filename(filename, PATH_MAX, sba, "attributes");
- err = textfile_foreach(filename, filter_keys, &match);
- if (err < 0)
- return err;
+ memset(address, 0, sizeof(address));
+ ba2str(dba, address);
- for (l = match.keys; l; l = l->next) {
- const char *key = l->data;
- textfile_del(filename, key);
- }
+ /* Deleting all characteristics of a given address */
+ create_filename(filename, PATH_MAX, sba, "characteristic");
+ delete_by_pattern(filename, address);
- g_slist_foreach(match.keys, (GFunc) g_free, NULL);
- g_slist_free(match.keys);
+ /* Deleting all attributes values of a given address */
+ create_filename(filename, PATH_MAX, sba, "attributes");
+ delete_by_pattern(filename, address);
- return 0;
+ /* Deleting all CCC values of a given address */
+ create_filename(filename, PATH_MAX, sba, "ccc");
+ delete_by_pattern(filename, address);
+
+ create_filename(filename, PATH_MAX, sba, "primary");
+ return textfile_del(filename, address);
}
char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba)
create_filename(filename, PATH_MAX, sba, "primary");
- create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
ba2str(dba, addr);
return textfile_caseget(filename, addr);
create_filename(filename, PATH_MAX, sba, "characteristic");
- create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
ba2str(dba, addr);
snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
create_filename(filename, PATH_MAX, sba, "attributes");
- create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
return textfile_foreach(filename, func, data);
}
-int write_device_type(const bdaddr_t *sba, const bdaddr_t *dba,
- device_type_t type)
+int read_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint16_t handle,
+ uint16_t *value)
{
- char filename[PATH_MAX + 1], addr[18], chars[3];
+ char filename[PATH_MAX + 1], addr[18], key[23];
+ char *str;
+ unsigned int config;
+ int err = 0;
- create_filename(filename, PATH_MAX, sba, "types");
+ create_filename(filename, PATH_MAX, local, "ccc");
- create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ ba2str(peer, addr);
+ snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
- ba2str(dba, addr);
+ str = textfile_caseget(filename, key);
+ if (str == NULL)
+ return -ENOENT;
- snprintf(chars, sizeof(chars), "%2.2X", type);
+ if (sscanf(str, "%04X", &config) != 1)
+ err = -ENOENT;
+ else
+ *value = config;
+
+ free(str);
- return textfile_put(filename, addr, chars);
+ return err;
}
-device_type_t read_device_type(const bdaddr_t *sba, const bdaddr_t *dba)
+int write_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint16_t handle,
+ uint16_t value)
{
- char filename[PATH_MAX + 1], addr[18], *chars;
- device_type_t type;
+ char filename[PATH_MAX + 1], addr[18], key[23], config[5];
- create_filename(filename, PATH_MAX, sba, "types");
+ create_filename(filename, PATH_MAX, local, "ccc");
create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- ba2str(dba, addr);
+ ba2str(peer, addr);
- chars = textfile_caseget(filename, addr);
- if (chars == NULL)
- return DEVICE_TYPE_UNKNOWN;
+ snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
+ snprintf(config, sizeof(config), "%04X", value);
+
+ return textfile_put(filename, key, config);
+}
- type = strtol(chars, NULL, 16);
+void delete_device_ccc(bdaddr_t *local, bdaddr_t *peer)
+{
+ char filename[PATH_MAX + 1], addr[18];
- free(chars);
+ ba2str(peer, addr);
- return type;
+ /* Deleting all CCC values of a given address */
+ create_filename(filename, PATH_MAX, local, "ccc");
+ delete_by_pattern(filename, addr);
}
int write_lastused_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm);
int write_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t type, int length);
int read_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t *type);
-int read_pin_code(bdaddr_t *local, bdaddr_t *peer, char *pin);
+ssize_t read_pin_code(bdaddr_t *local, bdaddr_t *peer, char *pin);
gboolean read_trust(const bdaddr_t *local, const char *addr, const char *service);
int write_trust(const char *src, const char *addr, const char *service, gboolean trust);
-GSList *list_trusts(bdaddr_t *local, const char *service);
int write_device_profiles(bdaddr_t *src, bdaddr_t *dst, const char *profiles);
int delete_entry(bdaddr_t *src, const char *storage, const char *key);
int store_record(const gchar *src, const gchar *dst, sdp_record_t *rec);
int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
uint16_t handle, const char *chars);
int read_device_attributes(const bdaddr_t *sba, textfile_cb func, void *data);
-int write_device_type(const bdaddr_t *sba, const bdaddr_t *dba,
- device_type_t type);
-device_type_t read_device_type(const bdaddr_t *sba, const bdaddr_t *dba);
+int read_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint16_t handle,
+ uint16_t *value);
+int write_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint16_t handle,
+ uint16_t value);
+void delete_device_ccc(bdaddr_t *local, bdaddr_t *peer);
#define PNP_UUID "00001200-0000-1000-8000-00805f9b34fb"
-#ifdef __TIZEN_PATCH__
-// Storing limited property
-int write_device_limited(bdaddr_t *local, gboolean mode);
-int read_device_limited(bdaddr_t *local, gboolean *mode);
-int read_version_info(const bdaddr_t *local, const char *addr,
- uint16_t *manufacturer, uint8_t *lmp_ver, uint16_t *lmp_subver,
- uint8_t *features);
-#endif
sprintf(str, "%s %s\n", key, value);
if (write(fd, str, size) < 0)
- err = errno;
+ err = -errno;
free(str);
{
struct stat st;
char *map, *off, *end, *str;
- off_t size, pos; size_t base;
+ off_t size;
+ size_t base;
int fd, len, err = 0;
fd = open(pathname, O_RDWR);
return -errno;
if (flock(fd, LOCK_EX) < 0) {
- err = errno;
+ err = -errno;
goto close;
}
if (fstat(fd, &st) < 0) {
- err = errno;
+ err = -errno;
goto unlock;
}
if (!size) {
if (value) {
- pos = lseek(fd, size, SEEK_SET);
+ lseek(fd, size, SEEK_SET);
err = write_key_value(fd, key, value);
}
goto unlock;
map = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_LOCKED, fd, 0);
if (!map || map == MAP_FAILED) {
- err = errno;
+ err = -errno;
goto unlock;
}
if (!off) {
if (value) {
munmap(map, size);
- pos = lseek(fd, size, SEEK_SET);
+ lseek(fd, size, SEEK_SET);
err = write_key_value(fd, key, value);
}
goto unlock;
end = strnpbrk(off, size, "\r\n");
if (!end) {
- err = EILSEQ;
+ err = -EILSEQ;
goto unmap;
}
if (!len) {
munmap(map, size);
if (ftruncate(fd, base) < 0) {
- err = errno;
+ err = -errno;
goto unlock;
}
- pos = lseek(fd, base, SEEK_SET);
+ lseek(fd, base, SEEK_SET);
if (value)
err = write_key_value(fd, key, value);
}
if (len < 0 || len > size) {
- err = EILSEQ;
+ err = -EILSEQ;
goto unmap;
}
str = malloc(len);
if (!str) {
- err = errno;
+ err = -errno;
goto unmap;
}
munmap(map, size);
if (ftruncate(fd, base) < 0) {
- err = errno;
+ err = -errno;
free(str);
goto unlock;
}
- pos = lseek(fd, base, SEEK_SET);
+ lseek(fd, base, SEEK_SET);
if (value)
err = write_key_value(fd, key, value);
if (write(fd, str, len) < 0)
- err = errno;
+ err = -errno;
free(str);
fdatasync(fd);
close(fd);
- errno = err;
+ errno = -err;
- return -err;
+ return err;
}
static char *read_key(const char *pathname, const char *key, int icase)
return NULL;
if (flock(fd, LOCK_SH) < 0) {
- err = errno;
+ err = -errno;
goto close;
}
if (fstat(fd, &st) < 0) {
- err = errno;
+ err = -errno;
goto unlock;
}
map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (!map || map == MAP_FAILED) {
- err = errno;
+ err = -errno;
goto unlock;
}
len = strlen(key);
off = find_key(map, size, key, len, icase);
if (!off) {
- err = EILSEQ;
+ err = -EILSEQ;
goto unmap;
}
end = strnpbrk(off, size - (map - off), "\r\n");
if (!end) {
- err = EILSEQ;
+ err = -EILSEQ;
goto unmap;
}
str = malloc(end - off - len);
if (!str) {
- err = EILSEQ;
+ err = -EILSEQ;
goto unmap;
}
close:
close(fd);
- errno = err;
+ errno = -err;
return str;
}
return -errno;
if (flock(fd, LOCK_SH) < 0) {
- err = errno;
+ err = -errno;
goto close;
}
if (fstat(fd, &st) < 0) {
- err = errno;
+ err = -errno;
goto unlock;
}
map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (!map || map == MAP_FAILED) {
- err = errno;
+ err = -errno;
goto unlock;
}
while (size - (off - map) > 0) {
end = strnpbrk(off, size - (off - map), " ");
if (!end) {
- err = EILSEQ;
+ err = -EILSEQ;
break;
}
key = malloc(len + 1);
if (!key) {
- err = errno;
+ err = -errno;
break;
}
off = end + 1;
if (size - (off - map) < 0) {
- err = EILSEQ;
+ err = -EILSEQ;
free(key);
break;
}
end = strnpbrk(off, size - (off - map), "\r\n");
if (!end) {
- err = EILSEQ;
+ err = -EILSEQ;
free(key);
break;
}
value = malloc(len + 1);
if (!value) {
- err = errno;
+ err = -errno;
free(key);
break;
}
close:
close(fd);
- errno = err;
+ errno = -err;
return 0;
}
static volatile sig_atomic_t __io_canceled = 0;
static volatile sig_atomic_t __io_terminated = 0;
+static volatile sig_atomic_t exit_on_release = 1;
static void sig_term(int sig)
{
if (!__io_canceled)
fprintf(stderr, "Agent has been released\n");
- __io_terminated = 1;
+ if (exit_on_release)
+ __io_terminated = 1;
reply = dbus_message_new_method_return(msg);
if (!reply) {
return 0;
}
+static void create_paired_device_reply(DBusPendingCall *pending,
+ void *user_data)
+{
+ __io_terminated = 1;
+ return;
+}
+
static int create_paired_device(DBusConnection *conn, const char *adapter_path,
const char *agent_path,
const char *capabilities,
{
dbus_bool_t success;
DBusMessage *msg;
+ DBusPendingCall *pending;
msg = dbus_message_new_method_call("org.bluez", adapter_path,
"org.bluez.Adapter",
DBUS_TYPE_STRING, &capabilities,
DBUS_TYPE_INVALID);
- success = dbus_connection_send(conn, msg, NULL);
+ exit_on_release = 0;
+ success = dbus_connection_send_with_reply(conn, msg, &pending, -1);
+ if (pending)
+ dbus_pending_call_set_notify(pending,
+ create_paired_device_reply,
+ NULL, NULL);
dbus_message_unref(msg);
print 'Usage: %s -i <dev> ListRecentRemoteDevices date' % self.name
else:
# FIXME: remove at future version
- print 'Script Error: Method %s not found. Maybe a mispelled word.' % (self.cmd_args)
+ print 'Script Error: Method %s not found. Maybe a misspelled word.' % (self.cmd_args)
except dbus.DBusException, e:
print '%s failed: %s' % (self.cmd, e)
sys.exit(1)
dump_avctp_header(hdr);
}
-static void usage()
+static void usage(void)
{
printf("avtest - Audio/Video testing ver %s\n", VERSION);
printf("Usage:\n"
printf("Reset device manually\n");
} else {
ioctl(dd, HCIDEVRESET, dev);
- printf("Device reset successully\n");
+ printf("Device reset successfully\n");
}
} else {
printf("Reset device now\n");
}
static void l2cap_connect(const char *src, const char *dst, uint16_t psm,
- gint disconn, gint sec)
+ gint disconn, gint sec,
+ gint prio)
{
struct io_data *data;
GError *err = NULL;
BT_IO_OPT_DEST, dst,
BT_IO_OPT_PSM, psm,
BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_PRIORITY, prio,
BT_IO_OPT_INVALID);
else
data->io = bt_io_connect(BT_IO_L2CAP, connect_cb, data,
BT_IO_OPT_DEST, dst,
BT_IO_OPT_PSM, psm,
BT_IO_OPT_SEC_LEVEL, sec,
+ BT_IO_OPT_PRIORITY, prio,
BT_IO_OPT_INVALID);
if (!data->io) {
static gint opt_accept = DEFAULT_ACCEPT_TIMEOUT;
static gint opt_sec = 0;
static gboolean opt_master = FALSE;
+static gint opt_priority = 0;
static GMainLoop *main_loop;
"Accept connection after N seconds" },
{ "master", 'm', 0, G_OPTION_ARG_NONE, &opt_master,
"Master role switch (incoming connections)" },
+ { "priority", 'P', 0, G_OPTION_ARG_INT, &opt_priority,
+ "Transmission priority: Setting a priority "
+ "outside the range 0 to 6 requires the"
+ "CAP_NET_ADMIN capability." },
{ NULL },
};
g_option_context_free(context);
- printf("accept=%d, reject=%d, discon=%d, defer=%d, sec=%d\n",
- opt_accept, opt_reject, opt_disconn, opt_defer, opt_sec);
+ printf("accept=%d, reject=%d, discon=%d, defer=%d, sec=%d, prio=%d\n",
+ opt_accept, opt_reject, opt_disconn, opt_defer, opt_sec,
+ opt_priority);
if (opt_psm) {
if (argc > 1)
l2cap_connect(opt_dev, argv[1], opt_psm,
- opt_disconn, opt_sec);
+ opt_disconn, opt_sec,
+ opt_priority);
else
l2cap_listen(opt_dev, opt_psm, opt_defer, opt_reject,
opt_disconn, opt_accept, opt_sec,
#include <stdio.h>
#include <errno.h>
-#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <syslog.h>
#include <sys/time.h>
-#include <sys/stat.h>
-#include <sys/poll.h>
-#include <sys/ioctl.h>
+#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/resource.h>
+#include <sys/stat.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
-
-#include <netdb.h>
-
-#include <glib.h>
-
-#define GHCI_DEV "/dev/ghci"
+#include <bluetooth/l2cap.h>
#define VHCI_DEV "/dev/vhci"
-#define VHCI_UDEV "/dev/hci_vhci"
#define VHCI_MAX_CONN 12
uint8_t dev_class[3];
uint8_t inq_mode;
uint8_t eir_fec;
- uint8_t eir_data[240];
+ uint8_t eir_data[HCI_MAX_EIR_LENGTH];
uint16_t acl_cnt;
bdaddr_t bdaddr;
- int fd;
+ int dev_fd;
+ int scan_fd;
int dd;
- GIOChannel *scan;
};
struct vhci_conn {
bdaddr_t dest;
uint16_t handle;
- GIOChannel *chan;
+ int fd;
};
struct vhci_link_info {
static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
-static GMainLoop *event_loop;
+#define MAX_EPOLL_EVENTS 10
-static volatile sig_atomic_t __io_canceled;
+static int epoll_fd;
-static inline void io_init(void)
-{
- __io_canceled = 0;
-}
-
-static inline void io_cancel(void)
-{
- __io_canceled = 1;
-}
+static volatile sig_atomic_t __io_canceled = 0;
static void sig_term(int sig)
{
- io_cancel();
- g_main_loop_quit(event_loop);
+ __io_canceled = 1;
}
-static gboolean io_acl_data(GIOChannel *chan, GIOCondition cond, gpointer data);
-static gboolean io_conn_ind(GIOChannel *chan, GIOCondition cond, gpointer data);
-static gboolean io_hci_data(GIOChannel *chan, GIOCondition cond, gpointer data);
-
static inline int read_n(int fd, void *buf, int len)
{
register int w, t = 0;
return fd;
}
-static int write_snoop(int fd, int type, int incoming, unsigned char *buf, int len)
+static int write_snoop(int fd, int type, int incoming,
+ unsigned char *buf, int len)
{
struct btsnoop_pkt pkt;
struct timeval tv;
uint32_t size = len;
uint64_t ts;
- int err;
if (fd < 0)
return -1;
if (type == HCI_COMMAND_PKT || type == HCI_EVENT_PKT)
pkt.flags |= ntohl(0x02);
- err = write(fd, &pkt, BTSNOOP_PKT_SIZE);
- err = write(fd, buf, size);
+ if (write(fd, &pkt, BTSNOOP_PKT_SIZE) < 0)
+ return -errno;
+
+ if (write(fd, buf, size) < 0)
+ return -errno;
return 0;
}
write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
- if (write(vdev.fd, buf, ptr - buf) < 0)
+ if (write(vdev.dev_fd, buf, ptr - buf) < 0)
syslog(LOG_ERR, "Can't send event: %s(%d)",
strerror(errno), errno);
}
write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
- if (write(vdev.fd, buf, ptr - buf) < 0)
+ if (write(vdev.dev_fd, buf, ptr - buf) < 0)
syslog(LOG_ERR, "Can't send event: %s(%d)",
strerror(errno), errno);
}
write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
- if (write(vdev.fd, buf, ptr - buf) < 0)
+ if (write(vdev.dev_fd, buf, ptr - buf) < 0)
syslog(LOG_ERR, "Can't send event: %s (%d)",
strerror(errno), errno);
}
write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
- if (write(vdev.fd, buf, ptr - buf) < 0)
+ if (write(vdev.dev_fd, buf, ptr - buf) < 0)
syslog(LOG_ERR, "Can't send event: %s (%d)",
strerror(errno), errno);
+
+ /* TODO: Add io_acl_data() handling */
}
static void disconn_complete(struct vhci_conn *conn)
write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
- if (write(vdev.fd, buf, ptr - buf) < 0)
+ if (write(vdev.dev_fd, buf, ptr - buf) < 0)
syslog(LOG_ERR, "Can't send event: %s (%d)",
strerror(errno), errno);
write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
- if (write(vdev.fd, buf, ptr - buf) < 0)
+ if (write(vdev.dev_fd, buf, ptr - buf) < 0)
syslog(LOG_ERR, "Can't send event: %s (%d)",
strerror(errno), errno);
}
static int scan_enable(uint8_t *data)
{
+ struct epoll_event scan_event;
struct sockaddr_in sa;
- GIOChannel *sk_io;
bdaddr_t ba;
int sk, opt;
if (!(*data & SCAN_PAGE)) {
- if (vdev.scan) {
- g_io_channel_shutdown(vdev.scan, TRUE, NULL);
- vdev.scan = NULL;
+ if (vdev.scan_fd >= 0) {
+ close(vdev.scan_fd);
+ vdev.scan_fd = -1;
}
return 0;
}
- if (vdev.scan)
+ if (vdev.scan_fd >= 0)
return 0;
if ((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
baswap(&ba, &vdev.bdaddr);
sa.sin_family = AF_INET;
memcpy(&sa.sin_addr.s_addr, &ba, sizeof(sa.sin_addr.s_addr));
- sa.sin_port = *(uint16_t *) &ba.b[4];
+ memcpy(&sa.sin_port, &ba.b[4], sizeof(sa.sin_port));
if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) {
syslog(LOG_ERR, "Can't bind socket: %s (%d)",
strerror(errno), errno);
goto failed;
}
- sk_io = g_io_channel_unix_new(sk);
- g_io_add_watch(sk_io, G_IO_IN | G_IO_NVAL, io_conn_ind, NULL);
- vdev.scan = sk_io;
+ memset(&scan_event, 0, sizeof(scan_event));
+ scan_event.events = EPOLLIN;
+ scan_event.data.fd = sk;
+
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sk, &scan_event) < 0) {
+ syslog(LOG_ERR, "Failed to setup scan event watch");
+ goto failed;
+ }
+
+ vdev.scan_fd = sk;
return 0;
failed:
return;
connect_complete(conn);
-
- g_io_add_watch(conn->chan, G_IO_IN | G_IO_NVAL | G_IO_HUP,
- io_acl_data, (gpointer) conn);
}
static void close_connection(struct vhci_conn *conn)
syslog(LOG_INFO, "Closing connection %s handle %d",
addr, conn->handle);
- g_io_channel_shutdown(conn->chan, TRUE, NULL);
- g_io_channel_unref(conn->chan);
+ close(conn->fd);
vconn[conn->handle - 1] = NULL;
disconn_complete(conn);
baswap(&ba, &cp->bdaddr);
sa.sin_family = AF_INET;
memcpy(&sa.sin_addr.s_addr, &ba, sizeof(sa.sin_addr.s_addr));
- sa.sin_port = *(uint16_t *) &ba.b[4];
+ memcpy(&sa.sin_port, &ba.b[4], sizeof(sa.sin_port));
if (connect(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
syslog(LOG_ERR, "Can't connect: %s (%d)",
strerror(errno), errno);
vconn[h] = conn;
conn->handle = h + 1;
- conn->chan = g_io_channel_unix_new(sk);
+ conn->fd = sk;
connect_complete(conn);
- g_io_add_watch(conn->chan, G_IO_IN | G_IO_NVAL | G_IO_HUP,
- io_acl_data, (gpointer) conn);
- return;
}
static void hci_link_control(uint16_t ocf, int plen, uint8_t *data)
case OCF_READ_EXT_INQUIRY_RESPONSE:
ir.status = 0x00;
ir.fec = vdev.eir_fec;
- memcpy(ir.data, vdev.eir_data, 240);
+ memcpy(ir.data, vdev.eir_data, HCI_MAX_EIR_LENGTH);
command_complete(ogf, ocf, sizeof(ir), &ir);
break;
case OCF_WRITE_EXT_INQUIRY_RESPONSE:
status = 0x00;
vdev.eir_fec = data[0];
- memcpy(vdev.eir_data, data + 1, 240);
+ memcpy(vdev.eir_data, data + 1, HCI_MAX_EIR_LENGTH);
command_complete(ogf, ocf, 1, &status);
break;
}
}
+static void hci_status_param(uint16_t ocf, int plen, uint8_t *data)
+{
+ read_local_amp_info_rp ai;
+ uint8_t status;
+
+ const uint16_t ogf = OGF_STATUS_PARAM;
+
+ switch (ocf) {
+ case OCF_READ_LOCAL_AMP_INFO:
+ memset(&ai, 0, sizeof(ai));
+
+ /* BT only */
+ ai.amp_status = 0x01;
+ ai.max_pdu_size = htobl(L2CAP_DEFAULT_MTU);
+ ai.controller_type = HCI_AMP;
+ ai.max_amp_assoc_length = htobl(HCI_MAX_ACL_SIZE);
+ /* No flushing at all */
+ ai.max_flush_timeout = 0xFFFFFFFF;
+ ai.best_effort_flush_timeout = 0xFFFFFFFF;
+
+ command_complete(ogf, ocf, sizeof(ai), &ai);
+ break;
+
+ default:
+ status = 0x01;
+ command_complete(ogf, ocf, 1, &status);
+ break;
+ }
+}
+
static void hci_command(uint8_t *data)
{
hci_command_hdr *ch;
case OGF_INFO_PARAM:
hci_info_param(ocf, ch->plen, ptr);
break;
+
+ case OGF_STATUS_PARAM:
+ hci_status_param(ocf, ch->plen, ptr);
+ break;
}
}
hci_acl_hdr *ah = (void *) data;
struct vhci_conn *conn;
uint16_t handle;
- int fd;
handle = acl_handle(btohs(ah->handle));
return;
}
- fd = g_io_channel_unix_get_fd(conn->chan);
- if (write_n(fd, data, btohs(ah->dlen) + HCI_ACL_HDR_SIZE) < 0) {
+ if (write_n(conn->fd, data, btohs(ah->dlen) + HCI_ACL_HDR_SIZE) < 0) {
close_connection(conn);
return;
}
}
}
-static gboolean io_acl_data(GIOChannel *chan, GIOCondition cond, gpointer data)
+#if 0
+static void io_acl_data(void *data)
{
- struct vhci_conn *conn = (struct vhci_conn *) data;
+ struct vhci_conn *conn = data;
unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
hci_acl_hdr *ah;
uint16_t flags;
- int fd, err, len;
-
- if (cond & G_IO_NVAL) {
- g_io_channel_unref(chan);
- return FALSE;
- }
-
- if (cond & G_IO_HUP) {
- close_connection(conn);
- return FALSE;
- }
-
- fd = g_io_channel_unix_get_fd(chan);
+ int len;
ptr = buf + 1;
- if (read_n(fd, ptr, HCI_ACL_HDR_SIZE) <= 0) {
+ if (read_n(conn->fd, ptr, HCI_ACL_HDR_SIZE) <= 0) {
close_connection(conn);
- return FALSE;
+ return;
}
ah = (void *) ptr;
ptr += HCI_ACL_HDR_SIZE;
len = btohs(ah->dlen);
- if (read_n(fd, ptr, len) <= 0) {
+ if (read_n(conn->fd, ptr, len) <= 0) {
close_connection(conn);
- return FALSE;
+ return;
}
buf[0] = HCI_ACLDATA_PKT;
write_snoop(vdev.dd, HCI_ACLDATA_PKT, 1, buf, len);
- err = write(vdev.fd, buf, len);
-
- return TRUE;
+ if (write(vdev.dev_fd, buf, len) < 0)
+ syslog(LOG_ERR, "ACL data write error");
}
+#endif
-static gboolean io_conn_ind(GIOChannel *chan, GIOCondition cond, gpointer data)
+static void io_conn_ind(void)
{
struct vhci_link_info info;
struct vhci_conn *conn;
struct sockaddr_in sa;
socklen_t len;
- int sk, nsk, h;
-
- if (cond & G_IO_NVAL)
- return FALSE;
-
- sk = g_io_channel_unix_get_fd(chan);
+ int nsk, h;
len = sizeof(sa);
- if ((nsk = accept(sk, (struct sockaddr *) &sa, &len)) < 0)
- return TRUE;
+ if ((nsk = accept(vdev.scan_fd, (struct sockaddr *) &sa, &len)) < 0)
+ return;
if (read_n(nsk, &info, sizeof(info)) < 0) {
syslog(LOG_ERR, "Can't read link info");
- return TRUE;
+ return;
}
if (!(conn = malloc(sizeof(*conn)))) {
syslog(LOG_ERR, "Can't alloc new connection");
close(nsk);
- return TRUE;
+ return;
}
bacpy(&conn->dest, &info.bdaddr);
syslog(LOG_ERR, "Too many connections");
free(conn);
close(nsk);
- return TRUE;
+ return;
accepted:
vconn[h] = conn;
conn->handle = h + 1;
- conn->chan = g_io_channel_unix_new(nsk);
+ conn->fd = nsk;
connect_request(conn);
-
- return TRUE;
}
-static gboolean io_hci_data(GIOChannel *chan, GIOCondition cond, gpointer data)
+static void io_hci_data(void)
{
unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
int type;
ssize_t len;
- int fd;
ptr = buf;
- fd = g_io_channel_unix_get_fd(chan);
-
- len = read(fd, buf, sizeof(buf));
+ len = read(vdev.dev_fd, buf, sizeof(buf));
if (len < 0) {
if (errno == EAGAIN)
- return TRUE;
+ return;
syslog(LOG_ERR, "Read failed: %s (%d)", strerror(errno), errno);
- g_io_channel_unref(chan);
- g_main_loop_quit(event_loop);
- return FALSE;
+ __io_canceled = 1;
+ return;
}
type = *ptr++;
syslog(LOG_ERR, "Unknown packet type 0x%2.2x", type);
break;
}
-
- return TRUE;
}
static int getbdaddrbyname(char *str, bdaddr_t *ba)
return 0;
}
- if (n == 1) {
- /* IP address + port */
- struct hostent *hent;
+ if (n == 0) {
+ /* loopback port */
+ in_addr_t addr = INADDR_LOOPBACK;
bdaddr_t b;
- char *ptr;
- ptr = strchr(str, ':');
- *ptr++ = 0;
-
- if (!(hent = gethostbyname(str))) {
- fprintf(stderr, "Can't resolve %s\n", str);
- return -2;
- }
-
- memcpy(&b, hent->h_addr, 4);
- *(uint16_t *) (&b.b[4]) = htons(atoi(ptr));
+ memcpy(&b, &addr, 4);
+ *(uint16_t *) (&b.b[4]) = htons(atoi(str));
baswap(ba, &b);
return 0;
return -1;
}
-static void rewrite_bdaddr(unsigned char *buf, int len, bdaddr_t *bdaddr)
-{
- hci_event_hdr *eh;
- unsigned char *ptr = buf;
- int type;
-
- if (!bdaddr)
- return;
-
- if (!bacmp(bdaddr, BDADDR_ANY))
- return;
-
- type = *ptr++;
-
- switch (type) {
- case HCI_EVENT_PKT:
- eh = (hci_event_hdr *) ptr;
- ptr += HCI_EVENT_HDR_SIZE;
-
- if (eh->evt == EVT_CMD_COMPLETE) {
- evt_cmd_complete *cc = (void *) ptr;
-
- ptr += EVT_CMD_COMPLETE_SIZE;
-
- if (cc->opcode == htobs(cmd_opcode_pack(OGF_INFO_PARAM,
- OCF_READ_BD_ADDR))) {
- bacpy((bdaddr_t *) (ptr + 1), bdaddr);
- }
- }
- break;
- }
-}
-
-static int run_proxy(int fd, int dev, bdaddr_t *bdaddr)
-{
- unsigned char buf[HCI_MAX_FRAME_SIZE + 1];
- struct hci_dev_info di;
- struct hci_filter flt;
- struct pollfd p[2];
- int dd, err, len, need_raw;
-
- dd = hci_open_dev(dev);
- if (dd < 0) {
- syslog(LOG_ERR, "Can't open device hci%d: %s (%d)",
- dev, strerror(errno), errno);
- return 1;
- }
-
- if (hci_devinfo(dev, &di) < 0) {
- syslog(LOG_ERR, "Can't get device info for hci%d: %s (%d)",
- dev, strerror(errno), errno);
- hci_close_dev(dd);
- return 1;
- }
-
- need_raw = !hci_test_bit(HCI_RAW, &di.flags);
-
- hci_filter_clear(&flt);
- hci_filter_all_ptypes(&flt);
- hci_filter_all_events(&flt);
-
- if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
- syslog(LOG_ERR, "Can't set filter for hci%d: %s (%d)",
- dev, strerror(errno), errno);
- hci_close_dev(dd);
- return 1;
- }
-
- if (need_raw) {
- if (ioctl(dd, HCISETRAW, 1) < 0) {
- syslog(LOG_ERR, "Can't set raw mode on hci%d: %s (%d)",
- dev, strerror(errno), errno);
- hci_close_dev(dd);
- return 1;
- }
- }
-
- p[0].fd = fd;
- p[0].events = POLLIN;
- p[1].fd = dd;
- p[1].events = POLLIN;
-
- while (!__io_canceled) {
- p[0].revents = 0;
- p[1].revents = 0;
- err = poll(p, 2, 500);
- if (err < 0)
- break;
- if (!err)
- continue;
-
- if (p[0].revents & POLLIN) {
- len = read(fd, buf, sizeof(buf));
- if (len > 0) {
- rewrite_bdaddr(buf, len, bdaddr);
- err = write(dd, buf, len);
- }
- }
-
- if (p[1].revents & POLLIN) {
- len = read(dd, buf, sizeof(buf));
- if (len > 0) {
- rewrite_bdaddr(buf, len, bdaddr);
- err = write(fd, buf, len);
- }
- }
- }
-
- if (need_raw) {
- if (ioctl(dd, HCISETRAW, 0) < 0)
- syslog(LOG_ERR, "Can't clear raw mode on hci%d: %s (%d)",
- dev, strerror(errno), errno);
- }
-
- hci_close_dev(dd);
-
- syslog(LOG_INFO, "Exit");
-
- return 0;
-}
-
static void usage(void)
{
printf("hciemu - HCI emulator ver %s\n", VERSION);
printf("Usage: \n");
- printf("\thciemu [options] local_address\n"
+ printf("\thciemu [options] port_number\n"
"Options:\n"
- "\t[-d device] use specified device\n"
- "\t[-b bdaddr] emulate specified address\n"
+ "\t[-d device] use specified device node\n"
"\t[-s file] create snoop file\n"
"\t[-n] do not detach\n"
"\t[-h] help, you are looking at it\n");
}
-static struct option main_options[] = {
+static const struct option options[] = {
{ "device", 1, 0, 'd' },
{ "bdaddr", 1, 0, 'b' },
{ "snoop", 1, 0, 's' },
{ "nodetach", 0, 0, 'n' },
{ "help", 0, 0, 'h' },
- { 0 }
+ { }
};
int main(int argc, char *argv[])
{
+ int exitcode = EXIT_FAILURE;
struct sigaction sa;
- GIOChannel *dev_io;
char *device = NULL, *snoop = NULL;
- bdaddr_t bdaddr;
- int fd, dd, opt, detach = 1, dev = -1;
-
- bacpy(&bdaddr, BDADDR_ANY);
+ int device_fd;
+ struct epoll_event device_event;
+ int dd, opt, detach = 1;
- while ((opt=getopt_long(argc, argv, "d:b:s:nh", main_options, NULL)) != EOF) {
+ while ((opt=getopt_long(argc, argv, "d:s:nh", options, NULL)) != EOF) {
switch(opt) {
case 'd':
device = strdup(optarg);
break;
-
- case 'b':
- str2ba(optarg, &bdaddr);
- break;
-
case 's':
snoop = strdup(optarg);
break;
-
case 'n':
detach = 0;
break;
-
case 'h':
- default:
usage();
exit(0);
+ default:
+ usage();
+ exit(1);
}
}
exit(1);
}
- if (strlen(argv[0]) > 3 && !strncasecmp(argv[0], "hci", 3)) {
- dev = hci_devid(argv[0]);
- if (dev < 0) {
- perror("Invalid device");
- exit(1);
- }
- } else {
- if (getbdaddrbyname(argv[0], &vdev.bdaddr) < 0)
- exit(1);
- }
+ if (getbdaddrbyname(argv[0], &vdev.bdaddr) < 0)
+ exit(1);
if (detach) {
if (daemon(0, 0)) {
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
- io_init();
-
- if (!device && dev >= 0)
- device = strdup(GHCI_DEV);
+ if (!device)
+ device = strdup(VHCI_DEV);
/* Open and create virtual HCI device */
- if (device) {
- fd = open(device, O_RDWR);
- if (fd < 0) {
- syslog(LOG_ERR, "Can't open device %s: %s (%d)",
- device, strerror(errno), errno);
- free(device);
- exit(1);
- }
+ device_fd = open(device, O_RDWR);
+ if (device_fd < 0) {
+ syslog(LOG_ERR, "Can't open device %s: %s (%d)",
+ device, strerror(errno), errno);
free(device);
- } else {
- fd = open(VHCI_DEV, O_RDWR);
- if (fd < 0) {
- fd = open(VHCI_UDEV, O_RDWR);
- if (fd < 0) {
- syslog(LOG_ERR, "Can't open device %s: %s (%d)",
- VHCI_DEV, strerror(errno), errno);
- exit(1);
- }
- }
+ return exitcode;
}
+ free(device);
+
/* Create snoop file */
if (snoop) {
dd = create_snoop(snoop);
dd = -1;
/* Create event loop */
- event_loop = g_main_loop_new(NULL, FALSE);
-
- if (dev >= 0)
- return run_proxy(fd, dev, &bdaddr);
+ epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (epoll_fd < 0) {
+ perror("Failed to create epoll descriptor");
+ goto close_device;
+ }
/* Device settings */
vdev.features[0] = 0xff;
vdev.eir_fec = 0x00;
memset(vdev.eir_data, 0, sizeof(vdev.eir_data));
- vdev.fd = fd;
+ vdev.dev_fd = device_fd;
vdev.dd = dd;
- dev_io = g_io_channel_unix_new(fd);
- g_io_add_watch(dev_io, G_IO_IN, io_hci_data, NULL);
+ memset(&device_event, 0, sizeof(device_event));
+ device_event.events = EPOLLIN;
+ device_event.data.fd = device_fd;
+
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, device_fd, &device_event) < 0) {
+ perror("Failed to setup device event watch");
+ goto close_device;
+ }
setpriority(PRIO_PROCESS, 0, -19);
/* Start event processor */
- g_main_loop_run(event_loop);
+ for (;;) {
+ struct epoll_event events[MAX_EPOLL_EVENTS];
+ int n, nfds;
+
+ if (__io_canceled)
+ break;
- close(fd);
+ nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, -1);
+ if (nfds < 0)
+ continue;
+
+ for (n = 0; n < nfds; n++) {
+ if (events[n].data.fd == vdev.dev_fd)
+ io_hci_data();
+ else if (events[n].data.fd == vdev.scan_fd)
+ io_conn_ind();
+ }
+ }
+
+ exitcode = EXIT_SUCCESS;
+
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, device_fd, NULL);
+
+close_device:
+ close(device_fd);
if (dd >= 0)
close(dd);
+ close(epoll_fd);
+
syslog(LOG_INFO, "Exit");
- return 0;
+ return exitcode;
}
char *filename;
mode_t filemode;
- int err, mode = 0;
+ int mode = 0;
int dd, rd, sd, fd;
uint16_t sco_handle, sco_mtu, vs;
fprintf(stderr, "SCO audio channel connected (handle %d, mtu %d)\n", sco_handle, sco_mtu);
- if (mode == RECORD)
- err = write(rd, "RING\r\n", 6);
+ if (mode == RECORD) {
+ if (write(rd, "RING\r\n", 6) < 0)
+ return -errno;
+ }
maxfd = (rd > sd) ? rd : sd;
--- /dev/null
+profile a2dp
+init_bt
+init_profile
+start_stream
+sleep 2
+stop_stream
+quit
+
--- /dev/null
+#!./ipctest
+
+profile a2dp
+init_bt
+
+init_profile
+
+start_stream
+sleep 1
+stop_stream
+
+stop_stream
+start_stream
+
+stop_stream
+start_stream
+
+stop_stream
+start_stream
+
+stop_stream
+start_stream
+
+stop_stream
+start_stream
+
+stop_stream
+stop_stream
+stop_stream
+
+start_stream
+start_stream
+start_stream
+
+stop_stream
+
+sleep 1
+
+start_stream
+start_stream
+start_stream
+stop_stream
+stop_stream
+
+start_stream
+sleep 1
+stop_stream
+
+start_stream
+stop_stream
+
+start_stream
+stop_stream
+
+shutdown_bt
+
+init_bt
+
+init_profile
+
+start_stream
+stop_stream
+stop_stream
+start_stream
+stop_stream
+start_stream
+stop_stream
+start_stream
+stop_stream
+start_stream
+stop_stream
+start_stream
+stop_stream
+stop_stream
+stop_stream
+
+start_stream
+start_stream
+start_stream
+stop_stream
+
+quit
--- /dev/null
+init_bt
+
+profile a2dp
+init_profile
+start_stream
+sleep 2
+stop_stream
+start_stream
+sleep 2
+stop_stream
+
+shutdown_bt
+init_bt
+
+profile hsp
+init_profile
+start_stream
+sleep 2
+stop_stream
+start_stream
+sleep 2
+stop_stream
+
+shutdown_bt
+init_bt
+
+profile a2dp
+init_profile
+start_stream
+sleep 2
+stop_stream
+start_stream
+sleep 2
+stop_stream
+
+shutdown_bt
+init_bt
+
+profile hsp
+init_profile
+start_stream
+sleep 2
+stop_stream
+start_stream
+sleep 2
+stop_stream
+
+shutdown_bt
+
+quit
--- /dev/null
+profile hsp
+init_bt
+init_profile
+start_stream
+sleep 2
+stop_stream
+quit
--- /dev/null
+#!./ipctest
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+
+init_bt
+sleep 1
+shutdown_bt
+
+init_bt
+sleep 1
+shutdown_bt
+
+init_bt
+sleep 1
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+init_bt
+shutdown_bt
+
+quit
+
else {
err = -errno;
ERR("Error sending data to audio service: %s(%d)",
- strerror(errno), errno);
+ strerror(-err), -err);
}
return err;
} else {
err = -errno;
ERR("Error receiving data from audio service: %s(%d)",
- strerror(errno), errno);
+ strerror(-err), -err);
}
return err;
DBG("bt_audio_service_open");
u->service_fd = bt_audio_service_open();
- if (u->service_fd <= 0) {
- perror(strerror(errno));
- return errno;
+ if (u->service_fd < 0) {
+ int err = -errno;
+
+ ERR("bt_audio_service_open() failed: %s (%d)", strerror(-err),
+ -err);
+
+ return err;
}
return 0;
static int count = 1;
/* Default delay after sending count number of frames */
-static unsigned long delay = 0;
+static unsigned long send_delay = 0;
+
+/* Default delay before receiving */
+static unsigned long recv_delay = 0;
static char *filename = NULL;
static int reliable = 0;
static int timestamp = 0;
static int defer_setup = 0;
+static int priority = -1;
+static int rcvbuf = 0;
+
+static struct {
+ char *name;
+ int flag;
+} l2cap_modes[] = {
+ { "basic", L2CAP_MODE_BASIC },
+ /* Not implemented
+ { "flowctl", L2CAP_MODE_FLOWCTL },
+ { "retrans", L2CAP_MODE_RETRANS },
+ */
+ { "ertm", L2CAP_MODE_ERTM },
+ { "streaming", L2CAP_MODE_STREAMING },
+ { 0 }
+};
+
+static void list_l2cap_modes(void)
+{
+ int i;
+
+ printf("l2test - L2CAP testing\n"
+ "List L2CAP modes:\n");
+ for (i=0; l2cap_modes[i].name; i++)
+ printf("\t%s\n", l2cap_modes[i].name);
+}
static float tv2fl(struct timeval tv)
{
goto error;
}
+ /* Set receive buffer size */
+ if (rcvbuf && setsockopt(sk, SOL_SOCKET, SO_RCVBUF,
+ &rcvbuf, sizeof(rcvbuf)) < 0) {
+ syslog(LOG_ERR, "Can't set socket rcv buf size: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ optlen = sizeof(rcvbuf);
+ if (getsockopt(sk, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get socket rcv buf size: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
/* Connect to remote device */
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
goto error;
}
+ if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY, &priority,
+ sizeof(priority)) < 0) {
+ syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ if (getsockopt(sk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
syslog(LOG_INFO, "Connected [imtu %d, omtu %d, flush_to %d, "
- "mode %d, handle %d, class 0x%02x%02x%02x]",
+ "mode %d, handle %d, class 0x%02x%02x%02x, priority %d, rcvbuf %d]",
opts.imtu, opts.omtu, opts.flush_to, opts.mode, conn.hci_handle,
- conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+ conn.dev_class[2], conn.dev_class[1], conn.dev_class[0], opt,
+ rcvbuf);
omtu = (opts.omtu > buffer_size) ? buffer_size : opts.omtu;
imtu = (opts.imtu > buffer_size) ? buffer_size : opts.imtu;
goto error;
}
+
/* Listen for connections */
if (listen(sk, 10)) {
syslog(LOG_ERR, "Can not listen on the socket: %s (%d)",
/* Child */
close(sk);
+ /* Set receive buffer size */
+ if (rcvbuf && setsockopt(nsk, SOL_SOCKET, SO_RCVBUF, &rcvbuf,
+ sizeof(rcvbuf)) < 0) {
+ syslog(LOG_ERR, "Can't set rcv buf size: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ optlen = sizeof(rcvbuf);
+ if (getsockopt(nsk, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen)
+ < 0) {
+ syslog(LOG_ERR, "Can't get rcv buf size: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
/* Get current options */
memset(&opts, 0, sizeof(opts));
optlen = sizeof(opts);
}
}
+ if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY,
+ &priority, sizeof(priority)) < 0) {
+ syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+ strerror(errno), errno);
+ close(nsk);
+ goto error;
+ }
+
+ optlen = sizeof(priority);
+ if (getsockopt(nsk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
ba2str(&addr.l2_bdaddr, ba);
- syslog(LOG_INFO, "Connect from %s [imtu %d, omtu %d, flush_to %d, "
- "mode %d, handle %d, class 0x%02x%02x%02x]",
- ba, opts.imtu, opts.omtu, opts.flush_to, opts.mode, conn.hci_handle,
- conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+ syslog(LOG_INFO, "Connect from %s [imtu %d, omtu %d, "
+ "flush_to %d, mode %d, handle %d, "
+ "class 0x%02x%02x%02x, priority %d, rcvbuf %d]",
+ ba, opts.imtu, opts.omtu, opts.flush_to,
+ opts.mode, conn.hci_handle, conn.dev_class[2],
+ conn.dev_class[1], conn.dev_class[0], opt,
+ rcvbuf);
omtu = (opts.omtu > buffer_size) ? buffer_size : opts.omtu;
imtu = (opts.imtu > buffer_size) ? buffer_size : opts.imtu;
syslog(LOG_INFO, "Initial bytes %d", len);
}
+ if (recv_delay)
+ usleep(recv_delay);
+
syslog(LOG_INFO, "Receiving ...");
memset(ts, 0, sizeof(ts));
size -= len;
}
- if (num_frames && delay && count && !(seq % count))
- usleep(delay);
+ if (num_frames && send_delay && count && !(seq % count))
+ usleep(send_delay);
}
}
case 0x0000:
memcpy(&mask, rsp->data, sizeof(mask));
printf("Extended feature mask is 0x%04x\n", btohl(mask));
- if (mask & 0x01)
+ if (mask & L2CAP_FEAT_FLOWCTL)
printf(" Flow control mode\n");
- if (mask & 0x02)
+ if (mask & L2CAP_FEAT_RETRANS)
printf(" Retransmission mode\n");
- if (mask & 0x04)
+ if (mask & L2CAP_FEAT_BIDIR_QOS)
printf(" Bi-directional QoS\n");
- if (mask & 0x08)
+ if (mask & L2CAP_FEAT_ERTM)
printf(" Enhanced Retransmission mode\n");
- if (mask & 0x10)
+ if (mask & L2CAP_FEAT_STREAMING)
printf(" Streaming mode\n");
- if (mask & 0x20)
+ if (mask & L2CAP_FEAT_FCS)
printf(" FCS Option\n");
- if (mask & 0x40)
+ if (mask & L2CAP_FEAT_EXT_FLOW)
printf(" Extended Flow Specification\n");
- if (mask & 0x80)
+ if (mask & L2CAP_FEAT_FIXED_CHAN)
printf(" Fixed Channels\n");
- if (mask & 0x0100)
+ if (mask & L2CAP_FEAT_EXT_WINDOW)
printf(" Extended Window Size\n");
- if (mask & 0x0200)
+ if (mask & L2CAP_FEAT_UCD)
printf(" Unicast Connectionless Data Reception\n");
break;
case 0x0001:
"\t[-N num] send num frames (default = infinite)\n"
"\t[-C num] send num frames before delay (default = 1)\n"
"\t[-D milliseconds] delay after sending num frames (default = 0)\n"
- "\t[-X mode] select retransmission/flow-control mode\n"
+ "\t[-K milliseconds] delay before receiving (default = 0)\n"
+ "\t[-X mode] l2cap mode (help for list, default = basic)\n"
"\t[-F fcs] use CRC16 check (default = 1)\n"
"\t[-Q num] Max Transmit value (default = 3)\n"
"\t[-Z size] Transmission Window size (default = 63)\n"
+ "\t[-Y priority] socket priority\n"
+ "\t[-H size] Maximum receive buffer size\n"
"\t[-R] reliable mode\n"
"\t[-G] use connectionless channel (datagram)\n"
"\t[-U] use sock stream\n"
int main(int argc, char *argv[])
{
struct sigaction sa;
- int opt, sk, mode = RECV, need_addr = 0;
+ int opt, sk, i, mode = RECV, need_addr = 0;
bacpy(&bdaddr, BDADDR_ANY);
- while ((opt=getopt(argc,argv,"rdscuwmntqxyzpb:i:P:I:O:J:B:N:L:W:C:D:X:F:Q:Z:RUGAESMT")) != EOF) {
+ while ((opt=getopt(argc,argv,"rdscuwmntqxyzpb:i:P:I:O:J:B:N:L:W:C:D:X:F:Q:Z:Y:H:K:RUGAESMT")) != EOF) {
switch(opt) {
case 'r':
mode = RECV;
break;
case 'D':
- delay = atoi(optarg) * 1000;
+ send_delay = atoi(optarg) * 1000;
+ break;
+
+ case 'K':
+ recv_delay = atoi(optarg) * 1000;
break;
case 'X':
- if (strcasecmp(optarg, "ertm") == 0)
- rfcmode = L2CAP_MODE_ERTM;
- else
- rfcmode = atoi(optarg);
+ rfcmode = -1;
+
+ for (i = 0; l2cap_modes[i].name; i++)
+ if (!strcasecmp(l2cap_modes[i].name, optarg))
+ rfcmode = l2cap_modes[i].flag;
+
+ if (!strcasecmp(optarg, "help") || rfcmode == -1) {
+ list_l2cap_modes();
+ exit(1);
+ }
+
+ break;
+
+ case 'Y':
+ priority = atoi(optarg);
break;
case 'F':
cid = atoi(optarg);
break;
+ case 'H':
+ rcvbuf = atoi(optarg);
+ break;
+
default:
usage();
exit(1);
print " %s = %s" % (key, list)
elif (key == "Class"):
print " %s = 0x%06x" % (key, value)
+ elif (key == "Vendor"):
+ print " %s = 0x%04x" % (key, value)
+ elif (key == "Product"):
+ print " %s = 0x%04x" % (key, value)
+ elif (key == "Version"):
+ print " %s = 0x%04x" % (key, value)
else:
print " %s = %s" % (key, value)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <getopt.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+static volatile sig_atomic_t __io_canceled = 0;
+static volatile sig_atomic_t __io_terminated = 0;
+static char *adapter = NULL;
+static DBusConnection *sys = NULL;
+static DBusConnection *session = NULL;
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static DBusMessage *get_all(DBusConnection *conn, const char *name)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+ const char *iface = "org.mpris.MediaPlayer2.Player";
+
+ msg = dbus_message_new_method_call(name, "/org/mpris/MediaPlayer2",
+ DBUS_INTERFACE_PROPERTIES, "GetAll");
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return NULL;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+ dbus_message_unref(msg);
+
+ if (!reply) {
+ fprintf(stderr, "Can't get default adapter\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return NULL;
+ }
+
+ return reply;
+}
+
+static void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+ DBusMessageIter value;
+ char sig[2] = { type, '\0' };
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
+
+ dbus_message_iter_append_basic(&value, type, val);
+
+ dbus_message_iter_close_container(iter, &value);
+}
+
+static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
+ void *val)
+{
+ DBusMessageIter entry;
+
+ if (type == DBUS_TYPE_STRING) {
+ const char *str = *((const char **) val);
+ if (str == NULL)
+ return;
+ }
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+
+ append_variant(&entry, type, val);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+static dbus_bool_t emit_property_changed(DBusConnection *conn,
+ const char *path,
+ const char *interface,
+ const char *name,
+ int type, void *value)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter;
+ dbus_bool_t result;
+
+ signal = dbus_message_new_signal(path, interface, "PropertyChanged");
+
+ if (!signal) {
+ fprintf(stderr, "Unable to allocate new %s.PropertyChanged"
+ " signal", interface);
+ return FALSE;
+ }
+
+ dbus_message_iter_init_append(signal, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name);
+
+ append_variant(&iter, type, value);
+
+ result = dbus_connection_send(conn, signal, NULL);
+ dbus_message_unref(signal);
+
+ return result;
+}
+
+static int parse_property(DBusConnection *conn, const char *path,
+ const char *key,
+ DBusMessageIter *entry,
+ DBusMessageIter *properties)
+{
+ DBusMessageIter var;
+
+ printf("property %s found\n", key);
+
+ if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(entry, &var);
+
+ if (strcasecmp(key, "PlaybackStatus") == 0) {
+ const char *value;
+
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ if (properties)
+ dict_append_entry(properties, "Status",
+ DBUS_TYPE_STRING, &value);
+ else
+ emit_property_changed(sys, path,
+ "org.bluez.MediaPlayer", "Status",
+ DBUS_TYPE_STRING, &value);
+ } else if (strcasecmp(key, "Position") == 0) {
+ int64_t usec, msec;
+
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_INT64)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&var, &usec);
+ msec = usec / 1000;
+
+ if (properties)
+ dict_append_entry(properties, "Position",
+ DBUS_TYPE_UINT32, &msec);
+ else
+ emit_property_changed(sys, path,
+ "org.bluez.MediaPlayer", "Position",
+ DBUS_TYPE_UINT32, &msec);
+ } else if (strcasecmp(key, "Shuffle") == 0) {
+ dbus_bool_t value;
+ const char *str;
+
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ str = value ? "on" : "off";
+ if (properties)
+ dict_append_entry(properties, "Shuffle",
+ DBUS_TYPE_STRING, &str);
+ else
+ emit_property_changed(sys, path,
+ "org.bluez.MediaPlayer", "Shuffle",
+ DBUS_TYPE_UINT32, &str);
+ }
+
+ return 0;
+}
+
+static int parse_properties(DBusConnection *conn, const char *path,
+ DBusMessageIter *args,
+ DBusMessageIter *properties)
+{
+ DBusMessageIter dict;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(args);
+ if (ctype != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(args, &dict);
+
+ while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+ DBUS_TYPE_INVALID) {
+ DBusMessageIter entry;
+ const char *key;
+
+ if (ctype != DBUS_TYPE_DICT_ENTRY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (parse_property(conn, path, key, &entry, properties) < 0)
+ return -EINVAL;
+
+ dbus_message_iter_next(&dict);
+ }
+
+ return 0;
+}
+
+static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
+ DBusMessageIter *metadata)
+{
+ DBusMessageIter var;
+
+ printf("metadata %s found\n", key);
+
+ if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(entry, &var);
+
+ if (strcasecmp(key, "xesam:title") == 0) {
+ const char *value;
+
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&var, &value);
+ dict_append_entry(metadata, "Title", DBUS_TYPE_STRING,
+ &value);
+ } else if (strcasecmp(key, "xesam:artist") == 0) {
+ const char *value;
+ DBusMessageIter array;
+
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&var, &array);
+
+ if (dbus_message_iter_get_arg_type(&array) !=
+ DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&array, &value);
+ dict_append_entry(metadata, "Artist", DBUS_TYPE_STRING,
+ &value);
+ } else if (strcasecmp(key, "xesam:album") == 0) {
+ const char *value;
+
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&var, &value);
+ dict_append_entry(metadata, "Album", DBUS_TYPE_STRING,
+ &value);
+ } else if (strcasecmp(key, "xesam:genre") == 0) {
+ const char *value;
+ DBusMessageIter array;
+
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&var, &array);
+
+ if (dbus_message_iter_get_arg_type(&array) !=
+ DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&array, &value);
+ dict_append_entry(metadata, "Genre", DBUS_TYPE_STRING,
+ &value);
+ } else if (strcasecmp(key, "mpris:length") == 0) {
+ int64_t usec, msec;
+
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_INT64)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&var, &usec);
+ msec = usec / 1000;
+
+ dict_append_entry(metadata, "Duration", DBUS_TYPE_UINT32,
+ &msec);
+ } else if (strcasecmp(key, "xesam:trackNumber") == 0) {
+ int32_t value;
+
+ if (dbus_message_iter_get_arg_type(&var) !=
+ DBUS_TYPE_INT32)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ dict_append_entry(metadata, "Number", DBUS_TYPE_UINT32,
+ &value);
+ }
+
+ return 0;
+}
+
+static int parse_track(DBusMessageIter *args, DBusMessageIter *metadata)
+{
+ DBusMessageIter var, dict;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(args);
+ if (ctype != DBUS_TYPE_VARIANT)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(args, &var);
+
+ ctype = dbus_message_iter_get_arg_type(&var);
+ if (ctype != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&var, &dict);
+
+ while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+ DBUS_TYPE_INVALID) {
+ DBusMessageIter entry;
+ const char *key;
+
+ if (ctype != DBUS_TYPE_DICT_ENTRY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (parse_metadata_entry(&entry, key, metadata) < 0)
+ return -EINVAL;
+
+ dbus_message_iter_next(&dict);
+ }
+
+ return 0;
+}
+
+static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata)
+{
+ DBusMessageIter dict;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(args);
+ if (ctype != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(args, &dict);
+
+ while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
+ DBUS_TYPE_INVALID) {
+ DBusMessageIter entry;
+ const char *key;
+
+ if (ctype != DBUS_TYPE_DICT_ENTRY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(&entry, &key);
+ dbus_message_iter_next(&entry);
+
+ if (strcasecmp(key, "Metadata") == 0)
+ return parse_track(&entry, metadata);
+
+ dbus_message_iter_next(&dict);
+ }
+
+ return -EINVAL;
+}
+
+static char *sender2path(const char *sender)
+{
+ char *path;
+
+ path = g_strconcat("/", sender, NULL);
+ return g_strdelimit(path, ":.", '_');
+}
+
+static DBusHandlerResult player_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ if (dbus_message_is_method_call(msg, "org.bluez.MediaPlayer",
+ "Release")) {
+ printf("Release\n");
+ exit(1);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static const DBusObjectPathVTable player_table = {
+ .message_function = player_message,
+};
+
+static void add_player(DBusConnection *conn, const char *name,
+ const char *sender)
+{
+ DBusMessage *reply = get_all(conn, name);
+ DBusMessage *msg;
+ DBusMessageIter iter, args, properties, metadata;
+ DBusError err;
+ char *path;
+
+ if (!reply)
+ return;
+
+ msg = dbus_message_new_method_call("org.bluez", adapter,
+ "org.bluez.Media",
+ "RegisterPlayer");
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return;
+ }
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ path = sender2path(sender);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &properties);
+
+ dbus_message_iter_init(reply, &args);
+
+ if (parse_properties(conn, path, &args, &properties) < 0)
+ goto done;
+
+ dbus_message_iter_close_container(&iter, &properties);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
+
+ dbus_message_iter_init(reply, &args);
+
+ if (parse_metadata(&args, &metadata) < 0)
+ goto done;
+
+ dbus_message_iter_close_container(&iter, &metadata);
+
+ dbus_message_unref(reply);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(sys, msg, -1, &err);
+ if (!reply) {
+ fprintf(stderr, "Can't register player\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ goto done;
+ }
+
+ if (!dbus_connection_register_object_path(sys, path, &player_table,
+ NULL))
+ fprintf(stderr, "Can't register object path for agent\n");
+
+done:
+ if (reply)
+ dbus_message_unref(reply);
+ dbus_message_unref(msg);
+ g_free(path);
+}
+
+static void remove_player(DBusConnection *conn, const char *sender)
+{
+ DBusMessage *msg;
+ char *path;
+
+ msg = dbus_message_new_method_call("org.bluez", adapter,
+ "org.bluez.Media",
+ "UnregisterPlayer");
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return;
+ }
+
+ path = sender2path(sender);
+ dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID);
+
+ dbus_connection_send(sys, msg, NULL);
+
+ dbus_message_unref(msg);
+ g_free(path);
+}
+
+static DBusHandlerResult properties_changed(DBusConnection *conn,
+ DBusMessage *msg)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter, entry, metadata;
+ const char *iface;
+ char *path;
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ dbus_message_iter_get_basic(&iter, &iface);
+
+ printf("PropertiesChanged interface %s\n", iface);
+
+ dbus_message_iter_next(&iter);
+
+ path = sender2path(dbus_message_get_sender(msg));
+ parse_properties(conn, path, &iter, NULL);
+
+ signal = dbus_message_new_signal(path, "org.bluez.MediaPlayer",
+ "TrackChanged");
+ if (!signal) {
+ fprintf(stderr, "Unable to allocate new PropertyChanged"
+ " signal\n");
+ goto err;
+ }
+
+ dbus_message_iter_init_append(signal, &entry);
+
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
+
+ dbus_message_iter_init(msg, &iter);
+ dbus_message_iter_next(&iter);
+
+ if (parse_metadata(&iter, &metadata) < 0)
+ goto err;
+
+ dbus_message_iter_close_container(&entry, &metadata);
+
+ dbus_connection_send(sys, signal, NULL);
+ dbus_message_unref(signal);
+ g_free(path);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+err:
+ if (signal)
+ dbus_message_unref(signal);
+ g_free(path);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult session_filter(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *name, *old, *new;
+
+ if (dbus_message_is_signal(msg, DBUS_INTERFACE_PROPERTIES,
+ "PropertiesChanged"))
+ return properties_changed(conn, msg);
+
+ if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (!g_str_has_prefix(name, "org.mpris"))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (*new == '\0') {
+ printf("player %s at %s disappear\n", name, old);
+ remove_player(conn, old);
+ } else {
+ printf("player %s at %s found\n", name, new);
+ add_player(conn, name, new);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult system_filter(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *name, *old, *new;
+
+ if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (!strcmp(name, "org.bluez") && *new == '\0') {
+ fprintf(stderr, "bluetoothd disconnected\n");
+ __io_terminated = 1;
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static char *get_default_adapter(DBusConnection *conn)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+ const char *reply_path;
+ char *path;
+
+ msg = dbus_message_new_method_call("org.bluez", "/",
+ "org.bluez.Manager", "DefaultAdapter");
+
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return NULL;
+ }
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+ dbus_message_unref(msg);
+
+ if (!reply) {
+ fprintf(stderr, "Can't get default adapter\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return NULL;
+ }
+
+ if (!dbus_message_get_args(reply, &err,
+ DBUS_TYPE_OBJECT_PATH, &reply_path,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Can't get reply arguments\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ dbus_message_unref(reply);
+ return NULL;
+ }
+
+ path = strdup(reply_path);
+
+ dbus_message_unref(reply);
+
+ dbus_connection_flush(conn);
+
+ return path;
+}
+
+static char *get_adapter(DBusConnection *conn, const char *adapter)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+ const char *reply_path;
+ char *path;
+
+ if (!adapter)
+ return get_default_adapter(conn);
+
+ msg = dbus_message_new_method_call("org.bluez", "/",
+ "org.bluez.Manager", "FindAdapter");
+
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return NULL;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &adapter,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+ dbus_message_unref(msg);
+
+ if (!reply) {
+ fprintf(stderr, "Can't find adapter %s\n", adapter);
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return NULL;
+ }
+
+ if (!dbus_message_get_args(reply, &err,
+ DBUS_TYPE_OBJECT_PATH, &reply_path,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Can't get reply arguments\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ dbus_message_unref(reply);
+ return NULL;
+ }
+
+ path = strdup(reply_path);
+
+ dbus_message_unref(reply);
+
+ dbus_connection_flush(conn);
+
+ return path;
+}
+
+static char *get_name_owner(DBusConnection *conn, const char *name)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+ char *owner;
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS, "GetNameOwner");
+
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return NULL;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+ dbus_message_unref(msg);
+
+ if (!reply) {
+ fprintf(stderr, "Can't find adapter %s\n", adapter);
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return NULL;
+ }
+
+ if (!dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_STRING, &owner,
+ DBUS_TYPE_INVALID)) {
+ dbus_message_unref(reply);
+ return NULL;
+ }
+
+ owner = g_strdup(owner);
+
+ dbus_message_unref(reply);
+
+ dbus_connection_flush(conn);
+
+ return owner;
+}
+
+static void parse_list_names(DBusConnection *conn, DBusMessageIter *args)
+{
+ DBusMessageIter array;
+ int ctype;
+
+ ctype = dbus_message_iter_get_arg_type(args);
+ if (ctype != DBUS_TYPE_ARRAY)
+ return;
+
+ dbus_message_iter_recurse(args, &array);
+
+ while ((ctype = dbus_message_iter_get_arg_type(&array)) !=
+ DBUS_TYPE_INVALID) {
+ const char *name;
+ char *owner;
+
+ if (ctype != DBUS_TYPE_STRING)
+ goto next;
+
+ dbus_message_iter_get_basic(&array, &name);
+
+ if (!g_str_has_prefix(name, "org.mpris"))
+ goto next;
+
+ owner = get_name_owner(conn, name);
+
+ if (owner == NULL)
+ goto next;
+
+ add_player(conn, name, owner);
+
+ g_free(owner);
+next:
+ dbus_message_iter_next(&array);
+ }
+}
+
+static void list_names(DBusConnection *conn)
+{
+ DBusMessage *msg, *reply;
+ DBusMessageIter iter;
+ DBusError err;
+
+ msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS, "ListNames");
+
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+ dbus_message_unref(msg);
+
+ if (!reply) {
+ fprintf(stderr, "Can't find adapter %s\n", adapter);
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ parse_list_names(conn, &iter);
+
+ dbus_message_unref(reply);
+
+ dbus_connection_flush(conn);
+}
+
+static void usage(void)
+{
+ printf("Bluetooth player ver %s\n\n", VERSION);
+
+ printf("Usage:\n"
+ "\tplayer [--adapter adapter id]\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "adapter", 1, 0, 'a' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ char *adapter_id = NULL;
+ char match[128];
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "+a,h", main_options, NULL)) != EOF) {
+ switch(opt) {
+ case '1':
+ adapter_id = optarg;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(1);
+ }
+ }
+
+ sys = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!sys) {
+ fprintf(stderr, "Can't get on system bus");
+ exit(1);
+ }
+
+ adapter = get_adapter(sys, adapter_id);
+ if (!adapter)
+ exit(1);
+
+ if (!dbus_connection_add_filter(sys, system_filter, NULL, NULL)) {
+ fprintf(stderr, "Can't add signal filter");
+ exit(1);
+ }
+
+ snprintf(match, sizeof(match),
+ "interface=%s,member=NameOwnerChanged,arg0=%s",
+ DBUS_INTERFACE_DBUS, "org.bluez");
+
+ dbus_bus_add_match(sys, match, NULL);
+
+ session = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+ if (!session) {
+ fprintf(stderr, "Can't get on session bus");
+ exit(1);
+ }
+
+ if (!dbus_connection_add_filter(session, session_filter, NULL, NULL)) {
+ fprintf(stderr, "Can't add signal filter");
+ exit(1);
+ }
+
+ snprintf(match, sizeof(match),
+ "interface=%s,member=NameOwnerChanged",
+ DBUS_INTERFACE_DBUS);
+
+ dbus_bus_add_match(session, match, NULL);
+
+ snprintf(match, sizeof(match),
+ "interface=%s,member=PropertiesChanged,arg0=%s",
+ DBUS_INTERFACE_PROPERTIES,
+ "org.mpris.MediaPlayer2.Player");
+
+ list_names(session);
+
+ dbus_bus_add_match(session, match, NULL);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ while (!__io_canceled && !__io_terminated) {
+ if (dbus_connection_read_write_dispatch(sys, 500) != TRUE)
+ break;
+ if (dbus_connection_read_write_dispatch(session, 500) != TRUE)
+ break;
+ }
+
+ dbus_connection_unref(sys);
+
+ return 0;
+}
static int linger = 0;
static int timestamp = 0;
static int defer_setup = 0;
+static int priority = -1;
static float tv2fl(struct timeval tv)
{
//goto error;
}
- syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x]",
- conn.hci_handle,
- conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+ if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY, &priority,
+ sizeof(priority)) < 0) {
+ syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ if (getsockopt(sk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x, "
+ "priority %d]", conn.hci_handle, conn.dev_class[2],
+ conn.dev_class[1], conn.dev_class[0], opt);
return sk;
//goto error;
}
+ if (priority > 0 && setsockopt(sk, SOL_SOCKET, SO_PRIORITY,
+ &priority, sizeof(priority)) < 0) {
+ syslog(LOG_ERR, "Can't set socket priority: %s (%d)",
+ strerror(errno), errno);
+ close(nsk);
+ goto error;
+ }
+
+ optlen = sizeof(priority);
+ if (getsockopt(nsk, SOL_SOCKET, SO_PRIORITY, &opt, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get socket priority: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
ba2str(&addr.rc_bdaddr, ba);
- syslog(LOG_INFO, "Connect from %s [handle %d, class 0x%02x%02x%02x]",
- ba, conn.hci_handle,
- conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+ syslog(LOG_INFO, "Connect from %s [handle %d, "
+ "class 0x%02x%02x%02x, priority %d]",
+ ba, conn.hci_handle, conn.dev_class[2],
+ conn.dev_class[1], conn.dev_class[0], opt);
#if 0
/* Enable SO_TIMESTAMP */
struct timeval tv_beg, tv_end, tv_diff;
char ts[30];
long total;
- uint32_t seq;
syslog(LOG_INFO, "Receiving ...");
memset(ts, 0, sizeof(ts));
- seq = 0;
while (1) {
gettimeofday(&tv_beg,NULL);
total = 0;
"\t[-N num] number of frames to send\n"
"\t[-C num] send num frames before delay (default = 1)\n"
"\t[-D milliseconds] delay after sending num frames (default = 0)\n"
+ "\t[-Y priority] socket priority\n"
"\t[-A] request authentication\n"
"\t[-E] request encryption\n"
"\t[-S] secure connection\n"
bacpy(&bdaddr, BDADDR_ANY);
- while ((opt=getopt(argc,argv,"rdscuwmnb:i:P:U:B:N:MAESL:W:C:D:T")) != EOF) {
+ while ((opt=getopt(argc,argv,"rdscuwmnb:i:P:U:B:N:MAESL:W:C:D:Y:T")) != EOF) {
switch (opt) {
case 'r':
mode = RECV;
delay = atoi(optarg) * 1000;
break;
+ case 'Y':
+ priority = atoi(optarg);
+ break;
+
case 'T':
timestamp = 1;
break;
--- /dev/null
+""" Copyright (C) 2010-2011 ST-Ericsson SA """
+
+""" Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson. """
+
+""" 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 """
+
+from array import array
+from bluetooth import *
+import time
+import re
+
+class SAPParam:
+ """ SAP Parameter Class """
+
+ MaxMsgSize = 0x00
+ ConnectionStatus = 0x01
+ ResultCode = 0x02
+ DisconnectionType = 0x03
+ CommandAPDU = 0x04
+ ResponseAPDU = 0x05
+ ATR = 0x06
+ CardReaderStatus = 0x07
+ StatusChange = 0x08
+ TransportProtocol = 0x09
+ CommandAPDU7816 = 0x10
+
+ def __init__(self, name, id, value = None):
+ self.name = name
+ self.id = id
+ self.value = value
+
+ def _padding(self, buf):
+ pad = array('B')
+ while ( (len(buf) + len(pad)) % 4 ) != 0:
+ pad.append(0)
+ return pad
+
+ def _basicCheck(self, buf):
+ if len(buf) < 4 or (len(buf) % 4) != 0 or buf[1] != 0:
+ return (-1, -1)
+ if buf[0] != self.id:
+ return (-1, -1)
+ plen = buf[2] * 256 + buf[3] + 4
+ if plen > len(buf):
+ return (-1, -1)
+ pad = plen
+ while (pad % 4) != 0:
+ if buf[pad] != 0:
+ return (-1, -1)
+ pad+=1
+ return (plen, pad)
+
+ def getID(self):
+ return self.id
+
+ def getValue(self):
+ return self.value
+
+ def getContent(self):
+ return "%s(id=0x%.2X), value=%s \n" % (self.name, self.id, self.value)
+
+ def serialize(self):
+ a = array('B', '\00\00\00\00')
+ a[0] = self.id
+ a[1] = 0 # reserved
+ a[2] = 0 # length
+ a[3] = 1 # length
+ a.append(self.value)
+ a.extend(self._padding(a))
+ return a
+
+ def deserialize(self, buf):
+ p = self._basicCheck(buf)
+ if p[0] == -1:
+ return -1
+ self.id = buf[0]
+ self.value = buf[4]
+ return p[1]
+
+
+class SAPParam_MaxMsgSize(SAPParam):
+ """MaxMsgSize Param """
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"MaxMsgSize", SAPParam.MaxMsgSize, value)
+ self.__validate()
+
+ def __validate(self):
+ if self.value > 0xFFFF:
+ self.value = 0xFFFF
+
+ def serialize(self):
+ a = array('B', '\00\00\00\00')
+ a[0] = self.id
+ a[3] = 2
+ a.append(self.value / 256)
+ a.append(self.value % 256)
+ a.extend(self._padding(a))
+ return a
+
+ def deserialize(self, buf):
+ p = self._basicCheck(buf)
+ if p[0] == -1 :
+ return -1
+ self.value = buf[4] * 256 + buf[5]
+ return p[1]
+
+class SAPParam_CommandAPDU(SAPParam):
+ def __init__(self, value = None):
+ if value is None:
+ SAPParam.__init__(self, "CommandAPDU", SAPParam.CommandAPDU, array('B'))
+ else:
+ SAPParam.__init__(self, "CommandAPDU", SAPParam.CommandAPDU, array('B', value))
+
+ def serialize(self):
+ a = array('B', '\00\00\00\00')
+ a[0] = self.id
+ plen = len(self.value)
+ a[2] = plen / 256
+ a[3] = plen % 256
+ a.extend(self.value)
+ a.extend(self._padding(a))
+ return a
+
+ def deserialize(self, buf):
+ p = self._basicCheck(buf)
+ if p[0] == -1:
+ return -1
+ self.value = buf[4:p[0]]
+ return p[1]
+
+class SAPParam_ResponseAPDU(SAPParam_CommandAPDU):
+ """ResponseAPDU Param """
+
+ def __init__(self, value = None):
+ if value is None:
+ SAPParam.__init__(self, "ResponseAPDU", SAPParam.ResponseAPDU, array('B'))
+ else:
+ SAPParam.__init__(self, "ResponseAPDU", SAPParam.ResponseAPDU, array('B', value))
+
+class SAPParam_ATR(SAPParam_CommandAPDU):
+ """ATR Param """
+
+ def __init__(self, value = None):
+ if value is None:
+ SAPParam.__init__(self, "ATR", SAPParam.ATR, array('B'))
+ else:
+ SAPParam.__init__(self, "ATR", SAPParam.ATR, array('B', value))
+
+class SAPParam_CommandAPDU7816(SAPParam_CommandAPDU):
+ """Command APDU7816 Param."""
+
+ def __init__(self, value = None):
+ if value is None:
+ SAPParam.__init__(self, "CommandAPDU7816", SAPParam.CommandAPDU7816, array('B'))
+ else:
+ SAPParam.__init__(self, "CommandAPDU7816", SAPParam.CommandAPDU7816, array('B', value))
+
+
+class SAPParam_ConnectionStatus(SAPParam):
+ """Connection status Param."""
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"ConnectionStatus", SAPParam.ConnectionStatus, value)
+ self.__validate()
+
+ def __validate(self):
+ if self.value is not None and self.value not in (0x00, 0x01, 0x02, 0x03, 0x04):
+ print "Warning. ConnectionStatus value in reserved range (0x%x)" % self.value
+
+ def deserialize(self, buf):
+ ret = SAPParam.deserialize(self, buf)
+ if ret == -1:
+ return -1
+ self.__validate()
+ return ret
+
+class SAPParam_ResultCode(SAPParam):
+ """ Result Code Param """
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"ResultCode", SAPParam.ResultCode, value)
+ self.__validate()
+
+ def __validate(self):
+ if self.value is not None and self.value not in (0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07):
+ print "Warning. ResultCode value in reserved range (0x%x)" % self.value
+
+ def deserialize(self, buf):
+ ret = SAPParam.deserialize(self, buf)
+ if ret == -1:
+ return -1
+ self.__validate()
+ return ret
+
+class SAPParam_DisconnectionType(SAPParam):
+ """Disconnection Type Param."""
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"DisconnectionType", SAPParam.DisconnectionType, value)
+ self.__validate()
+
+ def __validate(self):
+ if self.value is not None and self.value not in (0x00, 0x01):
+ print "Warning. DisconnectionType value in reserved range (0x%x)" % self.value
+
+ def deserialize(self, buf):
+ ret = SAPParam.deserialize(self, buf)
+ if ret == -1:
+ return -1
+ self.__validate()
+ return ret
+
+class SAPParam_CardReaderStatus(SAPParam_CommandAPDU):
+ """Card reader Status Param."""
+
+ def __init__(self, value = None):
+ if value is None:
+ SAPParam.__init__(self, "CardReaderStatus", SAPParam.CardReaderStatus, array('B'))
+ else:
+ SAPParam.__init__(self, "CardReaderStatus", SAPParam.CardReaderStatus, array('B', value))
+
+class SAPParam_StatusChange(SAPParam):
+ """Status Change Param """
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"StatusChange", SAPParam.StatusChange, value)
+
+ def __validate(self):
+ if self.value is not None and self.value not in (0x00, 0x01, 0x02, 0x03, 0x04, 0x05):
+ print "Warning. StatusChange value in reserved range (0x%x)" % self.value
+
+ def deserialize(self, buf):
+ ret = SAPParam.deserialize(self, buf)
+ if ret == -1:
+ return -1
+ self.__validate()
+ return ret
+
+class SAPParam_TransportProtocol(SAPParam):
+ """Transport Protocol Param """
+
+ def __init__(self, value = None):
+ SAPParam.__init__(self,"TransportProtocol", SAPParam.TransportProtocol, value)
+ self.__validate()
+
+ def __validate(self):
+ if self.value is not None and self.value not in (0x00, 0x01):
+ print "Warning. TransportProtoco value in reserved range (0x%x)" % self.value
+
+ def deserialize(self, buf):
+ ret = SAPParam.deserialize(self, buf)
+ if ret == -1:
+ return -1
+ self.__validate()
+ return ret
+
+class SAPMessage:
+
+ CONNECT_REQ = 0x00
+ CONNECT_RESP = 0x01
+ DISCONNECT_REQ = 0x02
+ DISCONNECT_RESP =0x03
+ DISCONNECT_IND = 0x04
+ TRANSFER_APDU_REQ = 0x05
+ TRANSFER_APDU_RESP = 0x06
+ TRANSFER_ATR_REQ = 0x07
+ TRANSFER_ATR_RESP = 0x08
+ POWER_SIM_OFF_REQ = 0x09
+ POWER_SIM_OFF_RESP = 0x0A
+ POWER_SIM_ON_REQ = 0x0B
+ POWER_SIM_ON_RESP = 0x0C
+ RESET_SIM_REQ = 0x0D
+ RESET_SIM_RESP = 0x0E
+ TRANSFER_CARD_READER_STATUS_REQ = 0x0F
+ TRANSFER_CARD_READER_STATUS_RESP = 0x10
+ STATUS_IND = 0x11
+ ERROR_RESP = 0x12
+ SET_TRANSPORT_PROTOCOL_REQ = 0x13
+ SET_TRANSPORT_PROTOCOL_RESP = 0x14
+
+ def __init__(self, name, id):
+ self.name = name
+ self.id = id
+ self.params = []
+ self.buf = array('B')
+
+ def _basicCheck(self, buf):
+ if len(buf) < 4 or (len(buf) % 4) != 0 :
+ return False
+
+ if buf[0] != self.id:
+ return False
+
+ return True
+
+ def getID(self):
+ return self.id
+
+ def getContent(self):
+ s = "%s(id=0x%.2X) " % (self.name, self.id)
+ if len( self.buf): s = s + "[%s]" % re.sub("(.{2})", "0x\\1 " , self.buf.tostring().encode("hex").upper(), re.DOTALL)
+ s = s + "\n\t"
+ for p in self.params:
+ s = s + "\t" + p.getContent()
+ return s
+
+ def getParams(self):
+ return self.params
+
+ def addParam(self, param):
+ self.params.append(param)
+
+ def serialize(self):
+ ret = array('B', '\00\00\00\00')
+ ret[0] = self.id
+ ret[1] = len(self.params)
+ ret[2] = 0 # reserved
+ ret[3] = 0 # reserved
+ for p in self.params:
+ ret.extend(p.serialize())
+
+ self.buf = ret
+ return ret
+
+ def deserialize(self, buf):
+ self.buf = buf
+ return len(buf) == 4 and buf[1] == 0 and self._basicCheck(buf)
+
+
+class SAPMessage_CONNECT_REQ(SAPMessage):
+ def __init__(self, MaxMsgSize = None):
+ SAPMessage.__init__(self,"CONNECT_REQ", SAPMessage.CONNECT_REQ)
+ if MaxMsgSize is not None:
+ self.addParam(SAPParam_MaxMsgSize(MaxMsgSize))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.MaxMsgSize:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_MaxMsgSize()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+
+ return False
+
+class SAPMessage_CONNECT_RESP(SAPMessage):
+ def __init__(self, ConnectionStatus = None, MaxMsgSize = None):
+ SAPMessage.__init__(self,"CONNECT_RESP", SAPMessage.CONNECT_RESP)
+ if ConnectionStatus is not None:
+ self.addParam(SAPParam_ConnectionStatus(ConnectionStatus))
+ if MaxMsgSize is not None:
+ self.addParam(SAPParam_MaxMsgSize(MaxMsgSize))
+
+ def _validate(self):
+ if len(self.params) > 0:
+ if self.params[0] .getID() == SAPParam.ConnectionStatus:
+ if self.params[0].getValue() == 0x02:
+ if len(self.params) == 2:
+ return True
+ else:
+ if len(self.params) == 1:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_ConnectionStatus()
+ r = p.deserialize(buf[4:])
+ if r != -1:
+ self.addParam(p)
+ if buf[1] == 2:
+ p = SAPParam_MaxMsgSize()
+ r = p.deserialize(buf[4+r:])
+ if r != -1:
+ self.addParam(p)
+
+ return self._validate()
+
+ return False
+
+class SAPMessage_DISCONNECT_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"DISCONNECT_REQ", SAPMessage.DISCONNECT_REQ)
+
+class SAPMessage_DISCONNECT_RESP(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"DISCONNECT_RESP", SAPMessage.DISCONNECT_RESP)
+
+class SAPMessage_DISCONNECT_IND(SAPMessage):
+ def __init__(self, Type = None):
+ SAPMessage.__init__(self,"DISCONNECT_IND", SAPMessage.DISCONNECT_IND)
+ if Type is not None:
+ self.addParam(SAPParam_DisconnectionType(Type))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.DisconnectionType:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_DisconnectionType()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+
+ return False
+
+
+class SAPMessage_TRANSFER_APDU_REQ(SAPMessage):
+ def __init__(self, APDU = None, T = False):
+ SAPMessage.__init__(self,"TRANSFER_APDU_REQ", SAPMessage.TRANSFER_APDU_REQ)
+ if APDU is not None:
+ if T :
+ self.addParam(SAPParam_CommandAPDU(APDU))
+ else:
+ self.addParam(SAPParam_CommandAPDU7816(APDU))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.CommandAPDU or self.params[0].getID() == SAPParam.CommandAPDU7816:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+
+ p = SAPParam_CommandAPDU()
+ p2 = SAPParam_CommandAPDU7816()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+ elif p2.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p2)
+ return self._validate()
+
+ return False
+
+class SAPMessage_TRANSFER_APDU_RESP(SAPMessage):
+ def __init__(self, ResultCode = None, Response = None):
+ SAPMessage.__init__(self,"TRANSFER_APDU_RESP", SAPMessage.TRANSFER_APDU_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+ if Response is not None:
+ self.addParam(SAPParam_ResponseAPDU(Response))
+
+ def _validate(self):
+ if len(self.params) > 0:
+ if self.params[0] .getID() == SAPParam.ResultCode:
+ if self.params[0].getValue() == 0x00:
+ if len(self.params) == 2:
+ return True
+ else:
+ if len(self.params) == 1:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_ResultCode()
+ r = p.deserialize(buf[4:])
+ if r != -1:
+ self.addParam(p)
+ if buf[1] == 2:
+ p = SAPParam_ResponseAPDU()
+ r = p.deserialize(buf[4+r:])
+ if r != -1:
+ self.addParam(p)
+
+ return self._validate()
+
+ return False
+
+class SAPMessage_TRANSFER_ATR_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"TRANSFER_ATR_REQ", SAPMessage.TRANSFER_ATR_REQ)
+
+class SAPMessage_TRANSFER_ATR_RESP(SAPMessage):
+ def __init__(self, ResultCode = None, ATR = None):
+ SAPMessage.__init__(self,"TRANSFER_ATR_RESP", SAPMessage.TRANSFER_ATR_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+ if ATR is not None:
+ self.addParam(SAPParam_ATR(ATR))
+
+ def _validate(self):
+ if len(self.params) > 0:
+ if self.params[0] .getID() == SAPParam.ResultCode:
+ if self.params[0].getValue() == 0x00:
+ if len(self.params) == 2:
+ return True
+ else:
+ if len(self.params) == 1:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+
+ if SAPMessage._basicCheck(self, buf):
+
+ p = SAPParam_ResultCode()
+ r = p.deserialize(buf[4:])
+
+ if r != -1:
+
+ self.addParam(p)
+ if buf[1] == 2:
+
+ p = SAPParam_ATR()
+ r = p.deserialize(buf[4+r:])
+ if r != -1:
+ self.addParam(p)
+
+ return self._validate()
+
+ return False
+
+class SAPMessage_POWER_SIM_OFF_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"POWER_SIM_OFF_REQ", SAPMessage.POWER_SIM_OFF_REQ)
+
+class SAPMessage_POWER_SIM_OFF_RESP(SAPMessage):
+ def __init__(self, ResultCode = None):
+ SAPMessage.__init__(self,"POWER_SIM_OFF_RESP", SAPMessage.POWER_SIM_OFF_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.ResultCode:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_ResultCode()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+
+ return False
+
+class SAPMessage_POWER_SIM_ON_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"POWER_SIM_ON_REQ", SAPMessage.POWER_SIM_ON_REQ)
+
+class SAPMessage_POWER_SIM_ON_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+ def __init__(self, ResultCode = None):
+ SAPMessage.__init__(self,"POWER_SIM_ON_RESP", SAPMessage.POWER_SIM_ON_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+
+class SAPMessage_RESET_SIM_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"RESET_SIM_REQ", SAPMessage.RESET_SIM_REQ)
+
+class SAPMessage_RESET_SIM_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+ def __init__(self, ResultCode = None):
+ SAPMessage.__init__(self,"RESET_SIM_RESP", SAPMessage.RESET_SIM_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+
+class SAPMessage_STATUS_IND(SAPMessage):
+ def __init__(self, StatusChange = None):
+ SAPMessage.__init__(self,"STATUS_IND", SAPMessage.STATUS_IND)
+ if StatusChange is not None:
+ self.addParam(SAPParam_StatusChange(StatusChange))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.StatusChange:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_StatusChange()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+
+ return False
+
+class SAPMessage_TRANSFER_CARD_READER_STATUS_REQ(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"TRANSFER_CARD_READER_STATUS_REQ", SAPMessage.TRANSFER_CARD_READER_STATUS_REQ)
+
+class SAPMessage_TRANSFER_CARD_READER_STATUS_RESP(SAPMessage):
+ def __init__(self, ResultCode = None, Status = None):
+ SAPMessage.__init__(self,"TRANSFER_CARD_READER_STATUS_RESP", SAPMessage.TRANSFER_CARD_READER_STATUS_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+ if Status is not None:
+ self.addParam(SAPParam_CardReaderStatus(Status))
+
+ def _validate(self):
+ if len(self.params) > 0:
+ if self.params[0] .getID() == SAPParam.ResultCode:
+ if self.params[0].getValue() == 0x00:
+ if len(self.params) == 2:
+ return True
+ else:
+ if len(self.params) == 1:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_ResultCode()
+ r = p.deserialize(buf[4:])
+ if r != -1:
+ self.addParam(p)
+ if buf[1] == 2:
+ p = SAPParam_CardReaderStatus()
+ r = p.deserialize(buf[4+r:])
+ if r != -1:
+ self.addParam(p)
+
+ return self._validate()
+
+ return False
+
+class SAPMessage_ERROR_RESP(SAPMessage):
+ def __init__(self):
+ SAPMessage.__init__(self,"ERROR_RESP", SAPMessage.ERROR_RESP)
+
+
+class SAPMessage_SET_TRANSPORT_PROTOCOL_REQ(SAPMessage):
+ def __init__(self, protocol = None):
+ SAPMessage.__init__(self,"SET_TRANSPORT_PROTOCOL_REQ", SAPMessage.SET_TRANSPORT_PROTOCOL_REQ)
+ if protocol is not None:
+ self.addParam(SAPParam_TransportProtocol(protocol))
+
+ def _validate(self):
+ if len(self.params) == 1:
+ if self.params[0].getID() == SAPParam.TransportProtocol:
+ return True
+ return False
+
+ def deserialize(self, buf):
+ self.buf = buf
+ self.params[:] = []
+ if SAPMessage._basicCheck(self, buf):
+ p = SAPParam_TransportProtocol()
+ if p.deserialize(buf[4:]) == len(buf[4:]):
+ self.addParam(p)
+ return self._validate()
+
+ return False
+
+class SAPMessage_SET_TRANSPORT_PROTOCOL_RESP(SAPMessage_POWER_SIM_OFF_RESP):
+ def __init__(self, ResultCode = None):
+ SAPMessage.__init__(self,"SET_TRANSPORT_PROTOCOL_RESP", SAPMessage.SET_TRANSPORT_PROTOCOL_RESP)
+ if ResultCode is not None:
+ self.addParam(SAPParam_ResultCode(ResultCode))
+
+
+class SAPClient:
+
+ CONNECTED = 1
+ DISCONNECTED = 0
+
+ uuid = "0000112D-0000-1000-8000-00805F9B34FB"
+ bufsize = 1024
+ timeout = 20
+ state = DISCONNECTED
+
+ def __init__(self, host = None, port = None):
+ self.sock = None
+
+ if host is None or is_valid_address(host):
+ self.host = host
+ else:
+ raise BluetoothError ("%s is not a valid BT address." % host)
+ self.host = None
+ return
+
+ if port is None:
+ self.__discover()
+ else:
+ self.port = port
+
+ self.__connectRFCOMM()
+
+ def __del__(self):
+ self.__disconnectRFCOMM()
+
+ def __disconnectRFCOMM(self):
+ if self.sock is not None:
+ self.sock.close()
+ self.state = self.DISCONNECTED
+
+ def __discover(self):
+ service_matches = find_service(self.uuid, self.host)
+
+ if len(service_matches) == 0:
+ raise BluetoothError ("No SAP service found")
+ return
+
+ first_match = service_matches[0]
+ self.port = first_match["port"]
+ self.host = first_match["host"]
+
+ print "SAP Service found on %s(%s)" % first_match["name"] % self.host
+
+ def __connectRFCOMM(self):
+ self.sock=BluetoothSocket( RFCOMM )
+ self.sock.connect((self.host, self.port))
+ self.sock.settimeout(self.timeout)
+ self.state = self.CONNECTED
+
+ def __sendMsg(self, msg):
+ if isinstance(msg, SAPMessage):
+ s = msg.serialize()
+ print "\tTX: " + msg.getContent()
+ return self.sock.send(s.tostring())
+
+ def __rcvMsg(self, msg):
+ if isinstance(msg, SAPMessage):
+ print "\tRX Wait: %s(id = 0x%.2x)" % (msg.name, msg.id)
+ data = self.sock.recv(self.bufsize)
+ if data:
+ if msg.deserialize(array('B',data)):
+ print "\tRX: len(%d) %s" % (len(data), msg.getContent())
+ return msg
+ else:
+ print "msg: %s" % array('B',data)
+ raise BluetoothError ("Message deserialization failed.")
+ else:
+ raise BluetoothError ("Timeout. No data received.")
+
+ def connect(self):
+ self.__connectRFCOMM()
+
+ def disconnect(self):
+ self.__disconnectRFCOMM()
+
+ def isConnected(self):
+ return self.state
+
+ def proc_connect(self):
+ try:
+ self.__sendMsg(SAPMessage_CONNECT_REQ(self.bufsize))
+ params = self.__rcvMsg(SAPMessage_CONNECT_RESP()).getParams()
+
+ if params[0].getValue() in (0x00, 0x04):
+ pass
+ elif params[0].getValue() == 0x02:
+ self.bufsize = params[1].getValue()
+
+ self.__sendMsg(SAPMessage_CONNECT_REQ(self.bufsize))
+ params = self.__rcvMsg(SAPMessage_CONNECT_RESP()).getParams()
+
+ if params[0].getValue() not in (0x00, 0x04):
+ return False
+ else:
+ return False
+
+ params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+ if params[0].getValue() == 0x00:
+ return False
+ elif params[0].getValue() == 0x01:
+ """OK, Card reset"""
+ return self.proc_transferATR()
+ elif params[0].getValue() == 0x02:
+ """T0 not supported"""
+ if self.proc_transferATR():
+ return self.proc_setTransportProtocol(1)
+ else:
+ return False
+ else:
+ return False
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_disconnectByClient(self, timeout=0):
+ try:
+ self.__sendMsg(SAPMessage_DISCONNECT_REQ())
+ self.__rcvMsg(SAPMessage_DISCONNECT_RESP())
+ time.sleep(timeout) # let srv to close rfcomm
+ self.__disconnectRFCOMM()
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_disconnectByServer(self, timeout=0):
+ try:
+ params = self.__rcvMsg(SAPMessage_DISCONNECT_IND()).getParams()
+
+ """graceful"""
+ if params[0].getValue() == 0x00:
+ if not self.proc_transferAPDU():
+ return False
+
+ return self.proc_disconnectByClient(timeout)
+
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_transferAPDU(self, apdu = "Sample APDU command"):
+ try:
+ self.__sendMsg(SAPMessage_TRANSFER_APDU_REQ(apdu))
+ params = self.__rcvMsg(SAPMessage_TRANSFER_APDU_RESP()).getParams()
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_transferATR(self):
+ try:
+ self.__sendMsg(SAPMessage_TRANSFER_ATR_REQ())
+ params = self.__rcvMsg(SAPMessage_TRANSFER_ATR_RESP()).getParams()
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_powerSimOff(self):
+ try:
+ self.__sendMsg(SAPMessage_POWER_SIM_OFF_REQ())
+ params = self.__rcvMsg(SAPMessage_POWER_SIM_OFF_RESP()).getParams()
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_powerSimOn(self):
+ try:
+ self.__sendMsg(SAPMessage_POWER_SIM_ON_REQ())
+ params = self.__rcvMsg(SAPMessage_POWER_SIM_ON_RESP()).getParams()
+ if params[0].getValue() == 0x00:
+ return self.proc_transferATR()
+
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_resetSim(self):
+ try:
+ self.__sendMsg(SAPMessage_RESET_SIM_REQ())
+ params = self.__rcvMsg(SAPMessage_RESET_SIM_RESP()).getParams()
+ if params[0].getValue() == 0x00:
+ return self.proc_transferATR()
+
+ return True
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_reportStatus(self):
+ try:
+ params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_transferCardReaderStatus(self):
+ try:
+ self.__sendMsg(SAPMessage_TRANSFER_CARD_READER_STATUS_REQ())
+ params = self.__rcvMsg(SAPMessage_TRANSFER_CARD_READER_STATUS_RESP()).getParams()
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_errorResponse(self):
+ try:
+ """ send malformed message, no mandatory maxmsgsize parameter"""
+ self.__sendMsg(SAPMessage_CONNECT_REQ())
+
+ params = self.__rcvMsg(SAPMessage_ERROR_RESP()).getParams()
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+ def proc_setTransportProtocol(self, protocol = 0):
+ try:
+ self.__sendMsg(SAPMessage_SET_TRANSPORT_PROTOCOL_REQ(protocol))
+ params = self.__rcvMsg(SAPMessage_SET_TRANSPORT_PROTOCOL_RESP()).getParams()
+
+ if params[0].getValue() == 0x00:
+ params = self.__rcvMsg(SAPMessage_STATUS_IND()).getParams()
+ if params[0].getValue() in (0x01, 0x02):
+ return self.proc_transferATR()
+ else:
+ return True
+ """return False ???"""
+ elif params[0].getValue == 0x07:
+ """not supported"""
+ return True
+ """return False ???"""
+ else:
+ return False
+
+ except BluetoothError , e:
+ print "Error. " +str(e)
+ return False
+
+if __name__ == "__main__":
+ pass
{
struct timeval tv_beg,tv_end,tv_diff;
long total;
- uint32_t seq;
syslog(LOG_INFO, "Receiving ...");
- seq = 0;
while (1) {
gettimeofday(&tv_beg, NULL);
total = 0;
import dbus
import dbus.service
import dbus.mainloop.glib
+from optparse import OptionParser
class Rejected(dbus.DBusException):
_dbus_error_name = "org.bluez.Error.Rejected"
manager = dbus.Interface(bus.get_object("org.bluez", "/"),
"org.bluez.Manager")
- if len(sys.argv) > 1:
- path = manager.FindAdapter(sys.argv[1])
+ capability = "DisplayYesNo"
+
+ parser = OptionParser()
+ parser.add_option("-c", "--capability", action="store",
+ type="string", dest="capability")
+ (options, args) = parser.parse_args()
+ if options.capability:
+ capability = options.capability
+
+ if len(args) > 0:
+ path = manager.FindAdapter(args[0])
else:
path = manager.DefaultAdapter()
mainloop = gobject.MainLoop()
- if len(sys.argv) > 2:
- if len(sys.argv) > 3:
- device = adapter.FindDevice(sys.argv[2])
+ if len(args) > 1:
+ if len(args) > 2:
+ device = adapter.FindDevice(args[1])
adapter.RemoveDevice(device)
agent.set_exit_on_release(False)
- adapter.CreatePairedDevice(sys.argv[2], path, "DisplayYesNo",
+ adapter.CreatePairedDevice(args[1], path, capability,
reply_handler=create_device_reply,
error_handler=create_device_error)
else:
- adapter.RegisterAgent(path, "DisplayYesNo")
+ adapter.RegisterAgent(path, capability)
print "Agent registered"
mainloop.run()
--- /dev/null
+#!/usr/bin/python
+
+from __future__ import print_function
+import os
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import gobject
+
+class Player(dbus.service.Object):
+ @dbus.service.method("org.bluez.MediaPlayer",
+ in_signature="", out_signature="")
+ def Release(self):
+ print("Release")
+ mainloop.quit()
+
+ @dbus.service.method("org.bluez.MediaPlayer",
+ in_signature="sv", out_signature="")
+ def SetProperty(self, key, value):
+ print("SetProperty (%s, %s)" % (key, value), file=sys.stderr)
+ return
+
+ @dbus.service.signal("org.bluez.MediaPlayer", signature="sv")
+ def PropertyChanged(self, setting, value):
+ """PropertyChanged(setting, value)
+
+ Send a PropertyChanged signal. 'setting' and 'value' are
+ string parameters as specified in doc/media-api.txt.
+ """
+ pass
+
+ @dbus.service.signal("org.bluez.MediaPlayer", signature="a{sv}")
+ def TrackChanged(self, metadata):
+ """TrackChanged(metadata)
+
+ Send a TrackChanged signal. 'metadata' parameter is a dictionary,
+ with values as defined in doc/media-api.txt.
+ """
+ pass
+
+ def help(self, func):
+ help(self.__class__.__dict__[func])
+
+class InputHandler:
+ commands = { 'TrackChanged': '(metadata)',
+ 'PropertyChanged': '(key, value)',
+ 'help': '(cmd)' }
+ def __init__(self, player):
+ self.player = player
+ print('\n\nAvailable commands:')
+ for cmd in self.commands:
+ print('\t', cmd, self.commands[cmd], sep='')
+
+ print("\nUse python syntax to pass arguments to available methods.\n" \
+ "E.g.: TrackChanged({'Title': 'My title', 'Album': 'my album' })")
+ self.prompt()
+
+ def prompt(self):
+ print('\n>>> ', end='')
+ sys.stdout.flush()
+
+ def handle(self, fd, condition):
+ s = os.read(fd.fileno(), 1024).strip()
+ try:
+ cmd = s[:s.find('(')]
+ if not cmd in self.commands:
+ print("Unknown command ", cmd)
+ except ValueError:
+ print("Malformed command")
+ return True
+
+ try:
+ exec "self.player.%s" % s
+ except Exception as e:
+ print(e)
+ pass
+ self.prompt()
+ return True
+
+
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ if len(sys.argv) > 1:
+ path = manager.FindAdapter(sys.argv[1])
+ else:
+ path = manager.DefaultAdapter()
+
+ media = dbus.Interface(bus.get_object("org.bluez", path),
+ "org.bluez.Media")
+
+ path = "/test/player"
+ player = Player(bus, path)
+ mainloop = gobject.MainLoop()
+
+ properties = dbus.Dictionary({ "Equalizer" : "off",
+ "Repeat" : "off",
+ "Shuffle" : "off",
+ "Scan" : "off",
+ "Status" : "playing",
+ "Position" : dbus.UInt32(0) }, signature="sv")
+
+ metadata = dbus.Dictionary({ "Title" : "Title",
+ "Artist" : "Artist",
+ "Album" : "Album",
+ "Genre" : "Genre",
+ "NumberOfTracks" : dbus.UInt32(10),
+ "Number" : dbus.UInt32(1),
+ "Duration" : dbus.UInt32(10000) }, signature="sv")
+
+ print('Register media player with:\n\tProperties: %s\n\tMetadata: %s' \
+ % (properties, metadata))
+
+ handler = InputHandler(player)
+ gobject.io_add_watch(sys.stdin, gobject.IO_IN, handler.handle)
+
+ media.RegisterPlayer(dbus.ObjectPath(path), properties, metadata)
+
+ mainloop.run()
print "Usage: %s <command>" % (sys.argv[0])
print ""
print " address"
+ print " list"
print " name [name]"
print " powered [on/off]"
print " pairable [on/off]"
adapter.SetProperty("Name", args[1])
sys.exit(0)
+if (args[0] == "list"):
+ if (len(args) < 2):
+ properties = manager.GetProperties()
+ for adapter_path in properties["Adapters"]:
+ adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+ prop = adapter.GetProperties()
+ print " [ %s ]" % (adapter_path)
+ for (key, value) in prop.iteritems():
+ if (key == "Class"):
+ print " %s = 0x%06x" % (key, value)
+ else:
+ print " %s = %s" % (key, value)
+ print
+ sys.exit(0)
+
if (args[0] == "powered"):
if (len(args) < 2):
properties = adapter.GetProperties()
--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import dbus
+import dbus.service
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+import sys
+
+DBusGMainLoop(set_as_default=True)
+loop = gobject.MainLoop()
+
+bus = dbus.SystemBus()
+
+def sig_received(*args, **kwargs):
+ if "member" not in kwargs:
+ return
+ if "path" not in kwargs:
+ return;
+ sig_name = kwargs["member"]
+ path = kwargs["path"]
+ print sig_name
+ print path
+ if sig_name == "PropertyChanged":
+ k, v = args
+ print k
+ print v
+ else:
+ ob = args[0]
+ print ob
+
+
+def enter_mainloop():
+ bus.add_signal_receiver(sig_received, bus_name="org.bluez",
+ dbus_interface = "org.bluez.HealthDevice",
+ path_keyword="path",
+ member_keyword="member",
+ interface_keyword="interface")
+
+ try:
+ print "Entering main lopp, push Ctrl+C for finish"
+
+ mainloop = gobject.MainLoop()
+ mainloop.run()
+ except KeyboardInterrupt:
+ pass
+ finally:
+ print "Exiting, bye"
+
+hdp_manager = dbus.Interface(bus.get_object("org.bluez", "/org/bluez"),
+ "org.bluez.HealthManager")
+
+role = None
+while role == None:
+ print "Select 1. source or 2. sink: ",
+ try:
+ sel = int(sys.stdin.readline())
+ if sel == 1:
+ role = "Source"
+ elif sel == 2:
+ role = "Sink"
+ else:
+ raise ValueError
+ except (TypeError, ValueError):
+ print "Wrong selection, try again: ",
+ except KeyboardInterrupt:
+ sys.exit()
+
+dtype = None
+while dtype == None:
+ print "Select a data type: ",
+ try:
+ sel = int(sys.stdin.readline())
+ if (sel < 0) or (sel > 65535):
+ raise ValueError
+ dtype = sel;
+ except (TypeError, ValueError):
+ print "Wrong selection, try again: ",
+ except KeyboardInterrupt:
+ sys.exit()
+
+pref = None
+if role == "Source":
+ while pref == None:
+ try:
+ print "Select a preferred data channel type 1.",
+ print "reliable 2. streaming: ",
+ sel = int(sys.stdin.readline())
+ if sel == 1:
+ pref = "Reliable"
+ elif sel == 2:
+ pref = "Streaming"
+ else:
+ raise ValueError
+
+ except (TypeError, ValueError):
+ print "Wrong selection, try again"
+ except KeyboardInterrupt:
+ sys.exit()
+
+ app_path = hdp_manager.CreateApplication({
+ "DataType": dbus.types.UInt16(dtype),
+ "Role": role,
+ "Description": "Test Source",
+ "ChannelType": pref})
+else:
+ app_path = hdp_manager.CreateApplication({
+ "DataType": dbus.types.UInt16(dtype),
+ "Description": "Test sink",
+ "Role": role})
+
+print "New application created:", app_path
+
+con = None
+while con == None:
+ try:
+ print "Connect to a remote device (y/n)? ",
+ sel = sys.stdin.readline()
+ if sel in ("y\n", "yes\n", "Y\n", "YES\n"):
+ con = True
+ elif sel in ("n\n", "no\n", "N\n", "NO\n"):
+ con = False
+ else:
+ print "Wrong selection, try again."
+ except KeyboardInterrupt:
+ sys.exit()
+
+if not con:
+ enter_mainloop()
+ sys.exit()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+adapters = manager.ListAdapters()
+
+i = 1
+for ad in adapters:
+ print "%d. %s" % (i, ad)
+ i = i + 1
+
+print "Select an adapter: ",
+select = None
+while select == None:
+ try:
+ pos = int(sys.stdin.readline()) - 1
+ if pos < 0:
+ raise TypeError
+ select = adapters[pos]
+ except (TypeError, IndexError, ValueError):
+ print "Wrong selection, try again: ",
+ except KeyboardInterrupt:
+ sys.exit()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", select),
+ "org.bluez.Adapter")
+
+devices = adapter.ListDevices()
+
+if len(devices) == 0:
+ print "No devices available"
+ sys.exit()
+
+i = 1
+for dev in devices:
+ print "%d. %s" % (i, dev)
+ i = i + 1
+
+print "Select a device: ",
+select = None
+while select == None:
+ try:
+ pos = int(sys.stdin.readline()) - 1
+ if pos < 0:
+ raise TypeError
+ select = devices[pos]
+ except (TypeError, IndexError, ValueError):
+ print "Wrong selection, try again: ",
+ except KeyboardInterrupt:
+ sys.exit()
+
+device = dbus.Interface(bus.get_object("org.bluez", select),
+ "org.bluez.HealthDevice")
+
+echo = None
+while echo == None:
+ try:
+ print "Perform an echo (y/n)? ",
+ sel = sys.stdin.readline()
+ if sel in ("y\n", "yes\n", "Y\n", "YES\n"):
+ echo = True
+ elif sel in ("n\n", "no\n", "N\n", "NO\n"):
+ echo = False
+ else:
+ print "Wrong selection, try again."
+ except KeyboardInterrupt:
+ sys.exit()
+
+if echo:
+ if device.Echo():
+ print "Echo was ok"
+ else:
+ print "Echo war wrong, exiting"
+ sys.exit()
+
+print "Connecting to device %s" % (select)
+
+if role == "Source":
+ chan = device.CreateChannel(app_path, "Reliable")
+else:
+ chan = device.CreateChannel(app_path, "Any")
+
+print chan
+
+enter_mainloop()
+
+hdp_manager.DestroyApplication(app_path)
--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import dbus
+import dbus.service
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+import sys
+
+DBusGMainLoop(set_as_default=True)
+loop = gobject.MainLoop()
+
+bus = dbus.SystemBus()
+
+hdp_manager = dbus.Interface(bus.get_object("org.bluez", "/org/bluez"),
+ "org.bluez.HealthManager")
+app_path = hdp_manager.CreateApplication({"DataType": dbus.types.UInt16(4103),
+ "Role": "sink"})
+
+print app_path
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+adapters = manager.ListAdapters()
+
+i = 1
+for ad in adapters:
+ print "%d. %s" % (i, ad)
+ i = i + 1
+
+print "Select an adapter: ",
+select = None
+while select == None:
+ try:
+ pos = int(sys.stdin.readline()) - 1
+ if pos < 0:
+ raise TypeError
+ select = adapters[pos]
+ except (TypeError, IndexError, ValueError):
+ print "Wrong selection, try again: ",
+ except KeyboardInterrupt:
+ sys.exit()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", select),
+ "org.bluez.Adapter")
+
+devices = adapter.ListDevices()
+
+if len(devices) == 0:
+ print "No devices available"
+ sys.exit()
+
+i = 1
+for dev in devices:
+ print "%d. %s" % (i, dev)
+ i = i + 1
+
+print "Select a device: ",
+select = None
+while select == None:
+ try:
+ pos = int(sys.stdin.readline()) - 1
+ if pos < 0:
+ raise TypeError
+ select = devices[pos]
+ except (TypeError, IndexError, ValueError):
+ print "Wrong selection, try again: ",
+ except KeyboardInterrupt:
+ sys.exit()
+
+print "Connecting to %s" % (select)
+device = dbus.Interface(bus.get_object("org.bluez", select),
+ "org.bluez.HealthDevice")
+
+chan = device.CreateChannel(app_path, "Any")
+
+print chan
+
+print "Push Enter for finishing"
+sys.stdin.readline()
+
+hdp_manager.DestroyApplication(app_path)
--- /dev/null
+#!/usr/bin/python
+
+import sys
+import time
+import dbus
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+server = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.NetworkServer")
+
+service = "nap"
+
+if (len(args) < 1):
+ bridge = "tether"
+else:
+ bridge = args[0]
+
+server.Register(service, bridge)
+
+print "Server for %s registered for %s" % (service, bridge)
+
+print "Press CTRL-C to disconnect"
+
+try:
+ time.sleep(1000)
+ print "Terminating connection"
+except:
+ pass
+
+server.Unregister(service)
--- /dev/null
+#!/usr/bin/python
+
+import gobject
+
+import dbus.mainloop.glib
+
+def create_device_reply(device):
+ print "Pairing succeed!"
+ mainloop.quit()
+
+def create_device_error(error):
+ print "Pairing failed."
+ mainloop.quit()
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ mainloop = gobject.MainLoop()
+
+ bus = dbus.SystemBus()
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ adapter0_path = manager.FindAdapter("hci0")
+ adapter1_path = manager.FindAdapter("hci1")
+
+ adapter0 = dbus.Interface(bus.get_object("org.bluez", adapter0_path),
+ "org.bluez.Adapter")
+ adapter1 = dbus.Interface(bus.get_object("org.bluez", adapter1_path),
+ "org.bluez.Adapter")
+
+ adapter0_address = adapter0.GetProperties()["Address"]
+ adapter1_address = adapter1.GetProperties()["Address"]
+ print "Adapters:"
+ print " hci0: " + adapter0_address
+ print " hci1: " + adapter1_address
+ print
+
+ print "Removing any existing bond..."
+
+ try:
+ device = adapter0.FindDevice(adapter1_address)
+ adapter0.RemoveDevice(device)
+ except:
+ pass
+
+ try:
+ device = adapter1.FindDevice(adapter0_address)
+ adapter1.RemoveDevice(device)
+ except:
+ pass
+
+ print "Done."
+ print
+ print "Reading local Out of Band data..."
+
+ oob_adapter0 = dbus.Interface(bus.get_object("org.bluez",
+ adapter0_path), "org.bluez.OutOfBand")
+ oob_adapter1 = dbus.Interface(bus.get_object("org.bluez",
+ adapter1_path), "org.bluez.OutOfBand")
+
+ oob0 = oob_adapter0.ReadLocalData()
+ oob1 = oob_adapter1.ReadLocalData()
+
+ print "Done."
+ print
+ print "Exchanging Out of Band data..."
+
+ oob_adapter0.AddRemoteData(adapter1_address, oob1[0], oob1[1])
+ oob_adapter1.AddRemoteData(adapter0_address, oob0[0], oob0[1])
+
+ print "Done."
+ print
+ print "Starting to pair."
+ adapter1.CreatePairedDevice(adapter0_address, "/test/agent_oob",
+ "DisplayYesNo",
+ reply_handler=create_device_reply,
+ error_handler=create_device_error)
+
+ mainloop.run()
--- /dev/null
+#!/usr/bin/python
+
+'''
+Proximity Monitor test script
+'''
+
+import gobject
+
+import sys
+import dbus
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+def property_changed(name, value):
+
+ print "PropertyChanged('%s', '%s')" % (name, value)
+ mainloop.quit()
+
+if __name__ == "__main__":
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ option_list = [
+ make_option("-i", "--adapter", action="store",
+ type="string", dest="dev_id"),
+ make_option("-b", "--device", action="store",
+ type="string", dest="address"),
+
+ ]
+ parser = OptionParser(option_list=option_list)
+
+ (options, args) = parser.parse_args()
+
+ if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+ else:
+ adapter_path = manager.DefaultAdapter()
+
+ adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+ if (len(args) < 1):
+ print "Usage: %s <command>" % (sys.argv[0])
+ print ""
+ print " -b MAC LinkLossAlertLevel <none|mild|high>"
+ print " -b MAC ImmediateAlertLevel <none|mild|high>"
+ sys.exit(1)
+
+ device_path = adapter.FindDevice(options.address)
+
+ bus.add_signal_receiver(property_changed, bus_name="org.bluez",
+ dbus_interface="org.bluez.Proximity",
+ signal_name="PropertyChanged")
+
+ proximity = dbus.Interface(bus.get_object("org.bluez",
+ device_path), "org.bluez.Proximity")
+
+ print "Proximity SetProperty('%s', '%s')" % (args[0], args[1])
+ proximity.SetProperty(args[0], args[1])
+
+ mainloop = gobject.MainLoop()
+ mainloop.run()
--- /dev/null
+#!/usr/bin/python
+
+from sap import *
+import time
+
+def connect_disconnect_by_client(sap):
+
+ print "[Test] Connect - Disconnect by client \n"
+
+ try:
+ if not sap.isConnected():
+ sap.connect()
+
+ if sap.proc_connect():
+ if sap.proc_disconnectByClient():
+ print "OK"
+ return 0
+
+ print "NOT OK"
+ return 1
+
+ except BluetoothError , e:
+ print "Error " + str(e)
+
+
+def connect_disconnect_by_server_gracefully(sap, timeout=0):
+
+ print "[Test] Connect - Disconnect by server with timer \n"
+
+ try:
+ if not sap.isConnected():
+ sap.connect()
+
+ if sap.proc_connect():
+ if sap.proc_disconnectByServer(timeout):
+ print "OK"
+ return 0
+
+ print "NOT OK"
+ return 1
+
+ except BluetoothError , e:
+ print "Error " + str(e)
+
+
+def connect_txAPDU_disconnect_by_client(sap):
+
+ print "[Test] Connect - TX APDU - Disconnect by client \n"
+
+ try:
+ if not sap.isConnected():
+ sap.connect()
+
+ if sap.proc_connect():
+ if not sap.proc_transferAPDU():
+ print "NOT OK 1"
+ return 1
+
+ if not sap.proc_transferAPDU():
+ print "NOT OK 2"
+ return 1
+
+ if not sap.proc_transferAPDU():
+ print "NOT OK 3"
+ return 1
+
+ if not sap.proc_transferAPDU():
+ print "NOT OK 4"
+ return 1
+
+ if sap.proc_disconnectByClient():
+ print "OK"
+ return 0
+
+ print "NOT OK"
+ return 1
+
+ except BluetoothError , e:
+ print "Error " + str(e)
+
+def connect_rfcomm_only_and_wait_for_close_by_server(sap):
+
+ print "[Test] Connect rfcomm only - Disconnect by server timeout \n"
+
+ if not sap.isConnected():
+ sap.connect()
+
+ time.sleep(40)
+ print "OK"
+
+def power_sim_off_on(sap):
+
+ print "[Test] Powe sim off \n"
+
+ try:
+ if not sap.isConnected():
+ sap.connect()
+
+ if sap.proc_connect():
+ if not sap.proc_resetSim():
+ print "NOT OK"
+ return 1
+
+ if not sap.proc_powerSimOff():
+ print "NOT OK"
+ return 1
+
+ if not sap.proc_powerSimOn():
+ print "NOT OK"
+ return 1
+
+ if sap.proc_disconnectByClient():
+ print "OK"
+ return 0
+
+ print "NOT OK"
+ return 1
+
+ except BluetoothError , e:
+ print "Error " + str(e)
+
+
+if __name__ == "__main__":
+
+ host = "00:00:00:00:00:0" # server bd_addr
+ port = 8 # sap server port
+
+ try:
+ s = SAPClient(host, port)
+ except BluetoothError , e:
+ print "Error " + str(e)
+
+ connect_disconnect_by_client(s)
+ connect_disconnect_by_server_gracefully(s)
+ connect_disconnect_by_server_gracefully(s, 40) # wait 40 sec for srv to close rfcomm sock
+ connect_rfcomm_only_and_wait_for_close_by_server(s)
+ connect_txAPDU_disconnect_by_client(s)
+ power_sim_off_on(s)
--- /dev/null
+#!/usr/bin/python
+
+import sys
+import time
+import dbus
+import socket
+from optparse import OptionParser, make_option
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+option_list = [
+ make_option("-i", "--device", action="store",
+ type="string", dest="dev_id"),
+ ]
+parser = OptionParser(option_list=option_list)
+
+(options, args) = parser.parse_args()
+
+if options.dev_id:
+ adapter_path = manager.FindAdapter(options.dev_id)
+else:
+ adapter_path = manager.DefaultAdapter()
+
+adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+if (len(args) < 1):
+ print "Usage: %s <socket_name> [service]" % (sys.argv[0])
+ sys.exit(1)
+
+socket_name = args[0]
+
+if (len(args) < 2):
+ service = "spp"
+else:
+ service = args[1]
+
+sk = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+sk.bind(socket_name)
+sk.listen(1)
+
+proxy_manager = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.SerialProxyManager")
+proxy_path = proxy_manager.CreateProxy(service, socket_name)
+
+proxy = dbus.Interface(bus.get_object("org.bluez", proxy_path),
+ "org.bluez.SerialProxy")
+proxy.Enable()
+
+conn, addr = sk.accept()
+
+print "Waiting for message"
+
+while 1:
+ data = conn.recv(1024)
+ if data:
+ print(data)
+ break
+
+proxy.Disable()
+proxy_manager.RemoveProxy(proxy_path)
+conn.close()
char filename[] = "/tmp/textfile";
char key[18], value[512], *str;
unsigned int i, j, size, max = 10;
- int fd, err;
+ int fd;
size = getpagesize();
printf("System uses a page size of %d bytes\n\n", size);
fd = creat(filename, 0644);
- err = ftruncate(fd, 0);
+ if (ftruncate(fd, 0) < 0)
+ return -errno;
memset(value, 0, sizeof(value));
- for (i = 0; i < (size / sizeof(value)); i++)
- err = write(fd, value, sizeof(value));
+ for (i = 0; i < (size / sizeof(value)); i++) {
+ if (write(fd, value, sizeof(value)) < 0)
+ return -errno;
+ }
close(fd);
sprintf(key, "11:11:11:11:11:11");
str = textfile_get(filename, key);
- err = truncate(filename, 0);
-
+ if (truncate(filename, 0) < 0)
+ return -errno;
sprintf(key, "00:00:00:00:00:00");
if (textfile_del(filename, key) < 0)
--- /dev/null
+#!/usr/bin/python
+
+'''
+Thermometer test script
+'''
+
+import gobject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+class Watcher(dbus.service.Object):
+ @dbus.service.method("org.bluez.ThermometerWatcher",
+ in_signature="a{sv}", out_signature="")
+ def MeasurementReceived(self, measure):
+ print measure["Measurement"], " measurement received"
+ print "Exponent: ", measure["Exponent"]
+ print "Mantissa: ", measure["Mantissa"]
+ print "Unit: ", measure["Unit"]
+
+ if measure.has_key("Time"):
+ print "Time: ", measure["Time"]
+
+ print "Type: ", measure["Type"]
+
+def property_changed(name, value):
+
+ print "PropertyChanged('%s', '%s')" % (name, value)
+
+if __name__ == "__main__":
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ option_list = [
+ make_option("-i", "--adapter", action="store",
+ type="string", dest="adapter"),
+ make_option("-b", "--device", action="store",
+ type="string", dest="address"),
+ ]
+
+ parser = OptionParser(option_list=option_list)
+
+ (options, args) = parser.parse_args()
+
+ if not options.address:
+ print "Usage: %s [-i <adapter>] -b <bdaddr> [command]" % (sys.argv[0])
+ print "Possible commands:"
+ print "\tEnableIntermediateMeasurement"
+ sys.exit(1)
+
+ if options.adapter:
+ adapter_path = manager.FindAdapter(options.adapter)
+ else:
+ adapter_path = manager.DefaultAdapter()
+
+ adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+ device_path = adapter.FindDevice(options.address)
+
+ bus.add_signal_receiver(property_changed, bus_name="org.bluez",
+ dbus_interface="org.bluez.Thermometer",
+ signal_name="PropertyChanged")
+
+ thermometer = dbus.Interface(bus.get_object("org.bluez",
+ device_path), "org.bluez.Thermometer")
+
+ path = "/test/watcher"
+ watcher = Watcher(bus, path)
+
+ thermometer.RegisterWatcher(path)
+
+ if len(args) > 0:
+ if args[0] == "EnableIntermediateMeasurement":
+ thermometer.EnableIntermediateMeasurement(path)
+ else:
+ print "unknown command"
+ sys.exit(1)
+
+ mainloop = gobject.MainLoop()
+ mainloop.run()
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
- *
+ * Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
*
* 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
#include <config.h>
#endif
+#include <glib.h>
#include <errno.h>
-
#include <gdbus.h>
#include "plugin.h"
#include "manager.h"
-static DBusConnection *connection;
+static DBusConnection *connection = NULL;
-static int attrib_init(void)
+static int thermometer_init(void)
{
connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
if (connection == NULL)
return -EIO;
- if (attrib_manager_init(connection) < 0) {
+ if (thermometer_manager_init(connection) < 0) {
dbus_connection_unref(connection);
return -EIO;
}
return 0;
}
-static void attrib_exit(void)
+static void thermometer_exit(void)
{
- attrib_manager_exit();
+ thermometer_manager_exit();
dbus_connection_unref(connection);
+ connection = NULL;
}
-BLUETOOTH_PLUGIN_DEFINE(attrib, VERSION,
- BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, attrib_init, attrib_exit)
+BLUETOOTH_PLUGIN_DEFINE(thermometer, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ thermometer_init, thermometer_exit)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ * 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
+ *
+ */
+
+#include <gdbus.h>
+#include <errno.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "att.h"
+#include "thermometer.h"
+#include "manager.h"
+
+#define HEALTH_THERMOMETER_UUID "00001809-0000-1000-8000-00805f9b34fb"
+
+static DBusConnection *connection = NULL;
+
+static int thermometer_driver_probe(struct btd_device *device, GSList *uuids)
+{
+ struct att_primary *tattr;
+ GSList *list;
+
+ list = device_services_from_record(device, uuids);
+ if (list == NULL)
+ return -EINVAL;
+
+ tattr = list->data;
+
+ return thermometer_register(connection, device, tattr);
+}
+
+static void thermometer_driver_remove(struct btd_device *device)
+{
+ thermometer_unregister(device);
+}
+
+static struct btd_device_driver thermometer_device_driver = {
+ .name = "thermometer-device-driver",
+ .uuids = BTD_UUIDS(HEALTH_THERMOMETER_UUID),
+ .probe = thermometer_driver_probe,
+ .remove = thermometer_driver_remove
+};
+
+int thermometer_manager_init(DBusConnection *conn)
+{
+ int ret;
+
+ ret = btd_register_device_driver(&thermometer_device_driver);
+ if (ret < 0)
+ return ret;
+
+ connection = dbus_connection_ref(conn);
+ return 0;
+}
+
+void thermometer_manager_exit(void)
+{
+ btd_unregister_device_driver(&thermometer_device_driver);
+
+ dbus_connection_unref(connection);
+ connection = NULL;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ * 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
+ *
+ */
+
+int thermometer_manager_init(DBusConnection *conn);
+void thermometer_manager_exit(void);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gdbus.h>
+#include <errno.h>
+#include <bluetooth/uuid.h>
+
+#include "dbus-common.h"
+#include "adapter.h"
+#include "device.h"
+#include "error.h"
+#include "log.h"
+#include "gattrib.h"
+#include "attio.h"
+#include "att.h"
+#include "gatt.h"
+#include "thermometer.h"
+#include "glib-compat.h"
+
+#define THERMOMETER_INTERFACE "org.bluez.Thermometer"
+
+#define TEMPERATURE_MEASUREMENT_UUID "00002a1c-0000-1000-8000-00805f9b34fb"
+#define TEMPERATURE_TYPE_UUID "00002a1d-0000-1000-8000-00805f9b34fb"
+#define INTERMEDIATE_TEMPERATURE_UUID "00002a1e-0000-1000-8000-00805f9b34fb"
+#define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb"
+
+/* Temperature measurement flag fields */
+#define TEMP_UNITS 0x01
+#define TEMP_TIME_STAMP 0x02
+#define TEMP_TYPE 0x04
+
+#define FLOAT_MAX_MANTISSA 16777216 /* 2^24 */
+
+struct thermometer {
+ DBusConnection *conn; /* The connection to the bus */
+ struct btd_device *dev; /* Device reference */
+ GAttrib *attrib; /* GATT connection */
+ struct att_range *svc_range; /* Thermometer range */
+ guint attioid; /* Att watcher id */
+ guint attindid; /* Att incications id */
+ guint attnotid; /* Att notifications id */
+ GSList *chars; /* Characteristics */
+ GSList *fwatchers; /* Final measurements */
+ GSList *iwatchers; /* Intermediate measurements */
+ gboolean intermediate;
+ uint8_t type;
+ uint16_t interval;
+ uint16_t max;
+ uint16_t min;
+ gboolean has_type;
+ gboolean has_interval;
+};
+
+struct characteristic {
+ struct att_char attr; /* Characteristic */
+ GSList *desc; /* Descriptors */
+ struct thermometer *t; /* Thermometer where the char belongs */
+};
+
+struct descriptor {
+ struct characteristic *ch;
+ uint16_t handle;
+ bt_uuid_t uuid;
+};
+
+struct watcher {
+ struct thermometer *t;
+ guint id;
+ char *srv;
+ char *path;
+};
+
+struct measurement {
+ int16_t exp;
+ int32_t mant;
+ uint64_t time;
+ gboolean suptime;
+ char *unit;
+ char *type;
+ char *value;
+};
+
+struct tmp_interval_data {
+ struct thermometer *thermometer;
+ uint16_t interval;
+};
+
+static GSList *thermometers = NULL;
+
+const char *temp_type[] = {
+ "<reserved>",
+ "Armpit",
+ "Body",
+ "Ear",
+ "Finger",
+ "Intestines",
+ "Mouth",
+ "Rectum",
+ "Toe",
+ "Tympanum"
+};
+
+static const gchar *temptype2str(uint8_t value)
+{
+ if (value > 0 && value < G_N_ELEMENTS(temp_type))
+ return temp_type[value];
+
+ error("Temperature type %d reserved for future use", value);
+ return NULL;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ if (watcher->id > 0)
+ g_dbus_remove_watch(watcher->t->conn, watcher->id);
+
+ g_free(watcher->path);
+ g_free(watcher->srv);
+ g_free(watcher);
+}
+
+static void destroy_char(gpointer user_data)
+{
+ struct characteristic *c = user_data;
+
+ g_slist_free_full(c->desc, g_free);
+ g_free(c);
+}
+
+static void destroy_thermometer(gpointer user_data)
+{
+ struct thermometer *t = user_data;
+
+ if (t->attioid > 0)
+ btd_device_remove_attio_callback(t->dev, t->attioid);
+
+ if (t->attindid > 0)
+ g_attrib_unregister(t->attrib, t->attindid);
+
+ if (t->attnotid > 0)
+ g_attrib_unregister(t->attrib, t->attnotid);
+
+ if (t->attrib != NULL)
+ g_attrib_unref(t->attrib);
+
+ if (t->chars != NULL)
+ g_slist_free_full(t->chars, destroy_char);
+
+ if (t->fwatchers != NULL)
+ g_slist_free_full(t->fwatchers, destroy_watcher);
+
+ dbus_connection_unref(t->conn);
+ btd_device_unref(t->dev);
+ g_free(t->svc_range);
+ g_free(t);
+}
+
+static gint cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct thermometer *t = a;
+ const struct btd_device *dev = b;
+
+ if (dev == t->dev)
+ return 0;
+
+ return -1;
+}
+
+static gint cmp_watcher(gconstpointer a, gconstpointer b)
+{
+ const struct watcher *watcher = a;
+ const struct watcher *match = b;
+ int ret;
+
+ ret = g_strcmp0(watcher->srv, match->srv);
+ if (ret != 0)
+ return ret;
+
+ return g_strcmp0(watcher->path, match->path);
+}
+
+static gint cmp_char_uuid(gconstpointer a, gconstpointer b)
+{
+ const struct characteristic *ch = a;
+ const char *uuid = b;
+
+ return g_strcmp0(ch->attr.uuid, uuid);
+}
+
+static gint cmp_char_val_handle(gconstpointer a, gconstpointer b)
+{
+ const struct characteristic *ch = a;
+ const uint16_t *handle = b;
+
+ return ch->attr.value_handle - *handle;
+}
+
+static gint cmp_descriptor(gconstpointer a, gconstpointer b)
+{
+ const struct descriptor *desc = a;
+ const bt_uuid_t *uuid = b;
+
+ return bt_uuid_cmp(&desc->uuid, uuid);
+}
+
+static struct characteristic *get_characteristic(struct thermometer *t,
+ const char *uuid)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(t->chars, uuid, cmp_char_uuid);
+ if (l == NULL)
+ return NULL;
+
+ return l->data;
+}
+
+static struct descriptor *get_descriptor(struct characteristic *ch,
+ const bt_uuid_t *uuid)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(ch->desc, uuid, cmp_descriptor);
+ if (l == NULL)
+ return NULL;
+
+ return l->data;
+}
+
+static void change_property(struct thermometer *t, const char *name,
+ gpointer value) {
+ if (g_strcmp0(name, "Intermediate") == 0) {
+ gboolean *intermediate = value;
+ if (t->intermediate == *intermediate)
+ return;
+
+ t->intermediate = *intermediate;
+ emit_property_changed(t->conn, device_get_path(t->dev),
+ THERMOMETER_INTERFACE, name,
+ DBUS_TYPE_BOOLEAN, &t->intermediate);
+ } else if (g_strcmp0(name, "Interval") == 0) {
+ uint16_t *interval = value;
+ if (t->has_interval && t->interval == *interval)
+ return;
+
+ t->has_interval = TRUE;
+ t->interval = *interval;
+ emit_property_changed(t->conn, device_get_path(t->dev),
+ THERMOMETER_INTERFACE, name,
+ DBUS_TYPE_UINT16, &t->interval);
+ } else if (g_strcmp0(name, "Maximum") == 0) {
+ uint16_t *max = value;
+ if (t->max == *max)
+ return;
+
+ t->max = *max;
+ emit_property_changed(t->conn, device_get_path(t->dev),
+ THERMOMETER_INTERFACE, name,
+ DBUS_TYPE_UINT16, &t->max);
+ } else if (g_strcmp0(name, "Minimum") == 0) {
+ uint16_t *min = value;
+ if (t->min == *min)
+ return;
+
+ t->min = *min;
+ emit_property_changed(t->conn, device_get_path(t->dev),
+ THERMOMETER_INTERFACE, name,
+ DBUS_TYPE_UINT16, &t->min);
+ } else
+ DBG("%s is not a thermometer property", name);
+}
+
+static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct descriptor *desc = user_data;
+ uint8_t value[ATT_MAX_MTU];
+ uint16_t max, min;
+ int vlen;
+
+ if (status != 0) {
+ DBG("Valid Range descriptor read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_read_resp(pdu, len, value, &vlen)) {
+ DBG("Protocol error\n");
+ return;
+ }
+
+ if (vlen < 4) {
+ DBG("Invalid range received");
+ return;
+ }
+
+ min = att_get_u16(&value[0]);
+ max = att_get_u16(&value[2]);
+
+ if (min == 0 || min > max) {
+ DBG("Invalid range");
+ return;
+ }
+
+ change_property(desc->ch->t, "Maximum", &max);
+ change_property(desc->ch->t, "Minimum", &min);
+}
+
+static void process_thermometer_desc(struct descriptor *desc)
+{
+ struct characteristic *ch = desc->ch;
+ char uuidstr[MAX_LEN_UUID_STR];
+ bt_uuid_t btuuid;
+
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+ if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0) {
+ uint8_t atval[2];
+ uint16_t val;
+
+ if (g_strcmp0(ch->attr.uuid,
+ TEMPERATURE_MEASUREMENT_UUID) == 0) {
+ if (g_slist_length(ch->t->fwatchers) == 0)
+ return;
+
+ val = ATT_CLIENT_CHAR_CONF_INDICATION;
+ } else if (g_strcmp0(ch->attr.uuid,
+ INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+ if (g_slist_length(ch->t->iwatchers) == 0)
+ return;
+
+ val = ATT_CLIENT_CHAR_CONF_NOTIFICATION;
+ } else if (g_strcmp0(ch->attr.uuid,
+ MEASUREMENT_INTERVAL_UUID) == 0)
+ val = ATT_CLIENT_CHAR_CONF_INDICATION;
+ else
+ goto done;
+
+ att_put_u16(val, atval);
+ gatt_write_char(ch->t->attrib, desc->handle, atval, 2,
+ NULL, NULL);
+ return;
+ }
+
+ bt_uuid16_create(&btuuid, GATT_CHARAC_VALID_RANGE_UUID);
+
+ if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0 && g_strcmp0(ch->attr.uuid,
+ MEASUREMENT_INTERVAL_UUID) == 0) {
+ gatt_read_char(ch->t->attrib, desc->handle, 0,
+ valid_range_desc_cb, desc);
+ return;
+ }
+
+done:
+ bt_uuid_to_string(&desc->uuid, uuidstr, MAX_LEN_UUID_STR);
+ DBG("Ignored descriptor %s in characteristic %s", uuidstr,
+ ch->attr.uuid);
+}
+
+static void discover_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+ struct att_data_list *list;
+ uint8_t format;
+ int i;
+
+ if (status != 0) {
+ error("Discover all characteristic descriptors failed [%s]: %s",
+ ch->attr.uuid, att_ecode2str(status));
+ return;
+ }
+
+ list = dec_find_info_resp(pdu, len, &format);
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < list->num; i++) {
+ struct descriptor *desc;
+ uint8_t *value;
+
+ value = list->data[i];
+ desc = g_new0(struct descriptor, 1);
+ desc->handle = att_get_u16(value);
+ desc->ch = ch;
+
+ if (format == 0x01)
+ desc->uuid = att_get_uuid16(&value[2]);
+ else
+ desc->uuid = att_get_uuid128(&value[2]);
+
+ ch->desc = g_slist_append(ch->desc, desc);
+ process_thermometer_desc(desc);
+ }
+
+ att_data_list_free(list);
+}
+
+static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+ struct thermometer *t = ch->t;
+ uint8_t value[ATT_MAX_MTU];
+ int vlen;
+
+ if (status != 0) {
+ DBG("Temperature Type value read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_read_resp(pdu, len, value, &vlen)) {
+ DBG("Protocol error.");
+ return;
+ }
+
+ if (vlen != 1) {
+ DBG("Invalid length for Temperature type");
+ return;
+ }
+
+ t->has_type = TRUE;
+ t->type = value[0];
+}
+
+static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+ uint8_t value[ATT_MAX_MTU];
+ uint16_t interval;
+ int vlen;
+
+ if (status != 0) {
+ DBG("Measurement Interval value read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_read_resp(pdu, len, value, &vlen)) {
+ DBG("Protocol error\n");
+ return;
+ }
+
+ if (vlen < 2) {
+ DBG("Invalid Interval received");
+ return;
+ }
+
+ interval = att_get_u16(&value[0]);
+ change_property(ch->t, "Interval", &interval);
+}
+
+static void process_thermometer_char(struct characteristic *ch)
+{
+ if (g_strcmp0(ch->attr.uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+ gboolean intermediate = TRUE;
+ change_property(ch->t, "Intermediate", &intermediate);
+ return;
+ } else if (g_strcmp0(ch->attr.uuid, TEMPERATURE_TYPE_UUID) == 0)
+ gatt_read_char(ch->t->attrib, ch->attr.value_handle, 0,
+ read_temp_type_cb, ch);
+ else if (g_strcmp0(ch->attr.uuid, MEASUREMENT_INTERVAL_UUID) == 0)
+ gatt_read_char(ch->t->attrib, ch->attr.value_handle, 0,
+ read_interval_cb, ch);
+}
+
+static void configure_thermometer_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ struct thermometer *t = user_data;
+ GSList *l;
+
+ if (status != 0) {
+ error("Discover thermometer characteristics: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (l = characteristics; l; l = l->next) {
+ struct att_char *c = l->data;
+ struct characteristic *ch;
+ uint16_t start, end;
+
+ ch = g_new0(struct characteristic, 1);
+ ch->attr.handle = c->handle;
+ ch->attr.properties = c->properties;
+ ch->attr.value_handle = c->value_handle;
+ memcpy(ch->attr.uuid, c->uuid, MAX_LEN_UUID_STR + 1);
+ ch->t = t;
+
+ t->chars = g_slist_append(t->chars, ch);
+
+ process_thermometer_char(ch);
+
+ start = c->value_handle + 1;
+
+ if (l->next != NULL) {
+ struct att_char *c = l->next->data;
+ if (start == c->handle)
+ continue;
+ end = c->handle - 1;
+ } else if (c->value_handle != t->svc_range->end)
+ end = t->svc_range->end;
+ else
+ continue;
+
+ gatt_find_info(t->attrib, start, end, discover_desc_cb, ch);
+ }
+}
+
+static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct thermometer *t = data;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(msg);
+ if (reply == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dict_append_entry(&dict, "Intermediate", DBUS_TYPE_BOOLEAN,
+ &t->intermediate);
+
+ if (t->has_interval) {
+ dict_append_entry(&dict, "Interval", DBUS_TYPE_UINT16,
+ &t->interval);
+ dict_append_entry(&dict, "Maximum", DBUS_TYPE_UINT16, &t->max);
+ dict_append_entry(&dict, "Minimum", DBUS_TYPE_UINT16, &t->min);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static void write_interval_cb (guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct tmp_interval_data *data = user_data;
+
+ if (status != 0) {
+ error("Interval Write Request failed %s",
+ att_ecode2str(status));
+ goto done;
+ }
+
+ if (!dec_write_resp(pdu, len)) {
+ error("Interval Write Request: protocol error");
+ goto done;
+ }
+
+ change_property(data->thermometer, "Interval", &data->interval);
+
+done:
+ g_free(user_data);
+}
+
+static DBusMessage *write_attr_interval(struct thermometer *t, DBusMessage *msg,
+ uint16_t value)
+{
+ struct tmp_interval_data *data;
+ struct characteristic *ch;
+ uint8_t atval[2];
+
+ if (t->attrib == NULL)
+ return btd_error_not_connected(msg);
+
+ ch = get_characteristic(t, MEASUREMENT_INTERVAL_UUID);
+ if (ch == NULL)
+ return btd_error_not_available(msg);
+
+ if (value < t->min || value > t->max)
+ return btd_error_invalid_args(msg);
+
+ att_put_u16(value, &atval[0]);
+
+ data = g_new0(struct tmp_interval_data, 1);
+ data->thermometer = t;
+ data->interval = value;
+ gatt_write_char(t->attrib, ch->attr.value_handle, atval, 2,
+ write_interval_cb, data);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct thermometer *t = data;
+ const char *property;
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+ uint16_t value;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ if (g_strcmp0("Interval", property) != 0)
+ return btd_error_invalid_args(msg);
+
+ if (!t->has_interval)
+ return btd_error_not_available(msg);
+
+ dbus_message_iter_next(&iter);
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &value);
+
+ return write_attr_interval(t, msg, value);
+}
+
+static void measurement_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ char *msg = user_data;
+
+ if (status != 0)
+ error("%s failed", msg);
+
+ g_free(msg);
+}
+
+static void enable_final_measurement(struct thermometer *t)
+{
+ struct characteristic *ch;
+ struct descriptor *desc;
+ bt_uuid_t btuuid;
+ uint8_t atval[2];
+ char *msg;
+
+ if (t->attrib == NULL)
+ return;
+
+ ch = get_characteristic(t, TEMPERATURE_MEASUREMENT_UUID);
+ if (ch == NULL) {
+ DBG("Temperature measurement characteristic not found");
+ return;
+ }
+
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ desc = get_descriptor(ch, &btuuid);
+ if (desc == NULL) {
+ DBG("Client characteristic configuration descriptor not found");
+ return;
+ }
+
+ atval[0] = 0x02;
+ atval[1] = 0x00;
+ msg = g_strdup("Enable final measurement");
+ gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
+}
+
+static void enable_intermediate_measurement(struct thermometer *t)
+{
+ struct characteristic *ch;
+ struct descriptor *desc;
+ bt_uuid_t btuuid;
+ uint8_t atval[2];
+ char *msg;
+
+ if (t->attrib == NULL)
+ return;
+
+ ch = get_characteristic(t, INTERMEDIATE_TEMPERATURE_UUID);
+ if (ch == NULL) {
+ DBG("Intermediate measurement characteristic not found");
+ return;
+ }
+
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ desc = get_descriptor(ch, &btuuid);
+ if (desc == NULL) {
+ DBG("Client characteristic configuration descriptor not found");
+ return;
+ }
+
+ atval[0] = 0x01;
+ atval[1] = 0x00;
+ msg = g_strdup("Enable intermediate measurement");
+ gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
+}
+
+static void disable_final_measurement(struct thermometer *t)
+{
+ struct characteristic *ch;
+ struct descriptor *desc;
+ bt_uuid_t btuuid;
+ uint8_t atval[2];
+ char *msg;
+
+ if (t->attrib == NULL)
+ return;
+
+ ch = get_characteristic(t, TEMPERATURE_MEASUREMENT_UUID);
+ if (ch == NULL) {
+ DBG("Temperature measurement characteristic not found");
+ return;
+ }
+
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ desc = get_descriptor(ch, &btuuid);
+ if (desc == NULL) {
+ DBG("Client characteristic configuration descriptor not found");
+ return;
+ }
+
+ atval[0] = 0x00;
+ atval[1] = 0x00;
+ msg = g_strdup("Disable final measurement");
+ gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
+}
+
+static void disable_intermediate_measurement(struct thermometer *t)
+{
+ struct characteristic *ch;
+ struct descriptor *desc;
+ bt_uuid_t btuuid;
+ uint8_t atval[2];
+ char *msg;
+
+ if (t->attrib == NULL)
+ return;
+
+ ch = get_characteristic(t, INTERMEDIATE_TEMPERATURE_UUID);
+ if (ch == NULL) {
+ DBG("Intermediate measurement characteristic not found");
+ return;
+ }
+
+ bt_uuid16_create(&btuuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ desc = get_descriptor(ch, &btuuid);
+ if (desc == NULL) {
+ DBG("Client characteristic configuration descriptor not found");
+ return;
+ }
+
+ atval[0] = 0x00;
+ atval[1] = 0x00;
+ msg = g_strdup("Disable intermediate measurement");
+ gatt_write_char(t->attrib, desc->handle, atval, 2, measurement_cb, msg);
+}
+
+static void remove_int_watcher(struct thermometer *t, struct watcher *w)
+{
+ if (!g_slist_find(t->iwatchers, w))
+ return;
+
+ t->iwatchers = g_slist_remove(t->iwatchers, w);
+
+ if (g_slist_length(t->iwatchers) == 0)
+ disable_intermediate_measurement(t);
+}
+
+static void watcher_exit(DBusConnection *conn, void *user_data)
+{
+ struct watcher *watcher = user_data;
+ struct thermometer *t = watcher->t;
+
+ DBG("Thermometer watcher %s disconnected", watcher->path);
+
+ remove_int_watcher(t, watcher);
+
+ t->fwatchers = g_slist_remove(t->fwatchers, watcher);
+ watcher->id = 0;
+
+ if (g_slist_length(t->fwatchers) == 0)
+ disable_final_measurement(t);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+ const char *path)
+{
+ struct watcher *match;
+ GSList *l;
+
+ match = g_new0(struct watcher, 1);
+ match->srv = g_strdup(sender);
+ match->path = g_strdup(path);
+
+ l = g_slist_find_custom(list, match, cmp_watcher);
+ destroy_watcher(match);
+
+ if (l != NULL)
+ return l->data;
+
+ return NULL;
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct thermometer *t = data;
+ struct watcher *watcher;
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(t->fwatchers, sender, path);
+ if (watcher != NULL)
+ return btd_error_already_exists(msg);
+
+ DBG("Thermometer watcher %s registered", path);
+
+ watcher = g_new0(struct watcher, 1);
+ watcher->srv = g_strdup(sender);
+ watcher->path = g_strdup(path);
+ watcher->t = t;
+ watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
+ watcher, destroy_watcher);
+
+ if (g_slist_length(t->fwatchers) == 0)
+ enable_final_measurement(t);
+
+ t->fwatchers = g_slist_prepend(t->fwatchers, watcher);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct thermometer *t = data;
+ struct watcher *watcher;
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(t->fwatchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ DBG("Thermometer watcher %s unregistered", path);
+
+ remove_int_watcher(t, watcher);
+
+ t->fwatchers = g_slist_remove(t->fwatchers, watcher);
+ destroy_watcher(watcher);
+
+ if (g_slist_length(t->fwatchers) == 0)
+ disable_final_measurement(t);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct thermometer *t = data;
+ struct watcher *watcher;
+ char *path;
+
+ if (!t->intermediate)
+ return btd_error_not_supported(msg);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(t->fwatchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ if (find_watcher(t->iwatchers, sender, path))
+ return btd_error_already_exists(msg);
+
+ DBG("Intermediate measurement watcher %s registered", path);
+
+ if (g_slist_length(t->iwatchers) == 0)
+ enable_intermediate_measurement(t);
+
+ t->iwatchers = g_slist_prepend(t->iwatchers, watcher);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct thermometer *t = data;
+ struct watcher *watcher;
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(t->iwatchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ DBG("Intermediate measurement %s unregistered", path);
+
+ remove_int_watcher(t, watcher);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable thermometer_methods[] = {
+ { "GetProperties", "", "a{sv}", get_properties },
+ { "SetProperty", "sv", "", set_property,
+ G_DBUS_METHOD_FLAG_ASYNC },
+ { "RegisterWatcher", "o", "", register_watcher },
+ { "UnregisterWatcher", "o", "", unregister_watcher },
+ { "EnableIntermediateMeasurement", "o", "", enable_intermediate },
+ { "DisableIntermediateMeasurement","o", "", disable_intermediate },
+ { }
+};
+
+static GDBusSignalTable thermometer_signals[] = {
+ { "PropertyChanged", "sv" },
+ { }
+};
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+ struct watcher *w = data;
+ struct measurement *m = user_data;
+ DBusConnection *conn = w->t->conn;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(w->srv, w->path,
+ "org.bluez.ThermometerWatcher",
+ "MeasurementReceived");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp);
+ dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant);
+ dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit);
+
+ if (m->suptime)
+ dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time);
+
+ dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type);
+ dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(conn, msg);
+}
+
+static void recv_measurement(struct thermometer *t, struct measurement *m)
+{
+ GSList *wlist;
+
+ if (g_strcmp0(m->value, "Intermediate") == 0)
+ wlist = t->iwatchers;
+ else
+ wlist = t->fwatchers;
+
+ g_slist_foreach(wlist, update_watcher, m);
+}
+
+static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
+ uint16_t len, gboolean final)
+{
+ struct measurement m;
+ const char *type;
+ uint8_t flags;
+ uint32_t raw;
+
+ if (len < 4) {
+ DBG("Mandatory flags are not provided");
+ return;
+ }
+
+ flags = pdu[3];
+ if (flags & TEMP_UNITS)
+ m.unit = "Fahrenheit";
+ else
+ m.unit = "Celsius";
+
+ if (len < 8) {
+ DBG("Temperature measurement value is not provided");
+ return;
+ }
+
+ raw = att_get_u32(&pdu[4]);
+ m.mant = raw & 0x00FFFFFF;
+ m.exp = ((int32_t) raw) >> 24;
+
+ if (m.mant & 0x00800000) {
+ /* convert to C2 negative value */
+ m.mant = m.mant - FLOAT_MAX_MANTISSA;
+ }
+
+ if (flags & TEMP_TIME_STAMP) {
+ struct tm ts;
+ time_t time;
+
+ if (len < 15) {
+ DBG("Can't get time stamp value");
+ return;
+ }
+
+ ts.tm_year = att_get_u16(&pdu[8]) - 1900;
+ ts.tm_mon = pdu[10] - 1;
+ ts.tm_mday = pdu[11];
+ ts.tm_hour = pdu[12];
+ ts.tm_min = pdu[13];
+ ts.tm_sec = pdu[14];
+ ts.tm_isdst = -1;
+
+ time = mktime(&ts);
+ m.time = (uint64_t) time;
+ m.suptime = TRUE;
+ } else
+ m.suptime = FALSE;
+
+ if (flags & TEMP_TYPE) {
+ uint8_t index;
+
+ if (m.suptime && len >= 16)
+ index = 15;
+ else if (!m.suptime && len >= 9)
+ index = 9;
+ else {
+ DBG("Can't get temperature type");
+ return;
+ }
+
+ type = temptype2str(pdu[index]);
+ } else if (t->has_type)
+ type = temptype2str(t->type);
+ else {
+ DBG("Can't get temperature type");
+ return;
+ }
+
+ if (type == NULL)
+ return;
+
+ m.type = g_strdup(type);
+ m.value = final ? "Final" : "Intermediate";
+
+ recv_measurement(t, &m);
+ g_free(m.type);
+}
+
+static void proc_measurement_interval(struct thermometer *t, const uint8_t *pdu,
+ uint16_t len)
+{
+ uint16_t interval;
+
+ if (len < 5) {
+ DBG("Measurement interval value is not provided");
+ return;
+ }
+
+ interval = att_get_u16(&pdu[3]);
+
+ change_property(t, "Interval", &interval);
+}
+
+static void ind_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+ struct thermometer *t = user_data;
+ const struct characteristic *ch;
+ uint8_t opdu[ATT_MAX_MTU];
+ uint16_t handle, olen;
+ GSList *l;
+
+ if (len < 3) {
+ DBG("Bad pdu received");
+ return;
+ }
+
+ handle = att_get_u16(&pdu[1]);
+ l = g_slist_find_custom(t->chars, &handle, cmp_char_val_handle);
+ if (l == NULL) {
+ DBG("Unexpected handle: 0x%04x", handle);
+ return;
+ }
+
+ ch = l->data;
+
+ if (g_strcmp0(ch->attr.uuid, TEMPERATURE_MEASUREMENT_UUID) == 0)
+ proc_measurement(t, pdu, len, TRUE);
+ else if (g_strcmp0(ch->attr.uuid, MEASUREMENT_INTERVAL_UUID) == 0)
+ proc_measurement_interval(t, pdu, len);
+
+ olen = enc_confirmation(opdu, sizeof(opdu));
+
+ if (olen > 0)
+ g_attrib_send(t->attrib, 0, opdu[0], opdu, olen, NULL, NULL,
+ NULL);
+}
+
+static void notif_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+ struct thermometer *t = user_data;
+ const struct characteristic *ch;
+ uint16_t handle;
+ GSList *l;
+
+ if (len < 3) {
+ DBG("Bad pdu received");
+ return;
+ }
+
+ handle = att_get_u16(&pdu[1]);
+ l = g_slist_find_custom(t->chars, &handle, cmp_char_val_handle);
+ if (l == NULL) {
+ DBG("Unexpected handle: 0x%04x", handle);
+ return;
+ }
+
+ ch = l->data;
+ if (g_strcmp0(ch->attr.uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0)
+ proc_measurement(t, pdu, len, FALSE);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct thermometer *t = user_data;
+
+ t->attrib = g_attrib_ref(attrib);
+
+ t->attindid = g_attrib_register(t->attrib, ATT_OP_HANDLE_IND,
+ ind_handler, t, NULL);
+ t->attnotid = g_attrib_register(t->attrib, ATT_OP_HANDLE_NOTIFY,
+ notif_handler, t, NULL);
+ gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end,
+ NULL, configure_thermometer_cb, t);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct thermometer *t = user_data;
+
+ DBG("GATT Disconnected");
+
+ if (t->attindid > 0) {
+ g_attrib_unregister(t->attrib, t->attindid);
+ t->attindid = 0;
+ }
+
+ if (t->attnotid > 0) {
+ g_attrib_unregister(t->attrib, t->attnotid);
+ t->attnotid = 0;
+ }
+
+ g_attrib_unref(t->attrib);
+ t->attrib = NULL;
+}
+
+int thermometer_register(DBusConnection *connection, struct btd_device *device,
+ struct att_primary *tattr)
+{
+ const gchar *path = device_get_path(device);
+ struct thermometer *t;
+
+ t = g_new0(struct thermometer, 1);
+ t->conn = dbus_connection_ref(connection);
+ t->dev = btd_device_ref(device);
+ t->svc_range = g_new0(struct att_range, 1);
+ t->svc_range->start = tattr->start;
+ t->svc_range->end = tattr->end;
+
+ if (!g_dbus_register_interface(t->conn, path, THERMOMETER_INTERFACE,
+ thermometer_methods, thermometer_signals,
+ NULL, t, destroy_thermometer)) {
+ error("D-Bus failed to register %s interface",
+ THERMOMETER_INTERFACE);
+ destroy_thermometer(t);
+ return -EIO;
+ }
+
+ thermometers = g_slist_prepend(thermometers, t);
+
+ t->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+ attio_disconnected_cb, t);
+ return 0;
+}
+
+void thermometer_unregister(struct btd_device *device)
+{
+ struct thermometer *t;
+ GSList *l;
+
+ l = g_slist_find_custom(thermometers, device, cmp_device);
+ if (l == NULL)
+ return;
+
+ t = l->data;
+ thermometers = g_slist_remove(thermometers, t);
+ g_dbus_unregister_interface(t->conn, device_get_path(t->dev),
+ THERMOMETER_INTERFACE);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ * 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
+ *
+ */
+
+int thermometer_register(DBusConnection *connection, struct btd_device *device,
+ struct att_primary *tattr);
+void thermometer_unregister(struct btd_device *device);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <glib.h>
+
+#include "plugin.h"
+#include "hcid.h"
+#include "log.h"
+#include "server.h"
+
+static int time_init(void)
+{
+ if (!main_opts.attrib_server) {
+ DBG("Attribute server is disabled");
+ return -1;
+ }
+
+ return time_server_init();
+}
+
+static void time_exit(void)
+{
+ if (!main_opts.attrib_server)
+ return;
+
+ time_server_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(time, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ time_init, time_exit)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <time.h>
+#include <errno.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+
+#include "att.h"
+#include "gattrib.h"
+#include "attrib-server.h"
+#include "gatt-service.h"
+#include "log.h"
+#include "server.h"
+
+#define CURRENT_TIME_SVC_UUID 0x1805
+
+#define LOCAL_TIME_INFO_CHR_UUID 0x2A0F
+#define CT_TIME_CHR_UUID 0x2A2B
+
+static int encode_current_time(uint8_t value[10])
+{
+ struct timespec tp;
+ struct tm tm;
+
+ if (clock_gettime(CLOCK_REALTIME, &tp) == -1) {
+ int err = -errno;
+
+ error("clock_gettime: %s", strerror(-err));
+ return err;
+ }
+
+ if (localtime_r(&tp.tv_sec, &tm) == NULL) {
+ error("localtime_r() failed");
+ /* localtime_r() does not set errno */
+ return -EINVAL;
+ }
+
+ att_put_u16(1900 + tm.tm_year, &value[0]); /* Year */
+ value[2] = tm.tm_mon + 1; /* Month */
+ value[3] = tm.tm_mday; /* Day */
+ value[4] = tm.tm_hour; /* Hours */
+ value[5] = tm.tm_min; /* Minutes */
+ value[6] = tm.tm_sec; /* Seconds */
+ value[7] = tm.tm_wday == 0 ? 7 : tm.tm_wday; /* Day of Week */
+ /* From Time Profile spec: "The number of 1/256 fractions of a second."
+ * In 1s there are 256 fractions, in 1ns there are 256/10^9 fractions.
+ * To avoid integer overflow, we use the equivalent 1/3906250 ratio. */
+ value[8] = tp.tv_nsec / 3906250; /* Fractions256 */
+ value[9] = 0x00; /* Adjust Reason */
+
+ return 0;
+}
+
+static uint8_t current_time_read(struct attribute *a, gpointer user_data)
+{
+ uint8_t value[10];
+
+ if (encode_current_time(value) < 0)
+ return ATT_ECODE_IO;
+
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_update(NULL, a->handle, NULL, value, sizeof(value), NULL);
+
+ return 0;
+}
+
+static uint8_t local_time_info_read(struct attribute *a, gpointer user_data)
+{
+ uint8_t value[2];
+
+ DBG("a=%p", a);
+
+ tzset();
+
+ /* FIXME: POSIX "daylight" variable only indicates whether there is DST
+ * for the local time or not. The offset is unknown. */
+ value[0] = daylight ? 0xff : 0x00;
+
+ /* Convert POSIX "timezone" (seconds West of GMT) to Time Profile
+ * format (offset from UTC in number of 15 minutes increments). */
+ value[1] = (uint8_t) (-1 * timezone / (60 * 15));
+
+ /* FIXME: Provide the adapter in next function */
+ attrib_db_update(NULL, a->handle, NULL, value, sizeof(value), NULL);
+
+ return 0;
+}
+
+static void register_current_time_service(void)
+{
+ /* Current Time service */
+ /* FIXME: Provide the adapter in next function */
+ gatt_service_add(NULL, GATT_PRIM_SVC_UUID, CURRENT_TIME_SVC_UUID,
+ /* CT Time characteristic */
+ GATT_OPT_CHR_UUID, CT_TIME_CHR_UUID,
+ GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ |
+ ATT_CHAR_PROPER_NOTIFY,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ current_time_read, NULL,
+
+ /* Local Time Information characteristic */
+ GATT_OPT_CHR_UUID, LOCAL_TIME_INFO_CHR_UUID,
+ GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ local_time_info_read, NULL,
+
+ GATT_OPT_INVALID);
+}
+
+int time_server_init(void)
+{
+ register_current_time_service();
+
+ return 0;
+}
+
+void time_server_exit(void)
+{
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int time_server_init(void);
+void time_server_exit(void);
return sk;
}
-static void usage()
+static void usage(void)
{
printf("avinfo - Audio/Video Info Tool ver %s\n", VERSION);
printf("Usage:\n"
array[1] = def >> 8;
err = transport_read(transport, CSR_VARID_GET_NEXT_BUILDDEF, array, 8);
- if (err < 0) {
- errno = -err;
+ if (err < 0)
break;
- }
nextdef = array[2] | (array[3] << 8);
array[1] = handle >> 8;
err = transport_read(transport, CSR_VARID_CRYPT_KEY_LENGTH, array, 8);
- if (err < 0) {
- errno = -err;
+ if (err < 0)
return -1;
- }
handle = array[0] | (array[1] << 8);
keylen = array[2] | (array[3] << 8);
memset(array, 0, sizeof(array));
err = transport_read(transport, CSR_VARID_BT_CLOCK, array, 8);
- if (err < 0) {
- errno = -err;
+ if (err < 0)
return -1;
- }
clock = array[2] | (array[3] << 8) | (array[0] << 16) | (array[1] << 24);
memset(array, 0, sizeof(array));
err = transport_read(transport, CSR_VARID_RAND, array, 8);
- if (err < 0) {
- errno = -err;
+ if (err < 0)
return -1;
- }
rand = array[0] | (array[1] << 8);
memset(array, 0, sizeof(array));
err = transport_read(transport, CSR_VARID_CHIPREV, array, 8);
- if (err < 0) {
- errno = -err;
+ if (err < 0)
return -1;
- }
rev = array[0] | (array[1] << 8);
memset(array, 0, sizeof(array));
err = transport_read(transport, CSR_VARID_READ_BUILD_NAME, array, 128);
- if (err < 0) {
- errno = -err;
+ if (err < 0)
return -1;
- }
for (i = 0; i < sizeof(name); i++)
name[i] = array[(i * 2) + 4];
memset(array, 0, sizeof(array));
err = transport_read(transport, CSR_VARID_PANIC_ARG, array, 8);
- if (err < 0) {
- errno = -err;
+ if (err < 0)
return -1;
- }
error = array[0] | (array[1] << 8);
memset(array, 0, sizeof(array));
err = transport_read(transport, CSR_VARID_FAULT_ARG, array, 8);
- if (err < 0) {
- errno = -err;
+ if (err < 0)
return -1;
- }
error = array[0] | (array[1] << 8);
if (transport_open(transport, device, bcsp_rate) < 0)
exit(1);
- if (device)
- free(device);
+ free(device);
for (i = 0; commands[i].str; i++) {
if (strcasecmp(commands[i].str, argv[0]))
if (timeout++ > 5000) {
fprintf(stderr, "Operation timed out\n");
+ errno = ETIMEDOUT;
return -1;
}
}
--- /dev/null
+// PSKEY_BDADDR
+&0001 = 0001 2821 005b 6789
+// PSKEY_ANA_FTRIM
+&01f6 = 0025
+// PSKEY_HOST_INTERFACE
+&01f9 = 0001
+// PSKEY_UART_BAUD_RATE
+&0204 = 01d8
+// PSKEY_ANA_FREQ
+&01fe = 0004
+// PSKEY_UART_CONFIG
+&0205 = 0006
char *bdaddr;
int (*init) (int fd, struct uart_t *u, struct termios *ti);
int (*post) (int fd, struct uart_t *u, struct termios *ti);
+
+/* __SAMSUNG_PATCH__ */
#ifdef __TI_PATCH__
uint16_t device_param;
#endif
static int texas(int fd, struct uart_t *u, struct termios *ti)
{
- return texas_init(fd, ti);
+ return texas_init(fd, &u->speed, ti);
}
static int texas2(int fd, struct uart_t *u, struct termios *ti)
static void bcsp_tshy_sig_alarm(int sig)
{
unsigned char bcsp_sync_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xda,0xdc,0xed,0xed,0xc0};
- int len;
static int retries = 0;
if (retries < bcsp_max_retries) {
retries++;
- len = write(serial_fd, &bcsp_sync_pkt, 10);
+ if (write(serial_fd, &bcsp_sync_pkt, 10) < 0)
+ return;
alarm(1);
return;
}
static void bcsp_tconf_sig_alarm(int sig)
{
unsigned char bcsp_conf_pkt[10] = {0xc0,0x00,0x41,0x00,0xbe,0xad,0xef,0xac,0xed,0xc0};
- int len;
static int retries = 0;
if (retries < bcsp_max_retries){
retries++;
- len = write(serial_fd, &bcsp_conf_pkt, 10);
+ if (write(serial_fd, &bcsp_conf_pkt, 10) < 0)
+ return;
alarm(1);
return;
}
}
if (!memcmp(bcspp, bcspsync, 4)) {
- len = write(fd, &bcsp_sync_resp_pkt,10);
+ if (write(fd, &bcsp_sync_resp_pkt,10) < 0)
+ return -1;
} else if (!memcmp(bcspp, bcspsyncresp, 4))
break;
}
len = write(fd, &bcsp_conf_resp_pkt, 10);
else if (!memcmp(bcspp, bcspconfresp, 4))
break;
+ else
+ continue;
+
+ if (len < 0)
+ return -errno;
}
/* State = garrulous */
nanosleep(&tm, NULL);
// now the uart baud rate on the silicon wave module is set and effective.
- // change our own baud rate as well. Then there is a reset event comming in
+ // change our own baud rate as well. Then there is a reset event coming in
// on the *new* baud rate. This is *undocumented*! The packet looks like this:
// 04 FF 01 0B (which would make that a confirmation of 0x0B = "Param
// subcommand class". So: change to new baud rate, read with timeout, parse
// data, error handling. BTW: all param access in Silicon Wave is done this way.
- // Maybe this code would belong in a seperate file, or at least code reuse...
+ // Maybe this code would belong in a separate file, or at least code reuse...
return 0;
}
/* Set the baud rate */
memset(cmd, 0, sizeof(cmd));
memset(resp, 0, sizeof(resp));
+
+/* __SAMSUNG_PATCH__ */
#ifndef __BROADCOM_PATCH__
cmd[0] = HCI_COMMAND_PKT;
cmd[1] = 0x18;
{ "swave", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
FLOW_CTL, DISABLE_PM, NULL, swave },
+/* __SAMSUNG_PATCH__ */
#ifdef __TI_PATCH__
/* Texas Instruments BRF63xx modules */
{ "texas", 0x0000, 0x0000, HCI_UART_LL, 115200,3000000, FLOW_CTL, NULL, texas, NULL/*texas_continue_script*/, BRF_DEEP_SLEEP_OPCODE},
#else
/* Texas Instruments Bluelink (BRF) modules */
- { "texas", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200, FLOW_CTL, NULL, texas, texas2 },
- { "texasalt", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200, FLOW_CTL, NULL, texasalt, NULL },
+ { "texas", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, texas, texas2 },
+
+ { "texasalt", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, texasalt, NULL },
#endif
/* ST Microelectronics minikits based on STLC2410/STLC2415 */
int fd, i;
unsigned long flags = 0;
+/* __SAMSUNG_PATCH__ */
#if defined(__TI_PATCH__) || defined(__BROADCOM_PATCH__)
int power;
#endif
cfmakeraw(&ti);
+/* __SAMSUNG_PATCH__ */
#ifndef __BROADCOM_PATCH__
ti.c_cflag |= CLOCAL;
if (u->flags & FLOW_CTL)
tcsendbreak(fd, 0);
usleep(500000);
}
+
+/* __SAMSUNG_PATCH__ */
#if defined(__TI_PATCH__) || defined(__BROADCOM_PATCH__)
/* Power up the BRF chip */
power = 1;
{
printf("hciattach - HCI UART driver initialization utility\n");
printf("Usage:\n");
+
+/* __SAMSUNG_PATCH__ */
#ifdef __TI_PATCH__
printf("\thciattach [-n] [-p] [-b] [-g device_param] [-r] [-f] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]\n");
#else
sigset_t sigs;
char dev[PATH_MAX];
+/* __SAMSUNG_PATCH__ */
#if defined(__TI_PATCH__) || defined(__BROADCOM_PATCH__)
int power;
#endif
#ifdef __TI_PATCH__
while ((opt=getopt(argc, argv, "bnprft:g:s:l")) != EOF)
#else
- while ((opt=getopt(argc, argv, "bnpt:s:l")) != EOF)
+ while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF)
#endif
{
switch(opt) {
to = atoi(optarg);
break;
+/* __SAMSUNG_PATCH__ */
#ifdef __TI_PATCH__
case 'g':
device_param = (uint16_t)strtol(optarg, NULL, 16);
}
n = argc - optind;
+/* __SAMSUNG_PATCH__ */
#ifdef __TI_PATCH__
if (!reset_device || (reset_device && n < 1))
#endif
break;
}
}
+/* __SAMSUNG_PATCH__ */
#ifdef __TI_PATCH__
if (reset_device)
{
exit(1);
}
+/* __SAMSUNG_PATCH__ */
#if defined(__TI_PATCH__) || defined(__BROADCOM_PATCH__)
/* Power down the BRF or BCMchip */
power = 0;
int read_hci_event(int fd, unsigned char* buf, int size);
int set_speed(int fd, struct termios *ti, int speed);
-int texas_init(int fd, struct termios *ti);
+int texas_init(int fd, int *speed, struct termios *ti);
int texas_post(int fd, struct termios *ti);
int texasalt_init(int fd, int speed, struct termios *ti);
int stlc2500_init(int fd, bdaddr_t *bdaddr);
return err;
err = read_ps_event(event, HCI_PS_CMD_OCF);
- if (event)
- free(event);
+
+ free(event);
return err;
}
err = read_ps_event(event, HCI_PS_CMD_OCF);
- if (event)
- free(event);
+ free(event);
return err;
}
if (read_ps_event(event, HCI_PS_CMD_OCF) >= 0)
err = -EILSEQ;
- if (!event)
- free(event);
+ free(event);
return err;
}
*code = reg;
cleanup:
- if (event)
- free(event);
+ free(event);
return err;
}
*build_version = status;
cleanup:
- if (event)
- free(event);
+ free(event);
return err;
}
err = read_ps_event(event, HCI_PS_CMD_OCF);
- if (event)
- free(event);
+ free(event);
return err;
}
#define TI_MANUFACTURER_ID 13
+/* __SAMSUNG_PATCH__ */
#ifdef __TI_PATCH__
#define FIRMWARE_DIRECTORY1 "/mnt/mmc/"
#define FIRMWARE_DIRECTORY2 "/usr/etc/bluetooth/"
fp = fopen(file_name, "rb");
if (!fp) {
perror("can't open firmware file");
- goto out;
+ return NULL;
}
if (1 != fread(&header, sizeof(struct bts_header), 1, fp)) {
if (NULL != version)
*version = header.version;
- goto out;
+ return fp;
errclose:
fclose(fp);
- fp = NULL;
-out:
- return fp;
+
+ return NULL;
}
static unsigned long bts_fetch_action(FILE* fp, unsigned char* action_buf,
if (version & 0x8000)
maj_ver |= 0x0008;
+/* __SAMSUNG_PATCH__ */
#ifdef __TI_PATCH__
- FILE *fp;
+ FILE *fp;
sprintf(firmware_file_name, FIRMWARE_DIRECTORY1 "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
- if( (fp = fopen(firmware_file_name, "r")) == NULL )
- {
+ if ((fp = fopen(firmware_file_name, "r")) == NULL ) {
extern int firmware_path;
if (firmware_path)
- {
- sprintf(firmware_file_name, FIRMWARE_DIRECTORY2 "TIInit_edutm_%d.%d.%d.bts", chip, maj_ver, min_ver);
- }
+ sprintf(firmware_file_name, FIRMWARE_DIRECTORY2 "TIInit_edutm_%d.%d.%d.bts", chip, maj_ver, min_ver);
else
- {
- sprintf(firmware_file_name, FIRMWARE_DIRECTORY2 "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
- }
- }
- else
- {
- fclose(fp);
- }
+ sprintf(firmware_file_name, FIRMWARE_DIRECTORY2 "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
+ }
+ else {
+ fclose(fp);
+ }
#else
sprintf(firmware_file_name, FIRMWARE_DIRECTORY "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
#endif
}
static int brf_set_serial_params(struct bts_action_serial *serial_action,
- int fd, struct termios *ti)
+ int fd, int *speed, struct termios *ti)
{
fprintf(stderr, "texas: changing baud rate to %u, flow control to %u\n",
serial_action->baud, serial_action->flow_control );
return -1;
}
+ if (speed)
+ *speed = serial_action->baud;
+
return 0;
}
}
static int brf_do_action(uint16_t brf_type, uint8_t *brf_action, long brf_size,
- int fd, struct termios *ti, int hcill_installed)
+ int fd, int *speed, struct termios *ti, int hcill_installed)
{
int ret = 0;
break;
case ACTION_SERIAL:
DPRINTF("S");
- ret = brf_set_serial_params((struct bts_action_serial *) brf_action, fd, ti);
+ ret = brf_set_serial_params((struct bts_action_serial *) brf_action, fd, speed, ti);
break;
case ACTION_DELAY:
DPRINTF("D");
* The second time it is called, it assumes HCILL protocol is set up,
* and sends rest of brf script via the supplied socket.
*/
-static int brf_do_script(int fd, struct termios *ti, const char *bts_file)
+static int brf_do_script(int fd, int *speed, struct termios *ti, const char *bts_file)
{
int ret = 0, hcill_installed = bts_file ? 0 : 1;
uint32_t vers;
/* execute current action and continue to parse brf script file */
while (brf_size != 0) {
ret = brf_do_action(brf_type, brf_action, brf_size,
- fd, ti, hcill_installed);
+ fd, speed, ti, hcill_installed);
if (ret == -1)
break;
/* if this is the first time we run (no HCILL yet) */
/* and a deep sleep command is encountered */
/* we exit */
-#ifndef __TI_PATCH__
if (!hcill_installed &&
brf_action_is_deep_sleep(brf_action,
brf_size, brf_type))
return 0;
-#endif
}
bts_unload_script(brf_script_file);
return ret;
}
-int texas_init(int fd, struct termios *ti)
+int texas_init(int fd, int *speed, struct termios *ti)
{
struct timespec tm = {0, 50000};
char cmd[4];
bts_file = get_firmware_name(resp);
fprintf(stderr, "Firmware file : %s\n", bts_file);
- n = brf_do_script(fd, ti, bts_file);
+ n = brf_do_script(fd, speed, ti, bts_file);
nanosleep(&tm, NULL);
return -1;
}
- ret = brf_do_script(dd, ti, NULL);
+ ret = brf_do_script(dd, NULL, ti, NULL);
hci_close_dev(dd);
dd = hci_open_dev(hdev);
if (dd < 0) {
- err = errno;
+ err = -errno;
fprintf(stderr, "Could not open device: %s(%d)\n",
- strerror(err), err);
+ strerror(-err), -err);
exit(1);
}
ret = hci_send_req(dd, &rq, 1000);
if (status || ret < 0) {
- err = errno;
+ err = -errno;
fprintf(stderr, "Can't set random address for hci%d: "
- "%s (%d)\n", hdev, strerror(err), err);
+ "%s (%d)\n", hdev, strerror(-err), -err);
}
hci_close_dev(dd);
}
hciver = hci_vertostr(ver.hci_ver);
- lmpver = lmp_vertostr(ver.hci_ver);
+ lmpver = lmp_vertostr(ver.lmp_ver);
print_dev_hdr(&di);
printf("\tHCI Version: %s (0x%x) Revision: 0x%x\n"
}
if (opt) {
- uint8_t fec = 0, data[240];
+ uint8_t fec = 0, data[HCI_MAX_EIR_LENGTH];
char tmp[3];
int i, size;
memset(tmp, 0, sizeof(tmp));
size = (strlen(opt) + 1) / 2;
- if (size > 240)
- size = 240;
+ if (size > HCI_MAX_EIR_LENGTH)
+ size = HCI_MAX_EIR_LENGTH;
for (i = 0; i < size; i++) {
memcpy(tmp, opt + (i * 2), 2);
exit(1);
}
} else {
- uint8_t fec, data[240], len, type, *ptr;
+ uint8_t fec, data[HCI_MAX_EIR_LENGTH], len, type, *ptr;
char *str;
if (hci_read_ext_inquiry_response(dd, &fec, data, 1000) < 0) {
print_dev_hdr(&di);
printf("\tFEC %s\n\t\t", fec ? "enabled" : "disabled");
- for (i = 0; i < 240; i++)
+ for (i = 0; i < HCI_MAX_EIR_LENGTH; i++)
printf("%02x%s%s", data[i], (i + 1) % 8 ? "" : " ",
(i + 1) % 16 ? " " : (i < 239 ? "\n\t\t" : "\n"));
st->byte_tx, st->acl_tx, st->sco_tx, st->cmd_tx, st->err_tx);
if (all && !hci_test_bit(HCI_RAW, &di->flags) &&
- bacmp(&di->bdaddr, BDADDR_ANY)) {
+ (bacmp(&di->bdaddr, BDADDR_ANY) || (di->type >> 4))) {
print_dev_features(di, 0);
print_pkt_type(di);
print_link_policy(di);
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
+#include <signal.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#define FLAGS_LIMITED_MODE_BIT 0x01
#define FLAGS_GENERAL_MODE_BIT 0x02
+#define EIR_FLAGS 0x01 /* flags */
+#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
+#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
+#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
+#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
+#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
+#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
+#define EIR_NAME_SHORT 0x08 /* shortened local name */
+#define EIR_NAME_COMPLETE 0x09 /* complete local name */
+#define EIR_TX_POWER 0x0A /* transmit power level */
+#define EIR_DEVICE_ID 0x10 /* device ID */
+
#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, NULL)) != -1)
+static volatile int signal_received = 0;
+
static void usage(void);
static int dev_info(int s, int dev_id, long arg)
static int read_flags(uint8_t *flags, const uint8_t *data, size_t size)
{
- unsigned int offset;
+ size_t offset;
if (!flags || !data)
return -EINVAL;
offset = 0;
while (offset < size) {
uint8_t len = data[offset];
- uint8_t type = data[offset + 1];
+ uint8_t type;
/* Check if it is the end of the significant part */
if (len == 0)
break;
+ if (len + offset > size)
+ break;
+
+ type = data[offset + 1];
+
if (type == FLAGS_AD_TYPE) {
*flags = data[offset + 2];
return 0;
return 0;
}
+static void sigint_handler(int sig)
+{
+ signal_received = sig;
+}
+
+static void eir_parse_name(uint8_t *eir, size_t eir_len,
+ char *buf, size_t buf_len)
+{
+ size_t offset;
+
+ offset = 0;
+ while (offset < eir_len) {
+ uint8_t field_len = eir[0];
+ size_t name_len;
+
+ /* Check for the end of EIR */
+ if (field_len == 0)
+ break;
+
+ if (offset + field_len > eir_len)
+ goto failed;
+
+ switch (eir[1]) {
+ case EIR_NAME_SHORT:
+ case EIR_NAME_COMPLETE:
+ name_len = field_len - 1;
+ if (name_len > buf_len)
+ goto failed;
+
+ memcpy(buf, &eir[2], name_len);
+ return;
+ }
+
+ offset += field_len + 1;
+ eir += field_len + 1;
+ }
+
+failed:
+ snprintf(buf, buf_len, "(unknown)");
+}
+
static int print_advertising_devices(int dd, uint8_t filter_type)
{
unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
struct hci_filter nf, of;
+ struct sigaction sa;
socklen_t olen;
- hci_event_hdr *hdr;
- int num, len;
+ int len;
olen = sizeof(of);
if (getsockopt(dd, SOL_HCI, HCI_FILTER, &of, &olen) < 0) {
return -1;
}
- /* Wait for 10 report events */
- num = 10;
- while (num--) {
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = sigint_handler;
+ sigaction(SIGINT, &sa, NULL);
+
+ while (1) {
evt_le_meta_event *meta;
le_advertising_info *info;
char addr[18];
while ((len = read(dd, buf, sizeof(buf))) < 0) {
+ if (errno == EINTR && signal_received == SIGINT) {
+ len = 0;
+ goto done;
+ }
+
if (errno == EAGAIN || errno == EINTR)
continue;
goto done;
}
- hdr = (void *) (buf + 1);
ptr = buf + (1 + HCI_EVENT_HDR_SIZE);
len -= (1 + HCI_EVENT_HDR_SIZE);
/* Ignoring multiple reports */
info = (le_advertising_info *) (meta->data + 1);
if (check_report_filter(filter_type, info)) {
+ char name[30];
+
+ memset(name, 0, sizeof(name));
+
ba2str(&info->bdaddr, addr);
- printf("%s\n", addr);
+ eir_parse_name(info->data, info->length,
+ name, sizeof(name) - 1);
+
+ printf("%s %s\n", addr, name);
}
}
{ "privacy", 0, 0, 'p' },
{ "passive", 0, 0, 'P' },
{ "discovery", 1, 0, 'd' },
+ { "duplicates", 0, 0, 'D' },
{ 0, 0, 0, 0 }
};
"\tlescan [--privacy] enable privacy\n"
"\tlescan [--passive] set scan type passive (default active)\n"
"\tlescan [--discovery=g|l] enable general or limited discovery"
- "procedure\n";
+ "procedure\n"
+ "\tlescan [--duplicates] don't filter duplicates\n";
static void cmd_lescan(int dev_id, int argc, char **argv)
{
uint8_t filter_type = 0;
uint16_t interval = htobs(0x0010);
uint16_t window = htobs(0x0010);
+ uint8_t filter_dup = 1;
for_each_opt(opt, lescan_options, NULL) {
switch (opt) {
interval = htobs(0x0012);
window = htobs(0x0012);
break;
+ case 'D':
+ filter_dup = 0x00;
+ break;
default:
printf("%s", lescan_help);
return;
exit(1);
}
- err = hci_le_set_scan_enable(dd, 0x01, 0x00, 1000);
+ err = hci_le_set_scan_enable(dd, 0x01, filter_dup, 1000);
if (err < 0) {
perror("Enable scan failed");
exit(1);
exit(1);
}
- err = hci_le_set_scan_enable(dd, 0x00, 0x00, 1000);
+ err = hci_le_set_scan_enable(dd, 0x00, filter_dup, 1000);
if (err < 0) {
perror("Disable scan failed");
exit(1);
hci_close_dev(dd);
if (err < 0) {
- err = errno;
+ err = -errno;
fprintf(stderr, "Can't add to white list: %s(%d)\n",
- strerror(err), err);
+ strerror(-err), -err);
exit(1);
}
}
hci_close_dev(dd);
if (err < 0) {
- err = errno;
+ err = -errno;
fprintf(stderr, "Can't read white list size: %s(%d)\n",
- strerror(err), err);
+ strerror(-err), -err);
exit(1);
}
hci_close_dev(dd);
if (err < 0) {
- err = errno;
+ err = -errno;
fprintf(stderr, "Can't clear white list: %s(%d)\n",
- strerror(err), err);
+ strerror(-err), -err);
exit(1);
}
}
if (hci_le_conn_update(dd, htobs(handle), htobs(min), htobs(max),
htobs(latency), htobs(timeout), 5000) < 0) {
- int err = errno;
+ int err = -errno;
fprintf(stderr, "Could not change connection params: %s(%d)\n",
- strerror(err), err);
+ strerror(-err), -err);
}
hci_close_dev(dd);
mode and back.
.SH OPTIONS
.TP
-.BI -h
-Gives a list of possible options.
-.TP
-.BI -q
-Don't display any messages.
-.TP
-.BI -r [hid,hci]
+.B --mode= [hid, hci]
Sets the mode to switch the device into
.TP
-.BI -v
-Specifies the 4 digit vendor ID assigned to the device being switched
+.B --method= [csr, logitech-hid, dell]
+Which vendor method to use for switching the device.
.TP
-.BI -p
-Specifies the 4 digit product ID assigned to the device being switched
+.B --devpath=
+Specifies the device path in /sys
+.TP
+.B --help
+Gives a list of possible options.
.TP
-.BI -m [csr, logitech, dell]
-Which vendor method to use for switching the device.
.SH AUTHOR
Written by Marcel Holtmann <marcel@holtmann.org>.
.br
/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
+ * hid2hci : switch the radio on devices that support
+ * it from HID to HCI and back
*
* Copyright (C) 2003-2010 Marcel Holtmann <marcel@holtmann.org>
- *
+ * Copyright (C) 2008-2009 Mario Limonciello <mario_limonciello@dell.com>
+ * Copyright (C) 2009-2011 Kay Sievers <kay.sievers@vrfy.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <string.h>
#include <getopt.h>
#include <sys/ioctl.h>
-
+#include <linux/types.h>
+#include <linux/hiddev.h>
#include <usb.h>
-#ifdef NEED_USB_GET_BUSSES
-static inline struct usb_bus *usb_get_busses(void)
-{
- return usb_busses;
-}
-#endif
-
-#ifndef USB_DIR_OUT
-#define USB_DIR_OUT 0x00
-#endif
-
-static char devpath[PATH_MAX + 1] = "/dev";
-
-struct hiddev_devinfo {
- unsigned int bustype;
- unsigned int busnum;
- unsigned int devnum;
- unsigned int ifnum;
- short vendor;
- short product;
- short version;
- unsigned num_applications;
-};
-
-struct hiddev_report_info {
- unsigned report_type;
- unsigned report_id;
- unsigned num_fields;
-};
-
-typedef __signed__ int __s32;
-
-struct hiddev_usage_ref {
- unsigned report_type;
- unsigned report_id;
- unsigned field_index;
- unsigned usage_index;
- unsigned usage_code;
- __s32 value;
-};
-
-#define HIDIOCGDEVINFO _IOR('H', 0x03, struct hiddev_devinfo)
-#define HIDIOCINITREPORT _IO('H', 0x05)
-#define HIDIOCSREPORT _IOW('H', 0x08, struct hiddev_report_info)
-#define HIDIOCSUSAGE _IOW('H', 0x0C, struct hiddev_usage_ref)
-
-#define HID_REPORT_TYPE_OUTPUT 2
-
-#define HCI 0
-#define HID 1
+#include "libudev.h"
-struct device_info {
- struct usb_device *dev;
- int mode;
- uint16_t vendor;
- uint16_t product;
+enum mode {
+ HCI = 0,
+ HID = 1,
};
-static int switch_csr(struct device_info *devinfo)
+static int usb_switch_csr(struct usb_dev_handle *dev, enum mode mode)
{
- struct usb_dev_handle *udev;
int err;
- udev = usb_open(devinfo->dev);
- if (!udev)
- return -errno;
-
- err = usb_control_msg(udev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 0, devinfo->mode, 0, NULL, 0, 10000);
-
+ err = usb_control_msg(dev,
+ USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, mode, 0, NULL, 0, 10000);
if (err == 0) {
err = -1;
errno = EALREADY;
- } else {
- if (errno == ETIMEDOUT)
- err = 0;
- }
-
- usb_close(udev);
+ } else if (errno == ETIMEDOUT)
+ err = 0;
return err;
}
-static int send_report(int fd, const char *buf, size_t size)
+static int hid_logitech_send_report(int fd, const char *buf, size_t size)
{
struct hiddev_report_info rinfo;
struct hiddev_usage_ref uref;
return err;
}
-static int switch_logitech(struct device_info *devinfo)
+static int hid_switch_logitech(const char *filename)
{
- char devname[PATH_MAX + 1];
- int i, fd, err = -1;
-
- for (i = 0; i < 16; i++) {
- struct hiddev_devinfo dinfo;
- char rep1[] = { 0xff, 0x80, 0x80, 0x01, 0x00, 0x00 };
- char rep2[] = { 0xff, 0x80, 0x00, 0x00, 0x30, 0x00 };
- char rep3[] = { 0xff, 0x81, 0x80, 0x00, 0x00, 0x00 };
-
- sprintf(devname, "%s/hiddev%d", devpath, i);
- fd = open(devname, O_RDWR);
- if (fd < 0) {
- sprintf(devname, "%s/usb/hiddev%d", devpath, i);
- fd = open(devname, O_RDWR);
- if (fd < 0) {
- sprintf(devname, "%s/usb/hid/hiddev%d", devpath, i);
- fd = open(devname, O_RDWR);
- if (fd < 0)
- continue;
- }
- }
-
- memset(&dinfo, 0, sizeof(dinfo));
- err = ioctl(fd, HIDIOCGDEVINFO, &dinfo);
- if (err < 0 || (int) dinfo.busnum != atoi(devinfo->dev->bus->dirname) ||
- (int) dinfo.devnum != atoi(devinfo->dev->filename)) {
- close(fd);
- continue;
- }
-
- err = ioctl(fd, HIDIOCINITREPORT, 0);
- if (err < 0) {
- close(fd);
- break;
- }
-
- err = send_report(fd, rep1, sizeof(rep1));
- if (err < 0) {
- close(fd);
- break;
- }
-
- err = send_report(fd, rep2, sizeof(rep2));
- if (err < 0) {
- close(fd);
- break;
- }
-
- err = send_report(fd, rep3, sizeof(rep3));
- close(fd);
- break;
- }
-
+ char rep1[] = { 0xff, 0x80, 0x80, 0x01, 0x00, 0x00 };
+ char rep2[] = { 0xff, 0x80, 0x00, 0x00, 0x30, 0x00 };
+ char rep3[] = { 0xff, 0x81, 0x80, 0x00, 0x00, 0x00 };
+ int fd;
+ int err = -1;
+
+ fd = open(filename, O_RDWR);
+ if (fd < 0)
+ return err;
+
+ err = ioctl(fd, HIDIOCINITREPORT, 0);
+ if (err < 0)
+ goto out;
+
+ err = hid_logitech_send_report(fd, rep1, sizeof(rep1));
+ if (err < 0)
+ goto out;
+
+ err = hid_logitech_send_report(fd, rep2, sizeof(rep2));
+ if (err < 0)
+ goto out;
+
+ err = hid_logitech_send_report(fd, rep3, sizeof(rep3));
+out:
+ close(fd);
return err;
}
-static int switch_dell(struct device_info *devinfo)
+static int usb_switch_dell(struct usb_dev_handle *dev, enum mode mode)
{
char report[] = { 0x7f, 0x00, 0x00, 0x00 };
-
- struct usb_dev_handle *handle;
int err;
- switch (devinfo->mode) {
+ switch (mode) {
case HCI:
report[1] = 0x13;
break;
break;
}
- handle = usb_open(devinfo->dev);
- if (!handle)
- return -EIO;
-
/* Don't need to check return, as might not be in use */
- usb_detach_kernel_driver_np(handle, 0);
+ usb_detach_kernel_driver_np(dev, 0);
- if (usb_claim_interface(handle, 0) < 0) {
- usb_close(handle);
+ if (usb_claim_interface(dev, 0) < 0)
return -EIO;
- }
- err = usb_control_msg(handle,
- USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ err = usb_control_msg(dev,
+ USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
USB_REQ_SET_CONFIGURATION, 0x7f | (0x03 << 8), 0,
- report, sizeof(report), 5000);
+ report, sizeof(report), 5000);
if (err == 0) {
err = -1;
if (errno == ETIMEDOUT)
err = 0;
}
-
- usb_close(handle);
-
return err;
}
-static int find_device(struct device_info* devinfo)
+/*
+ * libusb needs to scan and open all devices, just to to find the
+ * device we already have. This should be fixed in libusb.
+ */
+static struct usb_device *usb_device_open_from_udev(struct udev_device *usb_dev)
{
struct usb_bus *bus;
- struct usb_device *dev;
+ const char *str;
+ int busnum;
+ int devnum;
+ str = udev_device_get_sysattr_value(usb_dev, "busnum");
+ if (str == NULL)
+ return NULL;
+ busnum = strtol(str, NULL, 0);
+
+ str = udev_device_get_sysattr_value(usb_dev, "devnum");
+ if (str == NULL)
+ return NULL;
+ devnum = strtol(str, NULL, 0);
+
+ usb_init();
usb_find_busses();
usb_find_devices();
- for (bus = usb_get_busses(); bus; bus = bus->next)
+ for (bus = usb_get_busses(); bus; bus = bus->next) {
+ struct usb_device *dev;
+
+ if (strtol(bus->dirname, NULL, 10) != busnum)
+ continue;
+
for (dev = bus->devices; dev; dev = dev->next) {
- if (dev->descriptor.idVendor == devinfo->vendor &&
- dev->descriptor.idProduct == devinfo->product) {
- devinfo->dev=dev;
- return 1;
- }
+ if (dev->devnum == devnum)
+ return dev;
}
- return 0;
+ }
+
+ return NULL;
}
-static void usage(char* error)
+static struct usb_dev_handle *find_device(struct udev_device *udev_dev)
+{
+ struct usb_device *dev;
+
+ dev = usb_device_open_from_udev(udev_dev);
+ if (dev == NULL)
+ return NULL;
+ return usb_open(dev);
+}
+
+static void usage(const char *error)
{
if (error)
fprintf(stderr,"\n%s\n", error);
else
printf("hid2hci - Bluetooth HID to HCI mode switching utility\n\n");
- printf("Usage:\n"
- "\thid2hci [options]\n"
- "\n");
-
- printf("Options:\n"
- "\t-h, --help Display help\n"
- "\t-q, --quiet Don't display any messages\n"
- "\t-r, --mode= Mode to switch to [hid, hci]\n"
- "\t-v, --vendor= Vendor ID to act upon\n"
- "\t-p, --product= Product ID to act upon\n"
- "\t-m, --method= Method to use to switch [csr, logitech, dell]\n"
- "\n");
- if (error)
- exit(1);
+ printf("Usage: hid2hci [options]\n"
+ " --mode= mode to switch to [hid|hci] (default hci)\n"
+ " --devpath= sys device path\n"
+ " --method= method to use to switch [csr|logitech-hid|dell]\n"
+ " --help\n\n");
}
-static struct option main_options[] = {
- { "help", no_argument, 0, 'h' },
- { "quiet", no_argument, 0, 'q' },
- { "mode", required_argument, 0, 'r' },
- { "vendor", required_argument, 0, 'v' },
- { "product", required_argument, 0, 'p' },
- { "method", required_argument, 0, 'm' },
- { 0, 0, 0, 0 }
-};
-
int main(int argc, char *argv[])
{
- struct device_info dev = { NULL, HCI, 0, 0 };
- int opt, quiet = 0;
- int (*method)(struct device_info *dev) = NULL;
-
- while ((opt = getopt_long(argc, argv, "+r:v:p:m:qh", main_options, NULL)) != -1) {
- switch (opt) {
- case 'r':
- if (optarg && !strcmp(optarg, "hid"))
- dev.mode = HID;
- else if (optarg && !strcmp(optarg, "hci"))
- dev.mode = HCI;
- else
- usage("ERROR: Undefined radio mode\n");
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "mode", required_argument, NULL, 'm' },
+ { "devpath", required_argument, NULL, 'p' },
+ { "method", required_argument, NULL, 'M' },
+ { }
+ };
+ enum method {
+ METHOD_UNDEF,
+ METHOD_CSR,
+ METHOD_LOGITECH_HID,
+ METHOD_DELL,
+ } method = METHOD_UNDEF;
+ struct udev *udev;
+ struct udev_device *udev_dev = NULL;
+ char syspath[PATH_MAX];
+ int (*usb_switch)(struct usb_dev_handle *dev, enum mode mode) = NULL;
+ enum mode mode = HCI;
+ const char *devpath = NULL;
+ int err = -1;
+ int rc = 1;
+
+ for (;;) {
+ int option;
+
+ option = getopt_long(argc, argv, "m:p:M:h", options, NULL);
+ if (option == -1)
break;
- case 'v':
- sscanf(optarg, "%4hx", &dev.vendor);
+
+ switch (option) {
+ case 'm':
+ if (!strcmp(optarg, "hid")) {
+ mode = HID;
+ } else if (!strcmp(optarg, "hci")) {
+ mode = HCI;
+ } else {
+ usage("error: undefined radio mode\n");
+ exit(1);
+ }
break;
case 'p':
- sscanf(optarg, "%4hx", &dev.product);
- break;
- case 'm':
- if (optarg && !strcmp(optarg, "csr"))
- method = switch_csr;
- else if (optarg && !strcmp(optarg, "logitech"))
- method = switch_logitech;
- else if (optarg && !strcmp(optarg, "dell"))
- method = switch_dell;
- else
- usage("ERROR: Undefined switching method\n");
+ devpath = optarg;
break;
- case 'q':
- quiet = 1;
+ case 'M':
+ if (!strcmp(optarg, "csr")) {
+ method = METHOD_CSR;
+ usb_switch = usb_switch_csr;
+ } else if (!strcmp(optarg, "logitech-hid")) {
+ method = METHOD_LOGITECH_HID;
+ } else if (!strcmp(optarg, "dell")) {
+ method = METHOD_DELL;
+ usb_switch = usb_switch_dell;
+ } else {
+ usage("error: undefined switching method\n");
+ exit(1);
+ }
break;
case 'h':
usage(NULL);
- default:
- exit(0);
}
}
- if (!quiet && (!dev.vendor || !dev.product || !method))
- usage("ERROR: Vendor ID, Product ID, and Switching Method must all be defined.\n");
-
- argc -= optind;
- argv += optind;
- optind = 0;
+ if (!devpath || method == METHOD_UNDEF) {
+ usage("error: --devpath= and --method= must be defined\n");
+ exit(1);
+ }
- usb_init();
+ udev = udev_new();
+ if (udev == NULL)
+ goto exit;
- if (!find_device(&dev)) {
- if (!quiet)
- fprintf(stderr, "Device %04x:%04x not found on USB bus.\n",
- dev.vendor, dev.product);
- exit(1);
+ snprintf(syspath, sizeof(syspath), "%s/%s", udev_get_sys_path(udev), devpath);
+ udev_dev = udev_device_new_from_syspath(udev, syspath);
+ if (udev_dev == NULL) {
+ fprintf(stderr, "error: could not find '%s'\n", devpath);
+ goto exit;
}
- if (!quiet)
- printf("Attempting to switch device %04x:%04x to %s mode ",
- dev.vendor, dev.product, dev.mode ? "HID" : "HCI");
- fflush(stdout);
+ switch (method) {
+ case METHOD_CSR:
+ case METHOD_DELL: {
+ struct udev_device *dev;
+ struct usb_dev_handle *handle;
+ const char *type;
+
+ /* get the parent usb_device if needed */
+ dev = udev_dev;
+ type = udev_device_get_devtype(dev);
+ if (type == NULL || strcmp(type, "usb_device") != 0) {
+ dev = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device");
+ if (dev == NULL) {
+ fprintf(stderr, "error: could not find usb_device for '%s'\n", devpath);
+ goto exit;
+ }
+ }
+
+ handle = find_device(dev);
+ if (handle == NULL) {
+ fprintf(stderr, "error: unable to handle '%s'\n",
+ udev_device_get_syspath(dev));
+ goto exit;
+ }
+ err = usb_switch(handle, mode);
+ break;
+ }
+ case METHOD_LOGITECH_HID: {
+ const char *device;
- if (method(&dev) < 0 && !quiet)
- printf("failed (%s)\n", strerror(errno));
- else if (!quiet)
- printf("was successful\n");
+ device = udev_device_get_devnode(udev_dev);
+ if (device == NULL) {
+ fprintf(stderr, "error: could not find hiddev device node\n");
+ goto exit;
+ }
+ err = hid_switch_logitech(device);
+ break;
+ }
+ default:
+ break;
+ }
- return errno;
+ if (err < 0)
+ fprintf(stderr, "error: switching device '%s' failed.\n",
+ udev_device_get_syspath(udev_dev));
+exit:
+ udev_device_unref(udev_dev);
+ udev_unref(udev);
+ return rc;
}
a channel must be specified before cmd. If cmd is given, it will be
executed as soon as a client connects. When the child process
terminates or the client disconnect, the command will terminate.
-Occurences of {} in cmd will be replaced by the name of the device
+Occurrences of {} in cmd will be replaced by the name of the device
used by the connection. This command can be terminated with the key
sequence CTRL-C.
.TP
context.handle = base[i] + n;
err = get_service(&bdaddr, &context, 1);
if (err < 0)
- goto done;
+ return 0;
}
-done:
return 0;
}
\r
activity |= UBCSP_PACKET_RECEIVED;\r
}\r
- }\r
- }\r
-\r
- /* Just return any activity that occured */\r
-\r
- return activity;\r
-}\r
+ }
+ }
+
+ /* Just return any activity that occurred */
+
+ return activity;
+}
\r
/*****************************************************************************/\r
/** **/\r
\r
ubcsp_config.receive_index ++;\r
}\r
- else if (ubcsp_config.receive_index < ubcsp_config.receive_packet->length)\r
- {\r
- /* We are receiving the payload */\r
- /* We might stop comming here if we are receiving a\r
- packet which is longer than the receive_packet->length\r
- given by the host */\r
-\r
+ else if (ubcsp_config.receive_index < ubcsp_config.receive_packet->length)
+ {
+ /* We are receiving the payload */
+ /* We might stop coming here if we are receiving a
+ packet which is longer than the receive_packet->length
+ given by the host */
+
ubcsp_config.receive_packet->payload[ubcsp_config.receive_index] = value;\r
\r
ubcsp_config.receive_index ++;\r
openlog("hcitrace", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
- syslog(LOG_INFO, "HCI trace deamon %s", VERSION);
+ syslog(LOG_INFO, "HCI trace daemon %s", VERSION);
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_NOCLDSTOP;
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Intel Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <check.h>
+
+#include <stdint.h>
+
+#include <glib.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "eir.h"
+
+START_TEST(test_basic)
+{
+ struct eir_data data;
+ unsigned char buf[HCI_MAX_EIR_LENGTH];
+ int err;
+
+ memset(buf, 0, sizeof(buf));
+ memset(&data, 0, sizeof(data));
+
+ err = eir_parse(&data, buf, HCI_MAX_EIR_LENGTH);
+ ck_assert(err == 0);
+ ck_assert(data.services == NULL);
+ ck_assert(data.name == NULL);
+
+ eir_data_free(&data);
+}
+END_TEST
+
+static void add_test(Suite *s, const char *name, TFun func)
+{
+ TCase *t;
+
+ t = tcase_create(name);
+ tcase_add_test(t, func);
+ suite_add_tcase(s, t);
+}
+
+int main(int argc, char *argv[])
+{
+ int fails;
+ SRunner *sr;
+ Suite *s;
+
+ s = suite_create("EIR");
+
+ add_test(s, "basic", test_basic);
+
+ sr = srunner_create(s);
+
+ srunner_run_all(sr, CK_NORMAL);
+
+ fails = srunner_ntests_failed(sr);
+
+ srunner_free(sr);
+
+ if (fails > 0)
+ return -1;
+
+ return 0;
+}