Siarhei Siamashka <siarhei.siamashka@nokia.com>
Nick Pelly <npelly@google.com>
Lennart Poettering <lennart@poettering.net>
-Gustavo F. Padovan <padovan@profusion.mobi>
+Gustavo Padovan <gustavo@padovan.org>
Marc-Andre Lureau <marc-andre.lureau@nokia.com>
Bea Lam <bea.lam@nokia.com>
Zygo Blaxell <zygo.blaxell@xandros.com>
Sheldon Demario <sheldon.demario@openbossa.org>
Lucas De Marchi <lucas.demarchi@profusion.mobi>
Szymon Janc <szymon.janc@tieto.com>
+Syam Sidhardhan <s.syam@samsung.com>
+ver 4.101:
+ Fix issue with missing BlueZ service file.
+ Fix issue with aborting A2DP setup during AVDTP start.
+ Fix issue with handling of multiple A2DP indication.
+ Fix issue with handling AVDTP abort with invalid SEID.
+ Fix issue with rejecting AVDTP abort commands.
+ Add support for handling AVDTP command collision.
+
+ver 4.100:
+ Fix issue with crashing when SCO connection fails.
+ Fix issue with HFP gateway failing on first GSM connection.
+ Fix issue with AVRCP and handling of vendor commands.
+ Fix issue with handling AVRCP subunit info command.
+ Fix issue with missing capability for AVRCP track reached end.
+ Fix issue with AVDTP signaling and GStreamer SBC NULL check.
+ Fix issue with AVDTP Reconfigure Reject message.
+ Fix issue with incorrect EIR length parsing.
+ Fix issue with SDP disconnect for HIDSDPDisable.
+ Fix issue with SDP interoperability with Mac OS X Lion.
+ Fix issue with reverse SDP discovery with some devices.
+ Fix issue with discovering state during power off operation.
+ Add support for AVRCP Volume Changed notifications.
+ Add support for AVRCP Set Absolute Volume handling.
+ Add support for display legacy PIN code agent method.
+ Add support for multiple media transports per endpoint.
+ Add support for discovering device information characteristics.
+ Add support for vendor source for Device ID setting.
+ Add support for immediate alert server.
+ Add support for link loss server.
+
+ Notes:
+ This version requires D-Bus 1.4 or later.
+ This version requires GLib 2.28 or later.
+
+ver 4.99:
+ Fix issue with missing retries for BNEP connection setup.
+ Fix issue with not showing name if first EIR has no details.
+ Fix issue with running SDP discovery for LE devices.
+ Add support for GATT using 128-bit Bluetooth UUIDs.
+ Add support for retrieving key size information.
+ Add support for storing Long Term Keys.
+ Add support for Proximity Reporter API.
+ Add support for KeyboardDisplay IO capability.
+ Add support for version 1.0 of management API.
+ Add support for monitoring interface.
+
ver 4.98:
Fix issue with adapter list upon initialization failure.
Fix issue with missing legacy property for Low Energy.
include_HEADERS =
+AM_CFLAGS = $(WARNING_CFLAGS) $(MISC_CFLAGS)
+AM_LDFLAGS = $(MISC_LDFLAGS)
+
if DATAFILES
dbusdir = $(sysconfdir)/dbus-1/system.d
+dbusservicedir = $(datadir)/dbus-1/system-services
dbus_DATA = src/bluetooth.conf
+dbusservice_DATA = src/org.bluez.service
confdir = $(sysconfdir)/bluetooth
lib_headers = lib/bluetooth.h lib/hci.h lib/hci_lib.h lib/mgmt.h \
lib/sco.h lib/l2cap.h lib/sdp.h lib/sdp_lib.h lib/uuid.h \
- lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h
+ lib/rfcomm.h lib/bnep.h lib/cmtp.h lib/hidp.h lib/a2mp.h
local_headers = $(foreach file,$(lib_headers), lib/bluetooth/$(notdir $(file)))
BUILT_SOURCES = $(local_headers) src/builtin.h
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:5:11
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 16:0:13
lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
noinst_LTLIBRARIES += lib/libbluetooth-private.la
endif
endif
-attrib_sources = attrib/att.h attrib/att.c attrib/gatt.h attrib/gatt.c \
+attrib_sources = attrib/att.h attrib/att-database.h attrib/att.c \
+ attrib/gatt.h attrib/gatt.c \
attrib/gattrib.h attrib/gattrib.c attrib/client.h \
attrib/client.c attrib/gatt-service.h attrib/gatt-service.c
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 GATT_EXAMPLE_PLUGIN
-builtin_modules += gatt_example
-builtin_sources += plugins/gatt-example.c
-endif
-
-if TIMEPLUGIN
-builtin_modules += time
-builtin_sources += time/main.c \
- time/server.h time/server.c
-endif
-
-if ALERTPLUGIN
-builtin_modules += alert
-builtin_sources += alert/main.c \
- alert/server.h alert/server.c
-endif
-
if HEALTHPLUGIN
builtin_modules += health
builtin_sources += health/hdp_main.c health/hdp_types.h \
health/hdp_util.h health/hdp_util.c
endif
-if THERMOMETERPLUGIN
-builtin_modules += thermometer
+if GATTMODULES
+builtin_modules += thermometer alert time gatt_example proximity \
+ deviceinfo
builtin_sources += thermometer/main.c \
thermometer/manager.h thermometer/manager.c \
- thermometer/thermometer.h thermometer/thermometer.c
+ thermometer/thermometer.h thermometer/thermometer.c \
+ alert/main.c alert/server.h alert/server.c \
+ time/main.c time/server.h time/server.c \
+ plugins/gatt-example.c \
+ proximity/main.c proximity/manager.h proximity/manager.c \
+ proximity/monitor.h proximity/monitor.c \
+ proximity/reporter.h proximity/reporter.c \
+ proximity/linkloss.h proximity/linkloss.c \
+ proximity/immalert.h proximity/immalert.c \
+ deviceinfo/main.c \
+ deviceinfo/manager.h deviceinfo/manager.c \
+ deviceinfo/deviceinfo.h deviceinfo/deviceinfo.c
endif
+
builtin_modules += hciops mgmtops
builtin_sources += plugins/hciops.c plugins/mgmtops.c
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_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
+ -no-undefined
plugins_external_dummy_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
endif
src/attrib-server.h src/attrib-server.c \
src/sdp-xml.h src/sdp-xml.c \
src/sdp-client.h src/sdp-client.c \
- src/textfile.h src/textfile.c src/glib-compat.h \
+ src/textfile.h src/textfile.c \
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/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 \
+ -ldl -lrt
+src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
-Wl,--version-script=$(srcdir)/src/bluetooth.ver
src_bluetoothd_DEPENDENCIES = lib/libbluetooth-private.la
conf_DATA += src/main.conf
endif
-EXTRA_DIST += src/genbuiltin src/bluetooth.conf \
+EXTRA_DIST += src/genbuiltin src/bluetooth.conf src/org.bluez.service \
src/main.conf network/network.conf \
input/input.conf serial/serial.conf \
audio/audio.conf audio/telephony-dummy.c \
audio_libasound_module_pcm_bluetooth_la_SOURCES = audio/pcm_bluetooth.c \
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_LDFLAGS = $(AM_LDFLAGS) -module \
+ -avoid-version
audio_libasound_module_pcm_bluetooth_la_LIBADD = sbc/libsbc.la \
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_LDFLAGS = $(AM_LDFLAGS) -module \
+ -avoid-version
audio_libasound_module_ctl_bluetooth_la_LIBADD = \
lib/libbluetooth-private.la @ALSA_LIBS@
-audio_libasound_module_ctl_bluetooth_la_CFLAGS = $(AM_CLAGS) @ALSA_CFLAGS@
+audio_libasound_module_ctl_bluetooth_la_CFLAGS = $(AM_CFLAGS) @ALSA_CFLAGS@
if DATAFILES
alsaconfdir = $(datadir)/alsa
audio/gstsbcutil.h audio/gstsbcutil.c \
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_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version
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) @DBUS_CFLAGS@ @GSTREAMER_CFLAGS@
+ $(AM_CFLAGS) @DBUS_CFLAGS@ @GSTREAMER_CFLAGS@
endif
endif
if DATAFILES
rulesdir = @UDEV_DIR@/rules.d
-udev_files = scripts/bluetooth.rules
+udev_files =
if HID2HCI
udev_files += scripts/bluetooth-hid2hci.rules
CLEANFILES += $(rules_DATA)
-EXTRA_DIST += scripts/bluetooth.rules \
- scripts/bluetooth-hid2hci.rules scripts/bluetooth-serial.rules
+EXTRA_DIST += scripts/bluetooth-hid2hci.rules scripts/bluetooth-serial.rules
EXTRA_DIST += doc/manager-api.txt \
doc/adapter-api.txt doc/device-api.txt \
AM_YFLAGS = -d
-AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @CAPNG_CFLAGS@
+AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_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)/tools \
- -I$(builddir)/tools
+ -I$(builddir)/tools -I$(srcdir)/monitor
if MCAP
INCLUDES += -I$(builddir)/health
tools/hciattach_ti.c \
tools/hciattach_tialt.c \
tools/hciattach_ath3k.c \
- tools/hciattach_qualcomm.c
+ tools/hciattach_qualcomm.c \
+ tools/hciattach_intel.c
tools_hciattach_LDADD = lib/libbluetooth-private.la
tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c \
tools_hcieventmask_LDADD = lib/libbluetooth-private.la
-noinst_PROGRAMS += mgmt/btmgmt
-mgmt_btmgmt_SOURCES = mgmt/main.c
-mgmt_btmgmt_LDADD = lib/libbluetooth-private.la
+noinst_PROGRAMS += mgmt/btmgmt monitor/btmon emulator/btvirt
+
+mgmt_btmgmt_SOURCES = mgmt/main.c src/glib-helper.c
+mgmt_btmgmt_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@
+
+monitor_btmon_SOURCES = monitor/main.c monitor/bt.h \
+ monitor/mainloop.h monitor/mainloop.c \
+ monitor/hcidump.h monitor/hcidump.c \
+ monitor/btsnoop.h monitor/btsnoop.c \
+ monitor/control.h monitor/control.c \
+ monitor/packet.h monitor/packet.c
+monitor_btmon_LDADD = lib/libbluetooth-private.la
+
+emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
+ monitor/mainloop.h monitor/mainloop.c \
+ emulator/server.h emulator/server.c \
+ emulator/vhci.h emulator/vhci.c \
+ emulator/btdev.h emulator/btdev.c
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/utils.c src/log.c
attrib_gatttool_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @READLINE_LIBS@
endif
EXTRA_DIST += tools/rfcomm.conf
-if TRACER
-sbin_PROGRAMS += tracer/hcitrace
-
-tracer_hcitrace_SOURCES = tracer/main.c
-tracer_hcitrace_LDADD = lib/libbluetooth-private.la \
- @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@
-tracer_hcitrace_DEPENDENCIES = lib/libbluetooth-private.la
-endif
-
if BCCMD
sbin_PROGRAMS += tools/bccmd
test_uuidtest_SOURCES = test/uuidtest.c
test_uuidtest_LDADD = lib/libbluetooth-private.la
+test_mpris_player_SOURCES = test/mpris-player.c
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/sap-client test/hsplay test/hsmicro \
+EXTRA_DIST += 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/test-input test/test-sap-server test/test-oob \
+ test/test-attrib test/test-proximity test/test-thermometer \
+ test/test-serial-proxy 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
-
+ test/service-spp.xml test/service-opp.xml test/service-ftp.xml \
+ test/simple-player test/test-nap
if HIDD
bin_PROGRAMS += compat/hidd
Management Interface
====================
-- Device discovery support (both for BR/EDR & LE)
-
- Priority: High
- Complexity: C3
-
-- Blacklist support
-
- Priority: Medium
- Complexity: C1
-
-- mgmt_set_fast_connectable
-
- Priority: Medium
- Complexity: C1
-
- Whitelist support (initially only for LE)
Priority: Medium
])
AC_DEFUN([COMPILER_FLAGS], [
- if (test "${CFLAGS}" = ""); then
- CFLAGS="-Wall -O2"
- fi
+ with_cflags=""
if (test "$USE_MAINTAINER_MODE" = "yes"); then
- CFLAGS="$CFLAGS -Werror -Wextra"
- CFLAGS="$CFLAGS -Wno-unused-parameter"
- CFLAGS="$CFLAGS -Wno-missing-field-initializers"
- CFLAGS="$CFLAGS -Wdeclaration-after-statement"
- CFLAGS="$CFLAGS -Wmissing-declarations"
- CFLAGS="$CFLAGS -Wredundant-decls"
- CFLAGS="$CFLAGS -Wcast-align"
- CFLAGS="$CFLAGS -DG_DISABLE_DEPRECATED"
+ with_cflags="$with_cflags -Wall -Werror -Wextra"
+ with_cflags="$with_cflags -Wno-unused-parameter"
+ with_cflags="$with_cflags -Wno-missing-field-initializers"
+ with_cflags="$with_cflags -Wdeclaration-after-statement"
+ with_cflags="$with_cflags -Wmissing-declarations"
+ with_cflags="$with_cflags -Wredundant-decls"
+ with_cflags="$with_cflags -Wcast-align"
+ with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
fi
+
+ AC_SUBST([WARNING_CFLAGS], $with_cflags)
])
AC_DEFUN([AC_FUNC_PPOLL], [
])
AC_DEFUN([AC_PATH_DBUS], [
- PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.0, dummy=yes,
- AC_MSG_ERROR(D-Bus library is required))
- AC_CHECK_LIB(dbus-1, dbus_watch_get_unix_fd, dummy=yes,
- AC_DEFINE(NEED_DBUS_WATCH_GET_UNIX_FD, 1,
- [Define to 1 if you need the dbus_watch_get_unix_fd() function.]))
- AC_CHECK_LIB(dbus-1, dbus_connection_can_send_type, dummy=yes,
- AC_DEFINE(NEED_DBUS_CONNECTION_CAN_SEND_TYPE, 1,
- [Define to 1 if you need the dbus_connection_can_send_type() function.]
-))
+ PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes,
+ AC_MSG_ERROR(D-Bus >= 1.4 is required))
AC_SUBST(DBUS_CFLAGS)
AC_SUBST(DBUS_LIBS)
])
AC_DEFUN([AC_PATH_GLIB], [
- PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, dummy=yes,
- AC_MSG_ERROR(GLib library version 2.16 or later is required))
- AC_CHECK_LIB(glib-2.0, g_slist_free_full, dummy=yes,
- AC_DEFINE(NEED_G_SLIST_FREE_FULL, 1,
- [Define to 1 if you need g_slist_free_full() function.]))
+ PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes,
+ AC_MSG_ERROR(GLib >= 2.28 is required))
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
])
serial_enable=yes
network_enable=yes
sap_enable=no
- proximity_enable=no
- time_enable=no
- alert_enable=no
service_enable=yes
health_enable=no
pnat_enable=no
- gatt_example_enable=no
- tracer_enable=no
tools_enable=yes
hidd_enable=no
pand_enable=no
sap_driver=dummy
dbusoob_enable=no
wiimote_enable=no
- thermometer_enable=no
+ gatt_enable=no
AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable code optimization]), [
optimization_enable=${enableval}
])
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(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]), [
gstreamer_enable=${enableval}
])
usb_enable=${enableval}
])
- AC_ARG_ENABLE(tracer, AC_HELP_STRING([--enable-tracer], [install Tracing daemon]), [
- tracer_enable=${enableval}
- ])
-
AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools], [install Bluetooth utilities]), [
tools_enable=${enableval}
])
hal_enable=${enableval}
])
- AC_ARG_ENABLE(thermometer, AC_HELP_STRING([--enable-thermometer], [enable thermometer plugin]), [
- thermometer_enable=${enableval}
+ AC_ARG_ENABLE(gatt, AC_HELP_STRING([--enable-gatt], [enable gatt module]), [
+ gatt_enable=${enableval}
])
+ misc_cflags=""
+ misc_ldflags=""
+
if (test "${fortify_enable}" = "yes"); then
- CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2"
+ misc_cflags="$misc_cflags -D_FORTIFY_SOURCE=2"
fi
if (test "${pie_enable}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then
- CFLAGS="$CFLAGS -fPIC"
- LDFLAGS="$LDFLAGS -pie"
+ misc_cflags="$misc_cflags -fPIC"
+ misc_ldflags="$misc_ldflags -pie"
fi
if (test "${debug_enable}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then
- CFLAGS="$CFLAGS -g"
+ misc_cflags="$misc_cflags -g"
fi
if (test "${optimization_enable}" = "no"); then
- CFLAGS="$CFLAGS -O0"
+ misc_cflags="$misc_cflags -O0"
fi
+ AC_SUBST([MISC_CFLAGS], $misc_cflags)
+ AC_SUBST([MISC_LDFLAGS], $misc_ldflags)
+
if (test "${usb_enable}" = "yes" && test "${usb_found}" = "yes"); then
AC_DEFINE(HAVE_LIBUSB, 1, [Define to 1 if you have USB library.])
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(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(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")
+ AM_CONDITIONAL(GATTMODULES, test "${gatt_enable}" = "yes")
])
#include <stdint.h>
#include <glib.h>
+#include <errno.h>
#include "plugin.h"
#include "hcid.h"
static int alert_init(void)
{
- if (!main_opts.attrib_server) {
- DBG("Attribute server is disabled");
- return -1;
+ if (!main_opts.gatt_enabled) {
+ DBG("GATT is disabled");
+ return -ENOTSUP;
}
return alert_server_init();
static void alert_exit(void)
{
- if (!main_opts.attrib_server)
+ if (!main_opts.gatt_enabled)
return;
alert_server_exit();
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments 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
+ *
+ */
+
+/* Requirements for read/write operations */
+enum {
+ ATT_NONE, /* No restrictions */
+ ATT_AUTHENTICATION, /* Authentication required */
+ ATT_AUTHORIZATION, /* Authorization required */
+ ATT_NOT_PERMITTED, /* Operation not permitted */
+};
+
+struct attribute {
+ uint16_t handle;
+ bt_uuid_t uuid;
+ int read_reqs;
+ int write_reqs;
+ uint8_t (*read_cb)(struct attribute *a, struct btd_device *device,
+ gpointer user_data);
+ uint8_t (*write_cb)(struct attribute *a, struct btd_device *device,
+ gpointer user_data);
+ gpointer cb_user_data;
+ int len;
+ uint8_t *data;
+};
return "Insufficient Resources to complete the request";
case ATT_ECODE_IO:
return "Internal application error: I/O";
+ case ATT_ECODE_TIMEOUT:
+ return "A timeout occured";
+ case ATT_ECODE_ABORTED:
+ return "The operation was aborted";
default:
return "Unexpected error code";
}
*
*/
-/* GATT Profile Attribute types */
-#define GATT_PRIM_SVC_UUID 0x2800
-#define GATT_SND_SVC_UUID 0x2801
-#define GATT_INCLUDE_UUID 0x2802
-#define GATT_CHARAC_UUID 0x2803
-
-/* GATT Characteristic Types */
-#define GATT_CHARAC_DEVICE_NAME 0x2A00
-#define GATT_CHARAC_APPEARANCE 0x2A01
-#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG 0x2A02
-#define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03
-#define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04
-#define GATT_CHARAC_SERVICE_CHANGED 0x2A05
-
-/* GATT Characteristic Descriptors */
-#define GATT_CHARAC_EXT_PROPER_UUID 0x2900
-#define GATT_CHARAC_USER_DESC_UUID 0x2901
-#define GATT_CLIENT_CHARAC_CFG_UUID 0x2902
-#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_OP_MTU_REQ 0x02
#define ATT_ECODE_UNSUPP_GRP_TYPE 0x10
#define ATT_ECODE_INSUFF_RESOURCES 0x11
/* Application error */
-#define ATT_ECODE_IO 0xFF
+#define ATT_ECODE_IO 0x80
+#define ATT_ECODE_TIMEOUT 0x81
+#define ATT_ECODE_ABORTED 0x82
/* Characteristic Property bit field */
#define ATT_CHAR_PROPER_BROADCAST 0x01
#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 */
- ATT_AUTHENTICATION, /* Authentication required */
- ATT_AUTHORIZATION, /* Authorization required */
- ATT_NOT_PERMITTED, /* Operation not permitted */
-};
-
-struct attribute {
- uint16_t handle;
- bt_uuid_t uuid;
- int read_reqs;
- int write_reqs;
- uint8_t (*read_cb)(struct attribute *a, gpointer user_data);
- uint8_t (*write_cb)(struct attribute *a, gpointer user_data);
- gpointer cb_user_data;
- int len;
- uint8_t *data;
-};
-
struct att_data_list {
uint16_t num;
uint16_t len;
uint16_t end;
};
-struct att_primary {
- char uuid[MAX_LEN_UUID_STR + 1];
- uint16_t start;
- uint16_t end;
-};
-
-struct att_char {
- char uuid[MAX_LEN_UUID_STR + 1];
- uint16_t handle;
- uint8_t properties;
- uint16_t value_handle;
-};
-
/* These functions do byte conversion */
static inline uint8_t att_get_u8(const void *ptr)
{
#include <bluetooth/bluetooth.h>
#include <bluetooth/uuid.h>
-#include "glib-compat.h"
#include "adapter.h"
#include "device.h"
#include "log.h"
struct query {
DBusMessage *msg;
- guint attioid;
GSList *list;
};
struct gatt_service {
struct btd_device *dev;
- struct att_primary *prim;
+ struct gatt_primary *prim;
DBusConnection *conn;
GAttrib *attrib;
guint attioid;
g_free(gatt);
}
-static void gatt_get_address(struct gatt_service *gatt,
- bdaddr_t *sba, bdaddr_t *dba)
+static void remove_attio(struct gatt_service *gatt)
+{
+ if (gatt->offline_chars || gatt->watchers || gatt->query)
+ return;
+
+ if (gatt->attioid) {
+ btd_device_remove_attio_callback(gatt->dev, gatt->attioid);
+ gatt->attioid = 0;
+ }
+
+ if (gatt->attrib) {
+ g_attrib_unref(gatt->attrib);
+ gatt->attrib = NULL;
+ }
+}
+
+static void gatt_get_address(struct gatt_service *gatt, bdaddr_t *sba,
+ bdaddr_t *dba, uint8_t *bdaddr_type)
{
struct btd_device *device = gatt->dev;
struct btd_adapter *adapter;
adapter = device_get_adapter(device);
adapter_get_address(adapter, sba);
- device_get_address(device, dba, NULL);
+ device_get_address(device, dba, bdaddr_type);
}
static int characteristic_handle_cmp(gconstpointer a, gconstpointer b)
DBG("%s watcher %s exited", gatt->path, watcher->name);
gatt->watchers = g_slist_remove(gatt->watchers, watcher);
+ g_dbus_remove_watch(gatt->conn, watcher->id);
+ remove_attio(gatt);
}
static int characteristic_set_value(struct characteristic *chr,
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;
+ remove_attio(gatt);
}
static void offline_char_write(gpointer data, gpointer user_data)
offline_char_written, chr);
}
+static void char_discovered_cb(GSList *characteristics, guint8 status,
+ gpointer user_data);
+
static void attio_connected(GAttrib *attrib, gpointer user_data)
{
struct gatt_service *gatt = user_data;
events_handler, gatt, NULL);
g_slist_foreach(gatt->offline_chars, offline_char_write, attrib);
+
+ if (gatt->query) {
+ struct gatt_primary *prim = gatt->prim;
+ struct query_data *qchr;
+
+ qchr = g_slist_nth_data(gatt->query->list, 0);
+ gatt_discover_char(gatt->attrib, prim->range.start,
+ prim->range.end, NULL,
+ char_discovered_cb, qchr);
+ }
}
static void attio_disconnected(gpointer user_data)
{
struct gatt_service *gatt = user_data;
+ if (gatt->query && gatt->query->msg) {
+ DBusMessage *reply;
+
+ reply = btd_error_failed(gatt->query->msg,
+ "ATT IO channel was disconnected");
+ g_dbus_send_message(gatt->conn, reply);
+ dbus_message_unref(gatt->query->msg);
+ }
+
+ if (gatt->query) {
+ g_slist_free_full(gatt->query->list, g_free);
+ gatt->query = NULL;
+ }
+
if (gatt->attrib) {
g_attrib_unref(gatt->attrib);
gatt->attrib = NULL;
return btd_error_not_authorized(msg);
watcher = l->data;
- g_dbus_remove_watch(conn, watcher->id);
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;
- }
+ g_dbus_remove_watch(conn, watcher->id);
+ remove_attio(gatt);
return dbus_message_new_method_return(msg);
}
return btd_error_invalid_args(msg);
}
-static GDBusMethodTable char_methods[] = {
- { "GetProperties", "", "a{sv}", get_properties },
- { "SetProperty", "sv", "", set_property,
- G_DBUS_METHOD_FLAG_ASYNC},
+static const GDBusMethodTable char_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+ set_property) },
{ }
};
}
static void store_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
- uint16_t start, GSList *chars)
+ uint8_t bdaddr_type, uint16_t start,
+ GSList *chars)
{
char *characteristics;
characteristics = characteristic_list_to_string(chars);
- write_device_characteristics(sba, dba, start, characteristics);
+ write_device_characteristics(sba, dba, bdaddr_type, start,
+ characteristics);
g_free(characteristics);
}
{
GSList *chrs_list;
bdaddr_t sba, dba;
+ uint8_t bdaddr_type;
char *str;
- gatt_get_address(gatt, &sba, &dba);
+ gatt_get_address(gatt, &sba, &dba, &bdaddr_type);
- str = read_device_characteristics(&sba, &dba, start);
+ str = read_device_characteristics(&sba, &dba, bdaddr_type, start);
if (str == NULL)
return NULL;
static void store_attribute(struct gatt_service *gatt, uint16_t handle,
uint16_t type, uint8_t *value, gsize len)
{
+ struct btd_device *device = gatt->dev;
bdaddr_t sba, dba;
+ uint8_t bdaddr_type;
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]);
- gatt_get_address(gatt, &sba, &dba);
+ gatt_get_address(gatt, &sba, &dba, NULL);
- write_device_attribute(&sba, &dba, handle, str);
+ bdaddr_type = device_get_addr_type(device);
+
+ write_device_attribute(&sba, &dba, bdaddr_type, handle, str);
g_free(str);
}
if (query->list != NULL)
return;
- btd_device_remove_attio_callback(gatt->dev, query->attioid);
g_free(query);
-
gatt->query = NULL;
+
+ remove_attio(gatt);
}
static void update_char_desc(guint8 status, const guint8 *pdu, guint16 len,
(void *) chr->desc, len);
} else if (status == ATT_ECODE_INSUFF_ENC) {
GIOChannel *io = g_attrib_get_channel(gatt->attrib);
+ BtIOSecLevel level = BT_IO_SEC_HIGH;
+
+ bt_io_get(io, BT_IO_L2CAP, NULL,
+ BT_IO_OPT_SEC_LEVEL, &level,
+ BT_IO_OPT_INVALID);
+
+ if (level < BT_IO_SEC_HIGH)
+ level++;
if (bt_io_set(io, BT_IO_L2CAP, NULL,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
+ BT_IO_OPT_SEC_LEVEL, level,
BT_IO_OPT_INVALID)) {
gatt_read_char(gatt->attrib, current->handle, 0,
update_char_desc, current);
gatt_read_char(gatt->attrib, chr->handle, 0, update_char_value, qvalue);
}
+static DBusMessage *create_discover_char_reply(DBusMessage *msg, GSList *chars)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, array_iter;
+ GSList *l;
+
+ reply = dbus_message_new_method_return(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 = chars; l; l = l->next) {
+ struct characteristic *chr = l->data;
+
+ dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_OBJECT_PATH, &chr->path);
+ }
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ return reply;
+}
+
static void char_discovered_cb(GSList *characteristics, guint8 status,
gpointer user_data)
{
DBusMessage *reply;
- DBusMessageIter iter, array_iter;
struct query_data *current = user_data;
struct gatt_service *gatt = current->gatt;
- struct att_primary *prim = gatt->prim;
+ struct gatt_primary *prim = gatt->prim;
uint16_t *previous_end = NULL;
GSList *l;
bdaddr_t sba, dba;
+ uint8_t bdaddr_type;
if (status != 0) {
const char *str = att_ecode2str(status);
}
for (l = characteristics; l; l = l->next) {
- struct att_char *current_chr = l->data;
+ struct gatt_char *current_chr = l->data;
struct characteristic *chr;
guint handle = current_chr->value_handle;
GSList *lchr;
previous_end = &chr->end;
gatt->chars = g_slist_append(gatt->chars, chr);
+ register_characteristic(chr, gatt->path);
}
if (previous_end)
- *previous_end = prim->end;
-
- gatt_get_address(gatt, &sba, &dba);
- store_characteristics(&sba, &dba, prim->start, gatt->chars);
-
- g_slist_foreach(gatt->chars, register_characteristic, gatt->path);
-
- 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 = gatt->chars; l; l = l->next) {
- struct characteristic *chr = l->data;
-
- dbus_message_iter_append_basic(&array_iter,
- DBUS_TYPE_OBJECT_PATH, &chr->path);
- }
+ *previous_end = prim->range.end;
- dbus_message_iter_close_container(&iter, &array_iter);
+ gatt_get_address(gatt, &sba, &dba, &bdaddr_type);
+ store_characteristics(&sba, &dba, bdaddr_type, prim->range.start,
+ gatt->chars);
g_slist_foreach(gatt->chars, update_all_chars, gatt);
+ reply = create_discover_char_reply(gatt->query->msg, gatt->chars);
+
fail:
+ dbus_message_unref(gatt->query->msg);
+ gatt->query->msg = NULL;
+
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)
{
qchr->gatt = gatt;
query->msg = dbus_message_ref(msg);
- query->attioid = btd_device_add_attio_callback(gatt->dev,
- send_discover,
- cancel_discover,
- qchr);
+
+ if (gatt->attioid == 0) {
+ gatt->attioid = btd_device_add_attio_callback(gatt->dev,
+ attio_connected,
+ attio_disconnected,
+ gatt);
+ } else if (gatt->attrib) {
+ struct gatt_primary *prim = gatt->prim;
+ gatt_discover_char(gatt->attrib, prim->range.start,
+ prim->range.end, NULL,
+ char_discovered_cb, qchr);
+ }
gatt->query = query;
return reply;
}
-static GDBusMethodTable prim_methods[] = {
- { "DiscoverCharacteristics", "", "ao", discover_char,
- G_DBUS_METHOD_FLAG_ASYNC },
- { "RegisterCharacteristicsWatcher", "o", "",
- register_watcher },
- { "UnregisterCharacteristicsWatcher", "o", "",
- unregister_watcher },
- { "GetProperties", "", "a{sv}",prim_get_properties },
+static const GDBusMethodTable prim_methods[] = {
+ { GDBUS_ASYNC_METHOD("DiscoverCharacteristics",
+ NULL, GDBUS_ARGS({ "characteristics", "ao" }),
+ discover_char) },
+ { GDBUS_METHOD("RegisterCharacteristicsWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ register_watcher) },
+ { GDBUS_METHOD("UnregisterCharacteristicsWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ unregister_watcher) },
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ prim_get_properties) },
{ }
};
static struct gatt_service *primary_register(DBusConnection *conn,
struct btd_device *device,
- struct att_primary *prim,
+ struct gatt_primary *prim,
int psm)
{
struct gatt_service *gatt;
gatt->psm = psm;
gatt->conn = dbus_connection_ref(conn);
gatt->path = g_strdup_printf("%s/service%04x", device_path,
- prim->start);
+ prim->range.start);
g_dbus_register_interface(gatt->conn, gatt->path,
CHAR_INTERFACE, prim_methods,
NULL, NULL, gatt, NULL);
- gatt->chars = load_characteristics(gatt, prim->start);
+ gatt->chars = load_characteristics(gatt, prim->range.start);
g_slist_foreach(gatt->chars, register_characteristic, gatt->path);
return gatt;
GSList *l, *services;
for (l = primaries, services = NULL; l; l = l->next) {
- struct att_primary *prim = l->data;
+ struct gatt_primary *prim = l->data;
struct gatt_service *gatt;
gatt = primary_register(connection, device, prim, psm);
{
GSList *l;
- if (gatt->attioid)
- btd_device_remove_attio_callback(gatt->dev, gatt->attioid);
-
for (l = gatt->chars; l; l = l->next) {
struct characteristic *chr = l->data;
g_dbus_unregister_interface(gatt->conn, chr->path,
}
g_dbus_unregister_interface(gatt->conn, gatt->path, CHAR_INTERFACE);
+
+ remove_attio(gatt);
}
static int path_cmp(gconstpointer data, gconstpointer user_data)
#include <bluetooth/sdp.h>
#include <adapter.h>
-#include "att.h"
#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
#include "attrib-server.h"
#include "gatt-service.h"
#include "log.h"
-#include "glib-compat.h"
struct gatt_info {
bt_uuid_t uuid;
return l;
}
+static struct attribute *add_service_declaration(struct btd_adapter *adapter,
+ uint16_t handle, uint16_t svc, bt_uuid_t *uuid)
+{
+ bt_uuid_t bt_uuid;
+ uint8_t atval[16];
+ int len;
+
+ if (uuid->type == BT_UUID16) {
+ att_put_u16(uuid->value.u16, &atval[0]);
+ len = 2;
+ } else if (uuid->type == BT_UUID128) {
+ att_put_u128(uuid->value.u128, &atval[0]);
+ len = 16;
+ } else
+ return NULL;
+
+ bt_uuid16_create(&bt_uuid, svc);
+
+ return attrib_db_add(adapter, handle, &bt_uuid, ATT_NONE,
+ ATT_NOT_PERMITTED, atval, len);
+}
+
static int att_read_reqs(int authorization, int authentication, uint8_t props)
{
if (authorization == GATT_CHR_VALUE_READ ||
}
gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
- uint16_t svc_uuid, gatt_option opt1, ...)
+ bt_uuid_t *svc_uuid, gatt_option opt1, ...)
{
+ char uuidstr[MAX_LEN_UUID_STR];
uint16_t start_handle, h;
unsigned int size;
- bt_uuid_t bt_uuid;
- uint8_t atval[2];
va_list args;
GSList *chrs, *l;
+ bt_uuid_to_string(svc_uuid, uuidstr, MAX_LEN_UUID_STR);
+
+ if (svc_uuid->type != BT_UUID16 && svc_uuid->type != BT_UUID128) {
+ error("Invalid service uuid: %s", uuidstr);
+ return FALSE;
+ }
+
va_start(args, opt1);
chrs = parse_opts(opt1, args);
+ va_end(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);
+
+ start_handle = attrib_db_find_avail(adapter, svc_uuid, size);
if (start_handle == 0) {
error("Not enough free handles to register service");
- g_slist_free_full(chrs, free_gatt_info);
- return FALSE;
+ goto fail;
}
- DBG("New service: handle 0x%04x, UUID 0x%04x, %d attributes",
- start_handle, svc_uuid, size);
+ DBG("New service: handle 0x%04x, UUID %s, %d attributes",
+ start_handle, uuidstr, 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;
- }
+ if (add_service_declaration(adapter, h++, uuid, svc_uuid) == NULL)
+ goto fail;
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;
+ goto fail;
}
}
g_slist_free_full(chrs, free_gatt_info);
return TRUE;
+
+fail:
+ g_slist_free_full(chrs, free_gatt_info);
+ return FALSE;
}
} attrib_event_t;
gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
- uint16_t svc_uuid, gatt_option opt1, ...);
+ bt_uuid_t *svc_uuid, gatt_option opt1, ...);
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
-#include "glib-compat.h"
-
#include "att.h"
#include "gattrib.h"
#include "gatt.h"
for (i = 0, end = 0; i < list->num; i++) {
const uint8_t *data = list->data[i];
- struct att_primary *primary;
+ struct gatt_primary *primary;
bt_uuid_t uuid;
start = att_get_u16(&data[0]);
continue;
}
- primary = g_try_new0(struct att_primary, 1);
+ primary = g_try_new0(struct gatt_primary, 1);
if (!primary) {
err = ATT_ECODE_INSUFF_RESOURCES;
goto done;
}
- primary->start = start;
- primary->end = end;
+ primary->range.start = start;
+ primary->range.end = end;
bt_uuid_to_string(&uuid, primary->uuid, sizeof(primary->uuid));
dp->primaries = g_slist_append(dp->primaries, primary);
}
for (i = 0; i < list->num; i++) {
uint8_t *value = list->data[i];
- struct att_char *chars;
+ struct gatt_char *chars;
bt_uuid_t uuid;
last = att_get_u16(value);
} else
uuid = att_get_uuid128(&value[5]);
- chars = g_try_new0(struct att_char, 1);
+ chars = g_try_new0(struct gatt_char, 1);
if (!chars) {
err = ATT_ECODE_INSUFF_RESOURCES;
goto done;
att_data_list_free(list);
err = 0;
- if (last != 0) {
+ if (last != 0 && (last + 1 < dc->end)) {
buf = g_attrib_get_buffer(dc->attrib, &buflen);
bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
+
#include <bluetooth/sdp.h>
+/* GATT Profile Attribute types */
+#define GATT_PRIM_SVC_UUID 0x2800
+#define GATT_SND_SVC_UUID 0x2801
+#define GATT_INCLUDE_UUID 0x2802
+#define GATT_CHARAC_UUID 0x2803
+
+/* GATT Characteristic Types */
+#define GATT_CHARAC_DEVICE_NAME 0x2A00
+#define GATT_CHARAC_APPEARANCE 0x2A01
+#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG 0x2A02
+#define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03
+#define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04
+#define GATT_CHARAC_SERVICE_CHANGED 0x2A05
+
+/* GATT Characteristic Descriptors */
+#define GATT_CHARAC_EXT_PROPER_UUID 0x2900
+#define GATT_CHARAC_USER_DESC_UUID 0x2901
+#define GATT_CLIENT_CHARAC_CFG_UUID 0x2902
+#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
+
+/* Client Characteristic Configuration bit field */
+#define GATT_CLIENT_CHARAC_CFG_NOTIF_BIT 0x0001
+#define GATT_CLIENT_CHARAC_CFG_IND_BIT 0x0002
+
typedef void (*gatt_cb_t) (GSList *l, guint8 status, gpointer user_data);
+struct gatt_primary {
+ char uuid[MAX_LEN_UUID_STR + 1];
+ struct att_range range;
+};
+
+struct gatt_char {
+ char uuid[MAX_LEN_UUID_STR + 1];
+ uint16_t handle;
+ uint8_t properties;
+ uint16_t value_handle;
+};
+
guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func,
gpointer user_data);
#include <bluetooth/bluetooth.h>
#include <bluetooth/uuid.h>
+#include "log.h"
#include "att.h"
#include "btio.h"
#include "gattrib.h"
guint read_watch;
guint write_watch;
guint timeout_watch;
- GQueue *queue;
+ GQueue *requests;
+ GQueue *responses;
GSList *events;
guint next_cmd_id;
- guint next_evt_id;
GDestroyNotify destroy;
- GAttribDisconnectFunc disconnect;
gpointer destroy_user_data;
- gpointer disc_user_data;
+ gboolean stale;
};
struct command {
g_atomic_int_inc(&attrib->refs);
+ DBG("%p: ref=%d", attrib, attrib->refs);
+
return attrib;
}
GSList *l;
struct command *c;
- while ((c = g_queue_pop_head(attrib->queue)))
+ while ((c = g_queue_pop_head(attrib->requests)))
+ command_destroy(c);
+
+ while ((c = g_queue_pop_head(attrib->responses)))
command_destroy(c);
- g_queue_free(attrib->queue);
- attrib->queue = NULL;
+ g_queue_free(attrib->requests);
+ attrib->requests = NULL;
+
+ g_queue_free(attrib->responses);
+ attrib->responses = NULL;
for (l = attrib->events; l; l = l->next)
event_destroy(l->data);
if (attrib->write_watch > 0)
g_source_remove(attrib->write_watch);
- if (attrib->read_watch > 0) {
+ if (attrib->read_watch > 0)
g_source_remove(attrib->read_watch);
+
+ if (attrib->io)
g_io_channel_unref(attrib->io);
- }
g_free(attrib->buf);
void g_attrib_unref(GAttrib *attrib)
{
+ gboolean ret;
+
if (!attrib)
return;
- if (g_atomic_int_dec_and_test(&attrib->refs) == FALSE)
+ ret = g_atomic_int_dec_and_test(&attrib->refs);
+
+ DBG("%p: ref=%d", attrib, attrib->refs);
+
+ if (ret == FALSE)
return;
attrib_destroy(attrib);
return attrib->io;
}
-gboolean g_attrib_set_disconnect_function(GAttrib *attrib,
- GAttribDisconnectFunc disconnect, gpointer user_data)
-{
- if (attrib == NULL)
- return FALSE;
-
- attrib->disconnect = disconnect;
- attrib->disc_user_data = user_data;
-
- return TRUE;
-}
-
gboolean g_attrib_set_destroy_function(GAttrib *attrib,
GDestroyNotify destroy, gpointer user_data)
{
static gboolean disconnect_timeout(gpointer data)
{
struct _GAttrib *attrib = data;
+ struct command *c;
- attrib_destroy(attrib);
+ g_attrib_ref(attrib);
+
+ c = g_queue_pop_head(attrib->requests);
+ if (c == NULL)
+ goto done;
+
+ if (c->func)
+ c->func(ATT_ECODE_TIMEOUT, NULL, 0, c->user_data);
+
+ command_destroy(c);
+
+ while ((c = g_queue_pop_head(attrib->requests))) {
+ if (c->func)
+ c->func(ATT_ECODE_ABORTED, NULL, 0, c->user_data);
+ command_destroy(c);
+ }
+
+done:
+ attrib->stale = TRUE;
+
+ g_attrib_unref(attrib);
return FALSE;
}
GError *gerr = NULL;
gsize len;
GIOStatus iostat;
+ GQueue *queue;
- if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
- if (attrib->disconnect)
- attrib->disconnect(attrib->disc_user_data);
+ if (attrib->stale)
+ return FALSE;
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL))
return FALSE;
- }
- cmd = g_queue_peek_head(attrib->queue);
+ queue = attrib->responses;
+ cmd = g_queue_peek_head(queue);
+ if (cmd == NULL) {
+ queue = attrib->requests;
+ cmd = g_queue_peek_head(queue);
+ }
if (cmd == NULL)
return FALSE;
+ /*
+ * Verify that we didn't already send this command. This can only
+ * happen with elementes from attrib->requests.
+ */
+ if (cmd->sent)
+ return FALSE;
+
iostat = g_io_channel_write_chars(io, (gchar *) cmd->pdu, cmd->len,
&len, &gerr);
if (iostat != G_IO_STATUS_NORMAL)
return FALSE;
if (cmd->expected == 0) {
- g_queue_pop_head(attrib->queue);
+ g_queue_pop_head(queue);
command_destroy(cmd);
return TRUE;
struct _GAttrib *attrib = data;
attrib->write_watch = 0;
+ g_attrib_unref(attrib);
}
static void wake_up_sender(struct _GAttrib *attrib)
{
- if (attrib->write_watch == 0)
- attrib->write_watch = g_io_add_watch_full(attrib->io,
- G_PRIORITY_DEFAULT, G_IO_OUT, can_write_data,
- attrib, destroy_sender);
+ if (attrib->write_watch > 0)
+ return;
+
+ attrib = g_attrib_ref(attrib);
+ attrib->write_watch = g_io_add_watch_full(attrib->io,
+ G_PRIORITY_DEFAULT, G_IO_OUT,
+ can_write_data, attrib, destroy_sender);
}
static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data)
uint8_t buf[512], status;
gsize len;
GIOStatus iostat;
- gboolean qempty;
+ gboolean norequests, noresponses;
- if (attrib->timeout_watch > 0) {
- g_source_remove(attrib->timeout_watch);
- attrib->timeout_watch = 0;
- }
+ if (attrib->stale)
+ return FALSE;
if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
attrib->read_watch = 0;
- if (attrib->disconnect)
- attrib->disconnect(attrib->disc_user_data);
return FALSE;
}
if (is_response(buf[0]) == FALSE)
return TRUE;
- cmd = g_queue_pop_head(attrib->queue);
+ if (attrib->timeout_watch > 0) {
+ g_source_remove(attrib->timeout_watch);
+ attrib->timeout_watch = 0;
+ }
+
+ cmd = g_queue_pop_head(attrib->requests);
if (cmd == NULL) {
/* Keep the watch if we have events to report */
return attrib->events != NULL;
status = 0;
done:
- qempty = attrib->queue == NULL || g_queue_is_empty(attrib->queue);
+ norequests = attrib->requests == NULL ||
+ g_queue_is_empty(attrib->requests);
+ noresponses = attrib->responses == NULL ||
+ g_queue_is_empty(attrib->responses);
if (cmd) {
if (cmd->func)
command_destroy(cmd);
}
- if (!qempty)
+ if (!norequests || !noresponses)
wake_up_sender(attrib);
return TRUE;
GAttrib *g_attrib_new(GIOChannel *io)
{
struct _GAttrib *attrib;
- uint16_t omtu;
+ uint16_t imtu;
+ uint16_t att_mtu;
+ uint16_t cid;
+ GError *gerr = NULL;
g_io_channel_set_encoding(io, NULL, NULL);
g_io_channel_set_buffered(io, FALSE);
+ bt_io_get(io, BT_IO_L2CAP, &gerr,
+ BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_CID, &cid,
+ BT_IO_OPT_INVALID);
+
+ if (gerr) {
+ error("%s", gerr->message);
+ g_error_free(gerr);
+ return NULL;
+ }
+
attrib = g_try_new0(struct _GAttrib, 1);
if (attrib == NULL)
return NULL;
+ att_mtu = (cid == ATT_CID) ? ATT_DEFAULT_LE_MTU : imtu;
+
+ attrib->buf = g_malloc0(att_mtu);
+ attrib->buflen = att_mtu;
+
attrib->io = g_io_channel_ref(io);
- attrib->queue = g_queue_new();
+ attrib->requests = g_queue_new();
+ attrib->responses = g_queue_new();
attrib->read_watch = g_io_add_watch(attrib->io,
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);
}
gpointer user_data, GDestroyNotify notify)
{
struct command *c;
+ GQueue *queue;
+
+ if (attrib->stale)
+ return 0;
c = g_try_new0(struct command, 1);
if (c == NULL)
c->user_data = user_data;
c->notify = notify;
+ if (is_response(opcode))
+ queue = attrib->responses;
+ else
+ queue = attrib->requests;
+
if (id) {
c->id = id;
- g_queue_push_head(attrib->queue, c);
+ if (!is_response(opcode))
+ g_queue_push_head(queue, c);
+ else
+ /* Don't re-order responses even if an ID is given */
+ g_queue_push_tail(queue, c);
} else {
c->id = ++attrib->next_cmd_id;
- g_queue_push_tail(attrib->queue, c);
+ g_queue_push_tail(queue, c);
}
- if (g_queue_get_length(attrib->queue) == 1)
+ /*
+ * If a command was added to the queue and it was empty before, wake up
+ * the sender. If the sender was already woken up by the second queue,
+ * wake_up_sender will just return.
+ */
+ if (g_queue_get_length(queue) == 1)
wake_up_sender(attrib);
return c->id;
gboolean g_attrib_cancel(GAttrib *attrib, guint id)
{
- GList *l;
+ GList *l = NULL;
struct command *cmd;
+ GQueue *queue;
- if (attrib == NULL || attrib->queue == NULL)
+ if (attrib == NULL)
return FALSE;
- l = g_queue_find_custom(attrib->queue, GUINT_TO_POINTER(id),
- command_cmp_by_id);
+ queue = attrib->requests;
+ if (queue)
+ l = g_queue_find_custom(queue, GUINT_TO_POINTER(id),
+ command_cmp_by_id);
+ if (l == NULL) {
+ queue = attrib->responses;
+ if (!queue)
+ return FALSE;
+ l = g_queue_find_custom(queue, GUINT_TO_POINTER(id),
+ command_cmp_by_id);
+ }
+
if (l == NULL)
return FALSE;
cmd = l->data;
- if (cmd == g_queue_peek_head(attrib->queue) && cmd->sent)
+ if (cmd == g_queue_peek_head(queue) && cmd->sent)
cmd->func = NULL;
else {
- g_queue_remove(attrib->queue, cmd);
+ g_queue_remove(queue, cmd);
command_destroy(cmd);
}
return TRUE;
}
-gboolean g_attrib_cancel_all(GAttrib *attrib)
+static gboolean cancel_all_per_queue(GQueue *queue)
{
struct command *c, *head = NULL;
gboolean first = TRUE;
- if (attrib == NULL || attrib->queue == NULL)
+ if (queue == NULL)
return FALSE;
- while ((c = g_queue_pop_head(attrib->queue))) {
+ while ((c = g_queue_pop_head(queue))) {
if (first && c->sent) {
/* If the command was sent ignore its callback ... */
c->func = NULL;
if (head) {
/* ... and put it back in the queue */
- g_queue_push_head(attrib->queue, head);
+ g_queue_push_head(queue, head);
}
return TRUE;
}
+gboolean g_attrib_cancel_all(GAttrib *attrib)
+{
+ gboolean ret;
+
+ if (attrib == NULL)
+ return FALSE;
+
+ ret = cancel_all_per_queue(attrib->requests);
+ ret = cancel_all_per_queue(attrib->responses) && ret;
+
+ return ret;
+}
+
gboolean g_attrib_set_debug(GAttrib *attrib,
GAttribDebugFunc func, gpointer user_data)
{
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);
GAttribNotifyFunc func, gpointer user_data,
GDestroyNotify notify)
{
+ static guint next_evt_id = 0;
struct event *event;
event = g_try_new0(struct event, 1);
event->func = func;
event->user_data = user_data;
event->notify = notify;
- event->id = ++attrib->next_evt_id;
+ event->id = ++next_evt_id;
attrib->events = g_slist_append(attrib->events, event);
GIOChannel *g_attrib_get_channel(GAttrib *attrib);
-gboolean g_attrib_set_disconnect_function(GAttrib *attrib,
- GAttribDisconnectFunc disconnect, gpointer user_data);
-
gboolean g_attrib_set_destroy_function(GAttrib *attrib,
GDestroyNotify destroy, gpointer user_data);
static gchar *opt_src = NULL;
static gchar *opt_dst = NULL;
+static gchar *opt_dst_type = NULL;
static gchar *opt_value = NULL;
static gchar *opt_sec_level = NULL;
static bt_uuid_t *opt_uuid = NULL;
}
for (l = services; l; l = l->next) {
- struct att_primary *prim = l->data;
+ struct gatt_primary *prim = l->data;
g_print("attr handle = 0x%04x, end grp handle = 0x%04x "
- "uuid: %s\n", prim->start, prim->end, prim->uuid);
+ "uuid: %s\n", prim->range.start, prim->range.end, prim->uuid);
}
done:
}
for (l = characteristics; l; l = l->next) {
- struct att_char *chars = l->data;
+ struct gatt_char *chars = l->data;
g_print("handle = 0x%04x, char properties = 0x%02x, char value "
"handle = 0x%04x, uuid = %s\n", chars->handle,
"Specify local adapter interface", "hciX" },
{ "device", 'b', 0, G_OPTION_ARG_STRING, &opt_dst,
"Specify remote Bluetooth address", "MAC" },
+ { "addr-type", 't', 0, G_OPTION_ARG_STRING, &opt_dst_type,
+ "Set LE address type. Default: public", "[public | random]"},
{ "mtu", 'm', 0, G_OPTION_ARG_INT, &opt_mtu,
"Specify the MTU size", "MTU" },
{ "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
GError *gerr = NULL;
GIOChannel *chan;
+ opt_dst_type = g_strdup("public");
opt_sec_level = g_strdup("low");
context = g_option_context_new(NULL);
}
if (opt_interactive) {
- interactive(opt_src, opt_dst, opt_psm);
+ interactive(opt_src, opt_dst, opt_dst_type, opt_psm);
goto done;
}
goto done;
}
- chan = gatt_connect(opt_src, opt_dst, opt_sec_level,
+ chan = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
opt_psm, opt_mtu, connect_cb);
if (chan == NULL) {
got_error = TRUE;
*
*/
-int interactive(const gchar *src, const gchar *dst, gboolean le);
+int interactive(const gchar *src, const gchar *dst, const gchar *dst_type,
+ gboolean le);
GIOChannel *gatt_connect(const gchar *src, const gchar *dst,
- const gchar *sec_level, int psm, int mtu,
- BtIOConnect connect_cb);
+ const gchar *dst_type, const gchar *sec_level,
+ int psm, int mtu, BtIOConnect connect_cb);
size_t gatt_attr_data_from_string(const char *str, uint8_t **data);
static gchar *opt_src = NULL;
static gchar *opt_dst = NULL;
+static gchar *opt_dst_type = NULL;
static gchar *opt_sec_level = NULL;
static int opt_psm = 0;
static int opt_mtu = 0;
set_state(STATE_CONNECTED);
}
+static void disconnect_io()
+{
+ if (conn_state == STATE_DISCONNECTED)
+ return;
+
+ g_attrib_unref(attrib);
+ attrib = NULL;
+ opt_mtu = 0;
+
+ g_io_channel_shutdown(iochannel, FALSE, NULL);
+ g_io_channel_unref(iochannel);
+ iochannel = NULL;
+
+ set_state(STATE_DISCONNECTED);
+}
+
static void primary_all_cb(GSList *services, guint8 status, gpointer user_data)
{
GSList *l;
printf("\n");
for (l = services; l; l = l->next) {
- struct att_primary *prim = l->data;
+ struct gatt_primary *prim = l->data;
printf("attr handle: 0x%04x, end grp handle: 0x%04x "
- "uuid: %s\n", prim->start, prim->end, prim->uuid);
+ "uuid: %s\n", prim->range.start, prim->range.end, prim->uuid);
}
rl_forced_update_display();
printf("\n");
for (l = characteristics; l; l = l->next) {
- struct att_char *chars = l->data;
+ struct gatt_char *chars = l->data;
printf("handle: 0x%04x, char properties: 0x%02x, char value "
"handle: 0x%04x, uuid: %s\n", chars->handle,
g_main_loop_quit(event_loop);
}
+static gboolean channel_watcher(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ disconnect_io();
+
+ return FALSE;
+}
+
static void cmd_connect(int argcp, char **argvp)
{
if (conn_state != STATE_DISCONNECTED)
if (argcp > 1) {
g_free(opt_dst);
opt_dst = g_strdup(argvp[1]);
+
+ g_free(opt_dst_type);
+ if (argcp > 2)
+ opt_dst_type = g_strdup(argvp[2]);
+ else
+ opt_dst_type = g_strdup("public");
}
if (opt_dst == NULL) {
}
set_state(STATE_CONNECTING);
- iochannel = gatt_connect(opt_src, opt_dst, opt_sec_level, opt_psm,
- opt_mtu, connect_cb);
+ iochannel = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level,
+ opt_psm, opt_mtu, connect_cb);
if (iochannel == NULL)
set_state(STATE_DISCONNECTED);
+ else
+ g_io_add_watch(iochannel, G_IO_HUP, channel_watcher, NULL);
}
static void cmd_disconnect(int argcp, char **argvp)
{
- if (conn_state == STATE_DISCONNECTED)
- return;
-
- g_attrib_unref(attrib);
- attrib = NULL;
- opt_mtu = 0;
-
- g_io_channel_shutdown(iochannel, FALSE, NULL);
- g_io_channel_unref(iochannel);
- iochannel = NULL;
-
- set_state(STATE_DISCONNECTED);
+ disconnect_io();
}
static void cmd_primary(int argcp, char **argvp)
return;
}
- handle = strtoll(argvp[1], NULL, 16);
- if (errno != 0 || handle <= 0) {
+ handle = strtohandle(argvp[1]);
+ if (handle <= 0) {
printf("A valid handle is required\n");
return;
}
"Exit interactive mode" },
{ "quit", cmd_exit, "",
"Exit interactive mode" },
- { "connect", cmd_connect, "[address]",
+ { "connect", cmd_connect, "[address [address type]]",
"Connect to a remote device" },
{ "disconnect", cmd_disconnect, "",
"Disconnect from a remote device" },
return TRUE;
}
-int interactive(const gchar *src, const gchar *dst, int psm)
+static char *completion_generator(const char *text, int state)
+{
+ static int index = 0, len = 0;
+ const char *cmd = NULL;
+
+ if (state == 0) {
+ index = 0;
+ len = strlen(text);
+ }
+
+ while ((cmd = commands[index].cmd) != NULL) {
+ index++;
+ if (strncmp(cmd, text, len) == 0)
+ return strdup(cmd);
+ }
+
+ return NULL;
+}
+
+static char **commands_completion(const char *text, int start, int end)
+{
+ if (start == 0)
+ return rl_completion_matches(text, &completion_generator);
+ else
+ return NULL;
+}
+
+int interactive(const gchar *src, const gchar *dst,
+ const gchar *dst_type, int psm)
{
GIOChannel *pchan;
gint events;
opt_src = g_strdup(src);
opt_dst = g_strdup(dst);
+ opt_dst_type = g_strdup(dst_type);
opt_psm = psm;
prompt = g_string_new(NULL);
events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
g_io_add_watch(pchan, events, prompt_read, NULL);
+ rl_attempted_completion_function = commands_completion;
rl_callback_handler_install(get_prompt(), parse_line);
g_main_loop_run(event_loop);
#include "btio.h"
#include "gatttool.h"
-/* Minimum MTU for ATT connections */
-#define ATT_MIN_MTU_LE 23
-#define ATT_MIN_MTU_L2CAP 48
-
GIOChannel *gatt_connect(const gchar *src, const gchar *dst,
- const gchar *sec_level, int psm, int mtu,
- BtIOConnect connect_cb)
+ const gchar *dst_type, const gchar *sec_level,
+ int psm, int mtu, BtIOConnect connect_cb)
{
GIOChannel *chan;
bdaddr_t sba, dba;
+ uint8_t dest_type;
GError *err = NULL;
BtIOSecLevel sec;
- int minimum_mtu;
-
- /* This check is required because currently setsockopt() returns no
- * errors for MTU values smaller than the allowed minimum. */
- minimum_mtu = psm ? ATT_MIN_MTU_L2CAP : ATT_MIN_MTU_LE;
- if (mtu != 0 && mtu < minimum_mtu) {
- g_printerr("MTU cannot be smaller than %d\n", minimum_mtu);
- return NULL;
- }
/* Remote device */
if (dst == NULL) {
} else
bacpy(&sba, BDADDR_ANY);
+ /* Not used for BR/EDR */
+ if (strcmp(dst_type, "random") == 0)
+ dest_type = BDADDR_LE_RANDOM;
+ else
+ dest_type = BDADDR_LE_PUBLIC;
+
if (strcmp(sec_level, "medium") == 0)
sec = BT_IO_SEC_MEDIUM;
else if (strcmp(sec_level, "high") == 0)
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_DEST_TYPE, dest_type,
BT_IO_OPT_CID, ATT_CID,
- BT_IO_OPT_OMTU, mtu,
BT_IO_OPT_SEC_LEVEL, sec,
BT_IO_OPT_INVALID);
else
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &dba,
BT_IO_OPT_PSM, psm,
- BT_IO_OPT_OMTU, mtu,
+ BT_IO_OPT_IMTU, mtu,
BT_IO_OPT_SEC_LEVEL, sec,
BT_IO_OPT_INVALID);
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
-#include "glib-compat.h"
#include "log.h"
#include "device.h"
#include "manager.h"
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
DBG("Sink %p: Open_Ind", sep);
else
DBG("Source %p: Open_Ind", sep);
+
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return TRUE;
+
+ if (setup->reconfigure)
+ setup->reconfigure = FALSE;
+
+ finalize_config(setup);
+
return TRUE;
}
else
DBG("Source %p: Start_Ind", sep);
- setup = find_setup_by_session(session);
- if (setup)
- finalize_resume(setup);
-
if (!a2dp_sep->locked) {
a2dp_sep->session = avdtp_ref(session);
a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT,
a2dp_sep);
}
+ if (!a2dp_sep->starting)
+ return TRUE;
+
+ a2dp_sep->starting = FALSE;
+
+ setup = find_setup_by_session(session);
+ if (setup)
+ finalize_resume(setup);
+
return TRUE;
}
else
DBG("Source %p: Start_Cfm", sep);
+ a2dp_sep->starting = FALSE;
+
setup = find_setup_by_session(session);
if (!setup)
return;
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
+ gboolean start;
+ int start_err;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
DBG("Sink %p: Suspend_Ind", sep);
a2dp_sep->session = NULL;
}
+ if (!a2dp_sep->suspending)
+ return TRUE;
+
+ a2dp_sep->suspending = FALSE;
+
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return TRUE;
+
+ start = setup->start;
+ setup->start = FALSE;
+
+ finalize_suspend(setup);
+
+ if (!start)
+ return TRUE;
+
+ start_err = avdtp_start(session, a2dp_sep->stream);
+ if (start_err < 0 && start_err != -EINPROGRESS) {
+ error("avdtp_start: %s (%d)", strerror(-start_err),
+ -start_err);
+ finalize_setup_errno(setup, start_err, finalize_resume);
+ }
+
return TRUE;
}
struct a2dp_sep *a2dp_sep = user_data;
struct a2dp_setup *setup;
gboolean start;
- int perr;
+ int start_err;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
DBG("Sink %p: Suspend_Cfm", sep);
return;
}
- perr = avdtp_start(session, a2dp_sep->stream);
- if (perr < 0) {
- error("Error on avdtp_start %s (%d)", strerror(-perr), -perr);
- finalize_setup_errno(setup, -EIO, finalize_suspend, NULL);
+ start_err = avdtp_start(session, a2dp_sep->stream);
+ if (start_err < 0 && start_err != -EINPROGRESS) {
+ error("avdtp_start: %s (%d)", strerror(-start_err),
+ -start_err);
+ finalize_setup_errno(setup, start_err, finalize_suspend, NULL);
}
}
g_timeout_add(RECONFIGURE_TIMEOUT, a2dp_reconfigure, setup);
}
-static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
+static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, uint8_t *err,
void *user_data)
{
struct a2dp_sep *a2dp_sep = user_data;
+ struct a2dp_setup *setup;
if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK)
DBG("Sink %p: Abort_Ind", sep);
a2dp_sep->stream = NULL;
- return TRUE;
+ setup = find_setup_by_session(session);
+ if (!setup)
+ return;
+
+ finalize_setup_errno(setup, -ECONNRESET, finalize_suspend,
+ finalize_resume,
+ finalize_config);
+
+ return;
}
static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config)
{
- int sbc_srcs = 1, sbc_sinks = 1;
+ int sbc_srcs = 0, sbc_sinks = 0;
int mpeg12_srcs = 0, mpeg12_sinks = 0;
- gboolean source = TRUE, sink = FALSE, socket = TRUE;
+ gboolean source = TRUE, sink = FALSE, socket = FALSE;
gboolean delay_reporting = FALSE;
char *str;
GError *err = NULL;
source = TRUE;
if (strstr(str, "Source"))
sink = TRUE;
+ if (strstr(str, "Socket"))
+ socket = TRUE;
g_free(str);
}
}
/* Don't register any local sep if Socket is disabled */
- if (socket == FALSE) {
- sbc_srcs = 0;
- sbc_sinks = 0;
- mpeg12_srcs = 0;
- mpeg12_sinks = 0;
+ if (socket == FALSE)
goto proceed;
- }
str = g_key_file_get_string(config, "A2DP", "SBCSources", &err);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
+ sbc_srcs = 1;
} else {
sbc_srcs = atoi(str);
g_free(str);
if (err) {
DBG("audio.conf: %s", err->message);
g_clear_error(&err);
+ sbc_sinks = 1;
} else {
sbc_sinks = atoi(str);
g_free(str);
server = find_server(servers, src);
if (server == NULL) {
if (err)
- *err = -EINVAL;
+ *err = -EPROTONOSUPPORT;
return NULL;
}
return 31;
case SBC_CHANNEL_MODE_STEREO:
case SBC_CHANNEL_MODE_JOINT_STEREO:
-#ifdef USE_SBC_ORIGINAL_BITPOOL
return 53;
-#else
- return 32;
-#endif
default:
error("Invalid channel mode %u", mode);
return 53;
error("avdtp_start failed");
goto failed;
}
+ sep->starting = TRUE;
break;
case AVDTP_STATE_STREAMING:
if (!sep->suspending && sep->suspend_timer) {
# If we want to disable support for specific services
# Defaults to supporting all implemented services
-#Disable=Control,Source
+#Disable=Gateway,Source,Socket
#ifdef __TIZEN_PATCH__
-Enable=Socket
+Disable=Socket
#endif
# SCO routing. Either PCM or HCI (in which case audio is routed to/from ALSA)
MaxConnected=1
# Set to true to enable use of fast connectable mode (faster page scanning)
-# for HFP when incomming call starts. Default settings are restored after
+# for HFP when incoming call starts. Default settings are restored after
# call is answered or rejected. Page scan interval is much shorter and page
# scan type changed to interlaced. Such allows faster connection initiated
# by a headset.
FastConnectable=false
# Just an example of potential config options for the other interfaces
-#ifdef __TIZEN_PATCH__
-[A2DP]
-SBCSources=1
-#else
#[A2DP]
#SBCSources=1
#MPEG12Sources=0
-#endif
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
+#include <bluetooth/uuid.h>
#include <glib.h>
#include "manager.h"
#include "device.h"
#include "avctp.h"
+#include "avrcp.h"
#define QUIRK_NO_RELEASE 1 << 0
GSList *sessions;
};
+struct avctp_rsp_handler {
+ uint8_t id;
+ avctp_rsp_cb func;
+ void *user_data;
+};
+
struct avctp {
struct avctp_server *server;
bdaddr_t dst;
uint16_t mtu;
uint8_t key_quirks[256];
+ GSList *handlers;
};
struct avctp_pdu_handler {
static GSList *callbacks = NULL;
static GSList *servers = NULL;
static GSList *handlers = NULL;
+static uint8_t id = 0;
static void auth_cb(DBusError *derr, void *user_data);
break;
}
- if (key_map[i].name == NULL)
+ if (key_map[i].name == NULL) {
DBG("AV/C: unknown button 0x%02X %s",
operands[0] & 0x7F, status);
+ *code = AVC_CTYPE_NOT_IMPLEMENTED;
+ return 0;
+ }
done:
*code = AVC_CTYPE_ACCEPTED;
DBG("reply to AVC_OP_UNITINFO");
- return 0;
+ return operand_count;
}
static size_t handle_subunit_info(struct avctp *session,
DBG("reply to AVC_OP_SUBUNITINFO");
- return 0;
+ return operand_count;
}
static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode)
static void avctp_disconnected(struct avctp *session)
{
- struct avctp_server *server = session->server;
+ struct avctp_server *server;
if (!session)
return;
session->uinput = -1;
}
+ server = session->server;
server->sessions = g_slist_remove(server->sessions, session);
+ g_slist_free_full(session->handlers, g_free);
g_free(session);
}
}
}
+static void handle_response(struct avctp *session, struct avctp_header *avctp,
+ struct avc_header *avc, uint8_t *operands,
+ size_t operand_count)
+{
+ GSList *l;
+
+ for (l = session->handlers; l; l = l->next) {
+ struct avctp_rsp_handler *handler = l->data;
+
+ if (handler->id != avctp->transaction)
+ continue;
+
+ if (handler->func && handler->func(session, avc->code,
+ avc->subunit_type,
+ operands, operand_count,
+ handler->user_data))
+ return;
+
+ session->handlers = g_slist_remove(session->handlers, handler);
+ g_free(handler);
+
+ return;
+ }
+}
+
static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
gpointer data)
{
avc->code, avc->subunit_type, avc->subunit_id,
avc->opcode, operand_count);
- if (avctp->cr == AVCTP_RESPONSE)
+ if (avctp->cr == AVCTP_RESPONSE) {
+ handle_response(session, avctp, avc, operands, operand_count);
return TRUE;
+ }
packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH;
avctp->cr = AVCTP_RESPONSE;
handler = find_handler(handlers, avc->opcode);
if (!handler) {
DBG("handler not found for 0x%02x", avc->opcode);
- avc->code = AVC_CTYPE_REJECTED;
+ packet_size += avrcp_handle_vendor_reject(&code, operands);
+ avc->code = code;
goto done;
}
struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH];
uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH];
int sk;
- static uint8_t transaction = 0;
if (session->state != AVCTP_STATE_CONNECTED)
return -ENOTCONN;
memset(buf, 0, sizeof(buf));
- avctp->transaction = transaction++;
+ avctp->transaction = id++;
avctp->packet_type = AVCTP_PACKET_SINGLE;
avctp->cr = AVCTP_COMMAND;
avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
return -errno;
/* Button release */
- avctp->transaction = transaction++;
+ avctp->transaction = id++;
operands[0] |= 0x80;
if (write(sk, buf, sizeof(buf)) < 0)
return 0;
}
-int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
- uint8_t code, uint8_t subunit,
+static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr,
+ uint8_t code, uint8_t subunit, uint8_t opcode,
uint8_t *operands, size_t operand_count)
{
uint8_t *buf;
avctp->transaction = transaction;
avctp->packet_type = AVCTP_PACKET_SINGLE;
- avctp->cr = AVCTP_RESPONSE;
+ avctp->cr = cr;
avctp->pid = htons(AV_REMOTE_SVCLASS_ID);
avc->code = code;
avc->subunit_type = subunit;
- avc->opcode = AVC_OP_VENDORDEP;
+ avc->opcode = opcode;
memcpy(pdu, operands, operand_count);
return err;
}
+int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count)
+{
+ return avctp_send(session, transaction, AVCTP_RESPONSE, code, subunit,
+ AVC_OP_VENDORDEP, operands, operand_count);
+}
+
+int avctp_send_vendordep_req(struct avctp *session, uint8_t code,
+ uint8_t subunit, uint8_t *operands,
+ size_t operand_count,
+ avctp_rsp_cb func, void *user_data)
+{
+ struct avctp_rsp_handler *handler;
+ int err;
+
+ err = avctp_send(session, id, AVCTP_COMMAND, code, subunit,
+ AVC_OP_VENDORDEP, operands, operand_count);
+ if (err < 0)
+ return err;
+
+ handler = g_new0(struct avctp_rsp_handler, 1);
+ handler->id = id;
+ handler->func = func;
+ handler->user_data = user_data;
+
+ session->handlers = g_slist_prepend(session->handlers, handler);
+
+ id++;
+
+ return 0;
+}
+
unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data)
{
struct avctp_state_callback *state_cb;
uint8_t *code, uint8_t *subunit,
uint8_t *operands, size_t operand_count,
void *user_data);
+typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code,
+ uint8_t subunit, uint8_t *operands,
+ size_t operand_count, void *user_data);
unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data);
gboolean avctp_remove_state_cb(unsigned int id);
int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count);
+int avctp_send_vendordep_req(struct avctp *session, uint8_t code,
+ uint8_t subunit, uint8_t *operands,
+ size_t operand_count,
+ avctp_rsp_cb func, void *user_data);
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include <glib.h>
#include <dbus/dbus.h>
#include "manager.h"
#include "control.h"
#include "avdtp.h"
-#include "glib-compat.h"
#include "btio.h"
#include "sink.h"
#include "source.h"
#else
#define REQ_TIMEOUT 6
#endif
-
#define ABORT_TIMEOUT 2
#define DISCONNECT_TIMEOUT 1
#define STREAM_TIMEOUT 20
size_t data_size;
struct avdtp_stream *stream; /* Set if the request targeted a stream */
guint timeout;
+ gboolean collided;
};
struct avdtp_remote_sep {
break;
case AVDTP_STATE_OPEN:
stream->starting = FALSE;
- if (old_state > AVDTP_STATE_OPEN && session->auto_dc)
+ if ((old_state > AVDTP_STATE_OPEN && session->auto_dc) ||
+ stream->open_acp)
stream->idle_timer = g_timeout_add_seconds(STREAM_TIMEOUT,
stream_timeout,
stream);
break;
case AVDTP_STATE_STREAMING:
+ if (stream->idle_timer) {
+ g_source_remove(stream->idle_timer);
+ stream->idle_timer = 0;
+ }
+ stream->open_acp = FALSE;
+ break;
case AVDTP_STATE_CLOSING:
case AVDTP_STATE_ABORTING:
if (stream->idle_timer) {
case AVDTP_SEP_TYPE_SINK:
if (!dev->source) {
btd_device_add_uuid(dev->btd_dev, A2DP_SOURCE_UUID);
- if (!dev->sink) {
+ if (!dev->source) {
error("Unable to get a audio source object");
err = AVDTP_BAD_STATE;
goto failed;
static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction,
struct seid_req *req, int size)
{
- return avdtp_unknown_cmd(session, transaction, AVDTP_RECONFIGURE);
+ struct conf_rej rej;
+
+ rej.error = AVDTP_NOT_SUPPORTED_COMMAND;
+ rej.category = 0x00;
+
+ return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
+ AVDTP_RECONFIGURE, &rej, sizeof(rej));
+}
+
+static void check_seid_collision(struct pending_req *req, uint8_t id)
+{
+ struct seid_req *seid = req->data;
+
+ if (seid->acp_seid == id)
+ req->collided = TRUE;
+}
+
+static void check_start_collision(struct pending_req *req, uint8_t id)
+{
+ struct start_req *start = req->data;
+ struct seid *seid = &start->first_seid;
+ int count = 1 + req->data_size - sizeof(struct start_req);
+ int i;
+
+ for (i = 0; i < count; i++, seid++) {
+ if (seid->seid == id) {
+ req->collided = TRUE;
+ return;
+ }
+ }
+}
+
+static void check_suspend_collision(struct pending_req *req, uint8_t id)
+{
+ struct suspend_req *suspend = req->data;
+ struct seid *seid = &suspend->first_seid;
+ int count = 1 + req->data_size - sizeof(struct suspend_req);
+ int i;
+
+ for (i = 0; i < count; i++, seid++) {
+ if (seid->seid == id) {
+ req->collided = TRUE;
+ return;
+ }
+ }
+}
+
+static void avdtp_check_collision(struct avdtp *session, uint8_t cmd,
+ struct avdtp_stream *stream)
+{
+ struct pending_req *req = session->req;
+
+ if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT))
+ return;
+
+ if (cmd == AVDTP_ABORT)
+ cmd = req->signal_id;
+
+ switch (cmd) {
+ case AVDTP_OPEN:
+ case AVDTP_CLOSE:
+ check_seid_collision(req, stream->rseid);
+ break;
+ case AVDTP_START:
+ check_start_collision(req, stream->rseid);
+ break;
+ case AVDTP_SUSPEND:
+ check_suspend_collision(req, stream->rseid);
+ break;
+ }
}
static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction,
goto failed;
}
+ avdtp_check_collision(session, AVDTP_OPEN, stream);
+
if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
AVDTP_OPEN, NULL, 0))
return FALSE;
stream = sep->stream;
- /* Also reject start cmd if we already initiated start */
- if (sep->state != AVDTP_STATE_OPEN ||
- stream->starting == TRUE) {
+ /* Also reject start cmd if state is not open */
+ if (sep->state != AVDTP_STATE_OPEN) {
err = AVDTP_BAD_STATE;
goto failed;
}
goto failed;
}
+ avdtp_check_collision(session, AVDTP_START, stream);
+
avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING);
}
goto failed;
}
+ avdtp_check_collision(session, AVDTP_CLOSE, stream);
+
avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING);
if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
goto failed;
}
+ avdtp_check_collision(session, AVDTP_SUSPEND, stream);
+
avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN);
}
}
sep = find_local_sep_by_seid(session->server, req->acp_seid);
- if (!sep || !sep->stream) {
- err = AVDTP_BAD_ACP_SEID;
- goto failed;
- }
+ if (!sep || !sep->stream)
+ return TRUE;
- if (sep->ind && sep->ind->abort) {
- if (!sep->ind->abort(session, sep, sep->stream, &err,
- sep->user_data))
- goto failed;
- }
+ if (sep->ind && sep->ind->abort)
+ sep->ind->abort(session, sep, sep->stream, &err,
+ sep->user_data);
+
+ avdtp_check_collision(session, AVDTP_ABORT, sep->stream);
ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
AVDTP_ABORT, NULL, 0);
avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
return ret;
-
-failed:
- return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT,
- AVDTP_ABORT, &err, sizeof(err));
}
static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction,
if (session->streams && session->dc_timer)
remove_disconnect_timer(session);
+ if (session->req && session->req->collided) {
+ DBG("Collision detected");
+ goto next;
+ }
+
return TRUE;
}
break;
}
+next:
pending_req_free(session->req);
session->req = NULL;
BT_IO_OPT_SOURCE_BDADDR, &session->server->src,
BT_IO_OPT_DEST_BDADDR, &session->dst,
BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_INVALID);
if (!io) {
error("%s", err->message);
if (stream->lsep->state != AVDTP_STATE_OPEN)
return -EINVAL;
+ /* Recommendation 12:
+ * If the RD has configured and opened a stream it is also responsible
+ * to start the streaming via GAVDP_START.
+ */
+ if (stream->open_acp) {
+ stream->starting = TRUE;
+ return 0;
+ }
+
if (stream->close_int == TRUE) {
error("avdtp_start: rejecting start since close is initiated");
return -EINVAL;
if (stream->starting == TRUE) {
DBG("stream already started");
- return -EINVAL;
+ return -EINPROGRESS;
}
memset(&req, 0, sizeof(req));
gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, uint8_t *err,
void *user_data);
- gboolean (*abort) (struct avdtp *session, struct avdtp_local_sep *sep,
+ void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep,
struct avdtp_stream *stream, uint8_t *err,
void *user_data);
gboolean (*reconfigure) (struct avdtp *session,
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include <glib.h>
#include <dbus/dbus.h>
#include <gdbus.h>
+#include "../src/adapter.h"
+#include "../src/device.h"
+
#include "log.h"
#include "error.h"
#include "device.h"
#include "avctp.h"
#include "avrcp.h"
#include "sdpd.h"
-#include "glib-compat.h"
#include "dbus-common.h"
/* Company IDs for vendor dependent commands */
#define AVRCP_REGISTER_NOTIFICATION 0x31
#define AVRCP_REQUEST_CONTINUING 0x40
#define AVRCP_ABORT_CONTINUING 0x41
+#define AVRCP_SET_ABSOLUTE_VOLUME 0x50
/* Capabilities for AVRCP_GET_CAPABILITIES pdu */
#define CAP_COMPANY_ID 0x02
#define CAP_EVENTS_SUPPORTED 0x03
+#define AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH 5
+
+#define AVRCP_FEATURE_CATEGORY_1 0x0001
+#define AVRCP_FEATURE_CATEGORY_2 0x0002
+#define AVRCP_FEATURE_CATEGORY_3 0x0004
+#define AVRCP_FEATURE_CATEGORY_4 0x0008
+#define AVRCP_FEATURE_PLAYER_SETTINGS 0x0010
+
enum battery_status {
BATTERY_STATUS_NORMAL = 0,
BATTERY_STATUS_WARNING = 1,
IEEEID_BTSIG,
};
+static void register_volume_notification(struct avrcp_player *player);
+
static sdp_record_t *avrcp_ct_record(void)
{
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
sdp_record_t *record;
sdp_data_t *psm, *version, *features;
uint16_t lp = AVCTP_PSM;
- uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103, feat = 0x000f;
+ uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103;
+ uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
+ AVRCP_FEATURE_CATEGORY_2 |
+ AVRCP_FEATURE_CATEGORY_3 |
+ AVRCP_FEATURE_CATEGORY_4 );
record = sdp_record_alloc();
if (!record)
sdp_record_t *record;
sdp_data_t *psm, *version, *features;
uint16_t lp = AVCTP_PSM;
- uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0103, feat = 0x000f;
+ uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
+ uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
+ AVRCP_FEATURE_CATEGORY_2 |
+ AVRCP_FEATURE_CATEGORY_3 |
+ AVRCP_FEATURE_CATEGORY_4 |
+ AVRCP_FEATURE_PLAYER_SETTINGS );
record = sdp_record_alloc();
if (!record)
return AVC_CTYPE_STABLE;
case CAP_EVENTS_SUPPORTED:
- pdu->params_len = htons(5);
- pdu->params[1] = 3;
+ pdu->params[1] = 4;
pdu->params[2] = AVRCP_EVENT_STATUS_CHANGED;
pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED;
pdu->params[4] = AVRCP_EVENT_TRACK_REACHED_START;
+ pdu->params[5] = AVRCP_EVENT_TRACK_REACHED_END;
+ pdu->params_len = htons(2 + pdu->params[1]);
return AVC_CTYPE_STABLE;
}
if (len) {
pdu->params_len = 0;
- return AVC_CTYPE_STABLE;
+ return AVC_CTYPE_ACCEPTED;
}
err:
}
pdu->params_len = htons(len);
-
+#ifdef __TIZEN_PATCH__
+ return AVC_CTYPE_ACCEPTED;
+#else
return AVC_CTYPE_STABLE;
+#endif
err:
pdu->params_len = htons(1);
pdu->params[0] = E_INVALID_PARAM;
player_abort_pending_pdu(player);
pdu->params_len = 0;
- return AVC_CTYPE_STABLE;
+ return AVC_CTYPE_ACCEPTED;
err:
pdu->params_len = htons(1);
return AVC_CTYPE_REJECTED;
}
-
static struct pdu_handler {
uint8_t pdu_id;
uint8_t code;
return AVRCP_HEADER_LENGTH + 1;
}
+size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands)
+{
+ struct avrcp_header *pdu = (void *) operands;
+ uint32_t company_id = get_company_id(pdu->company_id);
+
+ *code = AVC_CTYPE_REJECTED;
+ pdu->params_len = htons(1);
+ pdu->params[0] = E_INTERNAL;
+
+ DBG("rejecting AVRCP PDU 0x%02X, company 0x%06X len 0x%04X",
+ pdu->pdu_id, company_id, pdu->params_len);
+
+ return AVRCP_HEADER_LENGTH + 1;
+}
+
static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src)
{
for (; list; list = list->next) {
return NULL;
}
+static gboolean avrcp_handle_volume_changed(struct avctp *session,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t volume;
+
+ if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED)
+ return FALSE;
+
+ volume = pdu->params[1] & 0x7F;
+
+ player->cb->set_volume(volume, player->dev, player->user_data);
+
+ if (code == AVC_CTYPE_CHANGED) {
+ register_volume_notification(player);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void register_volume_notification(struct avrcp_player *player)
+{
+ uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH];
+ struct avrcp_header *pdu = (void *) buf;
+ uint8_t length;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+ pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+ pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
+ pdu->params[0] = AVRCP_EVENT_VOLUME_CHANGED;
+ pdu->params_len = htons(AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH);
+
+ length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
+
+ avctp_send_vendordep_req(player->session, AVC_CTYPE_NOTIFY,
+ AVC_SUBUNIT_PANEL, buf, length,
+ avrcp_handle_volume_changed, player);
+}
+
static void state_changed(struct audio_device *dev, avctp_state_t old_state,
avctp_state_t new_state, void *user_data)
{
struct avrcp_server *server;
struct avrcp_player *player;
+ const sdp_record_t *rec;
+ sdp_list_t *list;
+ sdp_profile_desc_t *desc;
server = find_server(servers, &dev->src);
if (!server)
switch (new_state) {
case AVCTP_STATE_DISCONNECTED:
player->session = NULL;
+ player->dev = NULL;
player->registered_events = 0;
if (player->handler) {
break;
case AVCTP_STATE_CONNECTING:
player->session = avctp_connect(&dev->src, &dev->dst);
+ player->dev = dev;
if (!player->handler)
player->handler = avctp_register_pdu_handler(
handle_vendordep_pdu,
player);
break;
+ case AVCTP_STATE_CONNECTED:
+ rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID);
+ if (rec == NULL)
+ return;
+
+ if (sdp_get_profile_descs(rec, &list) < 0)
+ return;
+
+ desc = list->data;
+
+ if (desc && desc->version >= 0x0104)
+ register_volume_notification(player);
+
+ sdp_list_free(list, free);
default:
return;
}
player_destroy(player);
}
+
+static gboolean avrcp_handle_set_volume(struct avctp *session,
+ uint8_t code, uint8_t subunit,
+ uint8_t *operands, size_t operand_count,
+ void *user_data)
+{
+ struct avrcp_player *player = user_data;
+ struct avrcp_header *pdu = (void *) operands;
+ uint8_t volume;
+
+ if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED)
+ return FALSE;
+
+ volume = pdu->params[0] & 0x7F;
+
+ player->cb->set_volume(volume, player->dev, player->user_data);
+
+ return FALSE;
+}
+
+int avrcp_set_volume(struct audio_device *dev, uint8_t volume)
+{
+ struct avrcp_server *server;
+ struct avrcp_player *player;
+ uint8_t buf[AVRCP_HEADER_LENGTH + 1];
+ struct avrcp_header *pdu = (void *) buf;
+
+ server = find_server(servers, &dev->src);
+ if (server == NULL)
+ return -EINVAL;
+
+ player = server->active_player;
+ if (player == NULL)
+ return -ENOTSUP;
+
+ if (player->session == NULL)
+ return -ENOTCONN;
+
+ memset(buf, 0, sizeof(buf));
+
+ set_company_id(pdu->company_id, IEEEID_BTSIG);
+
+ pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
+ pdu->params[0] = volume;
+ pdu->params_len = htons(1);
+
+ DBG("volume=%u", volume);
+
+ return avctp_send_vendordep_req(player->session, AVC_CTYPE_CONTROL,
+ AVC_SUBUNIT_PANEL, buf, sizeof(buf),
+ avrcp_handle_set_volume, player);
+}
#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
+#define AVRCP_EVENT_VOLUME_CHANGED 0x0d
+#define AVRCP_EVENT_LAST AVRCP_EVENT_VOLUME_CHANGED
struct avrcp_player_cb {
int (*get_setting) (uint8_t attr, void *user_data);
GList *(*list_metadata) (void *user_data);
uint8_t (*get_status) (void *user_data);
uint32_t (*get_position) (void *user_data);
+ void (*set_volume) (uint8_t volume, struct audio_device *dev,
+ void *user_data);
};
int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config);
gboolean avrcp_connect(struct audio_device *dev);
void avrcp_disconnect(struct audio_device *dev);
+int avrcp_set_volume(struct audio_device *dev, uint8_t volume);
struct avrcp_player *avrcp_register_player(const bdaddr_t *src,
struct avrcp_player_cb *cb,
void avrcp_unregister_player(struct avrcp_player *player);
int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data);
+
+
+size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands);
return reply;
}
-static GDBusMethodTable control_methods[] = {
- { "IsConnected", "", "b", control_is_connected,
- G_DBUS_METHOD_FLAG_DEPRECATED },
- { "GetProperties", "", "a{sv}",control_get_properties },
- { "VolumeUp", "", "", volume_up },
- { "VolumeDown", "", "", volume_down },
- { NULL, NULL, NULL, NULL }
+static const GDBusMethodTable control_methods[] = {
+ { GDBUS_ASYNC_METHOD("IsConnected",
+ NULL, GDBUS_ARGS({ "connected", "b" }),
+ control_is_connected) },
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ control_get_properties) },
+ { GDBUS_METHOD("VolumeUp", NULL, NULL, volume_up) },
+ { GDBUS_METHOD("VolumeDown", NULL, NULL, volume_down) },
+ { }
};
-static GDBusSignalTable control_signals[] = {
- { "Connected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED},
- { "Disconnected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED},
- { "PropertyChanged", "sv" },
- { NULL, NULL }
+static const GDBusSignalTable control_signals[] = {
+ { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) },
+ { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) },
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
};
static void path_unregister(void *data)
return reply;
}
-static GDBusMethodTable dev_methods[] = {
- { "Connect", "", "", dev_connect,
- G_DBUS_METHOD_FLAG_ASYNC },
- { "Disconnect", "", "", dev_disconnect },
- { "GetProperties", "", "a{sv}",dev_get_properties },
- { NULL, NULL, NULL, NULL }
+static const GDBusMethodTable dev_methods[] = {
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) },
+ { GDBUS_METHOD("Disconnect", NULL, NULL, dev_disconnect) },
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ dev_get_properties) },
+ { }
};
-static GDBusSignalTable dev_signals[] = {
- { "PropertyChanged", "sv" },
- { NULL, NULL }
+static const GDBusSignalTable dev_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
};
struct audio_device *audio_device_register(DBusConnection *conn,
control_is_active(dev))
return TRUE;
else if (!strcmp(interface, AUDIO_GATEWAY_INTERFACE) && dev->gateway &&
- gateway_is_connected(dev))
+ gateway_is_active(dev))
return TRUE;
return FALSE;
*
*/
-#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb"
-
-#define HSP_HS_UUID "00001108-0000-1000-8000-00805f9b34fb"
-#define HSP_AG_UUID "00001112-0000-1000-8000-00805f9b34fb"
-
-#define HFP_HS_UUID "0000111e-0000-1000-8000-00805f9b34fb"
-#define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb"
-
-#define ADVANCED_AUDIO_UUID "0000110d-0000-1000-8000-00805f9b34fb"
-
-#define A2DP_SOURCE_UUID "0000110a-0000-1000-8000-00805f9b34fb"
-#define A2DP_SINK_UUID "0000110b-0000-1000-8000-00805f9b34fb"
-
-#define AVRCP_REMOTE_UUID "0000110e-0000-1000-8000-00805f9b34fb"
-#define AVRCP_TARGET_UUID "0000110c-0000-1000-8000-00805f9b34fb"
-
struct source;
struct control;
struct target;
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
-#include "glib-compat.h"
#include "sdp-client.h"
#include "device.h"
#include "gateway.h"
#include "btio.h"
#include "dbus-common.h"
-#ifndef DBUS_TYPE_UNIX_FD
-#define DBUS_TYPE_UNIX_FD -1
-#endif
-
struct hf_agent {
char *name; /* Bus id */
char *path; /* D-Bus path */
DBUS_TYPE_UINT16, &gw->version,
DBUS_TYPE_INVALID);
- if (dbus_connection_send_with_reply(dev->conn, msg, &call, -1) == FALSE)
+ if (dbus_connection_send_with_reply(dev->conn, msg,
+ &call, -1) == FALSE) {
+ dbus_message_unref(msg);
return FALSE;
+ }
dbus_pending_call_set_notify(call, notify, dev, NULL);
dbus_pending_call_unref(call);
if (err) {
error("sco_connect_cb(): %s", err->message);
- gateway_close(dev);
+ gateway_suspend_stream(dev);
return;
}
io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, &dev->src,
BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_CHANNEL, ch,
BT_IO_OPT_INVALID);
if (!io) {
if (!device->conn)
return NULL;
+ if (!gw->rfcomm)
+ return btd_error_not_connected(msg);
+
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
- if (!gw->rfcomm)
- return btd_error_not_connected(msg);
-
gateway_close(device);
ba2str(&device->dst, gw_addr);
DBG("Disconnected from %s, %s", gw_addr, device->path);
return dbus_message_new_method_return(msg);
}
-static GDBusMethodTable gateway_methods[] = {
- { "Connect", "", "", ag_connect, G_DBUS_METHOD_FLAG_ASYNC },
- { "Disconnect", "", "", ag_disconnect, G_DBUS_METHOD_FLAG_ASYNC },
- { "GetProperties", "", "a{sv}", ag_get_properties },
- { "RegisterAgent", "o", "", register_agent },
- { "UnregisterAgent", "o", "", unregister_agent },
- { NULL, NULL, NULL, NULL }
+static const GDBusMethodTable gateway_methods[] = {
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, ag_connect) },
+ { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, ag_disconnect) },
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ ag_get_properties) },
+ { GDBUS_METHOD("RegisterAgent",
+ GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) },
+ { GDBUS_METHOD("UnregisterAgent",
+ GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) },
+ { }
};
-static GDBusSignalTable gateway_signals[] = {
- { "PropertyChanged", "sv" },
- { NULL, NULL }
+static const GDBusSignalTable gateway_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
};
static void path_unregister(void *data)
struct gateway *gateway_init(struct audio_device *dev)
{
- if (DBUS_TYPE_UNIX_FD < 0)
- return NULL;
-
if (!g_dbus_register_interface(dev->conn, dev->path,
AUDIO_GATEWAY_INTERFACE,
gateway_methods, gateway_signals,
gateway_stream_cb_t cb, void *user_data)
{
struct gateway *gw = dev->gateway;
- unsigned int id;
GError *err = NULL;
GIOChannel *io;
- id = connect_cb_new(gw, cb, user_data);
-
if (!gw->rfcomm)
get_records(dev);
else if (!gw->sco) {
io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &dev->src,
BT_IO_OPT_DEST_BDADDR, &dev->dst,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_INVALID);
if (!io) {
error("%s", err->message);
} else
g_idle_add(request_stream_cb, dev);
- return id;
+ return connect_cb_new(gw, cb, user_data);
}
int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t cb,
return gst_element_register(plugin, "a2dpsink",
GST_RANK_MARGINAL, GST_TYPE_A2DP_SINK);
}
-
g_mutex_unlock(s->sink_lock); \
} G_STMT_END
-#ifndef DBUS_TYPE_UNIX_FD
-#define DBUS_TYPE_UNIX_FD -1
-#endif
-
struct bluetooth_data {
struct bt_get_capabilities_rsp *caps; /* Bluetooth device caps */
guint link_mtu;
sbc = (void *) gst_avdtp_find_caps(self, BT_A2DP_SBC_SINK);
mpeg = (void *) gst_avdtp_find_caps(self, BT_A2DP_MPEG12_SINK);
+ if (!sbc) {
+ GST_ERROR_OBJECT(self, "Failed to find mandatory SBC sink");
+ return FALSE;
+ }
+
sbc_structure = gst_avdtp_sink_parse_sbc_caps(self, sbc);
mpeg_structure = gst_avdtp_sink_parse_mpeg_caps(self, mpeg);
static gboolean gst_avdtp_sink_get_capabilities(GstAvdtpSink *self)
{
- gchar *buf[BT_SUGGESTED_BUFFER_SIZE];
+ gchar buf[BT_SUGGESTED_BUFFER_SIZE];
struct bt_get_capabilities_req *req = (void *) buf;
struct bt_get_capabilities_rsp *rsp = (void *) buf;
int err;
reply = dbus_connection_send_with_reply_and_block(self->data->conn,
msg, -1, &err);
+ dbus_message_unref(msg);
+
if (dbus_error_is_set(&err))
goto fail;
dbus_error_free(&err);
if (reply)
- dbus_message_unref(msg);
+ dbus_message_unref(reply);
return FALSE;
}
return gst_element_register(plugin, "rtpsbcpay", GST_RANK_NONE,
GST_TYPE_RTP_SBC_PAY);
}
-
return gst_element_register(plugin, "sbcdec", GST_RANK_PRIMARY,
GST_TYPE_SBC_DEC);
}
-
-
return gst_element_register(plugin, "sbcenc",
GST_RANK_NONE, GST_TYPE_SBC_ENC);
}
-
-
return gst_element_register(plugin, "sbcparse", GST_RANK_NONE,
GST_TYPE_SBC_PARSE);
}
-
GValue *value);
gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps);
-
#include "error.h"
#include "telephony.h"
#include "headset.h"
-#include "glib-compat.h"
#include "sdp-client.h"
#include "btio.h"
#include "dbus-common.h"
guint dc_timer;
-#ifdef __TIZEN_PATCH__
- guint rfcomm_io_id;
-#endif
gboolean hfp_active;
gboolean search_hfp;
gboolean rfcomm_initiator;
}
#ifdef __TIZEN_PATCH__
-int telephony_list_phonebook_store_rsp(void *telephony_device, const char *buf, cme_error_t err)
+int telephony_select_phonebook_memory_status_rsp(void *telephony_device, const char *path,
+ uint32_t total, uint32_t used,
+ 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) {
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 (NULL != buf) {
+ if (0 != total && 0 != used) {
if (!active_devices)
return -ENODEV;
send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPBS: %s\r\n", buf);
-
+ "\r\n+CPBS: %s,%d,%d\r\n",
+ path, used, total);
}
return headset_send(hs, "\r\nOK\r\n");
}
-int telephony_read_phonebook_store_rsp(void *telephony_device, char* pb_store, uint32_t total, uint32_t used, cme_error_t err)
+int telephony_select_phonebook_memory_list_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) {
+ 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 headset_send(hs, "\r\nERROR\r\n");
}
- if (0 != total && 0 != used) {
+ if (NULL != buf) {
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\r\n", buf);
+
}
return headset_send(hs, "\r\nOK\r\n");
}
+int telephony_select_phonebook_memory_rsp(void *telephony_device, cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
+
static int select_phonebook_memory(struct audio_device *device, const char *buf)
{
- struct headset *hs = device->headset;
- int err;
+ if (strlen(buf) < 8)
+ return -EINVAL;
- if (NULL != buf) {
- if (strlen(buf) < 9)
- return -EINVAL;
+ if (buf[7] == '?') {
+ telephony_select_phonebook_memory_status(device);
+ return 0;
+ }
- if (buf[7] == '?') {
- telephony_read_phonebook_store(device);
+ if (buf[7] == '=') {
+ if (buf[8] == '?') {
+ telephony_select_phonebook_memory_list(device);
return 0;
}
-
- if (buf[8] == '=') {
- if (buf[9] == '?') {
- telephony_list_phonebook_store(device);
- } else {
- telephony_set_phonebook_store(device, &buf[9]);
- return headset_send(hs, "\r\nOK\r\n");
- }
- }
+ telephony_select_phonebook_memory(device, &buf[8]);
+ return 0;
}
- return 0;
+
+ return -EINVAL;
}
-int telephony_read_phonebook_attributes_rsp(void *telephony_device,
- uint32_t total,
+int telephony_read_phonebook_entries_list_rsp(void *telephony_device,
+ uint32_t used,
uint32_t number_length,
uint32_t name_length,
cme_error_t err)
struct headset *hs = device->headset;
struct headset_slc *slc = hs->slc;
+ uint32_t index = 0;
+ int send_err = 0;
+
if (err != CME_ERROR_NONE) {
if (slc->cme_enabled)
return headset_send(hs, "\r\n+CME ERROR: %d\r\n", err);
return headset_send(hs, "\r\nERROR\r\n");
}
- if (total != 0 && name_length != 0 && number_length != 0) {
- if (!active_devices)
- return -ENODEV;
+ if (used < 0)
+ index = 1;
+
+ send_err = headset_send(hs, "\r\n+CPBR: (%d-%d),%d,%d\r\n",
+ index, used, number_length, name_length);
+ if (send_err < 0)
+ return send_err;
- send_foreach_headset(active_devices, hfp_cmp,
- "\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_entries_rsp(void *telephony_device, cme_error_t err)
{
- struct audio_device *device = telephony_device;
- struct headset *hs = device->headset;
- struct headset_slc *slc = hs->slc;
+ return telephony_generic_rsp(telephony_device, err);
+}
- 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");
- }
+int telephony_read_phonebook_entries_ind(const char *name, const char *number,
+ uint32_t handle)
+{
+ int type = 129;
+ const char *pos = NULL;
- if (NULL != data) {
- if (!active_devices)
- return -ENODEV;
+ pos = number;
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
- send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPBR: %s\r\n", data);
- }
+ /* 145 means international access code, otherwise 129 is used */
+ if (*pos == '+')
+ type = 145;
+
+ send_foreach_headset(active_devices, hfp_cmp,
+ "\r\n+CPBR: %d,\"%s\",%d,\"%s\"\r\n",
+ handle, number, type, name);
+ return 0;
}
static int read_phonebook_entries(struct audio_device *device, const char *buf)
{
- struct headset *hs = device->headset;
- int err;
+ if (strlen(buf) < 8)
+ return -EINVAL;
- if (NULL != buf) {
- if (strlen(buf) < 8)
- return -EINVAL;
+ if (buf[7] != '=')
+ return -EINVAL;
+
+ if (buf[8] == '?')
+ telephony_read_phonebook_entries_list(device);
+ else
+ telephony_read_phonebook_entries(device, &buf[8]);
- if (buf[9] == '?') {
- telephony_read_phonebook_attributes(device);
- } 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,
+int telephony_find_phonebook_entries_status_rsp(void *telephony_device,
cme_error_t err)
{
- struct audio_device *device = telephony_device;
- struct headset *hs = device->headset;
- struct headset_slc *slc = hs->slc;
+ return telephony_generic_rsp(telephony_device, err);
+}
- 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");
- }
+int telephony_find_phonebook_entries_rsp(void *telephony_device,
+ cme_error_t err)
+{
+ return telephony_generic_rsp(telephony_device, err);
+}
- if (max_name_length != 0 && max_number_length != 0) {
- if (!active_devices)
- return -ENODEV;
+int telephony_find_phonebook_entries_status_ind( uint32_t number_length,
+ uint32_t name_length)
+{
+ send_foreach_headset(active_devices, hfp_cmp,
+ "\r\n+CPBF: %d,%d\r\n",
+ number_length, name_length);
- send_foreach_headset(active_devices, hfp_cmp,
- "\r\n+CPBF: %d,%d\r\n",
- max_number_length,
- max_name_length);
- }
- return headset_send(hs, "\r\nOK\r\n");
+ return 0;
+}
+
+int telephony_find_phonebook_entries_ind(const char *name, const char *number,
+ uint32_t handle)
+{
+ int type = 129;
+ const char *pos = NULL;
+
+ pos = number;
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+
+ /* 145 means international access code, otherwise 129 is used */
+ if (*pos == '+')
+ type = 145;
+
+ send_foreach_headset(active_devices, hfp_cmp,
+ "\r\n+CPBF: %d,\"%s\",%d,\"%s\"\r\n",
+ handle, number, type, name);
+ return 0;
}
static int find_phonebook_entires(struct audio_device *device, const char *buf)
{
- struct headset *hs = device->headset;
- int err;
+ if (strlen(buf) < 8)
+ return -EINVAL;
- if (NULL != buf) {
- if (strlen(buf) < 8)
- return -EINVAL;
+ if (buf[7] != '=')
+ return -EINVAL;
+
+ if (buf[8] == '?')
+ telephony_find_phonebook_entries_status(device);
+ else
+ telephony_find_phonebook_entries(device, &buf[8]);
- if (buf[9] == '?')
- telephony_find_phonebook_entry_properties(device);
- else
- telephony_find_phonebook_entry(device, &buf[9]);
- }
return 0;
}
static int preffered_message_storage(struct audio_device *device, const char *buf)
{
struct headset *hs = device->headset;
- int err;
if (NULL != buf) {
if (strlen(buf) < 9)
static int select_character_set(struct audio_device *device, const char *buf)
{
struct headset *hs = device->headset;
- int err;
+
if (NULL != buf) {
if (strlen(buf) < 9)
return -EINVAL;
return 0;
}
+
+int telephony_battery_charge_status_rsp(void *telephony_device,
+ int32_t bcs,
+ int32_t bcl,
+ cme_error_t err)
+{
+ struct audio_device *device = telephony_device;
+
+ if (err == CME_ERROR_NONE) {
+ send_foreach_headset(active_devices, hfp_cmp,
+ "\r\n+CBC: %d,%d\r\n", bcs, bcl);
+ }
+
+ return telephony_generic_rsp(device, err);
+}
+
+static int get_battery_charge_status(struct audio_device *device, const char *buf)
+{
+ if (strlen(buf) < 8)
+ return -EINVAL;
+
+ if (buf[7] == '?')
+ telephony_get_battery_property(device);
+
+ return 0;
+}
+
+int telephony_signal_quality_rsp(void *telephony_device,
+ int32_t rssi,
+ int32_t ber,
+ cme_error_t err)
+{
+ struct audio_device *device = telephony_device;
+
+ if (err == CME_ERROR_NONE) {
+ send_foreach_headset(active_devices, hfp_cmp,
+ "\r\n+CSQ: %d,%d\r\n", rssi, ber);
+ }
+ return telephony_generic_rsp(device,err);
+}
+
+static int get_signal_quality(struct audio_device *device, const char *buf)
+{
+ if (strlen(buf) < 8)
+ return -EINVAL;
+
+ if (buf[7] == '?')
+ telephony_get_signal_quality(device);
+
+ return 0;
+
+}
+
#endif
static int apple_command(struct audio_device *device, const char *buf)
{ "AT+XAPL", apple_command },
{ "AT+IPHONEACCEV", apple_command },
#ifdef __TIZEN_PATCH__
- /*TIZEN 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 },
+ { "AT+CSQ", get_signal_quality },
+ { "AT+CBC", get_battery_charge_status },
#endif
{ 0 }
};
return FALSE;
hs = device->headset;
-#ifdef __TIZEN_PATCH__
- if (!hs)
- return FALSE;
-#endif
slc = hs->slc;
if (cond & (G_IO_ERR | G_IO_HUP)) {
if (free_space < (size_t) bytes_read) {
/* Very likely that the HS is sending us garbage so
* just ignore the data and disconnect */
- error("Too much data to fit incomming buffer");
+ error("Too much data to fit incoming buffer");
goto failed;
}
else
hs->auto_dc = FALSE;
-#ifdef __TIZEN_PATCH__
- hs->rfcomm_io_id = g_io_add_watch(chan,
- 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,
(GIOFunc) rfcomm_io_cb, dev);
-#endif
DBG("%s: Connected to %s", dev->path, hs_address);
if (svc == HANDSFREE_SVCLASS_ID) {
headset->hfp_handle = record->handle;
+ headset->hsp_handle = 0;
DBG("Discovered Handsfree service on channel %d", ch);
} else {
headset->hsp_handle = record->handle;
+ headset->hfp_handle = 0;
DBG("Discovered Headset service on channel %d", ch);
}
DBG("%s: Connecting to %s channel %d", dev->path, address,
hs->rfcomm_ch);
-#ifdef __TIZEN_PATCH__
- if (hs->rfcomm_io_id) {
- g_source_remove(hs->rfcomm_io_id);
- hs->rfcomm_io_id = 0;
- }
-#endif
hs->tmp_rfcomm = bt_io_connect(BT_IO_RFCOMM, headset_connect_cb, dev,
NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &dev->src,
BT_IO_OPT_DEST_BDADDR, &dev->dst,
BT_IO_OPT_CHANNEL, hs->rfcomm_ch,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_INVALID);
hs->rfcomm_ch = -1;
return btd_error_invalid_args(msg);
}
-static GDBusMethodTable headset_methods[] = {
- { "Connect", "", "", hs_connect,
- G_DBUS_METHOD_FLAG_ASYNC },
- { "Disconnect", "", "", hs_disconnect },
- { "IsConnected", "", "b", hs_is_connected },
- { "IndicateCall", "", "", hs_ring },
- { "CancelCall", "", "", hs_cancel_call },
- { "Play", "", "", hs_play,
- G_DBUS_METHOD_FLAG_ASYNC |
- G_DBUS_METHOD_FLAG_DEPRECATED },
- { "Stop", "", "", hs_stop },
- { "IsPlaying", "", "b", hs_is_playing,
- G_DBUS_METHOD_FLAG_DEPRECATED },
- { "GetSpeakerGain", "", "q", hs_get_speaker_gain,
- G_DBUS_METHOD_FLAG_DEPRECATED },
- { "GetMicrophoneGain", "", "q", hs_get_mic_gain,
- G_DBUS_METHOD_FLAG_DEPRECATED },
- { "SetSpeakerGain", "q", "", hs_set_speaker_gain,
- G_DBUS_METHOD_FLAG_DEPRECATED },
- { "SetMicrophoneGain", "q", "", hs_set_mic_gain,
- G_DBUS_METHOD_FLAG_DEPRECATED },
- { "GetProperties", "", "a{sv}",hs_get_properties },
- { "SetProperty", "sv", "", hs_set_property },
- { NULL, NULL, NULL, NULL }
+#ifdef __TIZEN_PATCH__
+static DBusMessage *hs_set_voice_dial(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct audio_device *device = data;
+ struct headset *hs = device->headset;
+ struct headset_slc *slc = hs->slc;
+ DBusMessage *reply;
+ int err;
+ dbus_bool_t enable;
+
+ if (hs->state < HEADSET_STATE_CONNECTED)
+ return btd_error_not_connected(msg);
+
+ if (!(slc->hf_features & HF_FEATURE_VOICE_RECOGNITION)) {
+ DBG("Voice Recognition is not supported by HF \n");
+ return btd_error_not_supported(msg);
+ }
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &enable,
+ DBUS_TYPE_INVALID))
+ return NULL;
+
+ DBG("hs_set_voice_dial = %d \n", enable);
+
+ reply = dbus_message_new_method_return(msg);
+
+ if (!reply)
+ return NULL;
+
+ err = headset_send(hs, "\r\n+BVRA: %d\r\n", enable);
+
+ if (err < 0) {
+ dbus_message_unref(reply);
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ return reply;
+}
+#endif
+
+static const GDBusMethodTable headset_methods[] = {
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, hs_connect) },
+ { GDBUS_METHOD("Disconnect", NULL, NULL, hs_disconnect) },
+ { GDBUS_METHOD("IsConnected",
+ NULL, GDBUS_ARGS({ "connected", "b" }),
+ hs_is_connected) },
+ { GDBUS_METHOD("IndicateCall", NULL, NULL, hs_ring) },
+ { GDBUS_METHOD("CancelCall", NULL, NULL, hs_cancel_call) },
+ { GDBUS_DEPRECATED_ASYNC_METHOD("Play", NULL, NULL, hs_play) },
+ { GDBUS_METHOD("Stop", NULL, NULL, hs_stop) },
+ { GDBUS_DEPRECATED_METHOD("IsPlaying",
+ NULL, GDBUS_ARGS({ "playing", "b" }),
+ hs_is_playing) },
+ { GDBUS_DEPRECATED_METHOD("GetSpeakerGain",
+ NULL, GDBUS_ARGS({ "gain", "q" }),
+ hs_get_speaker_gain) },
+ { GDBUS_DEPRECATED_METHOD("GetMicrophoneGain",
+ NULL, GDBUS_ARGS({ "gain", "q" }),
+ hs_get_mic_gain) },
+ { GDBUS_DEPRECATED_METHOD("SetSpeakerGain",
+ GDBUS_ARGS({ "gain", "q" }), NULL,
+ hs_set_speaker_gain) },
+ { GDBUS_DEPRECATED_METHOD("SetMicrophoneGain",
+ GDBUS_ARGS({ "gain", "q" }), NULL,
+ hs_set_mic_gain) },
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ hs_get_properties) },
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+ hs_set_property) },
+#ifdef __TIZEN_PATCH__
+ { GDBUS_METHOD("SetVoiceDial",
+ GDBUS_ARGS({ "enable", "b" }), NULL,
+ hs_set_voice_dial) },
+#endif
+ { }
};
-static GDBusSignalTable headset_signals[] = {
- { "Connected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
- { "Disconnected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
- { "AnswerRequested", "" },
- { "Stopped", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
- { "Playing", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
- { "SpeakerGainChanged", "q", G_DBUS_SIGNAL_FLAG_DEPRECATED },
- { "MicrophoneGainChanged", "q", G_DBUS_SIGNAL_FLAG_DEPRECATED },
- { "CallTerminated", "" },
- { "PropertyChanged", "sv" },
- { NULL, NULL }
+static const GDBusSignalTable headset_signals[] = {
+ { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) },
+ { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) },
+ { GDBUS_DEPRECATED_SIGNAL("AnswerRequested", NULL) },
+ { GDBUS_DEPRECATED_SIGNAL("Stopped", NULL) },
+ { GDBUS_DEPRECATED_SIGNAL("Playing", NULL) },
+ { GDBUS_DEPRECATED_SIGNAL("SpeakerGainChanged",
+ GDBUS_ARGS({ "gain", "q" })) },
+ { GDBUS_DEPRECATED_SIGNAL("MicrophoneGainChanged",
+ GDBUS_ARGS({ "gain", "q" })) },
+ { GDBUS_SIGNAL("CallTerminated", NULL) },
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
};
void headset_update(struct audio_device *dev, uint16_t svc,
hs->rfcomm = NULL;
}
-#ifdef __TIZEN_PATCH__
- if (hs->rfcomm_io_id) {
- g_source_remove(hs->rfcomm_io_id);
- hs->rfcomm_io_id = 0;
- }
-#endif
g_free(hs->slc);
hs->slc = NULL;
void headset_set_rfcomm_initiator(struct audio_device *dev,
gboolean initiator);
-void headset_set_authorized(struct audio_device *dev);
int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *chan);
int headset_connect_sco(struct audio_device *dev, GIOChannel *io);
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include <glib.h>
#include <dbus/dbus.h>
#include "telephony.h"
#include "unix.h"
-#ifndef DBUS_TYPE_UNIX_FD
-#define DBUS_TYPE_UNIX_FD -1
-#endif
-
typedef enum {
HEADSET = 1 << 0,
GATEWAY = 1 << 1,
return;
}
- server_uuid = HFP_AG_UUID;
- remote_uuid = HFP_HS_UUID;
+ server_uuid = HFP_HS_UUID;
+ remote_uuid = HFP_AG_UUID;
device = manager_get_device(&src, &dst, TRUE);
if (!device)
perr = audio_device_request_authorization(device, server_uuid,
gateway_auth_cb, device);
if (perr < 0) {
- DBG("Authorization denied!");
+ DBG("Authorization denied: %s", strerror(-perr));
gateway_set_state(device, GATEWAY_STATE_DISCONNECTED);
}
record = hfp_hs_record(chan);
if (!record) {
error("Unable to allocate new service record");
- return -1;
+ goto failed;
}
if (add_record_to_server(&src, record) < 0) {
error("Unable to register HFP HS service record");
sdp_record_free(record);
- g_io_channel_unref(adapter->hfp_hs_server);
- adapter->hfp_hs_server = NULL;
- return -1;
+ goto failed;
}
adapter->hfp_hs_record_id = record->handle;
return 0;
+
+failed:
+ g_io_channel_shutdown(adapter->hfp_hs_server, TRUE, NULL);
+ g_io_channel_unref(adapter->hfp_hs_server);
+ adapter->hfp_hs_server = NULL;
+ return -1;
}
static int audio_probe(struct btd_device *device, GSList *uuids)
adp = find_adapter(adapters, adapter);
if (!adp) {
adp = audio_adapter_create(adapter);
- if (!adp)
- return NULL;
adapters = g_slist_append(adapters, adp);
} else
audio_adapter_ref(adp);
/* telephony driver already initialized*/
if (telephony == TRUE)
return;
+#ifdef __TIZEN_PATCH__
+ if (telephony_init())
+ telephony = FALSE;
+ else
+ telephony = TRUE;
+#else
telephony_init();
telephony = TRUE;
+#endif
return;
}
if (!adp)
return -EINVAL;
- btd_adapter_register_powered_callback(adapter, state_changed);
- state_changed(adapter, TRUE);
-
err = headset_server_init(adp);
if (err < 0) {
audio_adapter_unref(adp);
return err;
}
+ btd_adapter_register_powered_callback(adapter, state_changed);
+
return 0;
}
DBG("path %s", path);
+ btd_adapter_unregister_powered_callback(adapter, state_changed);
+
adp = find_adapter(adapters, adapter);
if (!adp)
return;
adp->hfp_ag_server = NULL;
}
- btd_adapter_unregister_powered_callback(adapter, state_changed);
-
audio_adapter_unref(adp);
}
static int gateway_server_probe(struct btd_adapter *adapter)
{
struct audio_adapter *adp;
+ int err;
adp = audio_adapter_get(adapter);
if (!adp)
return -EINVAL;
- return gateway_server_init(adp);
+ err = gateway_server_init(adp);
+ if (err < 0)
+ audio_adapter_unref(adp);
+
+ return err;
}
static void gateway_server_remove(struct btd_adapter *adapter)
}
if (adp->hfp_hs_server) {
+ g_io_channel_shutdown(adp->hfp_hs_server, TRUE, NULL);
g_io_channel_unref(adp->hfp_hs_server);
adp->hfp_hs_server = NULL;
}
struct audio_adapter *adp;
const gchar *path = adapter_get_path(adapter);
bdaddr_t src;
+ int err;
DBG("path %s", path);
adapter_get_address(adapter, &src);
- return avrcp_register(connection, &src, config);
+ err = avrcp_register(connection, &src, config);
+ if (err < 0)
+ audio_adapter_unref(adp);
+
+ return err;
}
static void avrcp_server_remove(struct btd_adapter *adapter)
struct audio_adapter *adp;
const gchar *path = adapter_get_path(adapter);
bdaddr_t src;
+ int err;
DBG("path %s", path);
adapter_get_address(adapter, &src);
- return media_register(connection, path, &src);
+ err = media_register(connection, path, &src);
+ if (err < 0)
+ audio_adapter_unref(adp);
+
+ return err;
}
static void media_server_remove(struct btd_adapter *adapter)
btd_unregister_device_driver(&audio_driver);
}
-struct audio_device *manager_find_device(const char *path,
+GSList *manager_find_devices(const char *path,
const bdaddr_t *src,
const bdaddr_t *dst,
const char *interface,
gboolean connected)
{
+ GSList *result = NULL;
GSList *l;
for (l = devices; l != NULL; l = l->next) {
if (connected && !audio_device_is_active(dev, interface))
continue;
- return dev;
+ result = g_slist_append(result, dev);
}
- return NULL;
+ return result;
+}
+
+struct audio_device *manager_find_device(const char *path,
+ const bdaddr_t *src,
+ const bdaddr_t *dst,
+ const char *interface,
+ gboolean connected)
+{
+ struct audio_device *result;
+ GSList *l;
+
+ l = manager_find_devices(path, src, dst, interface, connected);
+ if (l == NULL)
+ return NULL;
+
+ result = l->data;
+ g_slist_free(l);
+ return result;
}
struct audio_device *manager_get_device(const bdaddr_t *src,
const char *interface,
gboolean connected);
+GSList *manager_find_devices(const char *path,
+ const bdaddr_t *src,
+ const bdaddr_t *dst,
+ const char *interface,
+ gboolean connected);
+
struct audio_device *manager_get_device(const bdaddr_t *src,
const bdaddr_t *dst,
gboolean create);
#include <errno.h>
+#include <bluetooth/uuid.h>
+
#include <glib.h>
#include <gdbus.h>
#include "gateway.h"
#include "manager.h"
-#ifndef DBUS_TYPE_UNIX_FD
-#define DBUS_TYPE_UNIX_FD -1
-#endif
-
#define MEDIA_INTERFACE "org.bluez.Media"
#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint"
#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer"
};
struct endpoint_request {
+ struct media_endpoint *endpoint;
DBusMessage *msg;
DBusPendingCall *call;
media_endpoint_cb_t cb;
guint hs_watch;
guint ag_watch;
guint watch;
- struct endpoint_request *request;
- struct media_transport *transport;
+ GSList *requests;
struct media_adapter *adapter;
+ GSList *transports;
};
struct media_player {
guint track_watch;
uint8_t status;
uint32_t position;
+ uint8_t volume;
GTimer *timer;
};
g_free(request);
}
-static void media_endpoint_cancel(struct media_endpoint *endpoint)
+static void media_endpoint_cancel(struct endpoint_request *request)
{
- struct endpoint_request *request = endpoint->request;
+ struct media_endpoint *endpoint = request->endpoint;
if (request->call)
dbus_pending_call_cancel(request->call);
+ endpoint->requests = g_slist_remove(endpoint->requests, request);
+
endpoint_request_free(request);
- endpoint->request = NULL;
+}
+
+static void media_endpoint_cancel_all(struct media_endpoint *endpoint)
+{
+ while (endpoint->requests != NULL)
+ media_endpoint_cancel(endpoint->requests->data);
}
static void media_endpoint_destroy(struct media_endpoint *endpoint)
if (endpoint->ag_watch)
gateway_remove_state_cb(endpoint->ag_watch);
- if (endpoint->request)
- media_endpoint_cancel(endpoint);
+ media_endpoint_cancel_all(endpoint);
- if (endpoint->transport)
- media_transport_destroy(endpoint->transport);
+ g_slist_free_full(endpoint->transports,
+ (GDestroyNotify) media_transport_destroy);
g_dbus_remove_watch(adapter->conn, endpoint->watch);
g_free(endpoint->capabilities);
headset_shutdown(dev);
}
-static void clear_configuration(struct media_endpoint *endpoint)
+static void clear_configuration(struct media_endpoint *endpoint,
+ struct media_transport *transport)
{
DBusConnection *conn;
DBusMessage *msg;
const char *path;
- struct media_transport *transport = endpoint->transport;
-
- if (endpoint->transport == NULL)
- return;
-
- if (endpoint->request)
- media_endpoint_cancel(endpoint);
conn = endpoint->adapter->conn;
goto done;
}
- path = media_transport_get_path(endpoint->transport);
+ path = media_transport_get_path(transport);
dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID);
g_dbus_send_message(conn, msg);
done:
- endpoint->transport = NULL;
+ endpoint->transports = g_slist_remove(endpoint->transports, transport);
media_transport_destroy(transport);
}
+static void clear_endpoint(struct media_endpoint *endpoint)
+{
+ media_endpoint_cancel_all(endpoint);
+
+ while (endpoint->transports != NULL)
+ clear_configuration(endpoint, endpoint->transports->data);
+}
+
static void endpoint_reply(DBusPendingCall *call, void *user_data)
{
- struct media_endpoint *endpoint = user_data;
- struct endpoint_request *request = endpoint->request;
+ struct endpoint_request *request = user_data;
+ struct media_endpoint *endpoint = request->endpoint;
DBusMessage *reply;
DBusError err;
gboolean value;
if (request->cb)
request->cb(endpoint, NULL, size,
request->user_data);
- clear_configuration(endpoint);
+ clear_endpoint(endpoint);
dbus_message_unref(reply);
dbus_error_free(&err);
return;
goto done;
}
- dbus_error_init(&err);
if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE,
"SelectConfiguration")) {
DBusMessageIter args, array;
if (request->cb)
request->cb(endpoint, ret, size, request->user_data);
- if (endpoint->request)
- endpoint_request_free(endpoint->request);
- endpoint->request = NULL;
+ endpoint->requests = g_slist_remove(endpoint->requests, request);
+ endpoint_request_free(request);
}
static gboolean media_endpoint_async_call(DBusConnection *conn,
{
struct endpoint_request *request;
- if (endpoint->request)
- return FALSE;
-
request = g_new0(struct endpoint_request, 1);
/* Timeout should be less than avdtp request timeout (4 seconds) */
return FALSE;
}
- dbus_pending_call_set_notify(request->call, endpoint_reply, endpoint, NULL);
+ dbus_pending_call_set_notify(request->call, endpoint_reply, request,
+ NULL);
+ request->endpoint = endpoint;
request->msg = msg;
request->cb = cb;
request->destroy = destroy;
request->user_data = user_data;
- endpoint->request = request;
+
+ endpoint->requests = g_slist_append(endpoint->requests, request);
DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
dbus_message_get_destination(msg),
DBusConnection *conn;
DBusMessage *msg;
- if (endpoint->request != NULL)
- return FALSE;
-
conn = endpoint->adapter->conn;
msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
destroy);
}
+static gint transport_device_cmp(gconstpointer data, gconstpointer user_data)
+{
+ struct media_transport *transport = (struct media_transport *) data;
+ const struct audio_device *device = user_data;
+
+ if (device == media_transport_get_dev(transport))
+ return 0;
+
+ return -1;
+}
+
+static struct media_transport *find_device_transport(
+ struct media_endpoint *endpoint,
+ struct audio_device *device)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(endpoint->transports, device,
+ transport_device_cmp);
+ if (match == NULL)
+ return NULL;
+
+ return match->data;
+}
+
static gboolean set_configuration(struct media_endpoint *endpoint,
struct audio_device *device,
uint8_t *configuration, size_t size,
DBusMessage *msg;
const char *path;
DBusMessageIter iter;
+ struct media_transport *transport;
- if (endpoint->transport != NULL || endpoint->request != NULL)
+ transport = find_device_transport(endpoint, device);
+
+ if (transport != NULL)
return FALSE;
conn = endpoint->adapter->conn;
- endpoint->transport = media_transport_create(conn, endpoint, device,
+ transport = media_transport_create(conn, endpoint, device,
configuration, size);
- if (endpoint->transport == NULL)
+ if (transport == NULL)
return FALSE;
msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
"SetConfiguration");
if (msg == NULL) {
error("Couldn't allocate D-Bus message");
+ media_transport_destroy(transport);
return FALSE;
}
+ endpoint->transports = g_slist_append(endpoint->transports, transport);
+
dbus_message_iter_init_append(msg, &iter);
- path = media_transport_get_path(endpoint->transport);
+ path = media_transport_get_path(transport);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
- transport_get_properties(endpoint->transport, &iter);
+ transport_get_properties(transport, &iter);
return media_endpoint_async_call(conn, msg, endpoint, cb, user_data,
destroy);
void *user_data)
{
struct media_endpoint *endpoint = user_data;
+ struct media_transport *transport;
DBG("");
+ if (bacmp(&endpoint->adapter->src, &dev->src) != 0)
+ return;
+
switch (new_state) {
case HEADSET_STATE_DISCONNECTED:
- if (endpoint->transport &&
- media_transport_get_dev(endpoint->transport) == dev) {
+ transport = find_device_transport(endpoint, dev);
+ if (transport != NULL) {
DBG("Clear endpoint %p", endpoint);
- clear_configuration(endpoint);
+ clear_configuration(endpoint, transport);
}
break;
case HEADSET_STATE_CONNECTING:
{
struct media_endpoint *endpoint = user_data;
- clear_configuration(endpoint);
+ clear_endpoint(endpoint);
}
static void set_delay(struct a2dp_sep *sep, uint16_t delay, void *user_data)
{
struct media_endpoint *endpoint = user_data;
- if (endpoint->transport == NULL)
+ if (endpoint->transports == NULL)
return;
- media_transport_update_delay(endpoint->transport, delay);
+ media_transport_update_delay(endpoint->transports->data, delay);
}
static struct a2dp_endpoint a2dp_endpoint = {
{
struct media_endpoint *endpoint = user_data;
- if (endpoint->transport) {
- media_transport_destroy(endpoint->transport);
- endpoint->transport = NULL;
- }
+ clear_endpoint(endpoint);
endpoint->sep = NULL;
release_endpoint(endpoint);
void *user_data)
{
struct media_endpoint *endpoint = user_data;
+ struct media_transport *transport;
DBG("");
+ if (bacmp(&endpoint->adapter->src, &dev->src) != 0)
+ return;
+
switch (new_state) {
case GATEWAY_STATE_DISCONNECTED:
- if (endpoint->transport &&
- media_transport_get_dev(endpoint->transport) == dev) {
-
+ transport = find_device_transport(endpoint, dev);
+ if (transport != NULL) {
DBG("Clear endpoint %p", endpoint);
- clear_configuration(endpoint);
+ clear_configuration(endpoint, transport);
}
break;
case GATEWAY_STATE_CONNECTING:
}
}
+static gboolean endpoint_init_a2dp_source(struct media_endpoint *endpoint,
+ gboolean delay_reporting,
+ int *err)
+{
+ endpoint->sep = a2dp_add_sep(&endpoint->adapter->src,
+ AVDTP_SEP_TYPE_SOURCE, endpoint->codec,
+ delay_reporting, &a2dp_endpoint,
+ endpoint, a2dp_destroy_endpoint, err);
+ if (endpoint->sep == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean endpoint_init_a2dp_sink(struct media_endpoint *endpoint,
+ gboolean delay_reporting,
+ int *err)
+{
+ endpoint->sep = a2dp_add_sep(&endpoint->adapter->src,
+ AVDTP_SEP_TYPE_SINK, endpoint->codec,
+ delay_reporting, &a2dp_endpoint,
+ endpoint, a2dp_destroy_endpoint, err);
+ if (endpoint->sep == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean endpoint_init_ag(struct media_endpoint *endpoint, int *err)
+{
+ GSList *list;
+ GSList *l;
+
+ endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
+ endpoint);
+ list = manager_find_devices(NULL, &endpoint->adapter->src, BDADDR_ANY,
+ AUDIO_HEADSET_INTERFACE, TRUE);
+
+ for (l = list; l != NULL; l = l->next) {
+ struct audio_device *dev = l->data;
+
+ set_configuration(endpoint, dev, NULL, 0,
+ headset_setconf_cb, dev, NULL);
+ }
+
+ g_slist_free(list);
+
+ return TRUE;
+}
+
+static gboolean endpoint_init_hs(struct media_endpoint *endpoint, int *err)
+{
+ GSList *list;
+ GSList *l;
+
+ endpoint->ag_watch = gateway_add_state_cb(gateway_state_changed,
+ endpoint);
+ list = manager_find_devices(NULL, &endpoint->adapter->src, BDADDR_ANY,
+ AUDIO_GATEWAY_INTERFACE, TRUE);
+
+ for (l = list; l != NULL; l = l->next) {
+ struct audio_device *dev = l->data;
+
+ set_configuration(endpoint, dev, NULL, 0,
+ gateway_setconf_cb, dev, NULL);
+ }
+
+ g_slist_free(list);
+
+ return TRUE;
+}
+
static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
const char *sender,
const char *path,
int *err)
{
struct media_endpoint *endpoint;
+ gboolean succeeded;
endpoint = g_new0(struct media_endpoint, 1);
endpoint->sender = g_strdup(sender);
endpoint->adapter = adapter;
- if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) {
- endpoint->sep = a2dp_add_sep(&adapter->src,
- AVDTP_SEP_TYPE_SOURCE, codec,
- delay_reporting, &a2dp_endpoint,
- endpoint, a2dp_destroy_endpoint, err);
- if (endpoint->sep == NULL)
- goto failed;
- } else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
- endpoint->sep = a2dp_add_sep(&adapter->src,
- AVDTP_SEP_TYPE_SINK, codec,
- delay_reporting, &a2dp_endpoint,
- endpoint, a2dp_destroy_endpoint, err);
- if (endpoint->sep == NULL)
- goto failed;
- } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
- strcasecmp(uuid, HSP_AG_UUID) == 0) {
- struct audio_device *dev;
+ if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0)
+ succeeded = endpoint_init_a2dp_source(endpoint,
+ delay_reporting, err);
+ else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0)
+ succeeded = endpoint_init_a2dp_sink(endpoint,
+ delay_reporting, err);
+ else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
+ strcasecmp(uuid, HSP_AG_UUID) == 0)
+ succeeded = endpoint_init_ag(endpoint, err);
+ else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
+ strcasecmp(uuid, HSP_HS_UUID) == 0)
+ succeeded = endpoint_init_hs(endpoint, err);
+ else {
+ succeeded = FALSE;
- endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
- endpoint);
- dev = manager_find_device(NULL, &adapter->src, BDADDR_ANY,
- AUDIO_HEADSET_INTERFACE, TRUE);
- if (dev)
- set_configuration(endpoint, dev, NULL, 0,
- headset_setconf_cb, dev, NULL);
- } else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
- strcasecmp(uuid, HSP_HS_UUID) == 0) {
- struct audio_device *dev;
-
- endpoint->ag_watch = gateway_add_state_cb(gateway_state_changed,
- endpoint);
- dev = manager_find_device(NULL, &adapter->src, BDADDR_ANY,
- AUDIO_GATEWAY_INTERFACE, TRUE);
- if (dev)
- set_configuration(endpoint, dev, NULL, 0,
- gateway_setconf_cb, dev, NULL);
- } else {
if (err)
*err = -EINVAL;
- goto failed;
+ }
+
+ if (!succeeded) {
+ g_free(endpoint);
+ return NULL;
}
endpoint->watch = g_dbus_add_disconnect_watch(adapter->conn, sender,
if (err)
*err = 0;
return endpoint;
-
-failed:
- g_free(endpoint);
- return NULL;
}
static struct media_endpoint *media_adapter_find_endpoint(
return mp->position + sec * 1000 + msec;
}
+static void set_volume(uint8_t volume, struct audio_device *dev, void *user_data)
+{
+ struct media_player *mp = user_data;
+ GSList *l;
+
+ if (mp->volume == volume)
+ return;
+
+ mp->volume = volume;
+
+ for (l = mp->adapter->endpoints; l; l = l->next) {
+ struct media_endpoint *endpoint = l->data;
+ struct media_transport *transport;
+
+ /* Volume is A2DP only */
+ if (endpoint->sep == NULL)
+ continue;
+
+ transport = find_device_transport(endpoint, dev);
+ if (transport == NULL)
+ continue;
+
+ media_transport_update_volume(transport, volume);
+ }
+}
+
static struct avrcp_player_cb player_cb = {
.get_setting = get_setting,
.set_setting = set_setting,
.get_uid = get_uid,
.get_metadata = get_metadata,
.get_position = get_position,
- .get_status = get_status
+ .get_status = get_status,
+ .set_volume = set_volume
};
static void media_player_exit(DBusConnection *connection, void *user_data)
DBG("%s=%s", key, value);
- if (!mp->settings)
- mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal);
-
g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr),
GUINT_TO_POINTER(val));
return NULL;
}
+ mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal);
+
adapter->players = g_slist_append(adapter->players, mp);
info("Player registered: sender=%s path=%s", sender, path);
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
-static GDBusMethodTable media_methods[] = {
- { "RegisterEndpoint", "oa{sv}", "", register_endpoint },
- { "UnregisterEndpoint", "o", "", unregister_endpoint },
- { "RegisterPlayer", "oa{sv}a{sv}","", register_player },
- { "UnregisterPlayer", "o", "", unregister_player },
+static const GDBusMethodTable media_methods[] = {
+ { GDBUS_METHOD("RegisterEndpoint",
+ GDBUS_ARGS({ "endpoint", "o" }, { "properties", "a{sv}" }),
+ NULL, register_endpoint) },
+ { GDBUS_METHOD("UnregisterEndpoint",
+ GDBUS_ARGS({ "endpoint", "o" }), NULL, unregister_endpoint) },
+ { GDBUS_METHOD("RegisterPlayer",
+ GDBUS_ARGS({ "player", "o" }, { "properties", "a{sv}" },
+ { "metadata", "a{sv}" }),
+ NULL, register_player) },
+ { GDBUS_METHOD("UnregisterPlayer",
+ GDBUS_ARGS({ "player", "o" }), NULL, unregister_player) },
{ },
};
{
struct media_adapter *adapter;
- if (DBUS_TYPE_UNIX_FD < 0)
- return -EPERM;
-
adapter = g_new0(struct media_adapter, 1);
adapter->conn = dbus_connection_ref(conn);
bacpy(&adapter->src, src);
char c = 'w';
int frags = periods - prev_periods, n;
- data->hw_ptr += frags * data->io.period_size;
+ data->hw_ptr += frags * data->io.period_size;
data->hw_ptr %= data->io.buffer_size;
for (n = 0; n < frags; n++) {
return reply;
}
-static GDBusMethodTable sink_methods[] = {
- { "Connect", "", "", sink_connect,
- G_DBUS_METHOD_FLAG_ASYNC },
- { "Disconnect", "", "", sink_disconnect,
- G_DBUS_METHOD_FLAG_ASYNC },
- { "IsConnected", "", "b", sink_is_connected,
- G_DBUS_METHOD_FLAG_DEPRECATED },
- { "GetProperties", "", "a{sv}",sink_get_properties },
- { NULL, NULL, NULL, NULL }
+static const GDBusMethodTable sink_methods[] = {
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, sink_connect) },
+ { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, sink_disconnect) },
+ { GDBUS_DEPRECATED_METHOD("IsConnected",
+ NULL, GDBUS_ARGS({ "connected", "b" }),
+ sink_is_connected) },
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ sink_get_properties) },
+ { }
};
-static GDBusSignalTable sink_signals[] = {
- { "Connected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
- { "Disconnected", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
- { "Playing", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
- { "Stopped", "", G_DBUS_SIGNAL_FLAG_DEPRECATED },
- { "PropertyChanged", "sv" },
- { NULL, NULL }
+static const GDBusSignalTable sink_signals[] = {
+ { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) },
+ { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) },
+ { GDBUS_DEPRECATED_SIGNAL("Playing", NULL) },
+ { GDBUS_DEPRECATED_SIGNAL("Stopped", NULL) },
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
};
static void sink_free(struct audio_device *dev)
return FALSE;
}
-avdtp_state_t sink_get_state(struct audio_device *dev)
+sink_state_t sink_get_state(struct audio_device *dev)
{
struct sink *sink = dev->sink;
- return sink->stream_state;
+ return sink->state;
}
gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session,
struct sink *sink_init(struct audio_device *dev);
void sink_unregister(struct audio_device *dev);
gboolean sink_is_active(struct audio_device *dev);
-avdtp_state_t sink_get_state(struct audio_device *dev);
+sink_state_t sink_get_state(struct audio_device *dev);
gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session,
struct avdtp_stream *stream);
gboolean sink_setup_stream(struct sink *sink, struct avdtp *session);
return reply;
}
-static GDBusMethodTable source_methods[] = {
- { "Connect", "", "", source_connect,
- G_DBUS_METHOD_FLAG_ASYNC },
- { "Disconnect", "", "", source_disconnect,
- G_DBUS_METHOD_FLAG_ASYNC },
- { "GetProperties", "", "a{sv}",source_get_properties },
- { NULL, NULL, NULL, NULL }
+static const GDBusMethodTable source_methods[] = {
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, source_connect) },
+ { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, source_disconnect) },
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ source_get_properties) },
+ { }
};
-static GDBusSignalTable source_signals[] = {
- { "PropertyChanged", "sv" },
- { NULL, NULL }
+static const GDBusSignalTable source_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
};
static void source_free(struct audio_device *dev)
return FALSE;
}
-avdtp_state_t source_get_state(struct audio_device *dev)
+source_state_t source_get_state(struct audio_device *dev)
{
struct source *source = dev->source;
- return source->stream_state;
+ return source->state;
}
gboolean source_new_stream(struct audio_device *dev, struct avdtp *session,
struct source *source_init(struct audio_device *dev);
void source_unregister(struct audio_device *dev);
gboolean source_is_active(struct audio_device *dev);
-avdtp_state_t source_get_state(struct audio_device *dev);
+source_state_t source_get_state(struct audio_device *dev);
gboolean source_new_stream(struct audio_device *dev, struct avdtp *session,
struct avdtp_stream *stream);
gboolean source_setup_stream(struct source *source, struct avdtp *session);
return dbus_message_new_method_return(msg);
}
-static GDBusMethodTable dummy_methods[] = {
- { "OutgoingCall", "s", "", outgoing_call },
- { "IncomingCall", "s", "", incoming_call },
- { "CancelCall", "", "", cancel_call },
- { "SignalStrength", "u", "", signal_strength },
- { "BatteryLevel", "u", "", battery_level },
- { "RoamingStatus", "b", "", roaming_status },
- { "RegistrationStatus", "b", "", registration_status },
- { "SetSubscriberNumber","s", "", set_subscriber_number },
+static const GDBusMethodTable dummy_methods[] = {
+ { GDBUS_METHOD("OutgoingCall",
+ GDBUS_ARGS({ "number", "s" }), NULL,
+ outgoing_call) },
+ { GDBUS_METHOD("IncomingCall",
+ GDBUS_ARGS({ "number", "s" }), NULL,
+ incoming_call) },
+ { GDBUS_METHOD("CancelCall", NULL, NULL, cancel_call) },
+ { GDBUS_METHOD("SignalStrength",
+ GDBUS_ARGS({ "strength", "u" }), NULL,
+ signal_strength) },
+ { GDBUS_METHOD("BatteryLevel",
+ GDBUS_ARGS({ "level", "u" }), NULL,
+ battery_level) },
+ { GDBUS_METHOD("RoamingStatus",
+ GDBUS_ARGS({ "roaming", "b" }), NULL,
+ roaming_status) },
+ { GDBUS_METHOD("RegistrationStatus",
+ GDBUS_ARGS({ "registration", "b" }), NULL,
+ registration_status) },
+ { GDBUS_METHOD("SetSubscriberNumber",
+ GDBUS_ARGS({ "number", "s" }), NULL,
+ set_subscriber_number) },
{ }
};
-static GDBusSignalTable dummy_signals[] = {
- { "VoiceDial", "" },
+static const GDBusSignalTable dummy_signals[] = {
+ { GDBUS_SIGNAL("VoiceDial", NULL) },
{ }
};
DBUS_TYPE_UINT32, &cause_type,
DBUS_TYPE_UINT32, &cause,
DBUS_TYPE_INVALID)) {
- error("Unexpected paramters in Instance.CallStatus() signal");
+ error("Unexpected parameters in Instance.CallStatus() signal");
return;
}
return btd_error_invalid_args(msg);
}
-static GDBusMethodTable telephony_maemo_methods[] = {
- {"SetCallerId", "s", "", set_callerid,
- G_DBUS_METHOD_FLAG_ASYNC},
+static const GDBusMethodTable telephony_maemo_methods[] = {
+ { GDBUS_ASYNC_METHOD("SetCallerId",
+ GDBUS_ARGS({ "id", "s" }), NULL,
+ set_callerid) },
{ }
};
#include <bluetooth/sdp.h>
-#include "glib-compat.h"
#include "log.h"
#include "telephony.h"
#include "error.h"
DBUS_TYPE_UINT32, &cause_type,
DBUS_TYPE_UINT32, &cause,
DBUS_TYPE_INVALID)) {
- error("Unexpected paramters in Instance.CallStatus() signal");
+ error("Unexpected parameters in Instance.CallStatus() signal");
return;
}
#include <bluetooth/sdp.h>
-#include "glib-compat.h"
#include "log.h"
#include "telephony.h"
#define CSD_CALL_BUS_NAME "org.tizen.csd"
#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_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_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_TELEPHONE_BATTERY "org.tizen.csd.CSNet.BatteryStrength"
-#define CSD_CSNET_SUBSCRIBER "org.tizen.csd.CSNet.SubscriberNumber"
+#define CSD_DEVICE_INTERFACE "org.tizen.device"
+/* Phonebook definitions */
+#define PHONEBOOK_BUS_NAME "org.bluez.pb_agent"
+#define PHONEBOOK_PATH "/org/bluez/pb_agent"
+#define PHONEBOOK_INTERFACE "org.bluez.PbAgent.At"
#define CALL_FLAG_NONE 0
#define CALL_FLAG_PRESENTATION_ALLOWED 0x01
#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02
+#define TELEPHONY_CSD_INTERFACE "org.tizen.telephony.csd"
+#define TELEPHONY_CSD_OBJECT_PATH "/org/tizen/csd"
+
+#define HFP_AGENT_SERVICE "org.bluez.hfp_agent"
+#define HFP_AGENT_PATH "/org/bluez/hfp_agent"
+#define HFP_AGENT_INTERFACE "Org.Hfp.Bluez.Interface"
+
/* Call status values as exported by the CSD CALL plugin */
#define CSD_CALL_STATUS_IDLE 0
#define CSD_CALL_STATUS_CREATE 1
#define CSD_CALL_STATUS_TERMINATED 15
#define CSD_CALL_STATUS_SWAP_INITIATED 16
-#define CALL_FLAG_NONE 0
-#define CALL_FLAG_PRESENTATION_ALLOWED 0x01
-#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 PHONEBOOK_STORE_LIST "(\"SM\",\"ME\",\"DC\",\"MC\",\"RC\")"
-#define PHONEBOOK_STORE_LIST_BLUENME "(\"ME\",\"DC\",\"MC\",\"RC\")"
#define PREFFERED_MESSAGE_STORAGE_LIST "(\"ME\",\"MT\",\"SM\",\"SR\"),(\"ME\",\"MT\",\"SM\",\"SR\"),(\"ME\",\"MT\",\"SM\",\"SR\")"
#define PHONEBOOK_CHARACTER_SET_LIST "(\"IRA\",\"GSM\",\"UCS2\")"
#define PHONEBOOK_CHARACTER_SET_SUPPORTED "\"GSM\""
#define CALL_LOG_COUNT_MAX 30
#define PHONEBOOK_COUNT_MAX 1000
-#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
+static const gchar *phonebook_store_list[] = {
+ "\"ME\"", "\"DC\"", "\"MC\"", "\"RC\""
+};
+
+#define PHONEBOOK_STORE_LIST_SIZE (sizeof(phonebook_store_list)/sizeof(const gchar *))
+
typedef struct {
uint8_t utf_8;
uint8_t gsm;
/*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},
- {0x86,0x1C},{0xA6,0x1D},{0x9F,0x1E},{0x89,0x1F},{0x84,0x5B},
- {0x96,0x5C},{0x91,0x5D},{0x9C,0x5E},{0x80,0x5F},{0xA4,0x7B},
- {0xB6,0x7C},{0xB1,0x7D},{0xBC,0x7E},{0xA0,0x7F},
+ {0xA8,0x04}, {0xA9,0x05}, {0xB9,0x06}, {0xAC,0x07}, {0xB2,0x08},
+ {0xB7,0x09}, {0x98,0x0B}, {0xB8,0x0C}, {0x85,0x0E}, {0xA5,0x0F},
+ {0x86,0x1C}, {0xA6,0x1D}, {0x9F,0x1E}, {0x89,0x1F}, {0x84,0x5B},
+ {0x96,0x5C}, {0x91,0x5D}, {0x9C,0x5E}, {0x80,0x5F}, {0xA4,0x7B},
+ {0xB6,0x7C}, {0xB1,0x7D}, {0xBC,0x7E}, {0xA0,0x7F},
};
/*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},
+ {0x85,0x14}, {0xA1,0x50}, {0x98,0x19}, {0xA0,0x16}, {0x94,0x10},
+ {0xA6,0x12}, {0x93,0x13}, {0x9E,0x1A}, {0x9B,0x14}, {0xA8,0x17},
{0xA9,0x15},
};
static DBusConnection *ag_connection = NULL;
static GSList *calls = NULL;
-static GSList *watches = NULL;
static gboolean events_enabled = FALSE;
static uint32_t callerid = 0;
/* Reference count for determining the call indicator status */
static GSList *active_calls = NULL;
+static GSList *sender_paths = NULL;
+
+typedef struct {
+ gchar *sender;
+ gchar *path;
+ unsigned int watch_id;
+} sender_path_t;
static struct indicator telephony_ag_indicators[] =
{
};
struct csd_call {
- char *object_path;
+ char *path;
int status;
gboolean originating;
gboolean emergency;
char *number;
gboolean setup;
uint32_t call_id;
+ char *sender;
};
static struct {
static struct {
- char path[PHONEBOOK_PATH_LENGTH];
+ int32_t path_id;
uint32_t type;
uint32_t max_size;
uint32_t used;
} ag_pb_info = {
- .path = {0,},
+ .path_id = 0,
.type = 0,
.max_size = 0,
- .used =0,
+ .used = 0,
};
+static void free_sender_path(sender_path_t *s_path)
+{
+ if (s_path == NULL)
+ return;
+
+ g_free(s_path->path);
+ g_free(s_path->sender);
+ g_free(s_path);
+}
+
+static void free_sender_list()
+{
+ GSList *l;
+
+ for (l = sender_paths; l != NULL; l = l->next) {
+ free_sender_path(l->data);
+ }
+ g_slist_free(sender_paths);
+ sender_paths = NULL;
+ return;
+}
+
+static int telephony_remove_from_sender_list(const char *sender, const char *path)
+{
+ GSList *l;
+ sender_path_t *s_path;
+
+ if (sender == NULL || path == NULL)
+ return -EINVAL;
+
+ for (l = sender_paths; l != NULL; l = l->next) {
+ s_path = l->data;
+ if (s_path == NULL)
+ return -ENOENT;
+ if (g_strcmp0(s_path->path, path) == 0) {
+ g_dbus_remove_watch(ag_connection, s_path->watch_id);
+ sender_paths = g_slist_remove(sender_paths, s_path);
+ free_sender_path(s_path);
+
+ /*Free sender_paths if no application is registered*/
+ if (0 == g_slist_length(sender_paths)) {
+ g_slist_free(sender_paths);
+ sender_paths = NULL;
+ }
+ return 0;
+ }
+ }
+ return -ENOENT;
+}
+
+static void telephony_app_exit_cb(DBusConnection *conn, void *user_data)
+{
+ sender_path_t *s_path = (sender_path_t *)user_data;
+
+ DBG("+\n");
+ if (!telephony_remove_from_sender_list(s_path->sender, s_path->path))
+ DBG("Application removed \n");
+ else
+ DBG("Application not removed \n");
+ DBG("-\n");
+}
+
+static gboolean telephony_is_registered(const char *path)
+{
+ GSList *l;
+ sender_path_t *s_path;
+
+ if (path == NULL || sender_paths == NULL)
+ return FALSE;
+
+ for (l = sender_paths; l != NULL; l = l->next) {
+ s_path = l->data;
+ if (s_path == NULL)
+ break;
+
+ if (g_strcmp0(s_path->path, path) == 0) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static gboolean telephony_is_call_allowed(const char *path)
+{
+ GSList *l;
+
+ if (path == NULL)
+ return FALSE;
+
+ /*if call list doesn't exist the call should be allowed, since its a new call*/
+ if (!calls)
+ return TRUE;
+
+ for (l = calls; l != NULL; l = l->next) {
+ struct csd_call *call = l->data;
+
+ if (g_strcmp0(call->path, path) == 0)
+ return TRUE;
+
+ }
+ return FALSE;
+}
+
+static int telephony_add_to_sender_list(const char *sender, const char *path)
+{
+ sender_path_t *s_path;
+
+ if (sender == NULL || path == NULL)
+ return -EINVAL;
+
+ /*check if already registered*/
+ if (telephony_is_registered(path)) {
+ return -EEXIST;
+ }
+
+ s_path = g_new0(sender_path_t, 1);
+ s_path->path = g_strdup(path);
+ s_path->sender = g_strdup(sender);
+ s_path->watch_id = g_dbus_add_disconnect_watch(ag_connection, sender,
+ telephony_app_exit_cb, s_path, NULL);
+ sender_paths = g_slist_append(sender_paths, s_path);
+ return 0;
+}
-static struct csd_call *find_call(uint32_t call_id)
+static struct csd_call *find_call_with_id(uint32_t call_id)
{
GSList *l;
return NULL;
}
-static void foreach_call_with_status(int status,
- int (*func)(struct csd_call *call))
-{
- GSList *l;
-
- for (l = calls; l != NULL; l = l->next) {
- struct csd_call *call = l->data;
-
- if (call->status == status)
- func(call);
- }
-}
-
static void csd_call_free(struct csd_call *call)
{
if (!call)
return;
- g_free(call->object_path);
+ g_free(call->path);
g_free(call->number);
+ g_free(call->sender);
g_free(call);
}
DBG("+\n");
+ DBG("telephony-tizen: releasing conference call");
+
msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
CSD_CALL_CONFERENCE_PATH,
CSD_CALL_INSTANCE,
DBG("+\n");
- msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
- call->object_path,
- CSD_CALL_INSTANCE,
- "Reject");
+ DBG("telephony-tizen: reject_call ");
+
+ msg = dbus_message_new_method_call(HFP_AGENT_SERVICE, HFP_AGENT_PATH,
+ HFP_AGENT_INTERFACE, "RejectCall");
if (!msg) {
error("Unable to allocate new D-Bus message");
return -ENOMEM;
}
- DBG(" Object Path =[ %s] and Call id = [%d]\n", call->object_path, call->call_id);
+ DBG(" Path =[ %s] and Call id = [%d]\n", call->path, call->call_id);
if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &call->call_id,
+ DBUS_TYPE_STRING, &call->path,
+ DBUS_TYPE_STRING, &call->sender,
DBUS_TYPE_INVALID)) {
- DBG("dbus_message_append_args -ERROR\n");
+ DBG("dbus_message_append_args -ENOMEM\n");
dbus_message_unref(msg);
return -ENOMEM;
}
return 0;
}
+
static int release_call(struct csd_call *call)
{
DBusMessage *msg;
DBG("+\n");
- msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
- call->object_path,
- CSD_CALL_INSTANCE,
- "Release");
+ DBG("telephony-tizen: release_call ");
+
+ msg = dbus_message_new_method_call(HFP_AGENT_SERVICE, HFP_AGENT_PATH,
+ HFP_AGENT_INTERFACE, "ReleaseCall");
if (!msg) {
error("Unable to allocate new D-Bus message");
return -ENOMEM;
}
- DBG(" Object Path =[ %s] and Call id = [%d]\n", call->object_path, call->call_id);
+ DBG("Path =[ %s] and Call id = [%d]\n", call->path, call->call_id);
if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &call->call_id,
+ DBUS_TYPE_STRING, &call->path,
+ DBUS_TYPE_STRING, &call->sender,
DBUS_TYPE_INVALID)) {
- DBG("dbus_message_append_args -ERROR\n");
+ DBG("dbus_message_append_args -ENOMEM\n");
dbus_message_unref(msg);
return -ENOMEM;
}
DBusMessage *msg;
DBG("+\n");
- msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
- call->object_path,
- CSD_CALL_INSTANCE,
- "Answer");
+ msg = dbus_message_new_method_call(HFP_AGENT_SERVICE, HFP_AGENT_PATH,
+ HFP_AGENT_INTERFACE, "AnswerCall");
if (!msg) {
error("Unable to allocate new D-Bus message");
return -ENOMEM;
}
if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &call->call_id,
+ DBUS_TYPE_STRING, &call->path,
+ DBUS_TYPE_STRING, &call->sender,
DBUS_TYPE_INVALID)) {
- DBG("dbus_message_append_args -ERROR\n");
+ DBG("dbus_message_append_args -ENOMEM\n");
dbus_message_unref(msg);
return -ENOMEM;
}
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,
+ DBG("Call %s Call id %d changed from %s to %s", call->path, call->call_id,
call_status_str[prev_status], call_status_str[status]);
if (prev_status == status) {
DBG("-\n");
}
-/**
- * This API shall invoke a dbus method call to bluetooth framework to split the call
- *
- * @return This function returns zero on success.
- * @param[in] call Pointer to the Call information structure.
- * @param[out] NONE.
- */
-static int split_call(struct csd_call *call)
-{
- DBusMessage *msg;
-
- DBG("+n");
-
- if (NULL != call) {
- msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
- call->object_path,
- CSD_CALL_INSTANCE,
- "Split");
- if (!msg) {
- error("Unable to allocate new D-Bus message");
- return -ENOMEM;
- }
-
- g_dbus_send_message(ag_connection, msg);
- }
- DBG("-\n");
-
- return 0;
-}
-/**
- * This API shall invoke a dbus method call to bluetooth framework to swap the calls
- *
- * @return This function returns zero on success.
- * @param[in] NONE.
- * @param[out] NONE.
- */
-static int swap_calls(void)
-{
- DBusMessage *msg;
- DBG("+\n");
-
- msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
- CSD_CALL_INTERFACE,
- "Swap");
- if (!msg) {
- error("Unable to allocate new D-Bus message");
- return -ENOMEM;
- }
-
- g_dbus_send_message(ag_connection, msg);
- DBG("-\n");
-
- return 0;
-}
-
-/**
- * This API shall invoke a dbus method call to bluetooth framework to hold the call
- *
- * @return This function returns zero on success.
- * @param[in] call Pointer to the Call information structure.
- * @param[out] NONE.
- */
-static int hold_call(struct csd_call *call)
-{
- DBusMessage *msg;
- DBG("+\n");
-
- msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
- CSD_CALL_INTERFACE,
- "Hold");
- if (!msg) {
- error("Unable to allocate new D-Bus message");
- return -ENOMEM;
- }
- 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)) {
-
- DBG("dbus_message_append_args -ERROR\n");
- dbus_message_unref(msg);
- return -ENOMEM;
- }
-
- g_dbus_send_message(ag_connection, msg);
- }
- DBG("-\n");
-
- return 0;
-}
-
-
-/**
- * This API shall invoke a dbus method call to bluetooth framework to unhold the call
- *
- * @return This function returns zero on success.
- * @param[in] call Pointer to the Call information structure.
- * @param[out] NONE.
- */
-static int unhold_call(struct csd_call *call)
-{
- DBusMessage *msg;
- DBG("+\n");
-
- msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
- CSD_CALL_INTERFACE,
- "Unhold");
- if (!msg) {
- error("Unable to allocate new D-Bus message");
- return -ENOMEM;
- }
-
- 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)) {
-
- DBG("dbus_message_append_args -ERROR\n");
- dbus_message_unref(msg);
- return -ENOMEM;
- }
-
- g_dbus_send_message(ag_connection, msg);
- }
- DBG("-\n");
-
- return 0;
-}
-
-/**
- * This API shall invoke a dbus method call to bluetooth framework to create conmference calls
- *
- * @return This function returns zero on success.
- * @param[in] NONE.
- * @param[out] NONE.
- */
-static int create_conference(void)
-{
- DBusMessage *msg;
- DBG("+\n");
-
- msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
- CSD_CALL_INTERFACE,
- "Conference");
- if (!msg) {
- error("Unable to allocate new D-Bus message");
- return -ENOMEM;
- }
-
- g_dbus_send_message(ag_connection, msg);
- DBG("-\n");
-
- return 0;
-}
-/**
- * This API shall invoke a dbus method call to bluetooth framework to transfer the call
- *
- * @return This function returns zero on success.
- * @param[in] NONE.
- * @param[out] NONE.
- */
-static int call_transfer(void)
-{
- DBusMessage *msg;
- DBG("+\n");
-
- msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
- CSD_CALL_INTERFACE,
- "Transfer");
- if (!msg) {
- error("Unable to allocate new D-Bus message");
- return -ENOMEM;
- }
-
- g_dbus_send_message(ag_connection, msg);
- DBG("-\n");
-
- return 0;
-}
-
static void telephony_chld_reply(DBusPendingCall *call, void *data)
{
DBusMessage *reply = dbus_pending_call_steal_reply(call);
const char *idx;
struct csd_call *call;
int err = 0;
- int chld_value = 0;
+ uint32_t chld_value;
+ GSList *l = NULL;
+ sender_path_t *s_path = NULL;
DBG("+\n");
- if (strlen(cmd) > 1)
- idx = &cmd[1];
- else
- idx = NULL;
+ DBG("telephony-tizen: got call hold request %s", cmd);
- if (idx)
- call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
- else
- call = NULL;
-#if 0
- switch (cmd[0]) {
- case '0':
- if (find_call_with_status(CSD_CALL_STATUS_WAITING))
- foreach_call_with_status(CSD_CALL_STATUS_WAITING,
- release_call);
- else
- foreach_call_with_status(CSD_CALL_STATUS_HOLD,
- release_call);
- break;
- case '1':
- if (idx) {
- if (call)
- err = release_call(call);
- break;
- }
- foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
- call = find_call_with_status(CSD_CALL_STATUS_WAITING);
- if (call) {
- err = answer_call(call);
- }
- else {
- struct csd_call *held;
- held = find_call_with_status(CSD_CALL_STATUS_HOLD);
- if(held)
- err = unhold_call(held);
- }
- break;
- case '2':
- if (idx) {
- if (call)
- err = split_call(call);
- } else {
- struct csd_call *held, *wait;
-
- call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
- held = find_call_with_status(CSD_CALL_STATUS_HOLD);
- wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
-
- if (wait)
- err = answer_call(wait);
- else if (call && held)
- err = swap_calls();
- else {
- if (call)
- err = hold_call(call);
- if (held)
- err = unhold_call(held);
+ /* Find any Ongoing call, in active/held/waiting */
+ if (NULL == (call = find_call_with_status(CSD_CALL_STATUS_ACTIVE)))
+ if (NULL == (call = find_call_with_status(
+ CSD_CALL_STATUS_HOLD)))
+ if (NULL == (call = find_call_with_status(
+ CSD_CALL_STATUS_WAITING))) {
+ DBG("No Onging Call \n");
+ telephony_call_hold_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
}
+
+ /*Get sender path using call path*/
+ for (l = sender_paths; l != NULL; l = l->next) {
+ s_path = l->data;
+ if (s_path == NULL) {
+ telephony_call_hold_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
}
- break;
- case '3':
- if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
- find_call_with_status(CSD_CALL_STATUS_WAITING))
- err = create_conference();
- break;
- case '4':
- err = call_transfer();
- break;
- default:
- DBG("Unknown call hold request");
- break;
+ if (g_strcmp0(s_path->path, call->path) == 0)
+ break;
}
- if (err)
- telephony_call_hold_rsp(telephony_device,
- CME_ERROR_AG_FAILURE);
- else
- telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
-#else
- idx = &cmd[0];
- chld_value = strtol(idx, NULL, 0);
-
- err = dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
- CSD_CALL_INSTANCE, "Threeway",
- telephony_chld_reply, telephony_device,
- DBUS_TYPE_INT32, &chld_value,
- DBUS_TYPE_INVALID);
-
- if (err)
+ if (s_path == NULL) {
telephony_call_hold_rsp(telephony_device,
- CME_ERROR_AG_FAILURE);
-#endif
- DBG("-\n");
-}
-
-static void handle_incoming_call(DBusMessage *msg)
-{
-
- const char *number, *call_path;
- struct csd_call *call;
- uint32_t call_id;
-
- DBG("+\n");
- DBG("handle_incoming_call()\n");
-
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_OBJECT_PATH, &call_path,
- DBUS_TYPE_STRING, &number,
- DBUS_TYPE_UINT32, &call_id,
- DBUS_TYPE_INVALID)) {
- error("Unexpected parameters in Call.Coming() signal");
+ CME_ERROR_AG_FAILURE);
return;
}
- call = g_new0(struct csd_call, 1);
- call->object_path = g_strdup(call_path);
- call->call_id = call_id;
- call->number = g_strdup(number);
- calls = g_slist_append(calls, call);
+ idx = &cmd[0];
+ chld_value = strtoul(idx, NULL, 0);
- DBG("Incoming call to %s from number %s call id %d", call_path, number, call_id);
+ DBG("Sender = %s path = %s \n", s_path->sender, s_path->path);
- if (find_call_with_status(CSD_CALL_STATUS_ACTIVE) ||
- 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 {
- telephony_incoming_call_ind(call->number,
- number_type(call->number));
+ err = dbus_method_call_send(HFP_AGENT_SERVICE, HFP_AGENT_PATH,
+ HFP_AGENT_INTERFACE, "ThreewayCall",
+ telephony_chld_reply, telephony_device,
+ DBUS_TYPE_UINT32, &chld_value,
+ DBUS_TYPE_STRING, &call->path,
+ DBUS_TYPE_STRING, &call->sender,
+ DBUS_TYPE_INVALID);
- call_set_status(call, CSD_CALL_STATUS_COMING);
- }
- telephony_update_indicator(telephony_ag_indicators, "callsetup",
- EV_CALLSETUP_INCOMING);
+ if (err)
+ telephony_call_hold_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
DBG("-\n");
}
-static void update_registration_status(uint8_t status)
+static int update_registration_status(uint8_t status)
{
uint8_t new_status;
+ int ret = 0;
DBG("+\n");
new_status = status;
if (net.status == new_status)
- return;
+ return ret;
switch (new_status) {
case NETWORK_REG_STATUS_HOME:
- telephony_update_indicator(telephony_ag_indicators, "roam",
+ ret = telephony_update_indicator(telephony_ag_indicators, "roam",
EV_ROAM_INACTIVE);
- if (net.status > NETWORK_REG_STATUS_ROAMING)
- telephony_update_indicator(telephony_ag_indicators,
+
+ if (net.status > NETWORK_REG_STATUS_ROAMING) {
+ ret = telephony_update_indicator(telephony_ag_indicators,
"service",
EV_SERVICE_PRESENT);
+ }
break;
case NETWORK_REG_STATUS_ROAMING:
- telephony_update_indicator(telephony_ag_indicators, "roam",
+ ret = telephony_update_indicator(telephony_ag_indicators, "roam",
EV_ROAM_ACTIVE);
- if (net.status > NETWORK_REG_STATUS_ROAMING)
- telephony_update_indicator(telephony_ag_indicators,
+
+ if (net.status > NETWORK_REG_STATUS_ROAMING) {
+ ret = telephony_update_indicator(telephony_ag_indicators,
"service",
EV_SERVICE_PRESENT);
+ }
break;
case NETWORK_REG_STATUS_OFFLINE:
case NETWORK_REG_STATUS_SEARCHING:
case NETWORK_REG_STATUS_NO_COVERAGE:
case NETWORK_REG_STATUS_REJECTED:
case NETWORK_REG_STATUS_UNKOWN:
- if (net.status < NETWORK_REG_STATUS_OFFLINE)
- telephony_update_indicator(telephony_ag_indicators,
+ if (net.status < NETWORK_REG_STATUS_OFFLINE) {
+ ret = telephony_update_indicator(telephony_ag_indicators,
"service",
EV_SERVICE_NONE);
+ }
break;
}
net.status = new_status;
+ DBG("telephony-tizen: registration status changed: %d", status);
DBG("-\n");
+
+ return ret;
}
-static void update_signal_strength(int32_t signal_bars)
+static int update_signal_strength(int32_t signal_bars)
{
DBG("+\n");
}
if (net.signal_bars == signal_bars)
- return;
-
- telephony_update_indicator(telephony_ag_indicators, "signal", signal_bars);
+ return 0;
net.signal_bars = signal_bars;
+ DBG("telephony-tizen: signal strength updated: %d/5", signal_bars);
- DBG("-\n");
+ return telephony_update_indicator(telephony_ag_indicators, "signal", signal_bars);
}
-static void update_battery_strength(int32_t battery_level)
+static int update_battery_strength(int32_t battery_level)
{
int current_battchg = 0;
battery_level = 5;
}
if (current_battchg == battery_level)
- return;
+ return 0;
+
+ DBG("telephony-tizen: battery strength updated: %d/5", battery_level);
+ DBG("-\n");
- telephony_update_indicator(telephony_ag_indicators,
+ return telephony_update_indicator(telephony_ag_indicators,
"battchg", battery_level);
+}
+static int update_operator_name(const char *name)
+{
+ DBG("+\n");
+ if (name == NULL)
+ return -EINVAL;
+
+ g_free(net.operator_name);
+ net.operator_name = g_strndup(name, 16);
+ DBG("telephony-tizen: operator name updated: %s", name);
DBG("-\n");
+ return 0;
}
-
-static void handle_outgoing_call(DBusMessage *msg)
+static int update_subscriber_number(const char *number)
{
- const char *number, *call_path;
- struct csd_call *call;
- uint32_t call_id;
-
DBG("+\n");
- DBG("handle_outgoing_call()\n");
+ if (number == NULL)
+ return -EINVAL;
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_OBJECT_PATH, &call_path,
- DBUS_TYPE_STRING, &number,
- DBUS_TYPE_UINT32, &call_id,
- DBUS_TYPE_INVALID)) {
- error("Unexpected parameters in Call.Coming() signal");
- return;
+ g_free(subscriber_number);
+ subscriber_number = g_strdup(number);
+ DBG("telephony-tizen: subscriber_number updated: %s", subscriber_number);
+ DBG("-\n");
+ return 0;
+}
+
+static DBusMessage *telephony_error_reply(DBusMessage *msg, int error)
+{
+ switch (error) {
+ case -ENOENT:
+ return btd_error_not_available(msg);
+ case -ENODEV:
+ return btd_error_not_connected(msg);
+ case -EBUSY:
+ return btd_error_busy(msg);
+ case -EINVAL:
+ return btd_error_invalid_args(msg);
+ case -EEXIST:
+ return btd_error_already_exists(msg);
+ case -ENOMEM:
+ return btd_error_failed(msg, "No memory");
+ case -EIO:
+ return btd_error_failed(msg, "I/O error");
+ default:
+ return dbus_message_new_method_return(msg);
}
+}
+
+static struct csd_call *create_call(DBusMessage *msg, const char *path,
+ const char *number, uint32_t call_id,
+ const char *sender)
+
+{
+ struct csd_call *call;
call = g_new0(struct csd_call, 1);
- call->object_path = g_strdup(call_path);
+ call->path = g_strdup(path);
call->call_id = call_id;
call->number = g_strdup(number);
+ call->sender = g_strdup(sender);
calls = g_slist_append(calls, call);
- DBG("Outgoing call to %s from number %s call id %d", call_path, number, call_id);
-
- call_set_status(call, CSD_CALL_STATUS_CREATE);
-
- telephony_update_indicator(telephony_ag_indicators, "callsetup",
- EV_CALLSETUP_OUTGOING);
- DBG("-\n");
+ return call;
}
-static void handle_create_requested(DBusMessage *msg)
-{
- DBG("+\n");
- DBG("handle_create_requested()\n");
- DBG("-\n");
-}
-
-static void handle_call_status(DBusMessage *msg, const char *call_path)
+static DBusMessage *incoming(DBusConnection *conn, DBusMessage *msg,
+ void *data)
{
+ const char *number, *call_path;
+ const char *sender;
struct csd_call *call;
- dbus_uint32_t status, call_id;
+ uint32_t call_id;
+ int ret;
DBG("+\n");
+ DBG("telephony_incoming()\n");
if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_UINT32, &status,
+ DBUS_TYPE_STRING, &call_path,
+ DBUS_TYPE_STRING, &number,
DBUS_TYPE_UINT32, &call_id,
+ DBUS_TYPE_STRING, &sender,
DBUS_TYPE_INVALID)) {
- error("Unexpected paramters in Instance.CallStatus() signal");
- return;
+
+ return btd_error_invalid_args(msg);
}
- DBG("status = [%d] and call_id = [%d]\n", status, call_id);
- call = find_call(call_id);
- if (!call) {
-/*
- call_path is equal to CSD_CALL_PATH then we should update the call list
- since the call_path is sent from native AG applicaton
+ ret = telephony_is_registered(call_path);
+ if (!ret)
+ return telephony_error_reply(msg, -ENOENT);
- Added for updation of the call status if the call is not added inthe call list
-*/
- if (g_str_equal(CSD_CALL_PATH, call_path)) {
- call = g_new0(struct csd_call, 1);
- call->object_path = g_strdup(call_path);
- call->call_id = call_id;
- calls = g_slist_append(calls, call);
- }
- }
+ /*Check in the active call list, if any of the call_path exists if not don't allow
+ the call since it is initated by other applicatoin*/
- if (status > 16) {
- error("Invalid call status %u", status);
- return;
- }
+ ret = telephony_is_call_allowed(call_path);
+ if (!ret)
+ return telephony_error_reply(msg, -ENOENT);
- call_set_status(call, status);
- DBG("-\n");
-}
-static void update_operator_name(const char *name)
-{
- DBG("+\n");
- if (name == NULL)
- return;
+ call = create_call(msg, call_path, number, call_id, sender);
- g_free(net.operator_name);
- net.operator_name = g_strndup(name, 16);
- DBG("-\n");
-}
+ DBG("Incoming call to %s from number %s call id %d", call_path, number, call_id);
+ ret = telephony_update_indicator(telephony_ag_indicators, "callsetup",
+ EV_CALLSETUP_INCOMING);
+ if (ret)
+ return telephony_error_reply(msg, ret);
-static void update_subscriber_number(const char *number)
-{
- DBG("+\n");
- if (number == NULL)
- return;
+ if (find_call_with_status(CSD_CALL_STATUS_ACTIVE) ||
+ find_call_with_status(CSD_CALL_STATUS_HOLD)) {
+ ret = telephony_call_waiting_ind(call->number,
+ number_type(call->number));
+ if (ret)
+ return telephony_error_reply(msg, ret);
- g_free(subscriber_number);
- subscriber_number = g_strdup(number);
- DBG("-\n");
-}
+ call_set_status(call, CSD_CALL_STATUS_WAITING);
+ } else {
+ ret = telephony_incoming_call_ind(call->number,
+ number_type(call->number));
+ if (ret)
+ return telephony_error_reply(msg, ret);
-static void handle_conference(DBusMessage *msg, gboolean joined)
-{
- DBG("+\n");
- DBG("handle_conference()\n");
+ call_set_status(call, CSD_CALL_STATUS_COMING);
+ }
DBG("-\n");
+ return dbus_message_new_method_return(msg);
}
-static void handle_registration_changed(DBusMessage *msg)
+static DBusMessage *outgoing(DBusConnection *conn, DBusMessage *msg,
+ void *data)
{
- uint8_t status;
+ const char *number, *call_path;
+ const char *sender;
+ struct csd_call *call;
+ uint32_t call_id;
+ int ret;
DBG("+\n");
- DBG("handle_registration_changed()\n");
+ DBG("telephony_outgoing_call()\n");
if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_BYTE, &status,
+ DBUS_TYPE_STRING, &call_path,
+ DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_UINT32, &call_id,
+ DBUS_TYPE_STRING, &sender,
DBUS_TYPE_INVALID)) {
- error("Unexpected parameters in RegistrationChanged");
- return;
+ return btd_error_invalid_args(msg);
}
- update_registration_status((uint8_t) status);
- DBG("-\n");
-}
+ ret = telephony_is_registered(call_path);
+ if (!ret)
+ return telephony_error_reply(msg, -ENOENT);
-static void handle_operator_name_changed(DBusMessage *msg)
-{
- const char *name;
+ /*Check in the active call list, if any of the call_path exists if not don't allow
+ the call since it is initated by other applicatoin*/
- DBG("+\n");
+ ret = telephony_is_call_allowed(call_path);
+ if (!ret)
+ return telephony_error_reply(msg, -ENOENT);
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID)) {
- error("Unexpected parameters in OperatorNameChanged");
- return;
- }
+ call = create_call(msg, call_path, number, call_id, sender);
+
+ DBG("Outgoing call to %s from number %s call id %d", call_path, number, call_id);
+
+ call_set_status(call, CSD_CALL_STATUS_CREATE);
+
+ ret = telephony_update_indicator(telephony_ag_indicators, "callsetup",
+ EV_CALLSETUP_OUTGOING);
+ if (ret)
+ return telephony_error_reply(msg, ret);
- update_operator_name(name);
DBG("-\n");
+ return dbus_message_new_method_return(msg);
}
-static void handle_signal_bars_changed(DBusMessage *msg)
+static DBusMessage *set_call_status(DBusConnection *conn, DBusMessage *msg,
+ void *data)
{
- int32_t signal_bars;
+ struct csd_call *call;
+ dbus_uint32_t status, call_id;
+ const char *call_path;
+ const char *sender;
+ int ret;
DBG("+\n");
if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_INT32, &signal_bars,
+ DBUS_TYPE_STRING, &call_path,
+ DBUS_TYPE_UINT32, &status,
+ DBUS_TYPE_UINT32, &call_id,
+ DBUS_TYPE_STRING, &sender,
DBUS_TYPE_INVALID)) {
- error("Unexpected parameters in SignalBarsChanged");
- return;
+ error("Unexpected paramters in Instance.CallStatus() signal");
+ return btd_error_invalid_args(msg);
}
- update_signal_strength(signal_bars);
- DBG("-\n");
-}
+ if (status > 16) {
+ return btd_error_invalid_args(msg);
+ }
-static void handle_battery_bars_changed(DBusMessage *msg)
-{
- int32_t battery_level;
+ ret = telephony_is_registered(call_path);
+ if (!ret)
+ return telephony_error_reply(msg, -ENOENT);
- DBG("+\n");
+ ret = telephony_is_call_allowed(call_path);
+ if (!ret)
+ return telephony_error_reply(msg, -ENOENT);
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_INT32, &battery_level,
- DBUS_TYPE_INVALID)) {
- error("Unexpected parameters in SignalBarsChanged");
- return;
- }
+ DBG("status = [%d] and call_id = [%d] \n", status, call_id);
+
+ call = find_call_with_id(call_id);
+ if (!call) {
+/*
+ call_path is equal to CSD_CALL_PATH then we should update the call list
+ since the call_path is sent from native AG applicaton
- update_battery_strength(battery_level);
+ Added for updation of the call status if the call is not added in the call list
+*/
+ call = create_call(msg, call_path, NULL, call_id, sender);
+ }
+ call_set_status(call, status);
DBG("-\n");
+ return dbus_message_new_method_return(msg);
}
-static void handle_subscriber_number_changed(DBusMessage *msg)
+static DBusMessage *register_telephony_agent(DBusConnection *conn, DBusMessage *msg,
+ void *data)
{
- const char *number;
+ gboolean flag;
+ const char *sender;
+ const char *path;
+ int ret;
DBG("+\n");
if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &number,
+ DBUS_TYPE_BOOLEAN, &flag,
+ DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_STRING, &sender,
DBUS_TYPE_INVALID)) {
- error("Unexpected parameters in SubscriberNumberChanged");
- return;
+ error("Unexpected parameters in RegisterSenderPath");
+ return btd_error_invalid_args(msg);
}
- update_subscriber_number(number);
- DBG("-\n");
-}
-static gboolean signal_filter(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- const char *path = NULL;
+ DBG("flag = %d \n", flag);
+ DBG("Sender = %s \n", sender);
+ DBG("path = %s \n", path);
- DBG("+\n");
+ if (flag)
+ ret = telephony_add_to_sender_list(sender, path);
+ else
+ ret = telephony_remove_from_sender_list(sender, path);
- path = dbus_message_get_path(msg);
-
- if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
- handle_incoming_call(msg);
- else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
- handle_outgoing_call(msg);
- else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
- "CreateRequested"))
- handle_create_requested(msg);
- else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
- handle_call_status(msg, path);
- else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
- handle_conference(msg, TRUE);
- else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
- handle_conference(msg, FALSE);
- else if (dbus_message_is_signal(msg, CSD_CSNET_REGISTRATION,
- "RegistrationChanged"))
- handle_registration_changed(msg);
- else if (dbus_message_is_signal(msg, CSD_CSNET_OPERATOR,
- "OperatorNameChanged"))
- handle_operator_name_changed(msg);
- else if (dbus_message_is_signal(msg, CSD_CSNET_SIGNAL,
- "SignalBarsChanged"))
- handle_signal_bars_changed(msg);
- else if (dbus_message_is_signal(msg, CSD_TELEPHONE_BATTERY,
- "BatteryBarsChanged"))
- handle_battery_bars_changed(msg);
- else if (dbus_message_is_signal(msg, CSD_CSNET_SUBSCRIBER,
- "SubscriberNumberChanged"))
- handle_subscriber_number_changed(msg);
+ if (ret)
+ return telephony_error_reply(msg, ret);
DBG("-\n");
- return TRUE;
+
+ return dbus_message_new_method_return(msg);
}
-static void dbus_add_watch(const char *sender, const char *path,
- const char *interface, const char *member)
+static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
+ void *data)
{
- guint watch;
-
- watch = g_dbus_add_signal_watch(ag_connection, sender, path, interface,
- member, signal_filter, NULL, NULL);
-
- watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
-}
+ const char *property;
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+ int ret;
+
+ 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 (g_str_equal("RegistrationChanged", property)) {
+ uint8_t value;
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BYTE)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &value);
+
+ ret = update_registration_status(value);
+ if (ret)
+ return telephony_error_reply(msg, ret);
+ } else if (g_str_equal("OperatorNameChanged", property)) {
+ const char *name;
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &name);
+
+ ret = update_operator_name(name);
+ if (ret)
+ return telephony_error_reply(msg, ret);
+ } else if (g_str_equal("SignalBarsChanged", property)) {
+ int32_t value;
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INT32)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &value);
+ ret = update_signal_strength(value);
+ if (ret)
+ return telephony_error_reply(msg, ret);
+
+ } else if (g_str_equal("BatteryBarsChanged", property)) {
+ int32_t value;
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INT32)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &value);
+ ret = update_battery_strength(value);
+ if (ret)
+ return telephony_error_reply(msg, ret);
+
+ } else if (g_str_equal("SubscriberNumberChanged", property)) {
+ const char *number;
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&sub, &number);
+ ret = update_subscriber_number(number);
+ if (ret)
+ return telephony_error_reply(msg, ret);
+ }
+
+ return dbus_message_new_method_return(msg);
+}
+
+static GDBusMethodTable telephony_methods[] = {
+ { GDBUS_METHOD("Incoming",
+ GDBUS_ARGS({ "path", "s" }, { "number", "s" },
+ { "id", "u" }, { "sender", "s" }),
+ NULL,
+ incoming) },
+ { GDBUS_METHOD("Outgoing",
+ GDBUS_ARGS({ "path", "s" }, { "number", "s" },
+ { "id", "u" }, { "sender", "s" }),
+ NULL,
+ outgoing) },
+ { GDBUS_METHOD("SetCallStatus",
+ GDBUS_ARGS({ "path", "s" }, { "status", "u" },
+ { "id", "u" }, { "sender", "s" }),
+ NULL,
+ set_call_status) },
+ { GDBUS_METHOD("RegisterTelephonyAgent",
+ GDBUS_ARGS({ "flag", "b" }, { "path", "s" },
+ { "sender", "s" }),
+ NULL,
+ register_telephony_agent) },
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "property", "v" }),
+ NULL,
+ set_property) },
+ { }
+};
-static const char *telephony_memory_dial_lookup(int location)
+static void path_unregister(void *data)
{
- /*memory dial not supported*/
- if (location == 1)
- return NULL;
- else
- return NULL;
+ DBG("+\n");
+ g_dbus_unregister_interface(ag_connection, TELEPHONY_CSD_OBJECT_PATH,
+ TELEPHONY_CSD_INTERFACE);
+ DBG("-\n");
}
/*API's that shall be ported*/
-
int telephony_init(void)
{
uint32_t features = AG_FEATURE_EC_ANDOR_NR |
AG_FEATURE_REJECT_A_CALL |
AG_FEATURE_ENHANCED_CALL_STATUS |
- AG_FEATURE_THREE_WAY_CALLING;
+ AG_FEATURE_THREE_WAY_CALLING |
+ AG_FEATURE_VOICE_RECOGNITION;
int i;
DBG("");
ag_connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
- 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");
+ if (!g_dbus_register_interface(ag_connection, TELEPHONY_CSD_OBJECT_PATH,
+ TELEPHONY_CSD_INTERFACE,
+ telephony_methods, NULL, NULL,
+ NULL, path_unregister)) {
+ error("D-Bus failed to register %s interface", TELEPHONY_CSD_INTERFACE);
+ return -1;
+ }
/* Reset indicators */
for (i = 0; telephony_ag_indicators[i].desc != NULL; i++) {
}
/*Initializatoin of the indicators*/
- telephony_ready_ind(features, telephony_ag_indicators, BTRH_NOT_SUPPORTED,
- telephony_chld_str);
+ telephony_ready_ind(features, telephony_ag_indicators,
+ BTRH_NOT_SUPPORTED,
+ telephony_chld_str);
return 0;
}
-static void remove_watch(gpointer data)
-{
- g_dbus_remove_watch(ag_connection, GPOINTER_TO_UINT(data));
-}
-
void telephony_exit(void)
{
DBG("");
g_slist_free(calls);
calls = NULL;
- g_slist_foreach(watches, (GFunc) remove_watch, NULL);
- g_slist_free(watches);
- watches = NULL;
+ free_sender_list();
+
+ g_dbus_unregister_interface(ag_connection, TELEPHONY_CSD_OBJECT_PATH,
+ TELEPHONY_CSD_INTERFACE);
dbus_connection_unref(ag_connection);
ag_connection = NULL;
void telephony_device_connected(void *telephony_device)
{
+ DBG("telephony-tizen: device %p connected", telephony_device);
}
void telephony_device_disconnected(void *telephony_device)
{
+ DBG("telephony-tizen: device %p disconnected", telephony_device);
events_enabled = FALSE;
}
void telephony_response_and_hold_req(void *telephony_device, int rh)
{
+ DBG("telephony-tizen: response_and_hold_req - device %p disconnected",
+ telephony_device);
+
telephony_response_and_hold_rsp(telephony_device,
CME_ERROR_NOT_SUPPORTED);
}
{
uint32_t flags = callerid;
+ DBG("telephony-tizen: dial request to %s", number);
+
if (strncmp(number, "*31#", 4) == 0) {
number += 4;
flags = CALL_FLAG_PRESENTATION_ALLOWED;
} else if (number[0] == '>') {
int location = strtol(&number[1], NULL, 0);
- 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 (0 != dbus_method_call_send(HFP_AGENT_SERVICE,
+ HFP_AGENT_PATH, HFP_AGENT_INTERFACE,
+ "DialMemory",
+ telephony_dial_number_reply, telephony_device,
+ DBUS_TYPE_INT32, &location,
+ DBUS_TYPE_INVALID)) {
telephony_dial_number_rsp(telephony_device,
CME_ERROR_AG_FAILURE);
}
return;
}
- if (0 != dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
- CSD_CALL_INTERFACE, "DialNo",
+ if (0 != dbus_method_call_send(HFP_AGENT_SERVICE, HFP_AGENT_PATH,
+ HFP_AGENT_INTERFACE, "DialNum",
NULL, NULL,
DBUS_TYPE_STRING, &number,
DBUS_TYPE_UINT32, &flags,
struct csd_call *active, *waiting;
int err;
+ DBG("telephony-tizen: got key press request for %s", keys);
+
waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
if (!waiting)
waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
void telephony_last_dialed_number_req(void *telephony_device)
{
- if (0 != dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
- CSD_CALL_INTERFACE, "DialLastNo",
+ DBG("telephony-tizen: last dialed number request");
+
+ if (0 != dbus_method_call_send(HFP_AGENT_SERVICE, HFP_AGENT_PATH,
+ HFP_AGENT_INTERFACE, "DialLastNum",
telephony_dial_number_reply, telephony_device,
DBUS_TYPE_INVALID)) {
telephony_dial_number_rsp(telephony_device,
void telephony_transmit_dtmf_req(void *telephony_device, char tone)
{
char buf[2] = { tone, '\0' }, *buf_ptr = buf;
+ struct csd_call *call;
- if (0 != dbus_method_call_send(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
- CSD_CALL_INTERFACE, "SendDtmf",
+ DBG("telephony-tizen: transmit dtmf: %s", buf);
+
+ /* Find any Ongoing call, in active/held/waiting */
+ if (NULL == (call = find_call_with_status(CSD_CALL_STATUS_ACTIVE)))
+ if (NULL == (call = find_call_with_status(
+ CSD_CALL_STATUS_HOLD)))
+ if (NULL == (call = find_call_with_status(
+ CSD_CALL_STATUS_WAITING))) {
+ DBG("No Onging Call \n");
+ telephony_transmit_dtmf_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ if (0 != dbus_method_call_send(HFP_AGENT_SERVICE, HFP_AGENT_PATH,
+ HFP_AGENT_INTERFACE, "SendDtmf",
NULL, NULL,
DBUS_TYPE_STRING, &buf_ptr,
+ DBUS_TYPE_STRING, &call->path,
+ DBUS_TYPE_STRING, &call->sender,
DBUS_TYPE_INVALID)) {
telephony_transmit_dtmf_rsp(telephony_device,
CME_ERROR_AG_FAILURE);
GSList *l;
int i;
+ DBG("telephony-tizen: list current calls request");
+
for (l = calls, i = 1; l != NULL; l = l->next, i++) {
struct csd_call *call = l->data;
int status, direction, multiparty;
void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
{
+ DBG("telephony-tizen: got %s NR and EC request",
+ enable ? "enable" : "disable");
telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
}
void telephony_voice_dial_req(void *telephony_device, gboolean enable)
{
- telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
+
+ DBG("telephony-tizen: got %s voice dial request",
+ enable ? "enable" : "disable");
+
+ if (0 != dbus_method_call_send(HFP_AGENT_SERVICE, HFP_AGENT_PATH,
+ HFP_AGENT_INTERFACE, "VoiceDial",
+ NULL, NULL, DBUS_TYPE_BOOLEAN, &enable,
+ DBUS_TYPE_INVALID)) {
+ telephony_voice_dial_rsp(telephony_device,
+ CME_ERROR_AG_FAILURE);
+ return;
+ }
+
+ telephony_voice_dial_rsp(telephony_device, CME_ERROR_NONE);
}
void telephony_subscriber_number_req(void *telephony_device)
{
+ DBG("telephony-tizen: subscriber number request");
if (subscriber_number)
telephony_subscriber_number_ind(subscriber_number,
number_type(subscriber_number),
telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
}
-void telephony_list_phonebook_store(void *telephony_device)
+static int convert_utf8_gsm(uint8_t ascii, uint8_t utf_8, uint8_t *gsm)
{
-/*
- 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);
+ uint32_t i;
+
+ 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) {
+ *gsm = gsm_unicode_CE[i].gsm;
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static void get_unicode_string(const char *name, char *unicodename)
+{
+ if (NULL != name && NULL != unicodename) {
+ int len = strlen(name);
+ int x, y;
+
+ if (len == 0)
+ return;
+
+ 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] == '_') {
+ unicodename[y] = ' ';
+ continue;
+ }
+
+ unicodename[y] = name[x];
+ }
+ }
+ return;
}
static int get_phonebook_count(const char *path, uint32_t *max_size,
- uint32_t *used)
+ uint32_t *used)
{
DBusConnection *conn;
- DBusMessageIter iter;
- DBusMessageIter value;
DBusMessage *message, *reply;
DBusError error;
return -1;
}
- message = dbus_message_new_method_call("org.bluez.pb_agent",
- "/org/bluez/pb_agent",
- "org.bluez.PbAgent",
- "GetCallLogSize");
+ message = dbus_message_new_method_call(PHONEBOOK_BUS_NAME,
+ PHONEBOOK_PATH,
+ PHONEBOOK_INTERFACE,
+ "GetPhonebookSizeAt");
if (!message) {
DBG("Can't allocate new message");
dbus_connection_unref(conn);
return -1;
}
- dbus_message_iter_init_append(message, &iter);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &path);
- dbus_message_iter_close_container(&iter, &value);
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_INVALID);
+
dbus_error_init(&error);
reply = dbus_connection_send_with_reply_and_block(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)
*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)
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);
- }
-}
-
-void telephony_set_phonebook_store(void *telephony_device, const char *path)
-{
- if (NULL != path) {
- DBG("set phonebook type to [%s]\n", path);
- g_strlcpy(ag_pb_info.path, path, sizeof(ag_pb_info.path));
- }
-}
-
-void telephony_read_phonebook_attributes(void *telephony_device)
-{
- 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);
- }
-}
-
-static int convert_utf8_gsm(uint8_t ascii, uint8_t utf_8, uint8_t *gsm)
-{
- 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) {
- *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) {
- *gsm = gsm_unicode_CE[i].gsm;
- return 0;
- }
- }
- }
-}
-
-static void get_unicode_string(const char *name, char *unicodename)
-{
- if (NULL != name || NULL != unicodename) {
- int len = strlen(name);
- if (len > 0) {
- int x = 0;
- 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] == '_') {
- unicodename[y] = ' ';
- continue;
- }
-
- unicodename[y] = name[x];
- }
- }
- }
- return;
-}
-
-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);
-
- 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);
-
- 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);
-
- 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 read_phonebook_entries(int start_index, int end_index)
{
DBusConnection *conn;
DBusMessage *message, *reply;
+ DBusMessageIter iter, iter_struct;
DBusError error;
- DBusMessageIter iter, iter_struct, entry;
- 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 > (int) ag_pb_info.max_size)
- end_index = PHONEBOOK_COUNT_MAX ;
conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
if (!conn) {
return -1;
}
- message = dbus_message_new_method_call("org.bluez.pb_agent",
- "/org/bluez/pb_agent",
- "org.bluez.PbAgent",
- "GetPhonebookList");
+ message = dbus_message_new_method_call(PHONEBOOK_BUS_NAME,
+ PHONEBOOK_PATH,
+ PHONEBOOK_INTERFACE,
+ "GetPhonebookEntriesAt");
if (!message) {
DBG("Can't allocate new message");
+ dbus_connection_unref(conn);
return -1;
}
+
+ dbus_message_append_args(message,
+ DBUS_TYPE_STRING,
+ &phonebook_store_list[ag_pb_info.path_id],
+ DBUS_TYPE_INT32, &start_index,
+ DBUS_TYPE_INT32, &end_index,
+ DBUS_TYPE_INVALID);
+
dbus_error_init(&error);
reply = dbus_connection_send_with_reply_and_block(conn,
} else {
DBG("Failed to get contacts");
}
+
+ dbus_message_unref(message);
+ dbus_connection_unref(conn);
+
return -1;
}
dbus_message_iter_init(reply, &iter);
dbus_message_iter_recurse(&iter, &iter_struct);
- idx = start_index;
- while (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_STRUCT) {
+ while(dbus_message_iter_get_arg_type(&iter_struct) ==
+ DBUS_TYPE_STRUCT) {
const char *name = NULL;
- const char *tel = NULL;
- uint32_t handle = 0;
+ const char *number = NULL;
- dbus_message_iter_recurse(&iter_struct, &entry);
+ char *uni_name;
+ char *uni_number;
- dbus_message_iter_get_basic(&entry, &name);
- dbus_message_iter_next(&entry);
- dbus_message_iter_get_basic(&entry, &tel);
- dbus_message_iter_next(&entry);
- dbus_message_iter_get_basic(&entry, &handle);
+ uint32_t handle = 0;
- DBG("[%d] handle:%d name:%s tel:%s]\n", handle, name, tel);
+ DBusMessageIter entry_iter;
- /*form the packet and sent to the remote headset*/
- 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)) {
- DBG("send_read_phonebook_resp - ERROR\n");
- telephony_read_phonebook_rsp(telephony_device, NULL,
- CME_ERROR_AG_FAILURE);
+ dbus_message_iter_recurse(&iter_struct,&entry_iter);
- dbus_message_unref(message);
- dbus_message_unref(reply);
- dbus_connection_unref(conn);
+ dbus_message_iter_get_basic(&entry_iter, &name);
+ dbus_message_iter_next(&entry_iter);
+ dbus_message_iter_get_basic(&entry_iter, &number);
+ dbus_message_iter_next(&entry_iter);
+ dbus_message_iter_get_basic(&entry_iter, &handle);
+ dbus_message_iter_next(&entry_iter);
- return -1;
- }
- idx++;
- }
- }
dbus_message_iter_next(&iter_struct);
- }
- telephony_read_phonebook_rsp(telephony_device, NULL, CME_ERROR_NONE);
+ uni_name = g_strndup(name, PHONEBOOK_NAME_MAX_LENGTH);
+ uni_number = g_strndup(number, PHONEBOOK_NAME_MAX_LENGTH);
+
+ get_unicode_string(name, uni_name);
+ get_unicode_string(number, uni_number);
+
+ telephony_read_phonebook_entries_ind(uni_name,
+ uni_number, handle);
+
+ g_free(uni_name);
+ g_free(uni_number);
+ }
dbus_message_unref(message);
dbus_message_unref(reply);
dbus_connection_unref(conn);
- /*Process the List and send response*/
return 0;
}
-static int get_call_log_list(void *telephony_device, char* path ,
- int32_t start_index, int32_t end_index)
+static int find_phonebook_entries(const char *str)
{
DBusConnection *conn;
- DBusMessage *message = NULL, *reply;
+ DBusMessage *message, *reply;
+ DBusMessageIter iter, iter_struct;
DBusError error;
- DBusMessageIter iter, iter_struct, entry;
- int32_t idx = 0;
-
- if ((start_index > (int) ag_pb_info.max_size) || (start_index <= 0) ||
- (start_index > CALL_LOG_COUNT_MAX)) {
- return -1;
- }
-
- if (end_index > (int) ag_pb_info.max_size)
- end_index = CALL_LOG_COUNT_MAX ;
-
conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
if (!conn) {
return -1;
}
- 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) {
- 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) {
- message = dbus_message_new_method_call("org.bluez.pb_agent",
- "/org/bluez/pb_agent",
- "org.bluez.PbAgent",
- "GetIncomingCallsList");
- }
+ message = dbus_message_new_method_call(PHONEBOOK_BUS_NAME,
+ PHONEBOOK_PATH,
+ PHONEBOOK_INTERFACE,
+ "GetPhonebookEntriesFindAt");
if (!message) {
DBG("Can't allocate new message");
dbus_connection_unref(conn);
return -1;
}
+
+ dbus_message_append_args(message,
+ DBUS_TYPE_STRING,
+ &phonebook_store_list[ag_pb_info.path_id],
+ DBUS_TYPE_STRING, &str,
+ DBUS_TYPE_INVALID);
+
dbus_error_init(&error);
reply = dbus_connection_send_with_reply_and_block(conn,
} else {
DBG("Failed to get contacts");
}
+
dbus_message_unref(message);
dbus_connection_unref(conn);
+
return -1;
}
dbus_message_iter_init(reply, &iter);
dbus_message_iter_recurse(&iter, &iter_struct);
- idx = start_index;
- while (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_STRUCT) {
+ while(dbus_message_iter_get_arg_type(&iter_struct) ==
+ DBUS_TYPE_STRUCT) {
const char *name = NULL;
- const char *tel = NULL;
- uint32_t handle = 0;
+ const char *number = NULL;
- dbus_message_iter_recurse(&iter_struct, &entry);
+ char *uni_name;
+ char *uni_number;
- dbus_message_iter_get_basic(&entry, &name);
- dbus_message_iter_next(&entry);
- dbus_message_iter_get_basic(&entry, &tel);
- dbus_message_iter_next(&entry);
- dbus_message_iter_get_basic(&entry, &handle);
+ uint32_t handle = 0;
- DBG("[%d] handle:%d name:%s tel:%s]\n", handle, name, tel);
+ DBusMessageIter entry_iter;
- /*form the packet and sent to the remote headset*/
- 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) {
- /* Need to form the time stamp pkt also */
- 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);
+ dbus_message_iter_recurse(&iter_struct,&entry_iter);
- dbus_message_unref(message);
- dbus_message_unref(reply);
- dbus_connection_unref(conn);
+ dbus_message_iter_get_basic(&entry_iter, &name);
+ dbus_message_iter_next(&entry_iter);
+ dbus_message_iter_get_basic(&entry_iter, &number);
+ dbus_message_iter_next(&entry_iter);
+ dbus_message_iter_get_basic(&entry_iter, &handle);
+ dbus_message_iter_next(&entry_iter);
- return -1;
- }
- idx++;
- }
- }
dbus_message_iter_next(&iter_struct);
- }
- telephony_read_phonebook_rsp(telephony_device, NULL, CME_ERROR_NONE);
+ uni_name = g_strndup(name, PHONEBOOK_NAME_MAX_LENGTH);
+ uni_number = g_strndup(number, PHONEBOOK_NAME_MAX_LENGTH);
+
+ get_unicode_string(name, uni_name);
+ get_unicode_string(number, uni_number);
+
+ telephony_find_phonebook_entries_ind(uni_name, uni_number, handle);
+
+ g_free(uni_name);
+ g_free(uni_number);
+ }
dbus_message_unref(message);
dbus_message_unref(reply);
dbus_connection_unref(conn);
- /*Process the List and send response*/
return 0;
+}
+
+void telephony_select_phonebook_memory_status(void *telephony_device)
+{
+ int32_t path_id = ag_pb_info.path_id;
+ cme_error_t err = CME_ERROR_NONE;
+
+ DBG("telephony-tizen: telephony_read_phonebook_store\n");
+ if (path_id < 0 || path_id >= PHONEBOOK_STORE_LIST_SIZE)
+ path_id = 0;
+
+ if (get_phonebook_count(phonebook_store_list[path_id],
+ &ag_pb_info.max_size, &ag_pb_info.used))
+ err = CME_ERROR_AG_FAILURE;
+
+ telephony_select_phonebook_memory_status_rsp(telephony_device,
+ phonebook_store_list[path_id],
+ ag_pb_info.max_size,
+ ag_pb_info.used,
+ err);
}
-void telephony_read_phonebook(void *telephony_device, const char *cmd)
+
+void telephony_select_phonebook_memory_list(void *telephony_device)
{
- char *ptr = 0;
+ DBG("telephony-tizen: telephony_select_phonebook_memory_list %d\n", PHONEBOOK_STORE_LIST_SIZE);
+/*
+ For Blue & Me car kit we may have to add the
+ patch here(similar to the patch done in H1 and H2 )
+*/
+ GString *str;
+ int i = 0;
- if (NULL != cmd) {
- int32_t start_index;
- int32_t end_index;
- ptr = (char *) strchr(cmd, (int32_t)',');
- if (NULL == ptr) {
- start_index = strtol(cmd, NULL, 0);
- end_index = -1;
- DBG("start_index = [%d] \n", start_index);
- } else {
- ptr++;
- start_index = strtol(cmd, NULL, 0);
- end_index = strtol(ptr, NULL, 0);
- DBG("start_index = [%d], end_index = [%d] \n",
- start_index, end_index);
+ str = g_string_new("(");
+ while (i < PHONEBOOK_STORE_LIST_SIZE) {
+ if (i > 0) {
+ g_string_append(str, ",");
}
+ g_string_append(str, phonebook_store_list[i]);
+ i++;
+ }
- 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;
- }
+ g_string_append(str, ")");
+
+ telephony_select_phonebook_memory_list_rsp(telephony_device,
+ g_string_free(str, FALSE), CME_ERROR_NONE);
+}
+
+void telephony_select_phonebook_memory(void *telephony_device, const gchar *path)
+{
+
+ int i = 0;
+ cme_error_t err;
+
+ DBG("telephony-tizen: telephony_select_phonebook_memory\n");
+ DBG("set phonebook type to [%s]\n", path);
+
+ while (i < PHONEBOOK_STORE_LIST_SIZE) {
+ if (strcmp(phonebook_store_list[i], path) == 0)
+ break;
+
+ i++;
+ }
+
+ if (i >= 0 && i < PHONEBOOK_STORE_LIST_SIZE) {
+ err = CME_ERROR_NONE;
+ if (ag_pb_info.path_id != i) {
+ ag_pb_info.type = 0;
+ ag_pb_info.max_size = 0;
+ ag_pb_info.used = 0;
}
- 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;
+
+ ag_pb_info.path_id = i;
+ } else {
+ err = CME_ERROR_INVALID_TEXT_STRING;
+ }
+ telephony_select_phonebook_memory_rsp(telephony_device, err);
+}
+
+void telephony_read_phonebook_entries_list(void *telephony_device)
+{
+ cme_error_t err = CME_ERROR_NONE;
+ int32_t path_id = ag_pb_info.path_id;
+
+ DBG("telephony-tizen: telephony_read_phonebook_entries_list\n");
+
+ if (path_id < 0 || path_id >= PHONEBOOK_STORE_LIST_SIZE)
+ err = CME_ERROR_INVALID_INDEX;
+ else {
+ if(ag_pb_info.max_size == 0 && ag_pb_info.used == 0) {
+ if (get_phonebook_count(phonebook_store_list[path_id],
+ &ag_pb_info.max_size, &ag_pb_info.used) != 0) {
+ err = CME_ERROR_NOT_ALLOWED;
}
}
+ }
-/*
- Using the start and end index get the contact list from the pbap agent and
- send the data to remote headset.
-*/
+ telephony_read_phonebook_entries_list_rsp(telephony_device, ag_pb_info.used,
+ PHONEBOOK_NUMBER_MAX_LENGTH, PHONEBOOK_NAME_MAX_LENGTH,
+ err);
+}
+
+void telephony_read_phonebook_entries(void *telephony_device, const char *cmd)
+{
+ int start_index = 0;
+ int end_index = 0;
+
+ char *str = NULL;
+ char *next = NULL;
+
+ cme_error_t err = CME_ERROR_NONE;
+
+ DBG("telephony-tizen: telephony_read_phonebook_entries\n");
+
+ if (cmd == NULL)
+ return;
+
+ str = g_strdup(cmd);
+ next = strchr(str, ',');
+
+ if (next) {
+ *next = '\0';
+ next++;
+
+ end_index = strtol(next, NULL, 10);
}
- }
-void telephony_find_phonebook_entry_properties(void *telephony_device)
+ start_index = strtol(str, NULL, 10);
+
+ g_free(str);
+
+ if(read_phonebook_entries(start_index, end_index))
+ err = CME_ERROR_AG_FAILURE;
+
+ telephony_read_phonebook_entries_rsp(telephony_device, err);
+}
+
+void telephony_find_phonebook_entries_status(void *telephony_device)
{
- telephony_find_phonebook_entry_properties_rsp(telephony_device,
+ telephony_find_phonebook_entries_status_ind(
PHONEBOOK_NUMBER_MAX_LENGTH,
- PHONEBOOK_NAME_MAX_LENGTH,
- CME_ERROR_NONE);
+ PHONEBOOK_NAME_MAX_LENGTH);
+ telephony_find_phonebook_entries_status_rsp(telephony_device,
+ CME_ERROR_NONE);
}
-void telephony_find_phonebook_entry(void *telephony_device, const char *cmd)
+void telephony_find_phonebook_entries(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
-*/
+ gchar *st = NULL;
+ gchar *unquoted = NULL;
+
+ cme_error_t err = CME_ERROR_NONE;
+
+ DBG("telephony-tizen: telephony_find_phonebook_entry\n");
+
+ /* remove quote and compress */
+ st = strchr(cmd, '"');
+ if (st == NULL)
+ unquoted = g_strdup(cmd);
+ else {
+ gchar *end = NULL;
+
+ end = strrchr(cmd, '"');
+ if(end == NULL)
+ unquoted = g_strdup(cmd);
+ else
+ unquoted = g_strndup(st + 1, end - st - 1);
+ }
+
+ if (find_phonebook_entries(unquoted))
+ err = CME_ERROR_AG_FAILURE;
+
+ telephony_find_phonebook_entries_rsp(telephony_device, err);
+ g_free(unquoted);
}
+
void telephony_get_preffered_store_capacity(void *telephony_device)
{
+ DBG("telephony-tizen: telephony_list_preffered_store_capcity\n");
+
telephony_get_preffered_store_capacity_rsp(telephony_device,
PREFFERED_MESSAGE_STORAGE_MAX,
CME_ERROR_NONE);
void telephony_list_preffered_store(void *telephony_device)
{
+ DBG("telephony-tizen: telephony_list_preffered_store_capcity\n");
+
telephony_list_preffered_store_rsp(telephony_device,
PREFFERED_MESSAGE_STORAGE_LIST,
CME_ERROR_NONE);
{
}
*/
+
void telephony_get_character_set(void *telephony_device)
{
+ DBG("telephony-tizen: telephony_get_character_set\n");
+
telephony_supported_character_generic_rsp(telephony_device,
PHONEBOOK_CHARACTER_SET_SUPPORTED,
CME_ERROR_NONE);
void telephony_list_supported_character(void *telephony_device)
{
+ DBG("telephony-tizen: telephony_list_supported_character_set\n");
+
telephony_supported_character_generic_rsp(telephony_device,
PHONEBOOK_CHARACTER_SET_LIST,
CME_ERROR_NONE);
}
+static void telephony_get_battery_property_reply(
+ DBusPendingCall *call, void *data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError derr;
+ int32_t bcs = 0;
+ int32_t bcl = 0;
+
+ DBG("battery_property_reply");
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ DBG("battery_property_reply: %s", derr.message);
+ dbus_error_free(&derr);
+ telephony_battery_charge_status_rsp(data, bcs,
+ bcl, CME_ERROR_AG_FAILURE);
+ goto done;
+ }
+
+ if (dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_INT32, &bcs,
+ DBUS_TYPE_INT32, &bcl,
+ DBUS_TYPE_INVALID) == FALSE) {
+ DBG("get_signal_quality_reply: Invalid arguments");
+ telephony_battery_charge_status_rsp(data, bcs,
+ bcl, CME_ERROR_AG_FAILURE);
+ goto done;
+
+ }
+
+ telephony_battery_charge_status_rsp(data, bcs,
+ bcl, CME_ERROR_NONE);
+
+done:
+ dbus_message_unref(reply);
+}
+
+void telephony_get_battery_property(void *telephony_device)
+{
+ DBG("telephony-tizen: telephony_get_battery_property\n");
+
+ if (0 != dbus_method_call_send(HFP_AGENT_SERVICE, HFP_AGENT_PATH,
+ HFP_AGENT_INTERFACE, "GetBatteryStatus",
+ telephony_get_battery_property_reply,
+ telephony_device, DBUS_TYPE_INVALID)) {
+ telephony_battery_charge_status_rsp(telephony_device, 0, 0,
+ CME_ERROR_AG_FAILURE);
+ }
+}
+
+static void telephony_get_signal_quality_reply(DBusPendingCall *call,
+ void *data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusError derr;
+ int32_t rssi = 0;
+ int32_t ber = 0;
+
+ DBG("get_signal_quality_reply");
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ DBG("get_signal_quality_reply: %s", derr.message);
+ dbus_error_free(&derr);
+ telephony_signal_quality_rsp(data, rssi,
+ ber, CME_ERROR_AG_FAILURE);
+ goto done;
+ }
+
+ if (dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_INT32, &rssi,
+ DBUS_TYPE_INT32, &ber,
+ DBUS_TYPE_INVALID) == FALSE) {
+ DBG("get_signal_quality_reply: Invalid arguments");
+ telephony_signal_quality_rsp(data, rssi,
+ ber, CME_ERROR_AG_FAILURE);
+ goto done;
+
+ }
+
+ telephony_signal_quality_rsp(data, rssi,
+ ber, CME_ERROR_NONE);
+
+done:
+ dbus_message_unref(reply);
+}
+
+void telephony_get_signal_quality(void *telephony_device)
+{
+ DBG("telephony-tizen: telephony_get_signal_quality\n");
+
+ if (0 != dbus_method_call_send(HFP_AGENT_SERVICE, HFP_AGENT_PATH,
+ HFP_AGENT_INTERFACE, "GetSignalQuality",
+ telephony_get_signal_quality_reply,
+ telephony_device, DBUS_TYPE_INVALID)) {
+ telephony_signal_quality_rsp(telephony_device, 0, 0,
+ CME_ERROR_AG_FAILURE);
+ }
+
+}
+
/*
void telephony_set_characterset(void *telephony_device, const char *cmd)
{
void telephony_voice_dial_req(void *telephony_device, gboolean enable);
void telephony_key_press_req(void *telephony_device, const char *keys);
+#ifdef __TIZEN_PATCH__
+void telephony_select_phonebook_memory_status(void *telephony_device);
+void telephony_select_phonebook_memory_list(void *telephony_device);
+void telephony_select_phonebook_memory(void *telephony_device, const gchar *path);
+void telephony_read_phonebook_entries_list(void *telephony_device);
+void telephony_read_phonebook_entries(void *telephony_device, const char *cmd);
+void telephony_find_phonebook_entries_status(void *telephony_device);
+void telephony_find_phonebook_entries(void *telephony_device, const char *cmd);
+void telephony_get_preffered_store_capacity(void *telephony_device);
+void telephony_list_preffered_store(void *telephony_device);
+void telephony_get_character_set(void *telephony_device);
+void telephony_list_supported_character(void *telephony_device);
+void telephony_get_battery_property(void *telephony_device);
+void telephony_get_signal_quality(void *telephony_device);
+
+int telephony_select_phonebook_memory_status_rsp(void *telephony_device, const gchar *path,
+ uint32_t total, uint32_t used,
+ cme_error_t err);
+int telephony_select_phonebook_memory_list_rsp(void *telephony_device, const char *buf,
+ cme_error_t err);
+int telephony_select_phonebook_memory_rsp(void *telephony_device, cme_error_t err);
+int telephony_read_phonebook_entries_list_rsp(void *telephony_device,
+ uint32_t used,
+ uint32_t number_length,
+ uint32_t name_length,
+ cme_error_t err);
+int telephony_find_phonebook_entries_status_rsp(void *telephony_device,
+ cme_error_t err);
+int telephony_read_phonebook_entries_rsp(void *telephony_device,
+ cme_error_t err);
+int telephony_find_phonebook_entries_rsp(void *telephony_device,
+ cme_error_t err);
+int telephony_list_preffered_store_rsp(void *telephony_device,
+ char *prefrd_list, cme_error_t err);
+int telephony_get_preffered_store_capacity_rsp(void *telephony_device,
+ uint32_t store_capacity,
+ cme_error_t err);
+int telephony_supported_character_generic_rsp(void *telephony_device,
+ char *character_set_list,
+ cme_error_t err);
+int telephony_battery_charge_status_rsp(void *telephony_device,
+ int32_t bcs,
+ int32_t bcl,
+ cme_error_t err);
+int telephony_signal_quality_rsp(void *telephony_device,
+ int32_t rssi,
+ int32_t ber,
+ cme_error_t err);
+
+int telephony_read_phonebook_entries_ind(const char *name, const char *number,
+ uint32_t handle);
+int telephony_find_phonebook_entries_status_ind( uint32_t number_length,
+ uint32_t name_length);
+int telephony_find_phonebook_entries_ind(const char *name, const char *number,
+ uint32_t handle);
+#endif
+
/* AG responses to HF requests. These are implemented by headset.c */
int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err);
int telephony_response_and_hold_rsp(void *telephony_device, cme_error_t err);
#include <errno.h>
+#include <bluetooth/uuid.h>
+
#include <glib.h>
#include <gdbus.h>
#include "a2dp.h"
#include "headset.h"
#include "gateway.h"
-
-#ifndef DBUS_TYPE_UNIX_FD
-#define DBUS_TYPE_UNIX_FD -1
-#endif
+#include "avrcp.h"
#define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport"
guint watch;
};
+struct a2dp_transport {
+ struct avdtp *session;
+ uint16_t delay;
+ uint16_t volume;
+};
+
+struct headset_transport {
+ struct audio_device *device;
+ unsigned int nrec_id;
+};
+
struct media_transport {
DBusConnection *conn;
char *path; /* Transport object path */
struct audio_device *device; /* Transport device */
- struct avdtp *session; /* Signalling session (a2dp only) */
struct media_endpoint *endpoint; /* Transport endpoint */
GSList *owners; /* Transport owners */
uint8_t *configuration; /* Transport configuration */
int fd; /* Transport file descriptor */
uint16_t imtu; /* Transport input mtu */
uint16_t omtu; /* Transport output mtu */
- uint16_t delay; /* Transport delay (a2dp only) */
- unsigned int nrec_id; /* Transport nrec watch (headset only) */
gboolean read_lock;
gboolean write_lock;
gboolean in_use;
struct media_transport *transport,
const char *property,
DBusMessageIter *value);
+ GDestroyNotify destroy;
+ void *data;
};
void media_transport_destroy(struct media_transport *transport)
static guint resume_a2dp(struct media_transport *transport,
struct media_owner *owner)
{
+ struct a2dp_transport *a2dp = transport->data;
struct media_endpoint *endpoint = transport->endpoint;
struct audio_device *device = transport->device;
struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
- if (transport->session == NULL) {
- transport->session = avdtp_get(&device->src, &device->dst);
- if (transport->session == NULL)
+ if (a2dp->session == NULL) {
+ a2dp->session = avdtp_get(&device->src, &device->dst);
+ if (a2dp->session == NULL)
return 0;
}
if (transport->in_use == TRUE)
goto done;
- transport->in_use = a2dp_sep_lock(sep, transport->session);
+ transport->in_use = a2dp_sep_lock(sep, a2dp->session);
if (transport->in_use == FALSE)
return 0;
done:
- return a2dp_resume(transport->session, sep, a2dp_resume_complete,
- owner);
+ return a2dp_resume(a2dp->session, sep, a2dp_resume_complete, owner);
}
static void a2dp_suspend_complete(struct avdtp *session,
{
struct media_owner *owner = user_data;
struct media_transport *transport = owner->transport;
+ struct a2dp_transport *a2dp = transport->data;
struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint);
/* Release always succeeds */
media_owner_remove(owner);
}
- a2dp_sep_unlock(sep, transport->session);
+ a2dp_sep_unlock(sep, a2dp->session);
transport->in_use = FALSE;
media_transport_remove(transport, owner);
}
static guint suspend_a2dp(struct media_transport *transport,
struct media_owner *owner)
{
+ struct a2dp_transport *a2dp = transport->data;
struct media_endpoint *endpoint = transport->endpoint;
struct a2dp_sep *sep = media_endpoint_get_sep(endpoint);
if (!owner) {
- a2dp_sep_unlock(sep, transport->session);
+ a2dp_sep_unlock(sep, a2dp->session);
transport->in_use = FALSE;
return 0;
}
- return a2dp_suspend(transport->session, sep, a2dp_suspend_complete,
- owner);
+ return a2dp_suspend(a2dp->session, sep, a2dp_suspend_complete, owner);
}
static void cancel_a2dp(struct media_transport *transport, guint id)
{
struct media_owner *owner = user_data;
struct media_transport *transport = owner->transport;
+ struct audio_device *device = transport->device;
/* Release always succeeds */
if (owner->pending) {
media_owner_remove(owner);
}
+ gateway_unlock(device, GATEWAY_LOCK_READ | GATEWAY_LOCK_WRITE);
transport->in_use = FALSE;
media_transport_remove(transport, owner);
return FALSE;
}
gateway_suspend_stream(device);
- gateway_unlock(device, GATEWAY_LOCK_READ | GATEWAY_LOCK_WRITE);
g_idle_add(gateway_suspend_complete, owner);
return id++;
}
DBG("Transport %s Owner %s", transport->path, owner->name);
transport->owners = g_slist_append(transport->owners, owner);
owner->transport = transport;
+ owner->watch = g_dbus_add_disconnect_watch(transport->conn, owner->name,
+ media_owner_exit,
+ owner, NULL);
}
static struct media_owner *media_owner_create(DBusConnection *conn,
owner = g_new0(struct media_owner, 1);
owner->name = g_strdup(dbus_message_get_sender(msg));
owner->accesstype = g_strdup(accesstype);
- owner->watch = g_dbus_add_disconnect_watch(conn, owner->name,
- media_owner_exit,
- owner, NULL);
DBG("Owner created: sender=%s accesstype=%s", owner->name,
accesstype);
const char *property,
DBusMessageIter *value)
{
+ struct a2dp_transport *a2dp = transport->data;
+
if (g_strcmp0(property, "Delay") == 0) {
if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
return -EINVAL;
- dbus_message_iter_get_basic(value, &transport->delay);
+ dbus_message_iter_get_basic(value, &a2dp->delay);
/* FIXME: send new delay */
return 0;
+ } else if (g_strcmp0(property, "Volume") == 0) {
+ uint16_t volume;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
+ return -EINVAL;
+
+ dbus_message_iter_get_basic(value, &volume);
+
+ if (volume > 127)
+ return -EINVAL;
+
+ if (a2dp->volume == volume)
+ return 0;
+
+ return avrcp_set_volume(transport->device, volume);
}
return -EINVAL;
static void get_properties_a2dp(struct media_transport *transport,
DBusMessageIter *dict)
{
- dict_append_entry(dict, "Delay", DBUS_TYPE_UINT16, &transport->delay);
+ struct a2dp_transport *a2dp = transport->data;
+
+ dict_append_entry(dict, "Delay", DBUS_TYPE_UINT16, &a2dp->delay);
+
+ if (a2dp->volume <= 127)
+ dict_append_entry(dict, "Volume", DBUS_TYPE_UINT16,
+ &a2dp->volume);
}
static void get_properties_headset(struct media_transport *transport,
return reply;
}
-static GDBusMethodTable transport_methods[] = {
- { "GetProperties", "", "a{sv}", get_properties },
- { "Acquire", "s", "h", acquire,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "Release", "s", "", release,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "SetProperty", "sv", "", set_property },
+static const GDBusMethodTable transport_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { GDBUS_ASYNC_METHOD("Acquire",
+ GDBUS_ARGS({ "access_type", "s" }),
+ GDBUS_ARGS({ "fd", "h" }, { "mtu_r", "q" },
+ { "mtu_w", "q" } ),
+ acquire) },
+ { GDBUS_ASYNC_METHOD("Release",
+ GDBUS_ARGS({ "access_type", "s" }), NULL,
+ release ) },
+ { GDBUS_ASYNC_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+ NULL, set_property) },
{ },
};
-static GDBusSignalTable transport_signals[] = {
- { "PropertyChanged", "sv" },
+static const GDBusSignalTable transport_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ }
};
+static void destroy_a2dp(void *data)
+{
+ struct a2dp_transport *a2dp = data;
+
+ if (a2dp->session)
+ avdtp_unref(a2dp->session);
+
+ g_free(a2dp);
+}
+
+static void destroy_headset(void *data)
+{
+ struct headset_transport *headset = data;
+
+ if (headset->nrec_id > 0)
+ headset_remove_nrec_cb(headset->device, headset->nrec_id);
+
+ g_free(headset);
+}
+
static void media_transport_free(void *data)
{
struct media_transport *transport = data;
g_slist_free(transport->owners);
- if (transport->session)
- avdtp_unref(transport->session);
-
- if (transport->nrec_id)
- headset_remove_nrec_cb(transport->device, transport->nrec_id);
+ if (transport->destroy != NULL)
+ transport->destroy(transport->data);
if (transport->conn)
dbus_connection_unref(transport->conn);
uuid = media_endpoint_get_uuid(endpoint);
if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0 ||
strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
+ struct a2dp_transport *a2dp;
+
+ a2dp = g_new0(struct a2dp_transport, 1);
+ a2dp->volume = -1;
+
transport->resume = resume_a2dp;
transport->suspend = suspend_a2dp;
transport->cancel = cancel_a2dp;
transport->get_properties = get_properties_a2dp;
transport->set_property = set_property_a2dp;
+ transport->data = a2dp;
+ transport->destroy = destroy_a2dp;
} else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
strcasecmp(uuid, HSP_AG_UUID) == 0) {
+ struct headset_transport *headset;
+
+ headset = g_new0(struct headset_transport, 1);
+ headset->device = device;
+ headset->nrec_id = headset_add_nrec_cb(device,
+ headset_nrec_changed,
+ transport);
+
transport->resume = resume_headset;
transport->suspend = suspend_headset;
transport->cancel = cancel_headset;
transport->get_properties = get_properties_headset;
transport->set_property = set_property_headset;
- transport->nrec_id = headset_add_nrec_cb(device,
- headset_nrec_changed,
- transport);
+ transport->data = headset;
+ transport->destroy = destroy_headset;
} else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
strcasecmp(uuid, HSP_HS_UUID) == 0) {
transport->resume = resume_gateway;
void media_transport_update_delay(struct media_transport *transport,
uint16_t delay)
{
+ struct a2dp_transport *a2dp = transport->data;
+
/* Check if delay really changed */
- if (transport->delay == delay)
+ if (a2dp->delay == delay)
return;
- transport->delay = delay;
+ a2dp->delay = delay;
emit_property_changed(transport->conn, transport->path,
MEDIA_TRANSPORT_INTERFACE, "Delay",
- DBUS_TYPE_UINT16, &transport->delay);
+ DBUS_TYPE_UINT16, &a2dp->delay);
}
struct audio_device *media_transport_get_dev(struct media_transport *transport)
{
return transport->device;
}
+
+void media_transport_update_volume(struct media_transport *transport,
+ uint8_t volume)
+{
+ struct a2dp_transport *a2dp = transport->data;
+
+ /* Check if volume really changed */
+ if (a2dp->volume == volume)
+ return;
+
+ a2dp->volume = volume;
+
+ emit_property_changed(transport->conn, transport->path,
+ MEDIA_TRANSPORT_INTERFACE, "Volume",
+ DBUS_TYPE_UINT16, &a2dp->volume);
+}
struct audio_device *media_transport_get_dev(struct media_transport *transport);
void media_transport_update_delay(struct media_transport *transport,
uint16_t delay);
+void media_transport_update_volume(struct media_transport *transport,
+ uint8_t volume);
void transport_get_properties(struct media_transport *transport,
DBusMessageIter *iter);
#include "source.h"
#include "gateway.h"
#include "unix.h"
-#include "glib-compat.h"
#define check_nul(str) (str[sizeof(str) - 1] == '\0')
space_left = BT_SUGGESTED_BUFFER_SIZE - rsp->h.length;
- /* endianess prevent direct cast */
+ /* endianness prevents direct cast */
if (codec_cap->media_codec_type == A2DP_CODEC_SBC) {
struct sbc_codec_cap *sbc_cap = (void *) codec_cap;
sbc_capabilities_t *sbc = (void *) codec;
#include <bluetooth/l2cap.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/sco.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
#include <glib.h>
struct set_opts {
bdaddr_t src;
bdaddr_t dst;
+ uint8_t dst_type;
int defer;
int sec_level;
uint8_t channel;
return 0;
}
-static int l2cap_connect(int sock, const bdaddr_t *dst,
- uint16_t psm, uint16_t cid)
+static int l2cap_connect(int sock, const bdaddr_t *dst, uint8_t dst_type,
+ uint16_t psm, uint16_t cid)
{
int err;
struct sockaddr_l2 addr;
else
addr.l2_psm = htobs(psm);
+ addr.l2_bdaddr_type = dst_type;
+
err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));
if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))
return -errno;
return 0;
}
+static gboolean get_key_size(int sock, int *size, GError **err)
+{
+ struct bt_security sec;
+ socklen_t len;
+
+ memset(&sec, 0, sizeof(sec));
+ len = sizeof(sec);
+ if (getsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) == 0) {
+ *size = sec.key_size;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu,
uint16_t omtu, uint8_t mode, int master,
int flushable, uint32_t priority, GError **err)
/* Set defaults */
opts->defer = DEFAULT_DEFER_TIMEOUT;
opts->master = -1;
- opts->sec_level = BT_IO_SEC_MEDIUM;
opts->mode = L2CAP_MODE_BASIC;
opts->flushable = -1;
opts->priority = 0;
+ opts->dst_type = BDADDR_BREDR;
while (opt != BT_IO_OPT_INVALID) {
switch (opt) {
case BT_IO_OPT_SOURCE:
str = va_arg(args, const char *);
- if (strncasecmp(str, "hci", 3) == 0)
- hci_devba(atoi(str + 3), &opts->src);
- else
- str2ba(str, &opts->src);
+ str2ba(str, &opts->src);
break;
case BT_IO_OPT_SOURCE_BDADDR:
bacpy(&opts->src, va_arg(args, const bdaddr_t *));
case BT_IO_OPT_DEST_BDADDR:
bacpy(&opts->dst, va_arg(args, const bdaddr_t *));
break;
+ case BT_IO_OPT_DEST_TYPE:
+ opts->dst_type = va_arg(args, int);
+ break;
case BT_IO_OPT_DEFER_TIMEOUT:
opts->defer = va_arg(args, int);
break;
case BT_IO_OPT_DEST_BDADDR:
bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr);
break;
+ case BT_IO_OPT_DEST_TYPE:
+ g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS,
+ "Not implemented");
+ return FALSE;
case BT_IO_OPT_DEFER_TIMEOUT:
len = sizeof(int);
if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP,
va_arg(args, int *), err))
return FALSE;
break;
+ case BT_IO_OPT_KEY_SIZE:
+ if (!get_key_size(sock, va_arg(args, int *), err))
+ return FALSE;
+ break;
case BT_IO_OPT_PSM:
*(va_arg(args, uint16_t *)) = src.l2_psm ?
btohs(src.l2_psm) : btohs(dst.l2_psm);
switch (type) {
case BT_IO_L2RAW:
case BT_IO_L2CAP:
+ case BT_IO_L2ERTM:
return l2cap_get(sock, err, opt1, args);
case BT_IO_RFCOMM:
return rfcomm_get(sock, err, opt1, args);
switch (type) {
case BT_IO_L2RAW:
case BT_IO_L2CAP:
+ case BT_IO_L2ERTM:
return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu,
opts.mode, opts.master, opts.flushable,
opts.priority, err);
opts->priority, err))
goto failed;
break;
+ case BT_IO_L2ERTM:
+ sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_L2CAP);
+ if (sock < 0) {
+ ERROR_FAILED(err, "socket(STREAM, L2CAP)", errno);
+ return NULL;
+ }
+ if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0,
+ opts->cid, err) < 0)
+ goto failed;
+ if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu,
+ opts->mode, opts->master, opts->flushable,
+ opts->priority, err))
+ goto failed;
+ break;
case BT_IO_RFCOMM:
sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (sock < 0) {
switch (type) {
case BT_IO_L2RAW:
- err = l2cap_connect(sock, &opts.dst, 0, opts.cid);
+ err = l2cap_connect(sock, &opts.dst, opts.dst_type, 0,
+ opts.cid);
break;
case BT_IO_L2CAP:
- err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid);
+ case BT_IO_L2ERTM:
+ err = l2cap_connect(sock, &opts.dst, opts.dst_type,
+ opts.psm, opts.cid);
break;
case BT_IO_RFCOMM:
err = rfcomm_connect(sock, &opts.dst, opts.channel);
typedef enum {
BT_IO_L2RAW,
BT_IO_L2CAP,
+ BT_IO_L2ERTM,
BT_IO_RFCOMM,
BT_IO_SCO,
} BtIOType;
BT_IO_OPT_SOURCE_BDADDR,
BT_IO_OPT_DEST,
BT_IO_OPT_DEST_BDADDR,
+ BT_IO_OPT_DEST_TYPE,
BT_IO_OPT_DEFER_TIMEOUT,
BT_IO_OPT_SEC_LEVEL,
+ BT_IO_OPT_KEY_SIZE,
BT_IO_OPT_CHANNEL,
BT_IO_OPT_SOURCE_CHANNEL,
BT_IO_OPT_DEST_CHANNEL,
BT_IO_SEC_HIGH,
} BtIOSecLevel;
+typedef enum {
+ BT_IO_MODE_BASIC = 0,
+ BT_IO_MODE_RETRANS,
+ BT_IO_MODE_FLOWCTL,
+ BT_IO_MODE_ERTM,
+ BT_IO_MODE_STREAMING
+} BtIOMode;
+
typedef void (*BtIOConfirm)(GIOChannel *io, gpointer user_data);
typedef void (*BtIOConnect)(GIOChannel *io, GError *err, gpointer user_data);
int id, try = 30;
struct rfcomm_dev_req req = {
- flags: (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
- dev_id: -1
+ .flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP),
+ .dev_id = -1
};
alen = sizeof(sa);
if (detach) {
if (daemon(0, 0)) {
perror("Can't start daemon");
- exit(1);
+ exit(1);
}
} else
log_option |= LOG_PERROR;
static sdp_record_t *record = NULL;
static sdp_session_t *session = NULL;
-static void add_lang_attr(sdp_record_t *r)
-{
- sdp_lang_attr_t base_lang;
- sdp_list_t *langs = 0;
-
- /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
- base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
- base_lang.encoding = 106;
- base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
- langs = sdp_list_append(0, &base_lang);
- sdp_set_lang_attr(r, langs);
- sdp_list_free(langs, 0);
-}
-
static void epox_endian_quirk(unsigned char *data, int size)
{
/* USAGE_PAGE (Keyboard) 05 07
aproto = sdp_list_append(NULL, apseq);
sdp_set_access_protos(record, aproto);
- add_lang_attr(record);
+ sdp_add_lang_attr(record);
sdp_list_free(proto[0], NULL);
sdp_list_free(proto[1], NULL);
AC_PREREQ(2.60)
-AC_INIT(bluez, 4.98)
+AC_INIT(bluez, 4.101)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
AM_CONFIG_HEADER(config.h)
AC_ARG_BLUEZ
-AC_ARG_ENABLE(capng, AC_HELP_STRING([--enable-capng],
- [enable capabilities support]), [enable_capng=${enableval}])
-if (test "${enable_capng}" = "yes"); then
- PKG_CHECK_MODULES(CAPNG, libcap-ng, dummy=yes,
- AC_MSG_ERROR(Capabilities library is required))
- AC_SUBST(CAPNG_CFLAGS)
- AC_SUBST(CAPNG_LIBS)
- 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`"])
fi
AM_CONDITIONAL(SYSTEMD, test -n "${path_systemdunit}")
-AC_OUTPUT(Makefile scripts/bluetooth.rules doc/version.xml
- src/bluetoothd.8 src/bluetooth.service bluez.pc)
+AC_OUTPUT(Makefile doc/version.xml src/bluetoothd.8 src/bluetooth.service bluez.pc)
-bluez (4.98-slp2+1-3) unstable; urgency=low
+bluez (4.101-slp2+2) unstable; urgency=low
- * Release Tizen 1.0
- * Git: pkgs/b/bluez
- * Tag: bluez_4.98-slp2+1-3
+ * Increase version for package upload
+ * Git: framework/connectivity/bluez
+ * Tag: bluez_4.101-slp2+2
- -- DoHyun Pyun <dh79.pyun@samsung.com> Tue, 20 Mar 2012 17:32:01 +0900
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Mon, 20 Aug 2012 14:51:40 +0900
-bluez (4.98-slp2+1-2) unstable; urgency=low
+bluez (4.101-slp2+1) unstable; urgency=low
- * Change "SAMSUNG_PATCH" -> "TIZEN_PATCH"
- * Git: pkgs/b/bluez
- * Tag: bluez_4.98-slp2+1-2
+ * Increase version for package upload
+ * Git: framework/connectivity/bluez
+ * Tag: bluez_4.101-slp2+1
- -- DoHyun Pyun <dh79.pyun@samsung.com> Tue, 21 Feb 2012 18:03:00 +0900
+ -- Jaganath K <jaganath.k@samsung.com> Mon, 13 Aug 2012 19:16:04 +0530
-bluez (4.98-slp2+1-1) unstable; urgency=low
+bluez (4.99-slp2+8) unstable; urgency=low
- * Merge with lastest private git
- * Git: pkgs/b/bluez
- * Tag: bluez_4.98-slp2+1-1
+ * Increase version for package upload
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.99-slp2+8
- -- DoHyun Pyun <dh79.pyun@samsung.com> Thu, 09 Feb 2012 14:31:53 +0900
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Wed, 08 Aug 2012 12:25:20 +0900
-bluez (4.90-slp2+18-2) unstable; urgency=low
+bluez (4.99-slp2+7) unstable; urgency=low
- * Change "SAMSUNG_PATCH" -> "TIZEN_PATCH"
- * Git: pkgs/b/bluez
- * Tag: bluez_4.90-slp2+18-2
+ * Increase version for package upload
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.99-slp2+7
- -- Hocheol Seo <hocheol.seo@samsung.com> Fri, 23 Dec 2011 11:26:09 +0900
+ -- Divya yadav <divya.yadav@samsung.com> Mon, 06 Aug 2012 15:23:20 +0530
-bluez (4.90-slp2+18-1) unstable; urgency=low
+bluez (4.99-slp2+6) unstable; urgency=low
- * Change "com.samsung" -> "org.tizen"
- * Git: pkgs/b/bluez
- * Tag: bluez_4.90-slp2+18-1
+ * Enable OOB profile
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.99-slp2+6
- -- DoHyun Pyun <dh79.pyun@samsung.com> Thu, 22 Dec 2011 18:03:02 +0900
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Thu, 26 Jul 2012 11:28:58 +0900
+
+bluez (4.99-slp2+5) unstable; urgency=low
+
+ * Increase version for package upload
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.99-slp2+5
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Wed, 18 Jul 2012 12:55:39 +0900
+
+bluez (4.99-slp2+4) unstable; urgency=low
+
+ * Increase version for package upload
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.99-slp2+4
+
+ -- Girish Ashok Joshi <girish.joshi@samsung.com> Tue, 05 Jun 2012 12:37:12 +0530
+
+bluez (4.99-slp2+3) unstable; urgency=low
+
+ * Increase version for package upload
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.99-slp2+3
+
+ -- Chethan T N <chethan.tn@samsung.com> Tue, 22 May 2012 11:36:35 +0530
+
+bluez (4.99-slp2+2) unstable; urgency=low
+
+ * Increase version for package upload
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.99-slp2+2
+
+ -- Divya yadav <divya.yadav@samsung.com> Tue, 15 May 2012 15:51:58 +0530
+
+bluez (4.99-slp2+1) unstable; urgency=low
+
+ * Upgrade bluez and package upload.
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.99-slp2+1
+
+ -- Chethan T N <chethan.tn@samsung.com> Mon, 07 May 2012 14:01:34 +0530
+
+bluez (4.98-slp2+3) unstable; urgency=low
+
+ * Increase version for package upload.
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.98-slp2+3
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Thu, 19 Apr 2012 11:30:40 +0530
+
+bluez (4.98-slp2+2) unstable; urgency=low
+
+ * Increase version for package upload.
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.98-slp2+2
+
+ -- Sunil Kumar Behera <sunil.behera@samsung.com> Mon, 16 Apr 2012 19:32:14 +0530
+
+bluez (4.98-slp2+1) unstable; urgency=low
+
+ * Upgrade the bluez and upload the package
+ * Git: slp/pkgs/b/bluez
+ * Tag: bluez_4.98-slp2+1
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Wed, 08 Feb 2012 15:38:55 +0530
bluez (4.90-slp2+18) unstable; urgency=low
- * Update Version for build
+ * Remove gstreamer dpkg from bluez dpkgs
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+18
+
+ -- Chanyeol Park <chanyeol.park@samsung.com> Mon, 05 Dec 2011 15:17:40 +0900
+
+bluez (4.90-slp2+17) unstable; urgency=low
+
+ * Remove the samsung path about service list
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+17
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Thu, 17 Nov 2011 09:13:45 +0900
+
+bluez (4.90-slp2+16) unstable; urgency=low
+
+ * Remove samsung dbus avrcp
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+16
+
+ -- Chanyeol Park <chanyeol.park@samsung.com> Sat, 15 Oct 2011 15:35:53 +0900
+
+bluez (4.90-slp2+15) unstable; urgency=low
+
+ * Upload the package
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+15
+
+ -- Chethan T N <chethan.tn@samsung.com> Mon, 10 Oct 2011 14:30:36 +0530
+
+bluez (4.90-slp2+14) unstable; urgency=low
+
+ * Upload packages for the changes
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+14
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Tue, 27 Sep 2011 12:36:09 +0530
+
+bluez (4.90-slp2+13) unstable; urgency=low
+
+ * Fix the headset connection problem
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+13
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Tue, 20 Sep 2011 20:04:28 +0900
+
+bluez (4.90-slp2+12) unstable; urgency=low
+
+ * Reduce A2DP SBC bitpool from 53 to 32
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+12
+
+ -- Chanyeol Park <chanyeol.park@samsung.com> Sat, 17 Sep 2011 16:59:58 +0900
+
+bluez (4.90-slp2+11) unstable; urgency=low
+
+ * Upload Package
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+11
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Fri, 09 Sep 2011 15:01:21 +0900
+
+bluez (4.90-slp2+10) unstable; urgency=low
+
+ * Enable bluez avrcp
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+10
+
+ -- Chanyeol Park <chanyeol.park@samsung.com> Thu, 01 Sep 2011 17:35:11 +0900
+
+bluez (4.90-slp2+9) unstable; urgency=low
+
+ * Upload Package
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+9
+
+ -- Jaganath K <jaganath.k@samsung.com> Tue, 23 Aug 2011 16:34:32 +0530
+
+bluez (4.90-slp2+8) unstable; urgency=low
+
+ * Upload Package
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+8
+
+ -- Jaganath K <jaganath.k@samsung.com> Tue, 23 Aug 2011 11:16:42 +0530
+
+bluez (4.90-slp2+7) unstable; urgency=low
+
+ * Upgrade bluez audio from 4.90 to 4.96
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+7
+
+ -- Chanyeol Park <chanyeol.park@samsung.com> Tue, 23 Aug 2011 09:24:03 +0900
+
+bluez (4.90-slp2+6) unstable; urgency=low
+
+ * Upload Package
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+6
+
+ -- Jaganath K <jaganath.k@samsung.com> Tue, 02 Aug 2011 17:42:12 +0530
+
+bluez (4.90-slp2+5) unstable; urgency=low
+
+ * Print dbus method call message
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+5
+
+ -- Chanyeol Park <chanyeol.park@samsung.com> Tue, 12 Jul 2011 20:21:10 +0900
+
+bluez (4.90-slp2+4) unstable; urgency=low
+
+ * Upload Package
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+4
+
+ -- Chethan T N <chethan.tn@samsung.com> Fri, 08 Jul 2011 15:11:07 +0530
- -- Chanyeol Park <chanyeol.park@samsung.com> Thu, 15 Dec 2011 12:56:21 +0900
+bluez (4.90-slp2+3) unstable; urgency=low
+
+ * Upload Package
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+3
+
+ -- Jaganath K <jaganath.k@samsung.com> Thu, 07 Jul 2011 13:54:04 +0530
+
+bluez (4.90-slp2+2) unstable; urgency=low
+
+ * BlueZ Upgrade 4.90 Merged from 4.90 branch
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+2
+
+ -- Chanyeol Park <chanyeol.park@samsung.com> Tue, 21 Jun 2011 21:44:48 +0900
bluez (4.90-slp2+1) unstable; urgency=low
- * Initial Release
+ * Migrated from Bluez 4.69 to Bluez 4.90
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.90-slp2+1
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Thu, 05 May 2011 13:46:04 +0900
+
+bluez (4.69-slp2+22) unstable; urgency=low
+
+ * Fix pairing B/S(root cause)
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.69-slp2+22
+
+ -- Chanyeol Park <chanyeol.park@samsung.com> Mon, 23 May 2011 15:03:34 +0900
+
+bluez (4.69-slp2+21) unstable; urgency=low
+
+ * Fix pairing B/S
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.69-slp2+21
+
+bluez (4.69-slp2+20) unstable; urgency=low
+
+ * Fix autogen bug by Mike
+ * Fix AVDTP crash
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.69-slp2+20
+
+ -- ChanYeol Park <chanyeol.park@samsung.com> Mon, 11 Apr 2011 13:46:04 +0900
+
+bluez (4.69-slp2+19) unstable; urgency=low
+
+ * Modify the bluetooth.conf file caused by dbus security
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.69-slp2+19
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Wed, 26 Jan 2011 16:21:29 +0900
+
+bluez (4.69-slp2+18) unstable; urgency=low
+
+ * Change localstatedir from /usr/var to /opt/var because /usr would be
+ RO(read only)
+ * Git: 165.213.180.234:slp/pkgs/b/bluez
+ * Tag: bluez_4.69-slp2+18
+
+ -- ChanYeol Park <chanyeol.park@samsung.com> Thu, 13 Jan 2011 10:03:05 +0900
+
+bluez (4.69-slp2+17) unstable; urgency=low
+
+ * Fix the A2DP bug
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+17
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Thu, 25 Nov 2010 20:53:03 +0900
+
+bluez (4.69-slp2+16) unstable; urgency=low
+
+ * Remove the dependency of the ALSA
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+16
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Fri, 19 Nov 2010 11:10:28 +0900
+
+bluez (4.69-slp2+15) unstable; urgency=low
+
+ * Disable the test setting
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+15
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Fri, 12 Nov 2010 11:16:27 +0900
+
+bluez (4.69-slp2+14) unstable; urgency=low
+
+ * Fix the bug about visibility
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+14
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Thu, 11 Nov 2010 11:31:05 +0900
+
+bluez (4.69-slp2+13) unstable; urgency=low
+
+ * Add the homepage filed in control file
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+13
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Fri, 29 Oct 2010 17:18:12 +0900
+
+bluez (4.69-slp2+12) unstable; urgency=low
+
+ * Fix the memory leak with the service watch
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+12
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Mon, 18 Oct 2010 17:44:34 +0530
+
+bluez (4.69-slp2+11) unstable; urgency=low
+
+ * Fix the bug about custom visibility
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+11
+
+ -- DoHyun Pyun <dh79.pyunamsung.com> Fri, 15 Oct 2010 11:47:55 +0900
+
+bluez (4.69-slp2+10) unstable; urgency=low
+
+ * Since the bluetooth and bluetooth-frwk is directly accessing the linkkey file, Given the permission to the group.
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+10
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Mon, 11 Oct 2010 09:30:07 +0530
+
+bluez (4.69-slp2+9) unstable; urgency=low
+
+ * Fixed the bluetoothd crash due to undefined reference symbol
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+9
+
+ -- Sunil Kumar Behera <sunil.behera@samsung.com> Sat, 09 Oct 2010 16:04:55 +0530
+
+bluez (4.69-slp2+8) unstable; urgency=low
+
+ * Fixed the bluetoothd crash due to undefined reference symbol
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+8
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Sat, 09 Oct 2010 15:44:26 +0530
+
+bluez (4.69-slp2+7) unstable; urgency=low
+
+ * Apply the proyo patch
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+7
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Fri, 17 Sep 2010 10:41:15 +0900
+
+bluez (4.69-slp2+6) unstable; urgency=low
+
+ * Add the apt-x codes in A2DP
+ * Patch the android code (adapter, device)
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+6
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Fri, 10 Sep 2010 19:34:44 +0900
+
+bluez (4.69-slp2+5) unstable; urgency=low
+
+ * Code cleanup
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+5
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Mon, 30 Aug 2010 13:33:19 +0900
+
+bluez (4.69-slp2+4) unstable; urgency=low
+
+ * Implement the NAP service
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+4
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Wed, 04 Aug 2010 22:05:47 +0900
+
+bluez (4.69-slp2+3) unstable; urgency=low
+
+ * Add HS UART patch for BRCM chip 3M mode
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+3
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Tue, 03 Aug 2010 17:23:25 +0900
+
+bluez (4.69-slp2+2) unstable; urgency=low
+
+ * Fix dbus policy conf file for DAC to allow any user use dbus interface
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez
+ * Tag: bluez_4.69-slp2+2
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Fri, 30 Jul 2010 11:16:09 +0900
+
+bluez (4.69-slp2+1) unstable; urgency=low
+
+ * Migrated from Bluez4.58 to Bluez4.69. Also removed the depricated files
+ in the Bluez4.69 from Git
+ * Git: 165.213.180.234:/git/slp/pkgs/bluez/
+ * Tag: bluez_4.69-slp2+1
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Thu, 22 Jul 2010 17:35:55 +0530
+
+bluez (4.58-slp2+20) unstable; urgency=low
+
+ * Modify the maintainer list
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+20
+
+ -- DoHyun Pyun <dh79.pyun@samsung.com> Thu, 08 Jul 2010 10:25:32 +0900
+
+bluez (4.58-slp2+19) unstable; urgency=low
+
+ * Add doc option flag
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+19
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Mon, 05 Jul 2010 19:04:32 +0900
+
+bluez (4.58-slp2+18) unstable; urgency=low
+
+ * FIx BlueZ SDP bug in the original src
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+18
+
+ -- chanyeol.park <chanyeol.park@samsung.com> Mon, 14 Jun 2010 22:06:04 +0900
+
+bluez (4.58-slp2+17) unstable; urgency=low
+
+ * Enable serial build option
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+17
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Fri, 28 May 2010 21:23:56 +0900
+
+bluez (4.58-slp2+16) unstable; urgency=low
+
+ * Fix done for resolving the service search issues
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+16
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Wed, 26 May 2010 16:51:27 +0530
+
+bluez (4.58-slp2+15) unstable; urgency=low
+
+ * Removed the unwanted #ifdef SISO_BLUETOOTHD_SOCKET_INTERFACE.
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+15
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Tue, 18 May 2010 17:42:52 +0530
+
+bluez (4.58-slp2+14) unstable; urgency=low
+
+ * Add bonding error code for reject by agent useraction
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+14
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Tue, 18 May 2010 09:38:06 +0900
+
+bluez (4.58-slp2+13) unstable; urgency=low
+
+ * Temporarly reverting back the automatic service search just after pairing
+ fix, because of the A2dp issue.
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+13
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Fri, 14 May 2010 16:59:24 +0530
+
+bluez (4.58-slp2+12) unstable; urgency=low
+
+ *Fix to come out of the sdp loop in the case of sdp error (JSR82).
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+12
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Thu, 06 May 2010 09:49:01 +0530
+
+bluez (4.58-slp2+11) unstable; urgency=low
+
+ * Fixed the issues related to automatic service search just after pairing.
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+11
+
+ -- Syam Sidhardhan <s.syam@samsung.com> Wed, 05 May 2010 12:14:52 +0530
+
+bluez (4.58-slp2+10) unstable; urgency=low
+
+ * Re-upload for installing verification
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+10
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Fri, 23 Apr 2010 23:12:57 +0900
+
+bluez (4.58-slp2+9) unstable; urgency=low
+
+ * Fix gstbluetooth plugin bug while avdtp get capability
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+9
+
+ -- chanyeol.park <chanyeol.park@samsung.com> Fri, 23 Apr 2010 20:04:26 +0900
+
+bluez (4.58-slp2+8) unstable; urgency=low
+
+ * Fix org.bluez.Device.DiscoverServices dbus command reply bug
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+8
+
+ -- chanyeol.park <chanyeol.park@samsung.com> Tue, 13 Apr 2010 14:40:23 +0900
+
+bluez (4.58-slp2+7) unstable; urgency=low
+
+ * Fix get_property to remove GetBasicProperties and to add
+ GetVersionProperties
+ * Add NULL check for confirm cb with remote cancel case
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+7
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Wed, 07 Apr 2010 15:08:10 +0900
+
+bluez (4.58-slp2+6) unstable; urgency=low
+
+ * reverted thread method and used open source method of service search
+ * Git: 165.213.180.234:/git/slp2.0/slp2.0-pkgs/bluez-4/
+ * Tag: bluez_4.58-slp2+6
+
+ -- Ankur Gandhi <a.gandhi@samsung.com> Mon, 05 Apr 2010 13:47:42 +0530
+
+bluez (4.58-slp2+5) unstable; urgency=low
+
+ * changes for SSP between sdp connection
+
+ -- Hathab Shajahan <hathab.s@samsung.com> Mon, 29 Mar 2010 17:50:24 +0530
+
+bluez (4.58-slp2+4) unstable; urgency=low
+
+ * Upload for Toolchain upgrade
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Thu, 25 Mar 2010 13:43:00 +0900
+
+bluez (4.58-slp2+3) unstable; urgency=low
+
+ * Rollback to 4.58-slp2+1 because of Pairing fail issue
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Mon, 22 Mar 2010 09:16:48 +0900
+
+bluez (4.58-slp2+2) unstable; urgency=low
+
+ * ftc disconnect fix
+
+ -- Hathab Shajahan <hathab.s@samsung.com> Fri, 19 Mar 2010 16:42:57 +0530
+
+bluez (4.58-slp2+1) unstable; urgency=low
+
+ * Re-versioning as SCM policy
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Thu, 18 Mar 2010 22:18:37 +0900
+
+bluez (4.58-19) unstable; urgency=low
+
+ * Add all config files to install list
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Thu, 18 Mar 2010 11:21:01 +0900
+
+bluez (4.58-18) unstable; urgency=low
+
+ * New service Search Method is Added
+
+ -- Hathab Shajahan <hathab.s@samsung.com> Fri, 05 Mar 2010 17:54:01 +0530
+
+bluez (4.58-17) unstable; urgency=low
+
+ * Fix BS when pairing fail with weak signal device
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Tue, 02 Mar 2010 20:47:16 +0900
+
+bluez (4.58-16) unstable; urgency=low
+
+ * unified authenticate interface for DM and JSR82
+
+ -- Ankur Gandhi <a.gandhi@samsung.com> Thu, 25 Feb 2010 10:26:24 +0530
+
+bluez (4.58-15) unstable; urgency=low
+
+ * Changes for Jsr82 authorization fix in bluez
+
+ -- Hathab Shajahan <hathab.s@samsung.com> Wed, 24 Feb 2010 12:17:00 +0530
+
+bluez (4.58-14) unstable; urgency=low
+
+ * Modified auth interface for JSR82 and corrected delete device problem when
+ device is paired using SSP
+
+ -- Ankur Gandhi <a.gandhi@samsung.com> Mon, 22 Feb 2010 18:35:37 +0530
+
+bluez (4.58-13) unstable; urgency=low
+
+ *To Take N-project Changes
+
+ -- Hathab Shajahan <hathab.s@samsung.com> Thu, 11 Feb 2010 16:40:01 +0530
+
+bluez (4.58-12) unstable; urgency=low
+
+ * Fix A2DP gstreamer plugin for 3gp play (merge back from N code - sbc/mpeg
+ dynamic bin to sbc static)
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Fri, 05 Feb 2010 22:38:13 +0900
+
+bluez (4.58-11) unstable; urgency=low
+
+ * Bluez merged with N-project to absorb the fixes.
+
+ -- Hathab Shajahan <hathab.s@samsung.com> Thu, 04 Feb 2010 17:14:02 +0530
+
+bluez (4.58-10) unstable; urgency=low
+
+ * Fix SDP record get after pairing crash
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Mon, 01 Feb 2010 19:53:19 +0900
+
+bluez (4.58-9) unstable; urgency=low
+
+ * Fix source file mod to 644
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Fri, 29 Jan 2010 02:02:25 +0900
+
+bluez (4.58-8) unstable; urgency=low
+
+ * Added JSR82 support
+
+ -- Hathab Shajahan <hathab.s@samsung.com> Thu, 28 Jan 2010 17:01:15 +0530
+
+bluez (4.58-7) unstable; urgency=low
+
+ * Added JSR82 support
+
+ -- Ankur Gandhi <a.gandhi@samsung.com> Thu, 28 Jan 2010 16:52:52 +0530
+
+bluez (4.58-6) unstable; urgency=low
+
+ * Add debug package having debugging symbols
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Thu, 21 Jan 2010 16:35:14 +0900
+
+bluez (4.58-5) unstable; urgency=low
+
+ * Fix cleaning rules, restore git original files
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Tue, 29 Dec 2009 19:09:13 +0900
+
+bluez (4.58-4) unstable; urgency=low
+
+ * Change Maintainer email address and Maintainers list format
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Tue, 22 Dec 2009 17:37:05 +0900
+
+bluez (4.58-3) unstable; urgency=low
+
+ * Fix install conf file and Add bluez-alsa package
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Tue, 08 Dec 2009 20:48:03 +0900
+
+bluez (4.58-2) unstable; urgency=low
+
+ * Add as-needed LDFLAGS
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Fri, 20 Nov 2009 16:31:44 +0900
+
+bluez (4.58-1) unstable; urgency=low
+
+ * Upgrade bluez-4.57 to bluez-4.58
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Thu, 19 Nov 2009 09:38:31 +0900
+
+bluez (4.57-4) unstable; urgency=low
+
+ * Fix bluez-gstreamer dependency
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Tue, 17 Nov 2009 15:35:30 +0900
+
+bluez (4.57-3) unstable; urgency=low
+
+ * Fix build break with wrong src dependency
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Tue, 17 Nov 2009 11:55:46 +0900
+
+bluez (4.57-2) unstable; urgency=low
+
+ * Delete install files
+
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Mon, 16 Nov 2009 15:10:46 +0900
+
+bluez (4.57-1) unstable; urgency=low
+
+ * Initial Release.
- -- Chanyeol Park <chanyeol.park@samsung.com> Wed, 07 Dec 2011 12:53:10 +0900
+ -- Seung-Woo Kim <sw0312.kim@samsung.com> Thu, 12 Nov 2009 09:50:16 +0900
DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
CFLAGS ?= -Wall -g
-LDFLAGS ?=
+LDFLAGS ?=
PREFIX ?= /usr
DATADIR ?= /opt
MACHINE="aquila"
BT_CHIP_CFLAGS=-D__BROADCOM_PATCH__
-CHIP_OPTIONS=--disable-bccmd
+CHIP_OPTIONS=--disable-bccmd
configure: configure.ac
aclocal
dh_testdir
# Add here commands to configure the package.
- CFLAGS="$(CFLAGS) -D__TIZEN_PATCH__ $(BT_CHIP_CFLAGS)" \
+ CFLAGS="$(CFLAGS) -D__TIZEN_PATCH__ -D__BROADCOM_PATCH__ $(BT_CHIP_CFLAGS)" \
LDFLAGS="$(LDFLAGS) -Wl,--warn-unresolved-symbols" \
- ./configure --prefix=$(PREFIX) \
+ ./configure --prefix=$(PREFIX) \
--sysconfdir=$(PREFIX)/etc \
--localstatedir=$(DATADIR)/var \
--enable-pie \
--disable-cups \
--disable-test \
--enable-health \
- --disable-udevrules \
--enable-dbusoob \
--with-telephony=tizen
install: build
dh_testdir
dh_testroot
- dh_clean -k
+ dh_clean -k
dh_installdirs
# Add here commands to install the package into debian/tmp.
binary-arch: build install
dh_testdir
dh_testroot
- dh_installchangelogs
+ dh_installchangelogs
dh_installdocs
dh_installexamples
dh_install --sourcedir=debian/tmp
dh_builddeb
binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install
+.PHONY: build clean binary-indep binary-arch binary install
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * 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 "device.h"
+#include "gattrib.h"
+#include "attio.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "log.h"
+#include "deviceinfo.h"
+
+struct deviceinfo {
+ struct btd_device *dev; /* Device reference */
+ GAttrib *attrib; /* GATT connection */
+ guint attioid; /* Att watcher id */
+ struct att_range *svc_range; /* DeviceInfo range */
+ GSList *chars; /* Characteristics */
+};
+
+static GSList *servers = NULL;
+
+struct characteristic {
+ struct gatt_char attr; /* Characteristic */
+ struct deviceinfo *d; /* deviceinfo where the char belongs */
+};
+
+static void deviceinfo_free(gpointer user_data)
+{
+ struct deviceinfo *d = user_data;
+
+ if (d->attioid > 0)
+ btd_device_remove_attio_callback(d->dev, d->attioid);
+
+ if (d->attrib != NULL)
+ g_attrib_unref(d->attrib);
+
+ g_slist_free_full(d->chars, g_free);
+
+ btd_device_unref(d->dev);
+ g_free(d->svc_range);
+ g_free(d);
+}
+
+static gint cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct deviceinfo *d = a;
+ const struct btd_device *dev = b;
+
+ if (dev == d->dev)
+ return 0;
+
+ return -1;
+}
+
+static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+ uint8_t value[ATT_MAX_MTU];
+ int vlen;
+
+ if (status != 0) {
+ error("Error reading PNP_ID value: %s", att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_read_resp(pdu, len, value, &vlen)) {
+ error("Error reading PNP_ID: Protocol error");
+ return;
+ }
+
+ if (vlen < 7) {
+ error("Error reading PNP_ID: Invalid pdu length received");
+ return;
+ }
+
+ device_set_pnpid(ch->d->dev, value[0], att_get_u16(&value[1]),
+ att_get_u16(&value[3]), att_get_u16(&value[5]));
+}
+
+static void process_deviceinfo_char(struct characteristic *ch)
+{
+ if (g_strcmp0(ch->attr.uuid, PNPID_UUID) == 0)
+ gatt_read_char(ch->d->attrib, ch->attr.value_handle, 0,
+ read_pnpid_cb, ch);
+}
+
+static void configure_deviceinfo_cb(GSList *characteristics, guint8 status,
+ gpointer user_data)
+{
+ struct deviceinfo *d = user_data;
+ GSList *l;
+
+ if (status != 0) {
+ error("Discover deviceinfo characteristics: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (l = characteristics; l; l = l->next) {
+ struct gatt_char *c = l->data;
+ struct characteristic *ch;
+
+ 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->d = d;
+
+ d->chars = g_slist_append(d->chars, ch);
+
+ process_deviceinfo_char(ch);
+ }
+}
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct deviceinfo *d = user_data;
+
+ d->attrib = g_attrib_ref(attrib);
+
+ gatt_discover_char(d->attrib, d->svc_range->start, d->svc_range->end,
+ NULL, configure_deviceinfo_cb, d);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct deviceinfo *d = user_data;
+
+ g_attrib_unref(d->attrib);
+ d->attrib = NULL;
+}
+
+int deviceinfo_register(struct btd_device *device, struct gatt_primary *prim)
+{
+ struct deviceinfo *d;
+
+ d = g_new0(struct deviceinfo, 1);
+ d->dev = btd_device_ref(device);
+ d->svc_range = g_new0(struct att_range, 1);
+ d->svc_range->start = prim->range.start;
+ d->svc_range->end = prim->range.end;
+
+ servers = g_slist_prepend(servers, d);
+
+ d->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+ attio_disconnected_cb, d);
+ return 0;
+}
+
+void deviceinfo_unregister(struct btd_device *device)
+{
+ struct deviceinfo *d;
+ GSList *l;
+
+ l = g_slist_find_custom(servers, device, cmp_device);
+ if (l == NULL)
+ return;
+
+ d = l->data;
+ servers = g_slist_remove(servers, d);
+
+ deviceinfo_free(d);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * 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 deviceinfo_register(struct btd_device *device, struct gatt_primary *prim);
+void deviceinfo_unregister(struct btd_device *device);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * 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 <errno.h>
+#include <stdint.h>
+
+#include "plugin.h"
+#include "manager.h"
+#include "hcid.h"
+#include "log.h"
+
+static int deviceinfo_init(void)
+{
+ if (!main_opts.gatt_enabled) {
+ error("DIS cannot start: GATT is disabled");
+ return -ENOTSUP;
+ }
+
+ return deviceinfo_manager_init();
+}
+
+static void deviceinfo_exit(void)
+{
+ deviceinfo_manager_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(deviceinfo, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ deviceinfo_init, deviceinfo_exit)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * 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 <glib.h>
+#include <errno.h>
+#include <bluetooth/uuid.h>
+
+#include "adapter.h"
+#include "device.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
+#include "deviceinfo.h"
+#include "manager.h"
+
+static gint primary_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_primary *prim = a;
+ const char *uuid = b;
+
+ return g_strcmp0(prim->uuid, uuid);
+}
+
+static int deviceinfo_driver_probe(struct btd_device *device, GSList *uuids)
+{
+ struct gatt_primary *prim;
+ GSList *primaries, *l;
+
+ primaries = btd_device_get_primaries(device);
+
+ l = g_slist_find_custom(primaries, DEVICE_INFORMATION_UUID,
+ primary_uuid_cmp);
+ if (l == NULL)
+ return -EINVAL;
+
+ prim = l->data;
+
+ return deviceinfo_register(device, prim);
+}
+
+static void deviceinfo_driver_remove(struct btd_device *device)
+{
+ deviceinfo_unregister(device);
+}
+
+static struct btd_device_driver deviceinfo_device_driver = {
+ .name = "deviceinfo-driver",
+ .uuids = BTD_UUIDS(DEVICE_INFORMATION_UUID),
+ .probe = deviceinfo_driver_probe,
+ .remove = deviceinfo_driver_remove
+};
+
+int deviceinfo_manager_init(void)
+{
+ return btd_register_device_driver(&deviceinfo_device_driver);
+}
+
+void deviceinfo_manager_exit(void)
+{
+ btd_unregister_device_driver(&deviceinfo_device_driver);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * 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 deviceinfo_manager_init(void);
+void deviceinfo_manager_exit(void);
of its registered agents will be removed.
The capability parameter can have the values
- "DisplayOnly", "DisplayYesNo", "KeyboardOnly" and
- "NoInputNoOutput" which reflects the input and output
- capabilities of the agent. If an empty string is
- used it will fallback to "DisplayYesNo".
+ "DisplayOnly", "DisplayYesNo", "KeyboardOnly",
+ "NoInputNoOutput" and "KeyboardDisplay" which reflects
+ the input and output capabilities of the agent. If an
+ empty string is used it will fallback to
+ "DisplayYesNo".
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.AlreadyExists
The pairable timeout in seconds. A value of zero
means that the timeout is disabled and it will stay in
- pareable mode forever.
+ pairable mode forever.
uint32 DiscoverableTimeout [readwrite]
so the display should be zero-padded at the start if
the value contains less than 6 digits.
+ void DisplayPinCode(object device, string pincode)
+
+ This method gets called when the service daemon
+ needs to display a pincode for an authentication.
+
+ An empty reply should be returned. When the pincode
+ needs no longer to be displayed, the Cancel method
+ of the agent will be called.
+
+ If this method is not implemented the RequestPinCode
+ method will be used instead.
+
+ This is used during the pairing process of keyboards
+ that don't support Bluetooth 2.1 Secure Simple Pairing,
+ in contrast to DisplayPasskey which is used for those
+ that do.
+
+ This method will only ever be called once since
+ older keyboards do not support typing notification.
+
+ Note that the PIN will always be a 6-digit number,
+ zero-padded to 6 digits. This is for harmony with
+ the later specification.
+
void RequestConfirmation(object device, uint32 passkey)
This method gets called when the service daemon
Possible Errors: org.bluez.Error.DoesNotExist
org.bluez.Error.InvalidArguments
+__TIZEN_PATCH__
+ void SetVoiceDial(boolean)
+
+ Sends enable/disable Voice Recognition to headset.
+__TIZEN_PATCH__
Signals void AnswerRequested()
uint16 RegistrationStatus [readonly]
- Service availability indicatior of AG, where:
+ Service availability indicator of AG, where:
0 implies no service. No Home/Roam network available.
1 implies presence of service. Home/Roam network
available.
Vendor unique numeric identifier.
+ uint16 VendorSource [readonly]
+
+ Vendor source numeric identifier.
+
uint16 Product [readonly]
Product unique numeric identifier.
device name. Setting an empty string as alias will
convert it back to the remote device name.
- When reseting the alias with an empty string, the
+ When resetting the alias with an empty string, the
emitted PropertyChanged signal will show the remote
name again.
void DestroyChannel(object channel)
Destroys the data channel object. Only the creator of the
- channel or the creator of the HealtApplication that received the
- data channel will be able to destroy it
+ channel or the creator of the HealthApplication that received
+ the data channel will be able to destroy it.
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.NotFound
Returns all properties for the interface. See the properties
section for available properties.
- Posible errors: org.bluez.Error.NotAllowed
+ Possible errors: org.bluez.Error.NotAllowed
fd Acquire()
This method gets called whenever a new handsfree
connection has been established. The objectpath
- contains the object path of the remote device. This
- method assumes that DBus daemon with file descriptor
- passing capability is being used.
+ contains the object path of the remote device.
The agent should only return successfully once the
establishment of the service level connection (SLC)
byte Codec:
- Assigned mumber of codec that the
+ Assigned number of codec that the
endpoint implements. The values should
match the profile specification which
is indicated by the UUID.
byte Codec [readonly]
- Assigned mumber of codec that the transport support.
+ Assigned number of codec that the transport support.
The values should match the profile specification which
is indicated by the UUID.
uint16 Delay [readwrite]
- Optional. Transport delay in 1/10 of milisecond, this
+ Optional. Transport delay in 1/10 of millisecond, this
property is only writeable when the transport was
acquired by the sender.
Optional. Indicates where is the transport being routed
Possible Values: "HCI" or "PCM"
+
+ uint16 Volume [readwrite]
+
+ Optional. Indicates volume level of the transport,
+ this property is only writeable when the transport was
+ acquired by the sender.
+
+ Possible Values: 0-127
+-------------------+-------------------+-------------------+
| |
+All fields are in little-endian byte order (least significant byte first).
+
Controller Index can have a special value <non-controller> to indicate that
command or event is not related to any controller. Possible values:
<non-controller> 0xFFFF
+Error Codes
+===========
+
+The following values have been defined for use with the Command Status
+and Command Complete events:
+
+0x00 Success
+0x01 Unknown Command
+0x02 Not Connected
+0x03 Failed
+0x04 Connect Failed
+0x05 Authentication Failed
+0x06 Not Paired
+0x07 No Resources
+0x08 Timeout
+0x09 Already Connected
+0x0A Busy
+0x0B Rejected
+0x0C Not Supported
+0x0D Invalid Parameters
+0x0E Disconnected
+0x0F Not Powered
+
+
Read Management Version Information Command
===========================================
Return Parameters: Version (1 Octets)
Revision (2 Octets)
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
-Read Management Supported Features Command
+
+Read Management Supported Commands Command
==========================================
Command Code: 0x0002
Controller Index: <non-controller>
Command Parameters:
- Return Parameters: Features (8 Octets)
+ Return Parameters: Num_Of_Commands (2 Octets)
+ Num_Of_Events (2 Octets)
+ Command1 (2 Octets)
+ Command2 (2 Octets)
+ ...
+ Event1 (2 Octets)
+ Event2 (2 Octets)
+ ...
+
+ The commands Read Management Version Information and Read
+ management Supported Commands are not included in this list.
+ Both commands are always supported and mandatory.
- Feature Bit 0: Controller Support
- Feature Bit 1: Tracing Support
+ The events Command Status and Command Complete are not included
+ in this list. Both are implicit and mandatory.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
Read Controller Index List Command
Return Parameters: Num_Controllers (2 Octets)
Controller_Index[i] (2 Octets)
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Read Controller Information Command
===================================
9 High Speed
10 Low Energy
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set Powered Command
===================
Command Parameters: Powered (1 Octet)
Return Parameters: Current_Settings (4 Octets)
+ If discoverable setting is activated with a timeout, then
+ switching the controller off will expire this timeout and
+ disable discoverable.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set Discoverable Command
========================
Timeout (2 Octets)
Return Parameters: Current_Settings (4 Octets)
- Timeout is the time in seconds and is only meningful when
+ Timeout is the time in seconds and is only meaningful when
Discoverable is set to 1.
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ However using a timeout when the controller is not powered will
+ return an error.
+
+ When switching discoverable on and the connectable setting is
+ off it will return an error.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set Connectable Command
=======================
Command Parameters: Connectable (1 Octet)
Return Parameters: Current_Settings (4 Octets)
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ When switching connectable off, it will also switch off the
+ discoverable setting. Switching connectable back on will not
+ restore a previous discoverable. It will stay off and needs
+ to be manually switched back on.
+
+ When switching connectable off, it will expire a discoverable
+ setting with a timeout.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set Fast Connectable Command
============================
Command Parameters: Enable (1 Octet)
Return Parameters: Current_Settings (4 Octets)
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ If connectable is not set, then this command will fail.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set Pairable Command
====================
Command Parameters: Pairable (1 Octet)
Return Parameters: Current_Settings (4 Octets)
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ Turning pairable on will not automatically switch the controller
+ into connectable mode. That needs to be done separately.
+
+ The setting will be remembered during power down/up toggles.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set Link Security Command
=========================
Command Parameters: Link_Security (1 Octet)
Return Parameters: Current_Settings (4 Octets)
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set Secure Simple Pairing Command
=================================
Command Parameters: Secure_Simple_Pairing (1 Octet)
Return Parameters: Current_Settings (4 Octets)
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ In case the controller does not support Secure Simple Pairing,
+ the command will fail regardless.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set High Speed Command
======================
Command Parameters: High_Speed (1 Octet)
Return Parameters: Current_Settings (4 Octets)
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ In case the kernel subsystem does not support High Speed, the
+ command will fail regardless.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set Low Energy Command
======================
Command Parameters: Low_Energy (1 Octet)
Return Parameters: Current_Settings (4 Octets)
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ In case the kernel subsystem does not support Low Energy or the
+ controller does not either, the command will fail regardless.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set Device Class
================
This command will also implicitly disable caching of pending CoD
and EIR updates.
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ In case the controller is powered off, 0x000000 will be returned
+ for the class of device parameter. And after power on the new
+ value will be announced via class of device changed event.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Set Local Name Command
======================
Return Parameters: Name (249 Octets)
Short_Name (11 Octets)
- The name parameters need to always end with a nul byte (failure
+ The name parameters need to always end with a null byte (failure
to do so will cause the command to fail).
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ The values of name and short name will be remembered when
+ switching the controller off and back on again. So the name
+ and short name only have to be set once when a new controller
+ is found and will stay until removed.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Add UUID Command
================
SVC_Hint (1 Octet)
Return Parameters: Class_Of_Device (3 Octets)
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ In case the controller is powered off, 0x000000 will be returned
+ for the class of device parameter. And after power on the new
+ value will be announced via class of device changed event.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Remove UUID Command
===================
Command Parameters: UUID (16 Octets)
Return Parameters: Class_Of_Device (3 Octets)
+ When the UUID parameter is an empty UUID (16 x 0x00), then all
+ previously loaded UUIDs will be removed.
+
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ In case the controller is powered off, 0x000000 will be returned
+ for the class of device parameter. And after power on the new
+ value will be announced via class of device changed event.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Load Link Keys Command
======================
Key_Count (2 Octets)
Key1 {
Address (6 Octets)
- Type (1 Octet)
+ Address_Type (1 Octet)
+ Key_Type (1 Octet)
Value (16 Octets)
PIN_Length (1 Octet)
}
...
Return Parameters:
+ This command can be used when the controller is not powered.
-Remove Keys Command
-===================
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+
+Load Long Term 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)
+ Command Parameters: Key Count (2 Octets)
+ Key1 {
+ Address (6 Octets)
+ Address_Type (1 Octet)
+ Authenticated (1 Octet)
+ Master (1 Octet)
+ Encryption Size (1 Octet)
+ Enc. Diversifier (2 Octets)
+ Random Number (8 Octets)
+ Value (16 Octets)
+ }
+ Key2 { }
+ ...
- Removes all keys associated with the remote device.
+ This command can be used when the controller is not powered.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
Disconnect Command
Command Code: 0x0014
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
Return Parameters: Address (6 Octets)
- Status (6 Octets)
+ Address_Type (1 Octet)
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
Get Connections Command
Return Parameters: Connection_Count (2 Octets)
Address1 {
Address (6 Octets)
- Type (1 Octet)
+ Address_Type (1 Octet)
}
Address2 { }
...
- Possible values for the Type parameter:
+ Possible values for the Address_Type parameter:
0 BR/EDR
1 LE Public
2 LE Random
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
PIN Code Reply Command
=======================
Command Code: 0x0016
Controller Index: <controller id>
- Command Parameters:
- Return Parameters: Address (6 Octets)
+ Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
PIN_Length (1 Octet)
PIN_Code (16 Octets)
+ Return Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
PIN Code Negative Reply Command
Command Code: 0x0017
Controller Index: <controller id>
- Command Parameters:
+ Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
Return Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
Set IO Capability Command
Command Parameters: IO_Capability (1 Octet)
Return Parameters:
+ This command can be used when the controller is not powered.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Pair Device Command
===================
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
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
+
+
+Cancel Pair Device
+==================
+
+ Command Code: 0x001A
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+ Return Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+
+ The Address and Address_Type parameters should match what was
+ given to a preceding Pair Device command.
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
+
+
+Unpair Device Command
+=====================
+
+ Command Code: 0x001B
+ Controller Index: <controller id>
+ Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+ Disconnect (1 Octet)
+ Return Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+
+ Removes all keys associated with the remote device.
+
+ The Disconnect parameter tells the kernel whether to forcefully
+ disconnect any existing connections to the device. It should in
+ practice always be 1 except for some special GAP qualification
+ test-cases where a key removal without disconnecting is needed.
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
+
User Confirmation Reply Command
===============================
- Command Code: 0x001A
+ Command Code: 0x001C
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
Return Parameters: Address (6 Octets)
- Status (1 Octet)
+ Address_Type (1 Octet)
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
User Confirmation Negative Reply Command
========================================
- Command Code: 0x001B
+ Command Code: 0x001D
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
Return Parameters: Address (6 Octets)
- Status (1 Octet)
+ Address_Type (1 Octet)
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
User Passkey Reply Command
==========================
- Command Code: 0x001C
+ Command Code: 0x001E
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
Passkey (4 Octets)
Return Parameters: Address (6 Octets)
- Status (1 Octet)
+ Address_Type (1 Octet)
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
User Passkey Negative Reply Command
===================================
- Command Code: 0x001D
+ Command Code: 0x001F
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
Return Parameters: Address (6 Octets)
- Status (1 Octet)
+ Address_Type (1 Octet)
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
Read Local Out Of Band Data Command
===================================
- Command Code: 0x001E
+ Command Code: 0x0020
Controller Index: <controller id>
Command Parameters:
Return Parameters: Hash (16 Octets)
Randomizer (16 Octets)
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
Add Remote Out Of Band Data Command
===================================
- Command Code: 0x001F
+ Command Code: 0x0021
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
Hash (16 Octets)
Randomizer (16 Octets)
- Return Parameters:
+ Return Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
Remove Remote Out Of Band Data Command
-========================================
+======================================
- Command Code: 0x0020
+ Command Code: 0x0022
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
- Return Parameters:
+ Address_Type (1 Octet)
+ Return Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
Start Discovery Command
=======================
- Command Code: 0x00021
+ Command Code: 0x0023
Controller Index: <controller id>
- Command Parameters: Type (1 Octet)
- Return Parameters:
+ Command Parameters: Address_Type (1 Octet)
+ Return Parameters: Address_Type (1 Octet)
- Possible values for the Type parameter are a bit-wise or of the
- following bits:
+ Possible values for the Address_Type parameter are a bit-wise or
+ of the following bits:
1 BR/EDR
2 LE Public
6 LE (public & random)
7 BR/EDR/LE (interleaved discovery)
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
+
Stop Discovery Command
======================
- Command Code: 0x00022
+ Command Code: 0x0024
Controller Index: <controller id>
- Command Parameters:
- Return Parameters:
+ Command Parameters: Address_Type (1 Octet)
+ Return Parameters: Address_Type (1 Octet)
+
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
Confirm Name Command
====================
- Command Code: 0x00023
+ Command Code: 0x0025
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
Name_Known (1 Octet)
Return Parameters: Address (6 Octets)
- Status (1 Octet)
+ Address_Type (1 Octet)
This command is only valid during device discovery and is
expected for each Device Found event with the Confirm Name
flag set.
+ This command can only be used when the controller is powered.
+
+ This command generates a Command Complete event on success
+ or failure.
+
Block Device Command
====================
- Command Code: 0x00024
+ Command Code: 0x0026
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
- Return Parameters:
+ Address_Type (1 Octet)
+ Return Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
+
+ This command can be used when the controller is not powered.
+
+ This command generates a Command Complete event on success
+ or failure.
Unblock Device Command
======================
- Command Code: 0x00025
+ Command Code: 0x0027
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
-=================================
+ Address_Type (1 Octet)
+ Return Parameters: Address (6 Octets)
+ Address_Type (1 Octet)
- Command Code: <not yet assigned>
- Controller Index: <non-controller>
- Command Parameters: Buffer_Size (2 Octets)
- Return Parameters: Status (1 Octet)
+ This command can be used when the controller is not powered.
- Buffer Size in Kilobytes
+ This command generates a Command Complete event on success
+ or failure.
-Read Controller Tracing Filter Command
-=======================================
+Set Device ID Command
+=====================
- Command Code: <not yet assigned>
+ Command Code: 0x0028
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)
+ Command Parameters: Source (2 Octets)
+ Vendor (2 Octets)
+ Product (2 Octets)
+ Version (2 Octets)
+ Return Parameters:
- Tracing_Enable: 0x00 Tracing disabled
- 0x01 Command and Event tracing
- 0x02 Command, Event and ACL tracing
- 0x03 Command, Event, ACL and SCO tracing
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+ The Source parameter selects the organization that assigned the
+ Vendor parameter:
-Write Controller Tracing Filter Command
-=======================================
+ 0x0000 Disable Device ID
+ 0x0001 Bluetooth SIG
+ 0x0002 USB Implementer’s Forum
- 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)
+ The information are put into the EIR data. If the controller does
+ not support EIR or if SSP is disabled, this command will still
+ succeed. The information are stored for later use and will survive
+ toggling SSP on and off.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
Command Complete Event
Event Code 0x0001
Controller Index: <controller id> or <non-controller>
Event Parameters Command_Opcode (2 Octets)
+ Status (1 Octet)
Return_Parameters
Event Code 0x0002
Controller Index: <controller id> or <non-controller>
-Event Parameters Status (1 Octet)
- Command_Opcode (2 Octets)
+Event Parameters Command_Opcode (2 Octets)
+ Status (1 Octet)
Controller Error Event
Controller Index: <controller id>
Event Parameters Key {
Address (6 Octets)
- Type (1 Octet)
+ Address_Type (1 Octet)
+ Key_Type (1 Octet)
Value (16 Octets)
PIN_Length (1 Octet)
}
- Old_Key_Type (1 Octet)
+
+
+New Long Term Key Event
+=======================
+
+Event Code 0x000A
+Controller Index <controller id>
+Event Parameters Store Hint (1 Octet)
+ Key {
+ Address (6 Octets)
+ Address_Type (1 Octet)
+ Authenticated (1 Octet)
+ Master (1 Octet)
+ Encryption Size (1 Octet)
+ Enc. Diversifier (2 Octets)
+ Random Number (8 Octets)
+ Value (16 Octets)
+ }
Device Connected Event
======================
-Event Code 0x000A
+Event Code 0x000B
Controller Index: <controller id>
Event Parameters Address (6 Octets)
- Type (1 Octet)
+ Address_Type (1 Octet)
+ Flags (4 Octets)
+ EIR_Data_Length (2 Octets)
+ EIR_Data (0-65535 Octets)
- Possible values for the Type parameter:
+ Possible values for the Address_Type parameter:
0 BR/EDR
1 LE Public
2 LE Random
+ The following bits are defined for the Flags parameter:
+ 0 Reserved (not in use)
+ 1 Legacy Pairing
+
Device Disconnected Event
=========================
-Event Code 0x000B
+Event Code 0x000C
Controller Index: <controller id>
Event Parameters Address (6 Octets)
- Type (1 Octet)
+ Address_Type (1 Octet)
- Possible values for the Type parameter:
+ Possible values for the Address_Type parameter:
0 BR/EDR
1 LE Public
2 LE Random
Connect Failed Event
====================
-Event Code 0x000C
+Event Code 0x000D
Controller Index: <controller id>
Event Parameters Address (6 Octets)
- Type (1 Octet)
+ Address_Type (1 Octet)
Status (1 Octet)
- Possible values for the Type parameter:
+ Possible values for the Address_Type parameter:
0 BR/EDR
1 LE Public
2 LE Random
PIN Code Request Event
======================
-Event Code 0x000D
+Event Code 0x000E
Controller Index: <controller id>
Event Parameters Address (6 Octets)
+ Address_Type (1 Octet)
Secure (1 Octet)
Secure: 0x01 secure PIN code required
User Confirmation Request Event
===============================
-Event Code 0x000E
+Event Code 0x000F
Controller Index: <controller id>
Event Parameters Address (6 Octets)
+ Address_Type (1 Octet)
+ Confirm_Hint (1 Octet)
Value (4 Octets)
User Passkey Request Event
==========================
-Event Code 0x000F
+Event Code 0x0010
Controller Index: <controller id>
Event Parameters Address (6 Octets)
+ Address_Type (1 Octet)
Authentication Failed Event
===========================
-Event Code 0x0010
+Event Code 0x0011
Controller Index: <controller id>
Event Parameters Address (6 Octets)
+ Address_Type (1 Octet)
Status (1 Octet)
Device Found Event
==================
-Event Code 0x0011
+Event Code 0x0012
Controller Index <controller id>
Event Parameters Address (6 Octets)
- Type (1 Octet)
- Class_Of_Device (3 Octets)
+ Address_Type (1 Octet)
RSSI (1 Octet)
- Confirm Name (1 Octet)
- EIR_Data (240 Octets)
+ Flags (4 Octets)
+ EIR_Data_Length (2 Octets)
+ EIR_Data (0-65535 Octets)
- Possible values for the Type parameter:
+ Possible values for the Address_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)
+ The following bits are defined for the Flags parameter:
+ 0 Confirm name
+ 1 Legacy Pairing
Discovering Event
=================
-Event Code 0x00013
+Event Code 0x0013
Controller Index <controller id>
-Event Parameters Discovering (1 Octet)
+Event Parameters Address_Type (1 Octet)
+ Discovering (1 Octet)
Device Blocked Event
====================
-Event Code 0x00014
+Event Code 0x0014
Controller Index <controller id>
Event Parameters Address (6 Octets)
+ Address_Type (1 Octet)
Device Unblocked Event
======================
-Event Code 0x00015
+Event Code 0x0015
Controller Index <controller id>
Event Parameters Address (6 Octets)
+ Address_Type (1 Octet)
-New Long Term Key Event
-=======================
+Device Unpaired 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)
- }
+Event Parameters Address (6 Octets)
+ Address_Type (1 Octet)
Copyright (C) 2011 Claudio Takahasi <claudio.takahasi@openbossa.org>
-Proximity hierarchy
-=================
+Proximity Monitor hierarchy
+===========================
Service org.bluez
Interface org.bluez.ProximityMonitor
"none" will be emitted after the configured timeout.
When changing the level, signal is the confirmation
that the value was written in the remote.
+
+Proximity Reporter hierarchy
+===========================
+
+Shared service used by Proximity Path Loss and Find Me. Allows per device
+alert level handling.
+
+Service org.bluez
+Interface org.bluez.ProximityReporter
+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.
+
+Signals PropertyChanged(string name, variant value)
+
+ This signal indicates a changed value of a given
+ property.
+
+Properties
+
+ string ImmediateAlertLevel [readonly]
+
+ This property indicates that Immediate Alert Level
+ characteristic in the local Immediate Alert Service
+ was changed by the remote device. Property shared
+ between Path Loss and Find Me. Values: "none", "mild",
+ "high".
+
+ string LinkLossAlertLevel [readonly]
+
+ This property indicates that Alert Level characteristic
+ in the local Link Loss Service was changed by the
+ remote device. Values: "none", "mild", "high".
timeouts in the client it is fine to call this method.
In that case one of patterns of the Connect method should
- be suplied instead of the TTY device.
+ be supplied instead of the TTY device.
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.DoesNotExist
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.
+ health devices. The Type entry is only present if the
+ measurement type is known. Otherwise, it is undefined.
Dict is defined as below:
{
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bt.h"
+#include "btdev.h"
+
+#define le16_to_cpu(val) (val)
+#define cpu_to_le16(val) (val)
+
+struct btdev {
+ struct btdev *conn;
+
+ btdev_send_func send_handler;
+ void *send_data;
+
+ uint16_t manufacturer;
+ uint8_t version;
+ uint16_t revision;
+ uint8_t commands[64];
+ uint8_t features[8];
+ uint16_t acl_mtu;
+ uint16_t acl_max_pkt;
+ uint8_t country_code;
+ uint8_t bdaddr[6];
+ uint8_t le_features[8];
+ uint8_t le_states[8];
+
+ uint16_t default_link_policy;
+ uint8_t event_mask[8];
+ uint8_t event_filter;
+ uint8_t name[248];
+ uint8_t dev_class[3];
+ uint16_t voice_setting;
+ uint16_t conn_accept_timeout;
+ uint16_t page_timeout;
+ uint8_t scan_enable;
+ uint8_t auth_enable;
+ uint8_t inquiry_mode;
+ uint8_t afh_assess_mode;
+ uint8_t ext_inquiry_fec;
+ uint8_t ext_inquiry_rsp[240];
+ uint8_t simple_pairing_mode;
+ uint8_t le_supported;
+ uint8_t le_simultaneous;
+ uint8_t le_event_mask[8];
+};
+
+#define MAX_BTDEV_ENTRIES 16
+
+static struct btdev *btdev_list[MAX_BTDEV_ENTRIES] = { };
+
+static inline int add_btdev(struct btdev *btdev)
+{
+ int i, index = -1;
+
+ for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+ if (btdev_list[i] == NULL) {
+ index = i;
+ btdev_list[index] = btdev;
+ break;
+ }
+ }
+
+ return index;
+}
+
+static inline int del_btdev(struct btdev *btdev)
+{
+ int i, index = -1;
+
+ for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+ if (btdev_list[i] == btdev) {
+ index = i;
+ btdev_list[index] = NULL;
+ break;
+ }
+ }
+
+ return index;
+}
+
+static inline struct btdev *find_btdev_by_bdaddr(const uint8_t *bdaddr)
+{
+ int i;
+
+ for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+ if (btdev_list[i] && !memcmp(btdev_list[i]->bdaddr, bdaddr, 6))
+ return btdev_list[i];
+ }
+
+ return NULL;
+}
+
+static void hexdump(const unsigned char *buf, uint16_t len)
+{
+ static const char hexdigits[] = "0123456789abcdef";
+ char str[68];
+ uint16_t i;
+
+ if (!len)
+ return;
+
+ for (i = 0; i < len; i++) {
+ str[((i % 16) * 3) + 0] = hexdigits[buf[i] >> 4];
+ str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf];
+ str[((i % 16) * 3) + 2] = ' ';
+ str[(i % 16) + 49] = isprint(buf[i]) ? buf[i] : '.';
+
+ if ((i + 1) % 16 == 0) {
+ str[47] = ' ';
+ str[48] = ' ';
+ str[65] = '\0';
+ printf("%-12c%s\n", ' ', str);
+ str[0] = ' ';
+ }
+ }
+
+ if (i % 16 > 0) {
+ uint16_t j;
+ for (j = (i % 16); j < 16; j++) {
+ str[(j * 3) + 0] = ' ';
+ str[(j * 3) + 1] = ' ';
+ str[(j * 3) + 2] = ' ';
+ str[j + 49] = ' ';
+ }
+ str[47] = ' ';
+ str[48] = ' ';
+ str[65] = '\0';
+ printf("%-12c%s\n", ' ', str);
+ }
+}
+
+static void get_bdaddr(uint16_t id, uint8_t *bdaddr)
+{
+ bdaddr[0] = id & 0xff;
+ bdaddr[1] = id >> 8;
+ bdaddr[2] = 0x00;
+ bdaddr[3] = 0x01;
+ bdaddr[4] = 0xaa;
+ bdaddr[5] = 0x00;
+}
+
+struct btdev *btdev_create(uint16_t id)
+{
+ struct btdev *btdev;
+
+ btdev = malloc(sizeof(*btdev));
+ if (!btdev)
+ return NULL;
+
+ memset(btdev, 0, sizeof(*btdev));
+
+ btdev->manufacturer = 63;
+ btdev->version = 0x06;
+ btdev->revision = 0x0000;
+
+ btdev->features[0] |= 0x04; /* Encryption */
+ btdev->features[0] |= 0x20; /* Role switch */
+ btdev->features[0] |= 0x80; /* Sniff mode */
+ btdev->features[1] |= 0x08; /* SCO link */
+ btdev->features[3] |= 0x40; /* RSSI with inquiry results */
+ btdev->features[3] |= 0x80; /* Extended SCO link */
+ btdev->features[4] |= 0x08; /* AFH capable slave */
+ btdev->features[4] |= 0x10; /* AFH classification slave */
+ btdev->features[4] |= 0x40; /* LE Supported */
+ btdev->features[5] |= 0x02; /* Sniff subrating */
+ btdev->features[5] |= 0x04; /* Pause encryption */
+ btdev->features[5] |= 0x08; /* AFH capable master */
+ btdev->features[5] |= 0x10; /* AFH classification master */
+ btdev->features[6] |= 0x01; /* Extended Inquiry Response */
+ btdev->features[6] |= 0x02; /* Simultaneous LE and BR/EDR */
+ btdev->features[6] |= 0x08; /* Secure Simple Pairing */
+ btdev->features[6] |= 0x10; /* Encapsulated PDU */
+ btdev->features[6] |= 0x20; /* Erroneous Data Reporting */
+ btdev->features[6] |= 0x40; /* Non-flushable Packet Boundary Flag */
+ btdev->features[7] |= 0x01; /* Link Supervision Timeout Event */
+ btdev->features[7] |= 0x02; /* Inquiry TX Power Level */
+ btdev->features[7] |= 0x80; /* Extended features */
+
+ btdev->acl_mtu = 192;
+ btdev->acl_max_pkt = 1;
+
+ btdev->country_code = 0x00;
+
+ get_bdaddr(id, btdev->bdaddr);
+
+ add_btdev(btdev);
+
+ return btdev;
+}
+
+void btdev_destroy(struct btdev *btdev)
+{
+ if (!btdev)
+ return;
+
+ del_btdev(btdev);
+
+ free(btdev);
+}
+
+void btdev_set_send_handler(struct btdev *btdev, btdev_send_func handler,
+ void *user_data)
+{
+ if (!btdev)
+ return;
+
+ btdev->send_handler = handler;
+ btdev->send_data = user_data;
+}
+
+static void send_packet(struct btdev *btdev, const void *data, uint16_t len)
+{
+ if (!btdev->send_handler)
+ return;
+
+ btdev->send_handler(data, len, btdev->send_data);
+}
+
+static void send_event(struct btdev *btdev, uint8_t event,
+ const void *data, uint8_t len)
+{
+ struct bt_hci_evt_hdr *hdr;
+ uint16_t pkt_len;
+ void *pkt_data;
+
+ pkt_len = 1 + sizeof(*hdr) + len;
+
+ pkt_data = malloc(pkt_len);
+ if (!pkt_data)
+ return;
+
+ ((uint8_t *) pkt_data)[0] = BT_H4_EVT_PKT;
+
+ hdr = pkt_data + 1;
+ hdr->evt = event;
+ hdr->plen = len;
+
+ if (len > 0)
+ memcpy(pkt_data + 1 + sizeof(*hdr), data, len);
+
+ send_packet(btdev, pkt_data, pkt_len);
+
+ free(pkt_data);
+}
+
+static void cmd_complete(struct btdev *btdev, uint16_t opcode,
+ const void *data, uint8_t len)
+{
+ struct bt_hci_evt_hdr *hdr;
+ struct bt_hci_evt_cmd_complete *cc;
+ uint16_t pkt_len;
+ void *pkt_data;
+
+ pkt_len = 1 + sizeof(*hdr) + sizeof(*cc) + len;
+
+ pkt_data = malloc(pkt_len);
+ if (!pkt_data)
+ return;
+
+ ((uint8_t *) pkt_data)[0] = BT_H4_EVT_PKT;
+
+ hdr = pkt_data + 1;
+ hdr->evt = BT_HCI_EVT_CMD_COMPLETE;
+ hdr->plen = sizeof(*cc) + len;
+
+ cc = pkt_data + 1 + sizeof(*hdr);
+ cc->ncmd = 0x01;
+ cc->opcode = cpu_to_le16(opcode);
+
+ if (len > 0)
+ memcpy(pkt_data + 1 + sizeof(*hdr) + sizeof(*cc), data, len);
+
+ send_packet(btdev, pkt_data, pkt_len);
+
+ free(pkt_data);
+}
+
+static void cmd_status(struct btdev *btdev, uint8_t status, uint16_t opcode)
+{
+ struct bt_hci_evt_cmd_status cs;
+
+ cs.status = status;
+ cs.ncmd = 0x01;
+ cs.opcode = cpu_to_le16(opcode);
+
+ send_event(btdev, BT_HCI_EVT_CMD_STATUS, &cs, sizeof(cs));
+}
+
+static void num_completed_packets(struct btdev *btdev)
+{
+ if (btdev->conn) {
+ struct bt_hci_evt_num_completed_packets ncp;
+
+ ncp.num_handles = 1;
+ ncp.handle = cpu_to_le16(42);
+ ncp.count = cpu_to_le16(1);
+
+ send_event(btdev, BT_HCI_EVT_NUM_COMPLETED_PACKETS,
+ &ncp, sizeof(ncp));
+ }
+}
+
+static void inquiry_complete(struct btdev *btdev, uint8_t status)
+{
+ struct bt_hci_evt_inquiry_complete ic;
+ int i;
+
+ for (i = 0; i < MAX_BTDEV_ENTRIES; i++) {
+ if (!btdev_list[i] || btdev_list[i] == btdev)
+ continue;
+
+ if (!(btdev_list[i]->scan_enable & 0x02))
+ continue;
+
+ if (btdev->inquiry_mode == 0x02 &&
+ btdev_list[i]->ext_inquiry_rsp[0]) {
+ struct bt_hci_evt_ext_inquiry_result ir;
+
+ ir.num_resp = 0x01;
+ memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+ memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+ ir.rssi = -60;
+ memcpy(ir.data, btdev_list[i]->ext_inquiry_rsp, 240);
+
+ send_event(btdev, BT_HCI_EVT_EXT_INQUIRY_RESULT,
+ &ir, sizeof(ir));
+ continue;
+ }
+
+ if (btdev->inquiry_mode > 0x00) {
+ struct bt_hci_evt_inquiry_result_with_rssi ir;
+
+ ir.num_resp = 0x01;
+ memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+ memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+ ir.rssi = -60;
+
+ send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI,
+ &ir, sizeof(ir));
+ } else {
+ struct bt_hci_evt_inquiry_result ir;
+
+ ir.num_resp = 0x01;
+ memcpy(ir.bdaddr, btdev_list[i]->bdaddr, 6);
+ memcpy(ir.dev_class, btdev_list[i]->dev_class, 3);
+
+ send_event(btdev, BT_HCI_EVT_INQUIRY_RESULT,
+ &ir, sizeof(ir));
+ }
+ }
+
+ ic.status = status;
+
+ send_event(btdev, BT_HCI_EVT_INQUIRY_COMPLETE, &ic, sizeof(ic));
+}
+
+static void conn_complete(struct btdev *btdev,
+ const uint8_t *bdaddr, uint8_t status)
+{
+ struct bt_hci_evt_conn_complete cc;
+
+ if (!status) {
+ struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+ btdev->conn = remote;
+ remote->conn = btdev;
+
+ cc.status = status;
+ memcpy(cc.bdaddr, btdev->bdaddr, 6);
+ cc.encr_mode = 0x00;
+
+ cc.handle = cpu_to_le16(42);
+ cc.link_type = 0x01;
+
+ send_event(remote, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
+
+ cc.handle = cpu_to_le16(42);
+ cc.link_type = 0x01;
+ } else {
+ cc.handle = cpu_to_le16(0x0000);
+ cc.link_type = 0x01;
+ }
+
+ cc.status = status;
+ memcpy(cc.bdaddr, bdaddr, 6);
+ cc.encr_mode = 0x00;
+
+ send_event(btdev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
+}
+
+static void conn_request(struct btdev *btdev, const uint8_t *bdaddr)
+{
+ struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+ if (remote) {
+ if (remote->scan_enable & 0x01) {
+ struct bt_hci_evt_conn_request cr;
+
+ memcpy(cr.bdaddr, btdev->bdaddr, 6);
+ memcpy(cr.dev_class, btdev->dev_class, 3);
+ cr.link_type = 0x01;
+
+ send_event(remote, BT_HCI_EVT_CONN_REQUEST,
+ &cr, sizeof(cr));
+ } else
+ conn_complete(btdev, bdaddr, BT_HCI_ERR_PAGE_TIMEOUT);
+ } else
+ conn_complete(btdev, bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+}
+
+static void disconnect_complete(struct btdev *btdev, uint16_t handle,
+ uint8_t reason)
+{
+ struct bt_hci_evt_disconnect_complete dc;
+ struct btdev *remote;
+
+ if (!btdev) {
+ dc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+ dc.handle = cpu_to_le16(handle);
+ dc.reason = 0x00;
+
+ send_event(btdev, BT_HCI_EVT_DISCONNECT_COMPLETE,
+ &dc, sizeof(dc));
+ return;
+ }
+
+ dc.status = BT_HCI_ERR_SUCCESS;
+ dc.handle = cpu_to_le16(handle);
+ dc.reason = reason;
+
+ remote = btdev->conn;
+
+ btdev->conn = NULL;
+ remote->conn = NULL;
+
+ send_event(btdev, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
+ send_event(remote, BT_HCI_EVT_DISCONNECT_COMPLETE, &dc, sizeof(dc));
+}
+
+static void name_request_complete(struct btdev *btdev,
+ const uint8_t *bdaddr, uint8_t status)
+{
+ struct bt_hci_evt_remote_name_req_complete nc;
+
+ nc.status = status;
+ memcpy(nc.bdaddr, bdaddr, 6);
+ memset(nc.name, 0, 248);
+
+ if (!status) {
+ struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
+
+ if (remote)
+ memcpy(nc.name, remote->name, 248);
+ else
+ nc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+ }
+
+ send_event(btdev, BT_HCI_EVT_REMOTE_NAME_REQUEST_COMPLETE,
+ &nc, sizeof(nc));
+}
+
+static void remote_features_complete(struct btdev *btdev, uint16_t handle)
+{
+ struct bt_hci_evt_remote_features_complete rfc;
+
+ if (btdev->conn) {
+ rfc.status = BT_HCI_ERR_SUCCESS;
+ rfc.handle = cpu_to_le16(handle);
+ memcpy(rfc.features, btdev->conn->features, 8);
+ } else {
+ rfc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+ rfc.handle = cpu_to_le16(handle);
+ memset(rfc.features, 0, 8);
+ }
+
+ send_event(btdev, BT_HCI_EVT_REMOTE_FEATURES_COMPLETE,
+ &rfc, sizeof(rfc));
+}
+
+static void remote_ext_features_complete(struct btdev *btdev, uint16_t handle,
+ uint8_t page)
+{
+ struct bt_hci_evt_remote_ext_features_complete refc;
+
+ if (btdev->conn && page < 0x02) {
+ refc.handle = cpu_to_le16(handle);
+ refc.page = page;
+ refc.max_page = 0x01;
+
+ switch (page) {
+ case 0x00:
+ refc.status = BT_HCI_ERR_SUCCESS;
+ memcpy(refc.features, btdev->conn->features, 8);
+ break;
+ case 0x01:
+ refc.status = BT_HCI_ERR_SUCCESS;
+ memset(refc.features, 0, 8);
+ break;
+ default:
+ refc.status = BT_HCI_ERR_INVALID_PARAMETERS;
+ memset(refc.features, 0, 8);
+ break;
+ }
+ } else {
+ refc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+ refc.handle = cpu_to_le16(handle);
+ refc.page = page;
+ refc.max_page = 0x01;
+ memset(refc.features, 0, 8);
+ }
+
+ send_event(btdev, BT_HCI_EVT_REMOTE_EXT_FEATURES_COMPLETE,
+ &refc, sizeof(refc));
+}
+
+static void remote_version_complete(struct btdev *btdev, uint16_t handle)
+{
+ struct bt_hci_evt_remote_version_complete rvc;
+
+ if (btdev->conn) {
+ rvc.status = BT_HCI_ERR_SUCCESS;
+ rvc.handle = cpu_to_le16(handle);
+ rvc.lmp_ver = btdev->conn->version;
+ rvc.manufacturer = cpu_to_le16(btdev->conn->manufacturer);
+ rvc.lmp_subver = cpu_to_le16(btdev->conn->revision);
+ } else {
+ rvc.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+ rvc.handle = cpu_to_le16(handle);
+ rvc.lmp_ver = 0x00;
+ rvc.manufacturer = cpu_to_le16(0);
+ rvc.lmp_subver = cpu_to_le16(0);
+ }
+
+ send_event(btdev, BT_HCI_EVT_REMOTE_VERSION_COMPLETE,
+ &rvc, sizeof(rvc));
+}
+
+static void process_cmd(struct btdev *btdev, const void *data, uint16_t len)
+{
+ const struct bt_hci_cmd_hdr *hdr = data;
+ const struct bt_hci_cmd_create_conn *cc;
+ const struct bt_hci_cmd_disconnect *dc;
+ const struct bt_hci_cmd_create_conn_cancel *ccc;
+ const struct bt_hci_cmd_accept_conn_request *acr;
+ const struct bt_hci_cmd_reject_conn_request *rcr;
+ const struct bt_hci_cmd_remote_name_request *rnr;
+ const struct bt_hci_cmd_remote_name_request_cancel *rnrc;
+ const struct bt_hci_cmd_read_remote_features *rrf;
+ const struct bt_hci_cmd_read_remote_ext_features *rref;
+ const struct bt_hci_cmd_read_remote_version *rrv;
+ const struct bt_hci_cmd_write_default_link_policy *wdlp;
+ const struct bt_hci_cmd_set_event_mask *sem;
+ const struct bt_hci_cmd_set_event_filter *sef;
+ const struct bt_hci_cmd_write_local_name *wln;
+ const struct bt_hci_cmd_write_conn_accept_timeout *wcat;
+ const struct bt_hci_cmd_write_page_timeout *wpt;
+ const struct bt_hci_cmd_write_scan_enable *wse;
+ const struct bt_hci_cmd_write_auth_enable *wae;
+ const struct bt_hci_cmd_write_class_of_dev *wcod;
+ const struct bt_hci_cmd_write_voice_setting *wvs;
+ const struct bt_hci_cmd_write_inquiry_mode *wim;
+ const struct bt_hci_cmd_write_afh_assess_mode *waam;
+ const struct bt_hci_cmd_write_ext_inquiry_rsp *weir;
+ const struct bt_hci_cmd_write_simple_pairing_mode *wspm;
+ const struct bt_hci_cmd_write_le_host_supported *wlhs;
+ const struct bt_hci_cmd_le_set_event_mask *lsem;
+ struct bt_hci_rsp_read_default_link_policy rdlp;
+ struct bt_hci_rsp_read_stored_link_key rslk;
+ struct bt_hci_rsp_write_stored_link_key wslk;
+ struct bt_hci_rsp_delete_stored_link_key dslk;
+ struct bt_hci_rsp_read_local_name rln;
+ struct bt_hci_rsp_read_conn_accept_timeout rcat;
+ struct bt_hci_rsp_read_page_timeout rpt;
+ struct bt_hci_rsp_read_scan_enable rse;
+ struct bt_hci_rsp_read_auth_enable rae;
+ struct bt_hci_rsp_read_class_of_dev rcod;
+ struct bt_hci_rsp_read_voice_setting rvs;
+ struct bt_hci_rsp_read_inquiry_mode rim;
+ struct bt_hci_rsp_read_afh_assess_mode raam;
+ struct bt_hci_rsp_read_ext_inquiry_rsp reir;
+ struct bt_hci_rsp_read_simple_pairing_mode rspm;
+ struct bt_hci_rsp_read_inquiry_rsp_tx_power rirtp;
+ struct bt_hci_rsp_read_le_host_supported rlhs;
+ struct bt_hci_rsp_read_local_version rlv;
+ struct bt_hci_rsp_read_local_commands rlc;
+ struct bt_hci_rsp_read_local_features rlf;
+ struct bt_hci_rsp_read_local_ext_features rlef;
+ struct bt_hci_rsp_read_buffer_size rbs;
+ struct bt_hci_rsp_read_country_code rcc;
+ struct bt_hci_rsp_read_bd_addr rba;
+ struct bt_hci_rsp_read_data_block_size rdbs;
+ struct bt_hci_rsp_le_read_buffer_size lrbs;
+ struct bt_hci_rsp_le_read_local_features lrlf;
+ struct bt_hci_rsp_le_read_supported_states lrss;
+ uint16_t opcode;
+ uint8_t status, page;
+
+ if (len < sizeof(*hdr))
+ return;
+
+ opcode = le16_to_cpu(hdr->opcode);
+
+ switch (opcode) {
+ case BT_HCI_CMD_INQUIRY:
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ inquiry_complete(btdev, BT_HCI_ERR_SUCCESS);
+ break;
+
+ case BT_HCI_CMD_INQUIRY_CANCEL:
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_CREATE_CONN:
+ cc = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ conn_request(btdev, cc->bdaddr);
+ break;
+
+ case BT_HCI_CMD_DISCONNECT:
+ dc = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ disconnect_complete(btdev, le16_to_cpu(dc->handle), dc->reason);
+ break;
+
+ case BT_HCI_CMD_CREATE_CONN_CANCEL:
+ ccc = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ conn_complete(btdev, ccc->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+ break;
+
+ case BT_HCI_CMD_ACCEPT_CONN_REQUEST:
+ acr = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ conn_complete(btdev, acr->bdaddr, BT_HCI_ERR_SUCCESS);
+ break;
+
+ case BT_HCI_CMD_REJECT_CONN_REQUEST:
+ rcr = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ conn_complete(btdev, rcr->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+ break;
+
+ case BT_HCI_CMD_REMOTE_NAME_REQUEST:
+ rnr = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ name_request_complete(btdev, rnr->bdaddr, BT_HCI_ERR_SUCCESS);
+ break;
+
+ case BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL:
+ rnrc = data + sizeof(*hdr);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ name_request_complete(btdev, rnrc->bdaddr,
+ BT_HCI_ERR_UNKNOWN_CONN_ID);
+ break;
+
+ case BT_HCI_CMD_READ_REMOTE_FEATURES:
+ rrf = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ remote_features_complete(btdev, le16_to_cpu(rrf->handle));
+ break;
+
+ case BT_HCI_CMD_READ_REMOTE_EXT_FEATURES:
+ rref = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ remote_ext_features_complete(btdev, le16_to_cpu(rref->handle),
+ rref->page);
+ break;
+
+ case BT_HCI_CMD_READ_REMOTE_VERSION:
+ rrv = data + sizeof(*hdr);
+ cmd_status(btdev, BT_HCI_ERR_SUCCESS, opcode);
+ remote_version_complete(btdev, le16_to_cpu(rrv->handle));
+ break;
+
+ case BT_HCI_CMD_READ_DEFAULT_LINK_POLICY:
+ rdlp.status = BT_HCI_ERR_SUCCESS;
+ rdlp.policy = cpu_to_le16(btdev->default_link_policy);
+ cmd_complete(btdev, opcode, &rdlp, sizeof(rdlp));
+ break;
+
+ case BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY:
+ wdlp = data + sizeof(*hdr);
+ btdev->default_link_policy = le16_to_cpu(wdlp->policy);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_SET_EVENT_MASK:
+ sem = data + sizeof(*hdr);
+ memcpy(btdev->event_mask, sem->mask, 8);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_RESET:
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_SET_EVENT_FILTER:
+ sef = data + sizeof(*hdr);
+ btdev->event_filter = sef->type;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_STORED_LINK_KEY:
+ rslk.status = BT_HCI_ERR_SUCCESS;
+ rslk.max_num_keys = cpu_to_le16(0);
+ rslk.num_keys = cpu_to_le16(0);
+ cmd_complete(btdev, opcode, &rslk, sizeof(rslk));
+ break;
+
+ case BT_HCI_CMD_WRITE_STORED_LINK_KEY:
+ wslk.status = BT_HCI_ERR_SUCCESS;
+ wslk.num_keys = 0;
+ cmd_complete(btdev, opcode, &wslk, sizeof(wslk));
+ break;
+
+ case BT_HCI_CMD_DELETE_STORED_LINK_KEY:
+ dslk.status = BT_HCI_ERR_SUCCESS;
+ dslk.num_keys = cpu_to_le16(0);
+ cmd_complete(btdev, opcode, &dslk, sizeof(dslk));
+ break;
+
+ case BT_HCI_CMD_WRITE_LOCAL_NAME:
+ wln = data + sizeof(*hdr);
+ memcpy(btdev->name, wln->name, 248);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_LOCAL_NAME:
+ rln.status = BT_HCI_ERR_SUCCESS;
+ memcpy(rln.name, btdev->name, 248);
+ cmd_complete(btdev, opcode, &rln, sizeof(rln));
+ break;
+
+ case BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT:
+ rcat.status = BT_HCI_ERR_SUCCESS;
+ rcat.timeout = cpu_to_le16(btdev->conn_accept_timeout);
+ cmd_complete(btdev, opcode, &rcat, sizeof(rcat));
+ break;
+
+ case BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT:
+ wcat = data + sizeof(*hdr);
+ btdev->conn_accept_timeout = le16_to_cpu(wcat->timeout);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_PAGE_TIMEOUT:
+ rpt.status = BT_HCI_ERR_SUCCESS;
+ rpt.timeout = cpu_to_le16(btdev->page_timeout);
+ cmd_complete(btdev, opcode, &rpt, sizeof(rpt));
+ break;
+
+ case BT_HCI_CMD_WRITE_PAGE_TIMEOUT:
+ wpt = data + sizeof(*hdr);
+ btdev->page_timeout = le16_to_cpu(wpt->timeout);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_SCAN_ENABLE:
+ rse.status = BT_HCI_ERR_SUCCESS;
+ rse.enable = btdev->scan_enable;
+ cmd_complete(btdev, opcode, &rse, sizeof(rse));
+ break;
+
+ case BT_HCI_CMD_WRITE_SCAN_ENABLE:
+ wse = data + sizeof(*hdr);
+ btdev->scan_enable = wse->enable;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_AUTH_ENABLE:
+ rae.status = BT_HCI_ERR_SUCCESS;
+ rae.enable = btdev->auth_enable;
+ cmd_complete(btdev, opcode, &rae, sizeof(rae));
+ break;
+
+ case BT_HCI_CMD_WRITE_AUTH_ENABLE:
+ wae = data + sizeof(*hdr);
+ btdev->auth_enable = wae->enable;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_CLASS_OF_DEV:
+ rcod.status = BT_HCI_ERR_SUCCESS;
+ memcpy(rcod.dev_class, btdev->dev_class, 3);
+ cmd_complete(btdev, opcode, &rcod, sizeof(rcod));
+ break;
+
+ case BT_HCI_CMD_WRITE_CLASS_OF_DEV:
+ wcod = data + sizeof(*hdr);
+ memcpy(btdev->dev_class, wcod->dev_class, 3);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_VOICE_SETTING:
+ rvs.status = BT_HCI_ERR_SUCCESS;
+ rvs.setting = cpu_to_le16(btdev->voice_setting);
+ cmd_complete(btdev, opcode, &rvs, sizeof(rvs));
+ break;
+
+ case BT_HCI_CMD_WRITE_VOICE_SETTING:
+ wvs = data + sizeof(*hdr);
+ btdev->voice_setting = le16_to_cpu(wvs->setting);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_INQUIRY_MODE:
+ rim.status = BT_HCI_ERR_SUCCESS;
+ rim.mode = btdev->inquiry_mode;
+ cmd_complete(btdev, opcode, &rim, sizeof(rim));
+ break;
+
+ case BT_HCI_CMD_WRITE_INQUIRY_MODE:
+ wim = data + sizeof(*hdr);
+ btdev->inquiry_mode = wim->mode;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_AFH_ASSESS_MODE:
+ raam.status = BT_HCI_ERR_SUCCESS;
+ raam.mode = btdev->afh_assess_mode;
+ cmd_complete(btdev, opcode, &raam, sizeof(raam));
+ break;
+
+ case BT_HCI_CMD_WRITE_AFH_ASSESS_MODE:
+ waam = data + sizeof(*hdr);
+ btdev->afh_assess_mode = waam->mode;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_EXT_INQUIRY_RSP:
+ reir.status = BT_HCI_ERR_SUCCESS;
+ reir.fec = btdev->ext_inquiry_fec;
+ memcpy(reir.data, btdev->ext_inquiry_rsp, 240);
+ cmd_complete(btdev, opcode, &reir, sizeof(reir));
+ break;
+
+ case BT_HCI_CMD_WRITE_EXT_INQUIRY_RSP:
+ weir = data + sizeof(*hdr);
+ btdev->ext_inquiry_fec = weir->fec;
+ memcpy(btdev->ext_inquiry_rsp, weir->data, 240);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE:
+ rspm.status = BT_HCI_ERR_SUCCESS;
+ rspm.mode = btdev->simple_pairing_mode;
+ cmd_complete(btdev, opcode, &rspm, sizeof(rspm));
+ break;
+
+ case BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE:
+ wspm = data + sizeof(*hdr);
+ btdev->simple_pairing_mode = wspm->mode;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_INQUIRY_RSP_TX_POWER:
+ rirtp.status = BT_HCI_ERR_SUCCESS;
+ rirtp.level = 0;
+ cmd_complete(btdev, opcode, &rirtp, sizeof(rirtp));
+ break;
+
+ case BT_HCI_CMD_READ_LE_HOST_SUPPORTED:
+ rlhs.status = BT_HCI_ERR_SUCCESS;
+ rlhs.supported = btdev->le_supported;
+ rlhs.simultaneous = btdev->le_simultaneous;
+ cmd_complete(btdev, opcode, &rlhs, sizeof(rlhs));
+ break;
+
+ case BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED:
+ wlhs = data + sizeof(*hdr);
+ btdev->le_supported = wlhs->supported;
+ btdev->le_simultaneous = wlhs->simultaneous;
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_READ_LOCAL_VERSION:
+ rlv.status = BT_HCI_ERR_SUCCESS;
+ rlv.hci_ver = btdev->version;
+ rlv.hci_rev = cpu_to_le16(btdev->revision);
+ rlv.lmp_ver = btdev->version;
+ rlv.manufacturer = cpu_to_le16(btdev->manufacturer);
+ rlv.lmp_subver = cpu_to_le16(btdev->revision);
+ cmd_complete(btdev, opcode, &rlv, sizeof(rlv));
+ break;
+
+ case BT_HCI_CMD_READ_LOCAL_COMMANDS:
+ rlc.status = BT_HCI_ERR_SUCCESS;
+ memcpy(rlc.commands, btdev->commands, 64);
+ cmd_complete(btdev, opcode, &rlc, sizeof(rlc));
+ break;
+
+ case BT_HCI_CMD_READ_LOCAL_FEATURES:
+ rlf.status = BT_HCI_ERR_SUCCESS;
+ memcpy(rlf.features, btdev->features, 8);
+ cmd_complete(btdev, opcode, &rlf, sizeof(rlf));
+ break;
+
+ case BT_HCI_CMD_READ_LOCAL_EXT_FEATURES:
+ page = ((const uint8_t *) data)[sizeof(*hdr)];
+ switch (page) {
+ case 0x00:
+ rlef.status = BT_HCI_ERR_SUCCESS;
+ rlef.page = 0x00;
+ rlef.max_page = 0x01;
+ memcpy(rlef.features, btdev->features, 8);
+ break;
+ case 0x01:
+ rlef.status = BT_HCI_ERR_SUCCESS;
+ rlef.page = 0x01;
+ rlef.max_page = 0x01;
+ memset(rlef.features, 0, 8);
+ if (btdev->simple_pairing_mode)
+ rlef.features[0] |= 0x01;
+ if (btdev->le_supported)
+ rlef.features[0] |= 0x02;
+ if (btdev->le_simultaneous)
+ rlef.features[0] |= 0x04;
+ break;
+ default:
+ rlef.status = BT_HCI_ERR_INVALID_PARAMETERS;
+ rlef.page = page;
+ rlef.max_page = 0x01;
+ memset(rlef.features, 0, 8);
+ break;
+ }
+ cmd_complete(btdev, opcode, &rlef, sizeof(rlef));
+ break;
+
+ case BT_HCI_CMD_READ_BUFFER_SIZE:
+ rbs.status = BT_HCI_ERR_SUCCESS;
+ rbs.acl_mtu = cpu_to_le16(btdev->acl_mtu);
+ rbs.sco_mtu = 0;
+ rbs.acl_max_pkt = cpu_to_le16(btdev->acl_max_pkt);
+ rbs.sco_max_pkt = cpu_to_le16(0);
+ cmd_complete(btdev, opcode, &rbs, sizeof(rbs));
+ break;
+
+ case BT_HCI_CMD_READ_COUNTRY_CODE:
+ rcc.status = BT_HCI_ERR_SUCCESS;
+ rcc.code = btdev->country_code;
+ cmd_complete(btdev, opcode, &rcc, sizeof(rcc));
+ break;
+
+ case BT_HCI_CMD_READ_BD_ADDR:
+ rba.status = BT_HCI_ERR_SUCCESS;
+ memcpy(rba.bdaddr, btdev->bdaddr, 6);
+ cmd_complete(btdev, opcode, &rba, sizeof(rba));
+ break;
+
+ case BT_HCI_CMD_READ_DATA_BLOCK_SIZE:
+ rdbs.status = BT_HCI_ERR_SUCCESS;
+ rdbs.max_acl_len = cpu_to_le16(btdev->acl_mtu);
+ rdbs.block_len = cpu_to_le16(btdev->acl_mtu);
+ rdbs.num_blocks = cpu_to_le16(btdev->acl_max_pkt);
+ cmd_complete(btdev, opcode, &rdbs, sizeof(rdbs));
+ break;
+
+ case BT_HCI_CMD_LE_SET_EVENT_MASK:
+ lsem = data + sizeof(*hdr);
+ memcpy(btdev->le_event_mask, lsem->mask, 8);
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_LE_READ_BUFFER_SIZE:
+ lrbs.status = BT_HCI_ERR_SUCCESS;
+ lrbs.le_mtu = cpu_to_le16(btdev->acl_mtu);
+ lrbs.le_max_pkt = btdev->acl_max_pkt;
+ cmd_complete(btdev, opcode, &lrbs, sizeof(lrbs));
+ break;
+
+ case BT_HCI_CMD_LE_READ_LOCAL_FEATURES:
+ lrlf.status = BT_HCI_ERR_SUCCESS;
+ memcpy(lrlf.features, btdev->le_features, 8);
+ cmd_complete(btdev, opcode, &lrlf, sizeof(lrlf));
+ break;
+
+ case BT_HCI_CMD_LE_SET_SCAN_PARAMETERS:
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_LE_SET_SCAN_ENABLE:
+ status = BT_HCI_ERR_SUCCESS;
+ cmd_complete(btdev, opcode, &status, sizeof(status));
+ break;
+
+ case BT_HCI_CMD_LE_READ_SUPPORTED_STATES:
+ lrss.status = BT_HCI_ERR_SUCCESS;
+ memcpy(lrss.states, btdev->le_states, 8);
+ cmd_complete(btdev, opcode, &lrss, sizeof(lrss));
+ break;
+
+ default:
+ printf("Unsupported command 0x%4.4x\n", opcode);
+ hexdump(data, len);
+ cmd_status(btdev, BT_HCI_ERR_UNKNOWN_COMMAND, opcode);
+ break;
+ }
+}
+
+void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len)
+{
+ uint8_t pkt_type;
+
+ if (!btdev)
+ return;
+
+ if (len < 1)
+ return;
+
+ pkt_type = ((const uint8_t *) data)[0];
+
+ switch (pkt_type) {
+ case BT_H4_CMD_PKT:
+ process_cmd(btdev, data + 1, len - 1);
+ break;
+ case BT_H4_ACL_PKT:
+ if (btdev->conn)
+ send_packet(btdev->conn, data, len);
+ num_completed_packets(btdev);
+ break;
+ default:
+ printf("Unsupported packet 0x%2.2x\n", pkt_type);
+ break;
+ }
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+
+typedef void (*btdev_send_func) (const void *data, uint16_t len,
+ void *user_data);
+
+struct btdev;
+
+struct btdev *btdev_create(uint16_t id);
+void btdev_destroy(struct btdev *btdev);
+
+void btdev_set_send_handler(struct btdev *btdev, btdev_send_func handler,
+ void *user_data);
+
+void btdev_receive_h4(struct btdev *btdev, const void *data, uint16_t len);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include "mainloop.h"
+#include "server.h"
+#include "vhci.h"
+
+static void signal_callback(int signum, void *user_data)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ mainloop_quit();
+ break;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct vhci *vhci;
+ struct server *server;
+ sigset_t mask;
+
+ mainloop_init();
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+ vhci = vhci_open(VHCI_TYPE_BREDR, 0x23);
+ if (!vhci) {
+ fprintf(stderr, "Failed to open Virtual HCI device\n");
+ return 1;
+ }
+
+ server = server_open_unix("/tmp/bt-server-bredr", 0x42);
+ if (!server) {
+ fprintf(stderr, "Failed to open server channel\n");
+ vhci_close(vhci);
+ return 1;
+ }
+
+ return mainloop_run();
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+
+#include "mainloop.h"
+#include "btdev.h"
+#include "server.h"
+
+struct server {
+ uint16_t id;
+ int fd;
+};
+
+struct client {
+ int fd;
+ struct btdev *btdev;
+ uint8_t *pkt_data;
+ uint8_t pkt_type;
+ uint16_t pkt_expect;
+ uint16_t pkt_len;
+ uint16_t pkt_offset;
+};
+
+static void server_destroy(void *user_data)
+{
+ struct server *server = user_data;
+
+ close(server->fd);
+
+ free(server);
+}
+
+static void client_destroy(void *user_data)
+{
+ struct client *client = user_data;
+
+ btdev_destroy(client->btdev);
+
+ close(client->fd);
+
+ free(client);
+}
+
+static void client_write_callback(const void *data, uint16_t len,
+ void *user_data)
+{
+ struct client *client = user_data;
+ ssize_t written;
+
+ written = send(client->fd, data, len, MSG_DONTWAIT);
+ if (written < 0)
+ return;
+}
+
+static void client_read_callback(int fd, uint32_t events, void *user_data)
+{
+ struct client *client = user_data;
+ static uint8_t buf[4096];
+ uint8_t *ptr = buf;
+ ssize_t len;
+ uint16_t count;
+
+ if (events & (EPOLLERR | EPOLLHUP))
+ return;
+
+again:
+ len = recv(fd, buf + client->pkt_offset,
+ sizeof(buf) - client->pkt_offset, MSG_DONTWAIT);
+ if (len < 0) {
+ if (errno == EAGAIN)
+ goto again;
+ return;
+ }
+
+ count = client->pkt_offset + len;
+
+ while (count > 0) {
+ hci_command_hdr *cmd_hdr;
+
+ if (!client->pkt_data) {
+ client->pkt_type = ptr[0];
+
+ switch (client->pkt_type) {
+ case HCI_COMMAND_PKT:
+ if (count < HCI_COMMAND_HDR_SIZE + 1) {
+ client->pkt_offset += len;
+ return;
+ }
+ cmd_hdr = (hci_command_hdr *) (ptr + 1);
+ client->pkt_expect = HCI_COMMAND_HDR_SIZE +
+ cmd_hdr->plen + 1;
+ client->pkt_data = malloc(client->pkt_expect);
+ client->pkt_len = 0;
+ break;
+ default:
+ printf("packet error\n");
+ return;
+ }
+
+ client->pkt_offset = 0;
+ }
+
+ if (count >= client->pkt_expect) {
+ memcpy(client->pkt_data + client->pkt_len,
+ ptr, client->pkt_expect);
+ ptr += client->pkt_expect;
+ count -= client->pkt_expect;
+
+ btdev_receive_h4(client->btdev, client->pkt_data,
+ client->pkt_len + client->pkt_expect);
+
+ free(client->pkt_data);
+ client->pkt_data = NULL;
+ } else {
+ memcpy(client->pkt_data + client->pkt_len, ptr, count);
+ client->pkt_len += count;
+ client->pkt_expect -= count;
+ count = 0;
+ }
+ }
+}
+
+static int accept_client(int fd)
+{
+ struct sockaddr_un addr;
+ socklen_t len;
+ int nfd;
+
+ memset(&addr, 0, sizeof(addr));
+ len = sizeof(addr);
+
+ if (getsockname(fd, (struct sockaddr *) &addr, &len) < 0) {
+ perror("Failed to get socket name");
+ return -1;
+ }
+
+ printf("Request for %s\n", addr.sun_path);
+
+ nfd = accept(fd, (struct sockaddr *) &addr, &len);
+ if (nfd < 0) {
+ perror("Failed to accept client socket");
+ return -1;
+ }
+
+ return nfd;
+}
+
+static void server_accept_callback(int fd, uint32_t events, void *user_data)
+{
+ struct server *server = user_data;
+ struct client *client;
+
+ if (events & (EPOLLERR | EPOLLHUP))
+ return;
+
+ client = malloc(sizeof(*client));
+ if (!client)
+ return;
+
+ memset(client, 0, sizeof(*client));
+
+ client->fd = accept_client(server->fd);
+ if (client->fd < 0) {
+ free(client);
+ return;
+ }
+
+ client->btdev = btdev_create(server->id);
+ if (!client->btdev) {
+ close(client->fd);
+ free(client);
+ return;
+ }
+
+ btdev_set_send_handler(client->btdev, client_write_callback, client);
+
+ if (mainloop_add_fd(client->fd, EPOLLIN, client_read_callback,
+ client, client_destroy) < 0) {
+ btdev_destroy(client->btdev);
+ close(client->fd);
+ free(client);
+ }
+}
+
+static int open_server(const char *path)
+{
+ struct sockaddr_un addr;
+ int fd;
+
+ unlink(path);
+
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("Failed to open server socket");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, path);
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Failed to bind server socket");
+ close(fd);
+ return -1;
+ }
+
+ if (listen(fd, 5) < 0) {
+ perror("Failed to listen server socket");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+struct server *server_open_unix(const char *path, uint16_t id)
+{
+ struct server *server;
+
+ server = malloc(sizeof(*server));
+ if (!server)
+ return NULL;
+
+ memset(server, 0, sizeof(*server));
+ server->id = id;
+
+ server->fd = open_server(path);
+ if (server->fd < 0) {
+ free(server);
+ return NULL;
+ }
+
+ if (mainloop_add_fd(server->fd, EPOLLIN, server_accept_callback,
+ server, server_destroy) < 0) {
+ close(server->fd);
+ free(server);
+ return NULL;
+ }
+
+ return server;
+}
+
+void server_close(struct server *server)
+{
+ if (!server)
+ return;
+
+ mainloop_remove_fd(server->fd);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+
+struct server;
+
+struct server *server_open_unix(const char *path, uint16_t id);
+void server_close(struct server *server);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mainloop.h"
+#include "btdev.h"
+#include "vhci.h"
+
+struct vhci {
+ enum vhci_type type;
+ int fd;
+ struct btdev *btdev;
+};
+
+static void vhci_destroy(void *user_data)
+{
+ struct vhci *vhci = user_data;
+
+ btdev_destroy(vhci->btdev);
+
+ close(vhci->fd);
+
+ free(vhci);
+}
+
+static void vhci_write_callback(const void *data, uint16_t len, void *user_data)
+{
+ struct vhci *vhci = user_data;
+ ssize_t written;
+
+ written = write(vhci->fd, data, len);
+ if (written < 0)
+ return;
+}
+
+static void vhci_read_callback(int fd, uint32_t events, void *user_data)
+{
+ struct vhci *vhci = user_data;
+ unsigned char buf[4096];
+ ssize_t len;
+
+ if (events & (EPOLLERR | EPOLLHUP))
+ return;
+
+ len = read(vhci->fd, buf, sizeof(buf));
+ if (len < 0)
+ return;
+
+ btdev_receive_h4(vhci->btdev, buf, len);
+}
+
+struct vhci *vhci_open(enum vhci_type type, uint16_t id)
+{
+ struct vhci *vhci;
+
+ switch (type) {
+ case VHCI_TYPE_BREDR:
+ break;
+ case VHCI_TYPE_AMP:
+ return NULL;
+ }
+
+ vhci = malloc(sizeof(*vhci));
+ if (!vhci)
+ return NULL;
+
+ memset(vhci, 0, sizeof(*vhci));
+ vhci->type = type;
+
+ vhci->fd = open("/dev/vhci", O_RDWR | O_NONBLOCK);
+ if (vhci->fd < 0) {
+ free(vhci);
+ return NULL;
+ }
+
+ vhci->btdev = btdev_create(id);
+ if (!vhci->btdev) {
+ close(vhci->fd);
+ free(vhci);
+ return NULL;
+ }
+
+ btdev_set_send_handler(vhci->btdev, vhci_write_callback, vhci);
+
+ if (mainloop_add_fd(vhci->fd, EPOLLIN, vhci_read_callback,
+ vhci, vhci_destroy) < 0) {
+ btdev_destroy(vhci->btdev);
+ close(vhci->fd);
+ free(vhci);
+ return NULL;
+ }
+
+ return vhci;
+}
+
+void vhci_close(struct vhci *vhci)
+{
+ if (!vhci)
+ return;
+
+ mainloop_remove_fd(vhci->fd);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+
+enum vhci_type {
+ VHCI_TYPE_BREDR = 0,
+ VHCI_TYPE_AMP = 1,
+};
+
+struct vhci;
+
+struct vhci *vhci_open(enum vhci_type type, uint16_t id);
+void vhci_close(struct vhci *vhci);
typedef struct {
const char *name;
const char *signature;
- const char *reply;
+} GDBusArgInfo;
+
+typedef struct {
+ const char *name;
GDBusMethodFunction function;
GDBusMethodFlags flags;
unsigned int privilege;
+ const GDBusArgInfo *in_args;
+ const GDBusArgInfo *out_args;
} GDBusMethodTable;
typedef struct {
const char *name;
- const char *signature;
GDBusSignalFlags flags;
+ const GDBusArgInfo *args;
} GDBusSignalTable;
typedef struct {
GDBusSecurityFunction function;
} GDBusSecurityTable;
+#define GDBUS_ARGS(args...) (const GDBusArgInfo[]) { args, { } }
+
+#define GDBUS_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function
+
+#define GDBUS_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function, \
+ .flags = G_DBUS_METHOD_FLAG_ASYNC
+
+#define GDBUS_DEPRECATED_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function, \
+ .flags = G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_DEPRECATED_ASYNC_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function, \
+ .flags = G_DBUS_METHOD_FLAG_ASYNC | G_DBUS_METHOD_FLAG_DEPRECATED
+
+#define GDBUS_NOREPLY_METHOD(_name, _in_args, _out_args, _function) \
+ .name = _name, \
+ .in_args = _in_args, \
+ .out_args = _out_args, \
+ .function = _function, \
+ .flags = G_DBUS_METHOD_FLAG_NOREPLY
+
+#define GDBUS_SIGNAL(_name, _args) \
+ .name = _name, \
+ .args = _args
+
+#define GDBUS_DEPRECATED_SIGNAL(_name, _args) \
+ .name = _name, \
+ .args = _args, \
+ .flags = G_DBUS_SIGNAL_FLAG_DEPRECATED
+
gboolean g_dbus_register_interface(DBusConnection *connection,
const char *path, const char *name,
const GDBusMethodTable *methods,
#include <glib.h>
#include <dbus/dbus.h>
-#ifdef NEED_DBUS_WATCH_GET_UNIX_FD
-#define dbus_watch_get_unix_fd dbus_watch_get_fd
-#endif
-
#include "gdbus.h"
#define DISPATCH_TIMEOUT 0
void *iface_user_data;
};
-static void print_arguments(GString *gstr, const char *sig,
+static void print_arguments(GString *gstr, const GDBusArgInfo *args,
const char *direction)
{
- int i;
-
- for (i = 0; sig[i]; i++) {
- char type[32];
- int struct_level, dict_level;
- unsigned int len;
- gboolean complete;
-
- complete = FALSE;
- struct_level = dict_level = 0;
- memset(type, 0, sizeof(type));
-
- /* Gather enough data to have a single complete type */
- for (len = 0; len < (sizeof(type) - 1) && sig[i]; len++, i++) {
- switch (sig[i]) {
- case '(':
- struct_level++;
- break;
- case ')':
- struct_level--;
- if (struct_level <= 0 && dict_level <= 0)
- complete = TRUE;
- break;
- case '{':
- dict_level++;
- break;
- case '}':
- dict_level--;
- if (struct_level <= 0 && dict_level <= 0)
- complete = TRUE;
- break;
- case 'a':
- break;
- default:
- if (struct_level <= 0 && dict_level <= 0)
- complete = TRUE;
- break;
- }
-
- type[len] = sig[i];
-
- if (complete)
- break;
- }
-
+ for (; args && args->name; args++) {
+ g_string_append_printf(gstr,
+ "\t\t\t<arg name=\"%s\" type=\"%s\"",
+ args->name, args->signature);
if (direction)
g_string_append_printf(gstr,
- "\t\t\t<arg type=\"%s\" direction=\"%s\"/>\n",
- type, direction);
+ " direction=\"%s\"/>\n", direction);
else
- g_string_append_printf(gstr,
- "\t\t\t<arg type=\"%s\"/>\n",
- type);
+ g_string_append_printf(gstr, "/>\n");
+
}
}
const GDBusSignalTable *signal;
for (method = iface->methods; method && method->name; method++) {
- if (!strlen(method->signature) && !strlen(method->reply))
+ gboolean deprecated = method->flags &
+ G_DBUS_METHOD_FLAG_DEPRECATED;
+ gboolean noreply = method->flags &
+ G_DBUS_METHOD_FLAG_NOREPLY;
+
+ if (!deprecated && !noreply &&
+ !(method->in_args && method->in_args->name) &&
+ !(method->out_args && method->out_args->name))
g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
method->name);
else {
g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
method->name);
- print_arguments(gstr, method->signature, "in");
- print_arguments(gstr, method->reply, "out");
+ print_arguments(gstr, method->in_args, "in");
+ print_arguments(gstr, method->out_args, "out");
+
+ if (deprecated)
+ g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n");
+
+ if (noreply)
+ g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Method.NoReply\" value=\"true\"/>\n");
+
g_string_append_printf(gstr, "\t\t</method>\n");
}
}
for (signal = iface->signals; signal && signal->name; signal++) {
- if (!strlen(signal->signature))
+ gboolean deprecated = signal->flags &
+ G_DBUS_SIGNAL_FLAG_DEPRECATED;
+
+ if (!deprecated && !(signal->args && signal->args->name))
g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
signal->name);
else {
g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
signal->name);
- print_arguments(gstr, signal->signature, NULL);
+ print_arguments(gstr, signal->args, NULL);
+
+ if (deprecated)
+ g_string_append_printf(gstr, "\t\t\t<annotation name=\"org.freedesktop.DBus.Deprecated\" value=\"true\"/>\n");
+
g_string_append_printf(gstr, "\t\t</signal>\n");
}
}
struct generic_data *data = user_data;
DBusMessage *reply;
- if (!dbus_message_has_signature(message, DBUS_TYPE_INVALID_AS_STRING)) {
- error("Unexpected signature to introspect call");
- return NULL;
- }
-
if (data->introspect == NULL)
generate_introspection_xml(connection, data,
dbus_message_get_path(message));
return NULL;
}
+static gboolean g_dbus_args_have_signature(const GDBusArgInfo *args,
+ DBusMessage *message)
+{
+ const char *sig = dbus_message_get_signature(message);
+ const char *p = NULL;
+
+ for (; args && args->signature && *sig; args++) {
+ p = args->signature;
+
+ for (; *sig && *p; sig++, p++) {
+ if (*p != *sig)
+ return FALSE;
+ }
+ }
+
+ if (*sig || (p && *p) || (args && args->signature))
+ return FALSE;
+
+ return TRUE;
+}
+
static DBusHandlerResult generic_message(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
method->name) == FALSE)
continue;
- if (dbus_message_has_signature(message,
- method->signature) == FALSE)
+ if (g_dbus_args_have_signature(method->in_args,
+ message) == FALSE)
continue;
if (check_privilege(connection, message, method,
return DBUS_HANDLER_RESULT_HANDLED;
#ifdef __TIZEN_PATCH__
- DBG("%s: %s.%s()",dbus_message_get_path(message),
- iface->name,method->name);
+ DBG("%s: %s.%s()", dbus_message_get_path(message),
+ iface->name, method->name);
#endif
return process_message(connection, message, method,
iface->user_data);
g_free(parent_path);
}
-static GDBusMethodTable introspect_methods[] = {
- { "Introspect", "", "s", introspect },
+static const GDBusMethodTable introspect_methods[] = {
+ { GDBUS_METHOD("Introspect", NULL,
+ GDBUS_ARGS({ "xml", "s" }), introspect) },
{ }
};
static gboolean check_signal(DBusConnection *conn, const char *path,
const char *interface, const char *name,
- const char **args)
+ const GDBusArgInfo **args)
{
struct generic_data *data = NULL;
struct interface_data *iface;
for (signal = iface->signals; signal && signal->name; signal++) {
if (!strcmp(signal->name, name)) {
- *args = signal->signature;
- break;
+ *args = signal->args;
+ return TRUE;
}
}
- if (*args == NULL) {
- error("No signal named %s on interface %s", name, interface);
- return FALSE;
- }
-
- return TRUE;
+ error("No signal named %s on interface %s", name, interface);
+ return FALSE;
}
static dbus_bool_t emit_signal_valist(DBusConnection *conn,
{
DBusMessage *signal;
dbus_bool_t ret;
- const char *signature, *args;
+ const GDBusArgInfo *args;
if (!check_signal(conn, path, interface, name, &args))
return FALSE;
if (!ret)
goto fail;
- signature = dbus_message_get_signature(signal);
- if (strcmp(args, signature) != 0) {
- error("%s.%s: expected signature'%s' but got '%s'",
+ if (g_dbus_args_have_signature(args, signal) == FALSE) {
+#ifdef __TIZEN_PATCH__
+ error("%s.%s: got unexpected signature '%s'", interface, name,
+ dbus_message_get_signature(signal));
+#else
+ error("%s.%s: expected signature'%s' but got %s'",
interface, name, args, signature);
+#endif
ret = FALSE;
goto fail;
}
if (data == NULL)
return 0;
- cb = filter_data_add_callback(data, connect, disconnect, NULL, NULL,
+ cb = filter_data_add_callback(data, connect, disconnect, NULL, destroy,
user_data);
if (cb == NULL)
return 0;
#include "hdp.h"
#include "mcap.h"
-#ifndef DBUS_TYPE_UNIX_FD
- #define DBUS_TYPE_UNIX_FD -1
-#endif
-
#define ECHO_TIMEOUT 1 /* second */
#define HDP_ECHO_LEN 15
g_slist_foreach(adapters, (GFunc) update_adapter, NULL);
}
-static GDBusMethodTable health_manager_methods[] = {
- {"CreateApplication", "a{sv}", "o", manager_create_application},
- {"DestroyApplication", "o", "", manager_destroy_application},
- { NULL }
+static const GDBusMethodTable health_manager_methods[] = {
+ { GDBUS_METHOD("CreateApplication",
+ GDBUS_ARGS({ "config", "a{sv}" }),
+ GDBUS_ARGS({ "application", "o" }),
+ manager_create_application) },
+ { GDBUS_METHOD("DestroyApplication",
+ GDBUS_ARGS({ "application", "o" }), NULL,
+ manager_destroy_application) },
+ { }
};
static DBusMessage *channel_get_properties(DBusConnection *conn,
DBUS_TYPE_INVALID);
if (hdp_chan == dev->fr) {
- char *empty_path;
-
hdp_channel_unref(dev->fr);
dev->fr = NULL;
- empty_path = "/";
- emit_property_changed(dev->conn, device_get_path(dev->dev),
- HEALTH_DEVICE, "MainChannel",
- DBUS_TYPE_OBJECT_PATH, &empty_path);
}
end:
hdp_channel_unref(hdp_chan);
}
-static GDBusMethodTable health_channels_methods[] = {
- {"GetProperties","", "a{sv}", channel_get_properties },
- {"Acquire", "", "h", channel_acquire,
- G_DBUS_METHOD_FLAG_ASYNC },
- {"Release", "", "", channel_release },
- { NULL }
+static const GDBusMethodTable health_channels_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ channel_get_properties) },
+ { GDBUS_ASYNC_METHOD("Acquire",
+ NULL, GDBUS_ARGS({ "fd", "h" }),
+ channel_acquire) },
+ { GDBUS_METHOD("Release", NULL, NULL, channel_release) },
+ { }
};
static struct hdp_channel *create_channel(struct hdp_device *dev,
struct hdp_device *device = user_data;
DBusMessageIter iter, dict;
DBusMessage *reply;
- char *path;
reply = dbus_message_new_method_return(msg);
if (reply == NULL)
DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
if (device->fr != NULL)
- path = g_strdup(device->fr->path);
- else
- path = g_strdup("");
- dict_append_entry(&dict, "MainChannel", DBUS_TYPE_OBJECT_PATH, &path);
- g_free(path);
+ dict_append_entry(&dict, "MainChannel", DBUS_TYPE_OBJECT_PATH,
+ &device->fr->path);
+
dbus_message_iter_close_container(&iter, &dict);
return reply;
health_device_unref(device);
}
-static GDBusMethodTable health_device_methods[] = {
- {"Echo", "", "b", device_echo,
- G_DBUS_METHOD_FLAG_ASYNC },
- {"CreateChannel", "os", "o", device_create_channel,
- G_DBUS_METHOD_FLAG_ASYNC },
- {"DestroyChannel", "o", "", device_destroy_channel,
- G_DBUS_METHOD_FLAG_ASYNC },
- {"GetProperties", "", "a{sv}", device_get_properties},
- { NULL }
+static const GDBusMethodTable health_device_methods[] = {
+ { GDBUS_ASYNC_METHOD("Echo",
+ NULL, GDBUS_ARGS({ "value", "b" }), device_echo) },
+ { GDBUS_ASYNC_METHOD("CreateChannel",
+ GDBUS_ARGS({ "application", "o" },
+ { "configuration", "s" }),
+ GDBUS_ARGS({ "channel", "o" }),
+ device_create_channel) },
+ { GDBUS_ASYNC_METHOD("DestroyChannel",
+ GDBUS_ARGS({ "channel", "o" }), NULL,
+ device_destroy_channel) },
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ device_get_properties) },
+ { }
};
-static GDBusSignalTable health_device_signals[] = {
- {"ChannelConnected", "o" },
- {"ChannelDeleted", "o" },
- {"PropertyChanged", "sv" },
- { NULL }
+static const GDBusSignalTable health_device_signals[] = {
+ { GDBUS_SIGNAL("ChannelConnected",
+ GDBUS_ARGS({ "channel", "o" })) },
+ { GDBUS_SIGNAL("ChannelDeleted",
+ GDBUS_ARGS({ "channel", "o" })) },
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
};
static struct hdp_device *create_health_device(DBusConnection *conn,
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include <btio.h>
#include <adapter.h>
#ifndef __HDP_TYPES_H__
#define __HDP_TYPES_H__
-#define HDP_UUID "00001400-0000-1000-8000-00805F9B34FB"
-#define HDP_SOURCE_UUID "00001401-0000-1000-8000-00805F9B34FB"
-#define HDP_SINK_UUID "00001402-0000-1000-8000-00805F9B34FB"
-
#define MANAGER_PATH "/org/bluez"
#define HEALTH_MANAGER "org.bluez.HealthManager"
#include <sdpd.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include <sdp-client.h>
#include <glib-helper.h>
default:
g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS,
"Unknown option %d", cb);
+ g_free(c);
return FALSE;
}
cb = va_arg(args, int);
#include <bluetooth/hidp.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include <glib.h>
#include <dbus/dbus.h>
#include "fakehid.h"
#include "btio.h"
+#include "sdp-client.h"
+
#define INPUT_DEVICE_INTERFACE "org.bluez.Input"
#define BUF_SIZE 16
struct fake_input *fake;
DBusMessage *pending_connect;
char *uuid;
- char *alias;
GIOChannel *ctrl_io;
GIOChannel *intr_io;
guint ctrl_watch;
guint intr_watch;
+ guint sec_watch;
int timeout;
+ struct hidp_connadd_req *req;
struct input_device *idev;
};
bdaddr_t dst;
uint32_t handle;
guint dc_id;
+ gboolean disable_sdp;
char *name;
struct btd_device *device;
GSList *connections;
if (!strcasecmp(iconn->uuid, pattern))
return iconn;
-
- if (!strcasecmp(iconn->alias, pattern))
- return iconn;
}
return NULL;
if (iconn->intr_watch)
g_source_remove(iconn->intr_watch);
+ if (iconn->sec_watch)
+ g_source_remove(iconn->sec_watch);
+
if (iconn->intr_io)
g_io_channel_unref(iconn->intr_io);
g_io_channel_unref(iconn->ctrl_io);
g_free(iconn->uuid);
- g_free(iconn->alias);
g_free(iconn->fake);
g_free(iconn);
}
NULL, err,
BT_IO_OPT_SOURCE_BDADDR, &idev->src,
BT_IO_OPT_DEST_BDADDR, &idev->dst,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_INVALID);
if (!io)
return FALSE;
g_free(req);
}
+static gboolean encrypt_notify(GIOChannel *io, GIOCondition condition,
+ gpointer data)
+{
+ struct input_conn *iconn = data;
+ struct hidp_connadd_req *req = iconn->req;
+
+ DBG(" ");
+
+ encrypt_completed(0, req);
+
+ iconn->sec_watch = 0;
+ iconn->req = NULL;
+
+ return FALSE;
+}
+
static int hidp_add_connection(const struct input_device *idev,
- const struct input_conn *iconn)
+ struct input_conn *iconn)
{
struct hidp_connadd_req *req;
struct fake_hid *fake_hid;
struct fake_input *fake;
sdp_record_t *rec;
char src_addr[18], dst_addr[18];
+ GError *gerr = NULL;
int err;
req = g_new0(struct hidp_connadd_req, 1);
if (err == 0) {
/* Waiting async encryption */
return 0;
- } else if (err != -EALREADY) {
+ }
+
+ if (err == -ENOSYS)
+ goto nosys;
+
+ if (err != -EALREADY) {
error("encrypt_link: %s (%d)", strerror(-err), -err);
goto cleanup;
}
g_free(req);
return err;
+
+nosys:
+ if (!bt_io_set(iconn->intr_io, BT_IO_L2CAP, &gerr,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID)) {
+ error("btio: %s", gerr->message);
+ g_error_free(gerr);
+ goto cleanup;
+ }
+
+ iconn->req = req;
+ iconn->sec_watch = g_io_add_watch(iconn->intr_io, G_IO_OUT,
+ encrypt_notify, iconn);
+ return 0;
}
static int is_connected(struct input_conn *iconn)
DBusMessage *reply;
GError *err = NULL;
- iconn = find_connection(idev->connections, "HID");
+ iconn = find_connection(idev->connections, HID_UUID);
if (!iconn)
return btd_error_not_supported(msg);
/* HID devices */
GIOChannel *io;
+ if (idev->disable_sdp)
+ bt_clear_cached_session(&idev->src, &idev->dst);
+
io = bt_io_connect(BT_IO_L2CAP, control_connect_cb, iconn,
NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &idev->src,
return reply;
}
-static GDBusMethodTable device_methods[] = {
- { "Connect", "", "", input_device_connect,
- G_DBUS_METHOD_FLAG_ASYNC },
- { "Disconnect", "", "", input_device_disconnect },
- { "GetProperties", "", "a{sv}",input_device_get_properties },
+static const GDBusMethodTable device_methods[] = {
+ { GDBUS_ASYNC_METHOD("Connect",
+ NULL, NULL, input_device_connect) },
+ { GDBUS_METHOD("Disconnect",
+ NULL, NULL, input_device_disconnect) },
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ input_device_get_properties) },
{ }
};
-static GDBusSignalTable device_signals[] = {
- { "PropertyChanged", "sv" },
+static const GDBusSignalTable device_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ }
};
static struct input_device *input_device_new(DBusConnection *conn,
- struct btd_device *device, const char *path,
- const bdaddr_t *src, const bdaddr_t *dst,
- const uint32_t handle)
+ struct btd_device *device, const char *path,
+ const uint32_t handle, gboolean disable_sdp)
{
+ struct btd_adapter *adapter = device_get_adapter(device);
struct input_device *idev;
char name[249], src_addr[18], dst_addr[18];
idev = g_new0(struct input_device, 1);
- bacpy(&idev->src, src);
- bacpy(&idev->dst, dst);
+ adapter_get_address(adapter, &idev->src);
+ device_get_address(device, &idev->dst, NULL);
idev->device = btd_device_ref(device);
idev->path = g_strdup(path);
idev->conn = dbus_connection_ref(conn);
idev->handle = handle;
+ idev->disable_sdp = disable_sdp;
- ba2str(src, src_addr);
- ba2str(dst, dst_addr);
+ ba2str(&idev->src, src_addr);
+ ba2str(&idev->dst, dst_addr);
if (read_device_name(src_addr, dst_addr, name) == 0)
idev->name = g_strdup(name);
}
static struct input_conn *input_conn_new(struct input_device *idev,
- const char *uuid, const char *alias,
- int timeout)
+ const char *uuid, int timeout)
{
struct input_conn *iconn;
iconn = g_new0(struct input_conn, 1);
iconn->timeout = timeout;
iconn->uuid = g_strdup(uuid);
- iconn->alias = g_strdup(alias);
iconn->idev = idev;
return iconn;
}
+static gboolean is_device_sdp_disable(const sdp_record_t *rec)
+{
+ sdp_data_t *data;
+
+ data = sdp_data_get(rec, SDP_ATTR_HID_SDP_DISABLE);
+
+ return data && data->val.uint8;
+}
+
int input_device_register(DBusConnection *conn, struct btd_device *device,
- const char *path, const bdaddr_t *src,
- const bdaddr_t *dst, const char *uuid,
- uint32_t handle, int timeout)
+ const char *path, const char *uuid,
+ const sdp_record_t *rec, int timeout)
{
struct input_device *idev;
struct input_conn *iconn;
idev = find_device_by_path(devices, path);
if (!idev) {
- idev = input_device_new(conn, device, path, src, dst, handle);
+ idev = input_device_new(conn, device, path, rec->handle,
+ is_device_sdp_disable(rec));
if (!idev)
return -EINVAL;
devices = g_slist_append(devices, idev);
}
- iconn = input_conn_new(idev, uuid, "hid", timeout);
- if (!iconn)
- return -EINVAL;
+ iconn = input_conn_new(idev, uuid, timeout);
idev->connections = g_slist_append(idev->connections, iconn);
}
int fake_input_register(DBusConnection *conn, struct btd_device *device,
- const char *path, bdaddr_t *src, bdaddr_t *dst,
- const char *uuid, uint8_t channel)
+ const char *path, const char *uuid, uint8_t channel)
{
struct input_device *idev;
struct input_conn *iconn;
idev = find_device_by_path(devices, path);
if (!idev) {
- idev = input_device_new(conn, device, path, src, dst, 0);
+ idev = input_device_new(conn, device, path, 0, FALSE);
if (!idev)
return -EINVAL;
devices = g_slist_append(devices, idev);
}
- iconn = input_conn_new(idev, uuid, "hsp", 0);
- if (!iconn)
- return -EINVAL;
-
+ iconn = input_conn_new(idev, uuid, 0);
iconn->fake = g_new0(struct fake_input, 1);
iconn->fake->ch = channel;
iconn->fake->connect = rfcomm_connect;
if (!idev)
return -ENOENT;
- iconn = find_connection(idev->connections, "hid");
+ iconn = find_connection(idev->connections, HID_UUID);
if (!iconn)
return -ENOENT;
if (!idev)
return -ENOENT;
- iconn = find_connection(idev->connections, "hid");
+ iconn = find_connection(idev->connections, HID_UUID);
if (!iconn)
return -ENOENT;
*
*/
-#define HSP_HS_UUID "00001108-0000-1000-8000-00805F9B34FB"
-#define HID_UUID "00001124-0000-1000-8000-00805f9b34fb"
-
#define L2CAP_PSM_HIDP_CTRL 0x11
#define L2CAP_PSM_HIDP_INTR 0x13
};
int fake_input_register(DBusConnection *conn, struct btd_device *device,
- const char *path, bdaddr_t *src, bdaddr_t *dst,
- const char *uuid, uint8_t channel);
+ const char *path, const char *uuid, uint8_t channel);
int input_device_register(DBusConnection *conn, struct btd_device *device,
- const char *path, const bdaddr_t *src,
- const bdaddr_t *dst, const char *uuid,
- uint32_t handle, int timeout);
+ const char *path, const char *uuid,
+ const sdp_record_t *rec, int timeout);
int input_device_unregister(const char *path, const char *uuid);
int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
#include <sys/types.h>
#include <bluetooth/bluetooth.h>
-#include <bluetooth/hidp.h>
-#include <bluetooth/sdp.h>
#include <glib.h>
-#include <dbus/dbus.h>
#include "../src/adapter.h"
#include "../src/device.h"
#include "fakehid.h"
#include "uinput.h"
-#define PS3_FLAGS_MASK 0xFFFFFF00
-
enum ps3remote_special_keys {
PS3R_BIT_PS = 0,
PS3R_BIT_ENTER = 3,
/* New device? Add it to the list of known devices,
* and create the uinput necessary */
- if (old == NULL) {
+ if (old == NULL || old->uinput < 0) {
if (fake_hid->setup_uinput(fake, fake_hid)) {
error("Error setting up uinput");
g_free(fake);
return NULL;
}
- fake_hid->devices = g_list_append(fake_hid->devices, fake);
}
+ if (old == NULL)
+ fake_hid->devices = g_list_append(fake_hid->devices, fake);
+
fake->io = g_io_channel_ref(intr_io);
g_io_channel_set_close_on_unref(fake->io, TRUE);
g_io_add_watch(fake->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
dbus_connection_unref(connection);
}
-BLUETOOTH_PLUGIN_DEFINE(input, VERSION,
- BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, input_init, input_exit)
+BLUETOOTH_PLUGIN_DEFINE(input, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ input_init, input_exit)
#include <bluetooth/hci.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include <gdbus.h>
static int hid_device_probe(struct btd_device *device, GSList *uuids)
{
- struct btd_adapter *adapter = device_get_adapter(device);
const gchar *path = device_get_path(device);
const sdp_record_t *rec = btd_device_get_record(device, uuids->data);
- bdaddr_t src, dst;
DBG("path %s", path);
if (!rec)
return -1;
- adapter_get_address(adapter, &src);
- device_get_address(device, &dst, NULL);
-
- return input_device_register(connection, device, path, &src, &dst,
- HID_UUID, rec->handle, idle_timeout * 60);
+ return input_device_register(connection, device, path, HID_UUID, rec,
+ idle_timeout * 60);
}
static void hid_device_remove(struct btd_device *device)
static int headset_probe(struct btd_device *device, GSList *uuids)
{
- struct btd_adapter *adapter = device_get_adapter(device);
const gchar *path = device_get_path(device);
const sdp_record_t *record;
sdp_list_t *protos;
int ch;
- bdaddr_t src, dst;
DBG("path %s", path);
return -EINVAL;
}
- adapter_get_address(adapter, &src);
- device_get_address(device, &dst, NULL);
-
- return fake_input_register(connection, device, path, &src, &dst,
- HSP_HS_UUID, ch);
+ return fake_input_register(connection, device, path, HSP_HS_UUID, ch);
}
static void headset_remove(struct btd_device *device)
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
+#include <bluetooth/uuid.h>
#include <glib.h>
#include <dbus/dbus.h>
struct input_server *server = user_data;
bdaddr_t src, dst;
GError *err = NULL;
+ char addr[18];
int ret;
bt_io_get(chan, BT_IO_L2CAP, &err,
if (ret == 0)
return;
+ ba2str(&src, addr);
+ error("input: authorization for %s failed: %s (%d)",
+ addr, strerror(-ret), ret);
+
g_io_channel_unref(server->confirm);
server->confirm = NULL;
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ * Copyright (c) 2012 Code Aurora Forum. 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
+ *
+ */
+
+#ifndef __A2MP_H
+#define __A2MP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A2MP Protocol */
+
+/* A2MP command codes */
+
+#define A2MP_COMMAND_REJ 0x01
+#define A2MP_DISCOVER_REQ 0x02
+#define A2MP_DISCOVER_RSP 0x03
+#define A2MP_CHANGE_NOTIFY 0x04
+#define A2MP_CHANGE_RSP 0x05
+#define A2MP_INFO_REQ 0x06
+#define A2MP_INFO_RSP 0x07
+#define A2MP_ASSOC_REQ 0x08
+#define A2MP_ASSOC_RSP 0x09
+#define A2MP_CREATE_REQ 0x0a
+#define A2MP_CREATE_RSP 0x0b
+#define A2MP_DISCONN_REQ 0x0c
+#define A2MP_DISCONN_RSP 0x0d
+
+struct a2mp_hdr {
+ uint8_t code;
+ uint8_t ident;
+ uint16_t len;
+} __attribute__ ((packed));
+#define A2MP_HDR_SIZE 4
+
+struct a2mp_command_rej {
+ uint16_t reason;
+} __attribute__ ((packed));
+
+struct a2mp_discover_req {
+ uint16_t mtu;
+ uint16_t mask;
+} __attribute__ ((packed));
+
+struct a2mp_ctrl {
+ uint8_t id;
+ uint8_t type;
+ uint8_t status;
+} __attribute__ ((packed));
+
+struct a2mp_discover_rsp {
+ uint16_t mtu;
+ uint16_t mask;
+ struct a2mp_ctrl ctrl_list[0];
+} __attribute__ ((packed));
+
+struct a2mp_info_req {
+ uint8_t id;
+} __attribute__ ((packed));
+
+struct a2mp_info_rsp {
+ uint8_t id;
+ uint8_t status;
+ uint32_t total_bw;
+ uint32_t max_bw;
+ uint32_t min_latency;
+ uint16_t pal_caps;
+ uint16_t assoc_size;
+} __attribute__ ((packed));
+
+struct a2mp_assoc_req {
+ uint8_t id;
+} __attribute__ ((packed));
+
+struct a2mp_assoc_rsp {
+ uint8_t id;
+ uint8_t status;
+ uint8_t assoc_data[0];
+} __attribute__ ((packed));
+
+struct a2mp_create_req {
+ uint8_t local_id;
+ uint8_t remote_id;
+ uint8_t assoc_data[0];
+} __attribute__ ((packed));
+
+struct a2mp_create_rsp {
+ uint8_t local_id;
+ uint8_t remote_id;
+ uint8_t status;
+} __attribute__ ((packed));
+
+struct a2mp_disconn_req {
+ uint8_t local_id;
+ uint8_t remote_id;
+} __attribute__ ((packed));
+
+struct a2mp_disconn_rsp {
+ uint8_t local_id;
+ uint8_t remote_id;
+ uint8_t status;
+} __attribute__ ((packed));
+
+#define A2MP_COMMAND_NOT_RECOGNIZED 0x0000
+
+/* AMP controller status */
+#define AMP_CTRL_POWERED_DOWN 0x00
+#define AMP_CTRL_BLUETOOTH_ONLY 0x01
+#define AMP_CTRL_NO_CAPACITY 0x02
+#define AMP_CTRL_LOW_CAPACITY 0x03
+#define AMP_CTRL_MEDIUM_CAPACITY 0x04
+#define AMP_CTRL_HIGH_CAPACITY 0x05
+#define AMP_CTRL_FULL_CAPACITY 0x06
+
+/* A2MP response status */
+#define A2MP_STATUS_SUCCESS 0x00
+#define A2MP_STATUS_INVALID_CTRL_ID 0x01
+#define A2MP_STATUS_UNABLE_START_LINK_CREATION 0x02
+#define A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS 0x02
+#define A2MP_STATUS_COLLISION_OCCURED 0x03
+#define A2MP_STATUS_DISCONN_REQ_RECVD 0x04
+#define A2MP_STATUS_PHYS_LINK_EXISTS 0x05
+#define A2MP_STATUS_SECURITY_VIOLATION 0x06
+
+#define A2MP_MAC_ADDR_TYPE 1
+#define A2MP_PREF_CHANLIST_TYPE 2
+#define A2MP_CONNECTED_CHAN 3
+#define A2MP_PAL_CAP_TYPE 4
+#define A2MP_PAL_VER_INFO 5
+
+struct a2mp_tlv {
+ uint8_t type;
+ uint16_t len;
+ uint8_t val[0];
+} __attribute__ ((packed));
+
+struct a2mp_pal_ver {
+ uint8_t ver;
+ uint16_t company_id;
+ uint16_t sub_ver;
+} __attribute__ ((packed));
+
+struct a2mp_country_triplet {
+ union {
+ struct {
+ uint8_t first_channel;
+ uint8_t num_channels;
+ int8_t max_power;
+ } __attribute__ ((packed)) chans;
+ struct {
+ uint8_t reg_extension_id;
+ uint8_t reg_class;
+ uint8_t coverage_class;
+ } __attribute__ ((packed)) ext;
+ };
+} __attribute__ ((packed));
+
+struct a2mp_chan_list {
+ uint8_t country_code[3];
+ struct a2mp_country_triplet triplets[0];
+} __attribute__ ((packed));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __A2MP_H */
return "Group Sense Ltd.";
case 116:
return "Zomm, LLC";
+ case 117:
+ return "Samsung Electronics Co. Ltd.";
+ case 118:
+ return "Creative Technology Ltd.";
+ case 119:
+ return "Laird Technologies";
+ case 120:
+ return "Nike, Inc.";
+ case 121:
+ return "lesswire AG";
+ case 122:
+ return "MStar Semiconductor, Inc.";
+ case 123:
+ return "Hanlynn Technologies";
+ case 124:
+ return "A & R Cambridge";
+ case 125:
+ return "Seers Technology Co. Ltd.";
+ case 126:
+ return "Sports Tracking Technologies Ltd.";
+ case 127:
+ return "Autonet Mobile";
+ case 128:
+ return "DeLorme Publishing Company, Inc.";
case 65535:
return "internal use";
default:
#define BT_SECURITY 4
struct bt_security {
uint8_t level;
+ uint8_t key_size;
};
#define BT_SECURITY_SDP 0
#define BT_SECURITY_LOW 1
#define BT_FLUSHABLE_OFF 0
#define BT_FLUSHABLE_ON 1
+#define BT_CHANNEL_POLICY 10
+
+/* BR/EDR only (default policy)
+ * AMP controllers cannot be used.
+ * Channel move requests from the remote device are denied.
+ * If the L2CAP channel is currently using AMP, move the channel to BR/EDR.
+ */
+#define BT_CHANNEL_POLICY_BREDR_ONLY 0
+
+/* BR/EDR Preferred
+ * Allow use of AMP controllers.
+ * If the L2CAP channel is currently on AMP, move it to BR/EDR.
+ * Channel move requests from the remote device are allowed.
+ */
+#define BT_CHANNEL_POLICY_BREDR_PREFERRED 1
+
+/* AMP Preferred
+ * Allow use of AMP controllers
+ * If the L2CAP channel is currently on BR/EDR and AMP controller
+ * resources are available, initiate a channel move to AMP.
+ * Channel move requests from the remote device are allowed.
+ * If the L2CAP socket has not been connected yet, try to create
+ * and configure the channel directly on an AMP controller rather
+ * than BR/EDR.
+ */
+#define BT_CHANNEL_POLICY_AMP_PREFERRED 2
+
/* Connection and socket states */
enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
({ \
struct __attribute__((packed)) { \
typeof(*(ptr)) __v; \
- } *__p = (void *) (ptr); \
+ } *__p = (typeof(__p)) (ptr); \
__p->__v; \
})
do { \
struct __attribute__((packed)) { \
typeof(*(ptr)) __v; \
- } *__p = (void *) (ptr); \
+ } *__p = (typeof(__p)) (ptr); \
__p->__v = (val); \
} while(0)
#if __BYTE_ORDER == __LITTLE_ENDIAN
-static inline uint64_t bt_get_le64(void *ptr)
+static inline uint64_t bt_get_le64(const void *ptr)
{
- return bt_get_unaligned((uint64_t *) ptr);
+ return bt_get_unaligned((const uint64_t *) ptr);
}
-static inline uint64_t bt_get_be64(void *ptr)
+static inline uint64_t bt_get_be64(const void *ptr)
{
- return bswap_64(bt_get_unaligned((uint64_t *) ptr));
+ return bswap_64(bt_get_unaligned((const uint64_t *) ptr));
}
-static inline uint32_t bt_get_le32(void *ptr)
+static inline uint32_t bt_get_le32(const void *ptr)
{
- return bt_get_unaligned((uint32_t *) ptr);
+ return bt_get_unaligned((const uint32_t *) ptr);
}
-static inline uint32_t bt_get_be32(void *ptr)
+static inline uint32_t bt_get_be32(const void *ptr)
{
- return bswap_32(bt_get_unaligned((uint32_t *) ptr));
+ return bswap_32(bt_get_unaligned((const uint32_t *) ptr));
}
-static inline uint16_t bt_get_le16(void *ptr)
+static inline uint16_t bt_get_le16(const void *ptr)
{
- return bt_get_unaligned((uint16_t *) ptr);
+ return bt_get_unaligned((const uint16_t *) ptr);
}
-static inline uint16_t bt_get_be16(void *ptr)
+static inline uint16_t bt_get_be16(const void *ptr)
{
- return bswap_16(bt_get_unaligned((uint16_t *) ptr));
+ return bswap_16(bt_get_unaligned((const uint16_t *) ptr));
}
#elif __BYTE_ORDER == __BIG_ENDIAN
-static inline uint64_t bt_get_le64(void *ptr)
+static inline uint64_t bt_get_le64(const void *ptr)
{
- return bswap_64(bt_get_unaligned((uint64_t *) ptr));
+ return bswap_64(bt_get_unaligned((const uint64_t *) ptr));
}
-static inline uint64_t bt_get_be64(void *ptr)
+static inline uint64_t bt_get_be64(const void *ptr)
{
- return bt_get_unaligned((uint64_t *) ptr);
+ return bt_get_unaligned((const uint64_t *) ptr);
}
-static inline uint32_t bt_get_le32(void *ptr)
+static inline uint32_t bt_get_le32(const void *ptr)
{
- return bswap_32(bt_get_unaligned((uint32_t *) ptr));
+ return bswap_32(bt_get_unaligned((const uint32_t *) ptr));
}
-static inline uint32_t bt_get_be32(void *ptr)
+static inline uint32_t bt_get_be32(const void *ptr)
{
- return bt_get_unaligned((uint32_t *) ptr);
+ return bt_get_unaligned((const uint32_t *) ptr);
}
-static inline uint16_t bt_get_le16(void *ptr)
+static inline uint16_t bt_get_le16(const void *ptr)
{
- return bswap_16(bt_get_unaligned((uint16_t *) ptr));
+ return bswap_16(bt_get_unaligned((const uint16_t *) ptr));
}
-static inline uint16_t bt_get_be16(void *ptr)
+static inline uint16_t bt_get_be16(const void *ptr)
{
- return bt_get_unaligned((uint16_t *) ptr);
+ return bt_get_unaligned((const uint16_t *) ptr);
}
#else
#error "Unknown byte order"
uint8_t b[6];
} __attribute__((packed)) bdaddr_t;
+/* BD Address type */
+#define BDADDR_BREDR 0x00
+#define BDADDR_LE_PUBLIC 0x01
+#define BDADDR_LE_RANDOM 0x02
+
#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
#define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}})
#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}})
static int __other_bdaddr(int dd, int dev_id, long arg)
{
- struct hci_dev_info di = { dev_id: dev_id };
+ struct hci_dev_info di = { .dev_id = dev_id };
if (ioctl(dd, HCIGETDEVINFO, (void *) &di))
return 0;
static int __same_bdaddr(int dd, int dev_id, long arg)
{
- struct hci_dev_info di = { dev_id: dev_id };
+ struct hci_dev_info di = { .dev_id = dev_id };
if (ioctl(dd, HCIGETDEVINFO, (void *) &di))
return 0;
return -1;
}
+#ifdef __TIZEN_PATCH__
+ strncpy(name, (char *) rp.name, len - 1);
+ name[len - 1] = '\0';
+#else
rp.name[247] = '\0';
strncpy(name, (char *) rp.name, len);
+#endif
return 0;
}
int hci_le_create_conn(int dd, uint16_t interval, uint16_t window,
uint8_t initiator_filter, uint8_t peer_bdaddr_type,
bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type,
- uint16_t min_interval, uint16_t max_interval,
+ uint16_t min_interval, uint16_t max_interval,
uint16_t latency, uint16_t supervision_timeout,
uint16_t min_ce_length, uint16_t max_ce_length,
uint16_t *handle, int to)
#define LMP_EXT_FEAT 0x80
/* Extended LMP features */
-#define LMP_HOST_LE 0x02
+#define LMP_HOST_SSP 0x01
+#define LMP_HOST_LE 0x02
+#define LMP_HOST_LE_BREDR 0x04
/* Link policies */
#define HCI_LP_RSWITCH 0x0001
#define HCI_LM_RELIABLE 0x0010
#define HCI_LM_SECURE 0x0020
+/* Link Key types */
+#define HCI_LK_COMBINATION 0x00
+#define HCI_LK_LOCAL_UNIT 0x01
+#define HCI_LK_REMOTE_UNIT 0x02
+#define HCI_LK_DEBUG_COMBINATION 0x03
+#define HCI_LK_UNAUTH_COMBINATION 0x04
+#define HCI_LK_AUTH_COMBINATION 0x05
+#define HCI_LK_CHANGED_COMBINATION 0x06
+#define HCI_LK_INVALID 0xFF
+
/* ----- HCI Commands ----- */
/* Link Control */
} __attribute__ ((packed)) write_best_effort_flush_timeout_rp;
#define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 1
+#define OCF_READ_LE_HOST_SUPPORTED 0x006C
+typedef struct {
+ uint8_t status;
+ uint8_t le;
+ uint8_t simul;
+} __attribute__ ((packed)) read_le_host_supported_rp;
+#define READ_LE_HOST_SUPPORTED_RP_SIZE 3
+
#define OCF_WRITE_LE_HOST_SUPPORTED 0x006D
typedef struct {
uint8_t le;
#define HCI_DEV_NONE 0xffff
#define HCI_CHANNEL_RAW 0
-#define HCI_CHANNEL_CONTROL 1
+#define HCI_CHANNEL_MONITOR 2
+#define HCI_CHANNEL_CONTROL 3
struct hci_filter {
uint32_t type_mask;
uint8_t out;
uint16_t state;
uint32_t link_mode;
+#ifdef __TIZEN_PATCH__
+/* Workaround to avoid HID crash during connection n PQ because of the
+ * data structure mismatch. This will work for U1 kernel aswell.
+ */
+ uint32_t mtu;
+ uint32_t cnt;
+ uint32_t pkts;
+#endif
};
struct hci_dev_req {
int hci_le_create_conn(int dd, uint16_t interval, uint16_t window,
uint8_t initiator_filter, uint8_t peer_bdaddr_type,
bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type,
- uint16_t min_interval, uint16_t max_interval,
+ uint16_t min_interval, uint16_t max_interval,
uint16_t latency, uint16_t supervision_timeout,
uint16_t min_ce_length, uint16_t max_ce_length,
uint16_t *handle, int to);
* Copyright (C) 2000-2001 Qualcomm Incorporated
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
* Copyright (C) 2002-2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (c) 2012 Code Aurora Forum. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
unsigned short l2_psm;
bdaddr_t l2_bdaddr;
unsigned short l2_cid;
+ uint8_t l2_bdaddr_type;
};
/* L2CAP socket options */
#define L2CAP_ECHO_RSP 0x09
#define L2CAP_INFO_REQ 0x0a
#define L2CAP_INFO_RSP 0x0b
+#define L2CAP_CREATE_REQ 0x0c
+#define L2CAP_CREATE_RSP 0x0d
+#define L2CAP_MOVE_REQ 0x0e
+#define L2CAP_MOVE_RSP 0x0f
+#define L2CAP_MOVE_CFM 0x10
+#define L2CAP_MOVE_CFM_RSP 0x11
/* L2CAP extended feature mask */
#define L2CAP_FEAT_FLOWCTL 0x00000001
#define L2CAP_IR_SUCCESS 0x0000
#define L2CAP_IR_NOTSUPP 0x0001
+typedef struct {
+ uint16_t psm;
+ uint16_t scid;
+ uint8_t id;
+} __attribute__ ((packed)) l2cap_create_req;
+#define L2CAP_CREATE_REQ_SIZE 5
+
+typedef struct {
+ uint16_t dcid;
+ uint16_t scid;
+ uint16_t result;
+ uint16_t status;
+} __attribute__ ((packed)) l2cap_create_rsp;
+#define L2CAP_CREATE_RSP_SIZE 8
+
+typedef struct {
+ uint16_t icid;
+ uint8_t id;
+} __attribute__ ((packed)) l2cap_move_req;
+#define L2CAP_MOVE_REQ_SIZE 3
+
+typedef struct {
+ uint16_t icid;
+ uint16_t result;
+} __attribute__ ((packed)) l2cap_move_rsp;
+#define L2CAP_MOVE_RSP_SIZE 4
+
+typedef struct {
+ uint16_t icid;
+ uint16_t result;
+} __attribute__ ((packed)) l2cap_move_cfm;
+#define L2CAP_MOVE_CFM_SIZE 4
+
+typedef struct {
+ uint16_t icid;
+} __attribute__ ((packed)) l2cap_move_cfm_rsp;
+#define L2CAP_MOVE_CFM_RSP_SIZE 2
+
#ifdef __cplusplus
}
#endif
#define MGMT_INDEX_NONE 0xFFFF
+#define MGMT_STATUS_SUCCESS 0x00
+#define MGMT_STATUS_UNKNOWN_COMMAND 0x01
+#define MGMT_STATUS_NOT_CONNECTED 0x02
+#define MGMT_STATUS_FAILED 0x03
+#define MGMT_STATUS_CONNECT_FAILED 0x04
+#define MGMT_STATUS_AUTH_FAILED 0x05
+#define MGMT_STATUS_NOT_PAIRED 0x06
+#define MGMT_STATUS_NO_RESOURCES 0x07
+#define MGMT_STATUS_TIMEOUT 0x08
+#define MGMT_STATUS_ALREADY_CONNECTED 0x09
+#define MGMT_STATUS_BUSY 0x0a
+#define MGMT_STATUS_REJECTED 0x0b
+#define MGMT_STATUS_NOT_SUPPORTED 0x0c
+#define MGMT_STATUS_INVALID_PARAMS 0x0d
+#define MGMT_STATUS_DISCONNECTED 0x0e
+#define MGMT_STATUS_NOT_POWERED 0x0f
+#define MGMT_STATUS_CANCELLED 0x10
+#define MGMT_STATUS_INVALID_INDEX 0x11
+
struct mgmt_hdr {
uint16_t opcode;
uint16_t index;
} __packed;
#define MGMT_HDR_SIZE 6
+struct mgmt_addr_info {
+ bdaddr_t bdaddr;
+ uint8_t type;
+} __packed;
+
#define MGMT_OP_READ_VERSION 0x0001
struct mgmt_rp_read_version {
uint8_t version;
uint16_t revision;
} __packed;
-#define MGMT_OP_READ_FEATURES 0x0002
-struct mgmt_rp_read_features {
- uint8_t features[8];
+#define MGMT_OP_READ_COMMANDS 0x0002
+struct mgmt_rp_read_commands {
+ uint16_t num_commands;
+ uint16_t num_events;
+ uint16_t opcodes[0];
} __packed;
#define MGMT_OP_READ_INDEX_LIST 0x0003
} __packed;
struct mgmt_link_key_info {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint8_t type;
uint8_t val[16];
uint8_t pin_len;
struct mgmt_link_key_info keys[0];
} __packed;
-#define MGMT_OP_REMOVE_KEYS 0x0013
-struct mgmt_cp_remove_keys {
- bdaddr_t bdaddr;
- uint8_t disconnect;
+struct mgmt_ltk_info {
+ struct mgmt_addr_info addr;
+ uint8_t authenticated;
+ uint8_t master;
+ uint8_t enc_size;
+ uint16_t ediv;
+ uint8_t rand[8];
+ uint8_t val[16];
} __packed;
-struct mgmt_rp_remove_keys {
- bdaddr_t bdaddr;
- uint8_t status;
+
+#define MGMT_OP_LOAD_LONG_TERM_KEYS 0x0013
+struct mgmt_cp_load_long_term_keys {
+ uint16_t key_count;
+ struct mgmt_ltk_info keys[0];
} __packed;
#define MGMT_OP_DISCONNECT 0x0014
struct mgmt_cp_disconnect {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
} __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;
+ struct mgmt_addr_info addr;
} __packed;
#define MGMT_OP_GET_CONNECTIONS 0x0015
#define MGMT_OP_PIN_CODE_REPLY 0x0016
struct mgmt_cp_pin_code_reply {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint8_t pin_len;
uint8_t pin_code[16];
} __packed;
#define MGMT_OP_PIN_CODE_NEG_REPLY 0x0017
struct mgmt_cp_pin_code_neg_reply {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
} __packed;
#define MGMT_OP_SET_IO_CAPABILITY 0x0018
} __packed;
struct mgmt_rp_pair_device {
struct mgmt_addr_info addr;
- uint8_t status;
} __packed;
-#define MGMT_OP_USER_CONFIRM_REPLY 0x001A
+#define MGMT_OP_CANCEL_PAIR_DEVICE 0x001A
+
+#define MGMT_OP_UNPAIR_DEVICE 0x001B
+struct mgmt_cp_unpair_device {
+ struct mgmt_addr_info addr;
+ uint8_t disconnect;
+} __packed;
+struct mgmt_rp_unpair_device {
+ struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_USER_CONFIRM_REPLY 0x001C
struct mgmt_cp_user_confirm_reply {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
} __packed;
struct mgmt_rp_user_confirm_reply {
- bdaddr_t bdaddr;
- uint8_t status;
+ struct mgmt_addr_info addr;
} __packed;
-#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001B
+#define MGMT_OP_USER_CONFIRM_NEG_REPLY 0x001D
-#define MGMT_OP_USER_PASSKEY_REPLY 0x001C
+#define MGMT_OP_USER_PASSKEY_REPLY 0x001E
struct mgmt_cp_user_passkey_reply {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint32_t passkey;
} __packed;
struct mgmt_rp_user_passkey_reply {
- bdaddr_t bdaddr;
- uint8_t status;
+ struct mgmt_addr_info addr;
} __packed;
-#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001D
+#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x001F
struct mgmt_cp_user_passkey_neg_reply {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
} __packed;
-#define MGMT_OP_READ_LOCAL_OOB_DATA 0x001E
+#define MGMT_OP_READ_LOCAL_OOB_DATA 0x0020
struct mgmt_rp_read_local_oob_data {
uint8_t hash[16];
uint8_t randomizer[16];
} __packed;
-#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x001F
+#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021
struct mgmt_cp_add_remote_oob_data {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint8_t hash[16];
uint8_t randomizer[16];
} __packed;
-#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0020
+#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0022
struct mgmt_cp_remove_remote_oob_data {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
} __packed;
-#define MGMT_OP_START_DISCOVERY 0x0021
+#define MGMT_OP_START_DISCOVERY 0x0023
struct mgmt_cp_start_discovery {
uint8_t type;
} __packed;
-#define MGMT_OP_STOP_DISCOVERY 0x0022
+#define MGMT_OP_STOP_DISCOVERY 0x0024
+struct mgmt_cp_stop_discovery {
+ uint8_t type;
+} __packed;
-#define MGMT_OP_CONFIRM_NAME 0x0023
+#define MGMT_OP_CONFIRM_NAME 0x0025
struct mgmt_cp_confirm_name {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint8_t name_known;
} __packed;
struct mgmt_rp_confirm_name {
- bdaddr_t bdaddr;
- uint8_t status;
+ struct mgmt_addr_info addr;
} __packed;
-#define MGMT_OP_BLOCK_DEVICE 0x0024
+#define MGMT_OP_BLOCK_DEVICE 0x0026
struct mgmt_cp_block_device {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
} __packed;
-#define MGMT_OP_UNBLOCK_DEVICE 0x0025
+#define MGMT_OP_UNBLOCK_DEVICE 0x0027
struct mgmt_cp_unblock_device {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_SET_DEVICE_ID 0x0028
+struct mgmt_cp_set_device_id {
+ uint16_t source;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
uint16_t opcode;
+ uint8_t status;
uint8_t data[0];
} __packed;
#define MGMT_EV_CMD_STATUS 0x0002
struct mgmt_ev_cmd_status {
- uint8_t status;
uint16_t opcode;
+ uint8_t status;
} __packed;
#define MGMT_EV_CONTROLLER_ERROR 0x0003
struct mgmt_link_key_info key;
} __packed;
-#define MGMT_EV_DEVICE_CONNECTED 0x000A
+#define MGMT_EV_NEW_LONG_TERM_KEY 0x000A
+struct mgmt_ev_new_long_term_key {
+ uint8_t store_hint;
+ struct mgmt_ltk_info key;
+} __packed;
+
+#define MGMT_EV_DEVICE_CONNECTED 0x000B
+struct mgmt_ev_device_connected {
+ struct mgmt_addr_info addr;
+ uint32_t flags;
+ uint16_t eir_len;
+ uint8_t eir[0];
+} __packed;
-#define MGMT_EV_DEVICE_DISCONNECTED 0x000B
+#define MGMT_EV_DEVICE_DISCONNECTED 0x000C
+struct mgmt_ev_device_disconnected {
+ struct mgmt_addr_info addr;
+} __packed;
-#define MGMT_EV_CONNECT_FAILED 0x000C
+#define MGMT_EV_CONNECT_FAILED 0x000D
struct mgmt_ev_connect_failed {
struct mgmt_addr_info addr;
uint8_t status;
} __packed;
-#define MGMT_EV_PIN_CODE_REQUEST 0x000D
+#define MGMT_EV_PIN_CODE_REQUEST 0x000E
struct mgmt_ev_pin_code_request {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint8_t secure;
} __packed;
-#define MGMT_EV_USER_CONFIRM_REQUEST 0x000E
+#define MGMT_EV_USER_CONFIRM_REQUEST 0x000F
struct mgmt_ev_user_confirm_request {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint8_t confirm_hint;
uint32_t value;
} __packed;
-#define MGMT_EV_USER_PASSKEY_REQUEST 0x000F
+#define MGMT_EV_USER_PASSKEY_REQUEST 0x0010
struct mgmt_ev_user_passkey_request {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
} __packed;
-#define MGMT_EV_AUTH_FAILED 0x0010
+#define MGMT_EV_AUTH_FAILED 0x0011
struct mgmt_ev_auth_failed {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
uint8_t status;
} __packed;
-#define MGMT_EV_DEVICE_FOUND 0x0011
+#define MGMT_DEV_FOUND_CONFIRM_NAME 0x01
+#define MGMT_DEV_FOUND_LEGACY_PAIRING 0x02
+
+#define MGMT_EV_DEVICE_FOUND 0x0012
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];
+ uint32_t flags;
+ uint16_t eir_len;
+ uint8_t eir[0];
} __packed;
#define MGMT_EV_DISCOVERING 0x0013
+struct mgmt_ev_discovering {
+ uint8_t type;
+ uint8_t discovering;
+} __packed;
#define MGMT_EV_DEVICE_BLOCKED 0x0014
struct mgmt_ev_device_blocked {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
} __packed;
#define MGMT_EV_DEVICE_UNBLOCKED 0x0015
struct mgmt_ev_device_unblocked {
- bdaddr_t bdaddr;
+ struct mgmt_addr_info addr;
} __packed;
+
+#define MGMT_EV_DEVICE_UNPAIRED 0x0016
+struct mgmt_ev_device_unpaired {
+ struct mgmt_addr_info addr;
+} __packed;
+
+static const char *mgmt_op[] = {
+ "<0x0000>",
+ "Read Version",
+ "Read Commands",
+ "Read Index List",
+ "Read Controller Info",
+ "Set Powered",
+ "Set Discoverable",
+ "Set Connectable",
+ "Set Fast Connectable", /* 0x0008 */
+ "Set Pairable",
+ "Set Link Security",
+ "Set Secure Simple Pairing",
+ "Set High Speed",
+ "Set Low Energy",
+ "Set Dev Class",
+ "Set Local Name",
+ "Add UUID", /* 0x0010 */
+ "Remove UUID",
+ "Load Link Keys",
+ "Load Long Term Keys",
+ "Disconnect",
+ "Get Connections",
+ "PIN Code Reply",
+ "PIN Code Neg Reply",
+ "Set IO Capability", /* 0x0018 */
+ "Pair Device",
+ "Cancel Pair Device",
+ "Unpair Device",
+ "User Confirm Reply",
+ "User Confirm Neg Reply",
+ "User Passkey Reply",
+ "User Passkey Neg Reply",
+ "Read Local OOB Data", /* 0x0020 */
+ "Add Remote OOB Data",
+ "Remove Remove OOB Data",
+ "Start Discovery",
+ "Stop Discovery",
+ "Confirm Name",
+ "Block Device",
+ "Unblock Device",
+ "Set Device ID",
+};
+
+static const char *mgmt_ev[] = {
+ "<0x0000>",
+ "Command Complete",
+ "Command Status",
+ "Controller Error",
+ "Index Added",
+ "Index Removed",
+ "New Settings",
+ "Class of Device Changed",
+ "Local Name Changed", /* 0x0008 */
+ "New Link Key",
+ "New Long Term Key",
+ "Device Connected",
+ "Device Disconnected",
+ "Connect Failed",
+ "PIN Code Request",
+ "User Confirm Request",
+ "User Passkey Request", /* 0x0010 */
+ "Authentication Failed",
+ "Device Found",
+ "Discovering",
+ "Device Blocked",
+ "Device Unblocked",
+ "Device Unpaired",
+};
+
+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",
+ "Cancelled",
+ "Invalid Index",
+};
+
+#ifndef NELEM
+#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+static inline const char *mgmt_opstr(uint16_t op)
+{
+ if (op >= NELEM(mgmt_op))
+ return "<unknown opcode>";
+ return mgmt_op[op];
+}
+
+static inline const char *mgmt_evstr(uint16_t ev)
+{
+ if (ev >= NELEM(mgmt_ev))
+ return "<unknown event>";
+ return mgmt_ev[ev];
+}
+
+static inline const char *mgmt_errstr(uint8_t status)
+{
+ if (status >= NELEM(mgmt_status))
+ return "<unknown status>";
+ return mgmt_status[status];
+}
#define SDPDBG(fmt...)
#endif
-#define BASE_UUID "00000000-0000-1000-8000-00805F9B34FB"
-
static uint128_t bluetooth_base_uuid = {
.data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }
SDPDBG("=====================================\n");
SDPDBG("ATTRIBUTE IDENTIFIER : 0x%x\n", d->attrId);
- SDPDBG("ATTRIBUTE VALUE PTR : 0x%x\n", (uint32_t)value);
+ SDPDBG("ATTRIBUTE VALUE PTR : %p\n", value);
if (d)
sdp_data_print(d);
else
if (!u)
goto fail;
- memset(u, 0, sizeof(uuid_t));
*u = d->val.uuid;
*seqp = sdp_list_append(*seqp, u);
}
uint16_t version = 0x100;
if (SDP_IS_UUID(seq->dtd)) {
+ sdp_data_t *next = seq->next;
uuid = &seq->val.uuid;
- } else {
+ if (next && next->dtd == SDP_UINT16) {
+ version = next->val.uint16;
+ seq = next;
+ }
+ } else if (SDP_IS_SEQ(seq->dtd)) {
sdp_data_t *puuid = seq->val.dataseq;
sdp_data_t *pVnum = seq->val.dataseq->next;
if (puuid && pVnum) {
scanned += sizeof(uint16_t);
pdata_len -= sizeof(uint16_t);
- SDPDBG("Total svc count: %d\n", total_rec_count);
SDPDBG("Current svc count: %d\n", rec_count);
SDPDBG("ResponseLength: %d\n", rsplen);
/*
* This function starts an asynchronous service search request.
- * The incomming and outgoing data are stored in the transaction structure
- * buffers. When there is incomming data the sdp_process function must be
+ * The incoming and outgoing data are stored in the transaction structure
+ * buffers. When there is incoming data the sdp_process function must be
* called to get the data and handle the continuation state.
*
* INPUT :
/*
* This function starts an asynchronous service attribute request.
- * The incomming and outgoing data are stored in the transaction structure
- * buffers. When there is incomming data the sdp_process function must be
+ * The incoming and outgoing data are stored in the transaction structure
+ * buffers. When there is incoming data the sdp_process function must be
* called to get the data and handle the continuation state.
*
* INPUT :
/*
* This function starts an asynchronous service search attributes.
- * It is a service search request combined with attribute request. The incomming
+ * It is a service search request combined with attribute request. The incoming
* and outgoing data are stored in the transaction structure buffers. When there
- * is incomming data the sdp_process function must be called to get the data
+ * is incoming data the sdp_process function must be called to get the data
* and handle the continuation state.
*
* INPUT:
}
/*
- * Receive the incomming SDP PDU. This function must be called when there is data
+ * Receive the incoming SDP PDU. This function must be called when there is data
* available to be read. On continuation state, the original request (with a new
* transaction ID) and the continuation state data will be appended in the initial PDU.
* If an error happens or the transaction finishes the callback function will be called.
}
if (n == 0 || reqhdr->tid != rsphdr->tid ||
- (n != (ntohs(rsphdr->plen) + (int) sizeof(sdp_pdu_hdr_t)))) {
+ (n != (int) (ntohs(rsphdr->plen) + sizeof(sdp_pdu_hdr_t)))) {
t->err = EPROTO;
SDPERR("Protocol error.");
goto end;
return -1;
}
+void sdp_add_lang_attr(sdp_record_t *rec)
+{
+ sdp_lang_attr_t base_lang;
+ sdp_list_t *langs;
+
+ base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
+ base_lang.encoding = 106;
+ base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
+
+ langs = sdp_list_append(0, &base_lang);
+ sdp_set_lang_attr(rec, langs);
+ sdp_list_free(langs, NULL);
+}
} uuid_t;
#define SDP_IS_UUID(x) ((x) == SDP_UUID16 || (x) == SDP_UUID32 || (x) ==SDP_UUID128)
+#define SDP_IS_SEQ(x) ((x) == SDP_SEQ8 || (x) == SDP_SEQ16 || (x) == SDP_SEQ32)
typedef struct _sdp_list sdp_list_t;
struct _sdp_list {
int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *req, uint8_t *rsp, uint32_t reqsize, uint32_t *rspsize);
+void sdp_add_lang_attr(sdp_record_t *rec);
+
#ifdef __cplusplus
}
#endif
#include <stdint.h>
#include <bluetooth/bluetooth.h>
+#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb"
+
+#define HSP_HS_UUID "00001108-0000-1000-8000-00805f9b34fb"
+#define HSP_AG_UUID "00001112-0000-1000-8000-00805f9b34fb"
+
+#define HFP_HS_UUID "0000111e-0000-1000-8000-00805f9b34fb"
+#define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb"
+
+#define ADVANCED_AUDIO_UUID "0000110d-0000-1000-8000-00805f9b34fb"
+
+#define A2DP_SOURCE_UUID "0000110a-0000-1000-8000-00805f9b34fb"
+#define A2DP_SINK_UUID "0000110b-0000-1000-8000-00805f9b34fb"
+
+#define AVRCP_REMOTE_UUID "0000110e-0000-1000-8000-00805f9b34fb"
+#define AVRCP_TARGET_UUID "0000110c-0000-1000-8000-00805f9b34fb"
+
+#define PANU_UUID "00001115-0000-1000-8000-00805f9b34fb"
+#define NAP_UUID "00001116-0000-1000-8000-00805f9b34fb"
+#define GN_UUID "00001117-0000-1000-8000-00805f9b34fb"
+#define BNEP_SVC_UUID "0000000f-0000-1000-8000-00805f9b34fb"
+
+#define PNPID_UUID "00002a50-0000-1000-8000-00805f9b34fb"
+#define DEVICE_INFORMATION_UUID "0000180a-0000-1000-8000-00805f9b34fb"
+
+#define GATT_UUID "00001801-0000-1000-8000-00805f9b34fb"
+#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"
+
+#define SAP_UUID "0000112D-0000-1000-8000-00805f9b34fb"
+
+#define HEALTH_THERMOMETER_UUID "00001809-0000-1000-8000-00805f9b34fb"
+#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"
+
+#define RFCOMM_UUID_STR "00000003-0000-1000-8000-00805f9b34fb"
+
+#define HDP_UUID "00001400-0000-1000-8000-00805f9b34fb"
+#define HDP_SOURCE_UUID "00001401-0000-1000-8000-00805f9b34fb"
+#define HDP_SINK_UUID "00001402-0000-1000-8000-00805f9b34fb"
+
+#define HSP_HS_UUID "00001108-0000-1000-8000-00805f9b34fb"
+#define HID_UUID "00001124-0000-1000-8000-00805f9b34fb"
+
+#define DUN_GW_UUID "00001103-0000-1000-8000-00805f9b34fb"
+
+#define GAP_SVC_UUID "00001800-0000-1000-8000-00805f9b34fb"
+#define PNP_UUID "00001200-0000-1000-8000-00805f9b34fb"
+
typedef struct {
enum {
BT_UUID_UNSPEC = 0,
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_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",
-};
+#include <glib.h>
+#include "glib-helper.h"
static bool monitor = false;
static bool discovery = false;
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)
{
c->cb(mgmt_sk, op, index, status, data, len, c->user_data);
free(c);
+ break;
}
}
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);
+ mgmt_check_pending(mgmt_sk, op, index, ev->status, ev->data, len);
return 0;
}
}
static int mgmt_discovering(int mgmt_sk, uint16_t index,
- struct mgmt_mode *ev, uint16_t len)
+ struct mgmt_ev_discovering *ev, uint16_t len)
{
if (len < sizeof(*ev)) {
fprintf(stderr, "Too short (%u bytes) discovering event\n",
return -EINVAL;
}
- if (ev->val == 0 && discovery)
+ if (ev->discovering == 0 && discovery)
exit(EXIT_SUCCESS);
if (monitor)
- printf("hci%u discovering %s\n", index,
- ev->val ? "on" : "off");
+ printf("hci%u type %u discovering %s\n", index,
+ ev->type, ev->discovering ? "on" : "off");
return 0;
}
if (monitor) {
char addr[18];
- ba2str(&ev->key.bdaddr, addr);
+ ba2str(&ev->key.addr.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);
{
const char *str[] = { "BR/EDR", "LE Public", "LE Random" };
- if (type <= MGMT_ADDR_LE_RANDOM)
+ if (type <= BDADDR_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)
+static int mgmt_connected(int mgmt_sk, uint16_t index,
+ struct mgmt_ev_device_connected *ev,
+ uint16_t len)
{
- const char *ev_name = connected ? "connected" : "disconnected";
+ uint16_t eir_len;
+
+ if (len < sizeof(*ev)) {
+ fprintf(stderr,
+ "Invalid connected event length (%u bytes)\n", len);
+ return -EINVAL;
+ }
+
+ eir_len = bt_get_le16(&ev->eir_len);
+ if (len != sizeof(*ev) + eir_len) {
+ fprintf(stderr, "Invalid connected event length "
+ "(%u bytes, eir_len %u bytes)\n", len, eir_len);
+ return -EINVAL;
+ }
+
+ if (monitor) {
+ char addr[18];
+ ba2str(&ev->addr.bdaddr, addr);
+ printf("hci%u %s type %s connected eir_len %u\n", index, addr,
+ typestr(ev->addr.type), eir_len);
+ }
+
+ return 0;
+}
+static int mgmt_disconnected(int mgmt_sk, uint16_t index,
+ struct mgmt_addr_info *ev, uint16_t len)
+{
if (len != sizeof(*ev)) {
fprintf(stderr,
- "Invalid %s event length (%u bytes)\n", ev_name, len);
+ "Invalid disconnected event length (%u bytes)\n", 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);
+ printf("hci%u %s type %s disconnected\n", index, addr,
+ typestr(ev->type));
}
return 0;
if (monitor) {
char addr[18];
- ba2str(&ev->bdaddr, addr);
+ ba2str(&ev->addr.bdaddr, addr);
printf("hci%u %s auth failed with status 0x%02x (%s)\n",
index, addr, ev->status, mgmt_errstr(ev->status));
}
struct mgmt_rp_confirm_name *rp = rsp;
char addr[18];
- if (status != 0) {
+ if (len == 0 && status != 0) {
fprintf(stderr,
"hci%u confirm_name failed with status 0x%02x (%s)\n",
id, status, mgmt_errstr(status));
return;
}
- ba2str(&rp->bdaddr, addr);
+ ba2str(&rp->addr.bdaddr, addr);
- if (rp->status != 0)
+ if (status != 0)
fprintf(stderr,
"hci%u confirm_name for %s failed: 0x%02x (%s)\n",
- id, addr, rp->status, mgmt_errstr(status));
+ id, addr, 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)) {
+ uint32_t flags;
+ uint16_t eir_len;
+
+ if (len < sizeof(*ev)) {
fprintf(stderr,
- "Invalid device_found event length (%u bytes)\n", len);
+ "Too short device_found length (%u bytes)\n", len);
+ return -EINVAL;
+ }
+
+ flags = btohs(ev->flags);
+
+ eir_len = bt_get_le16(&ev->eir_len);
+ if (len != sizeof(*ev) + eir_len) {
+ fprintf(stderr, "dev_found: expected %zu bytes, got %u bytes",
+ sizeof(*ev) + eir_len, 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");
+ printf("hci%u dev_found: %s type %s rssi %d "
+ "flags 0x%04x eir_len %u\n", index, addr,
+ typestr(ev->addr.type), ev->rssi, flags, eir_len);
}
- if (discovery && ev->confirm_name) {
+ if (discovery && (flags & MGMT_DEV_FOUND_CONFIRM_NAME)) {
struct mgmt_cp_confirm_name cp;
memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, &ev->addr.bdaddr);
+ memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
if (resolve_names)
cp.name_known = 0;
else
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)
{
printf("hci%u PIN Reply successful\n", id);
}
-static int mgmt_pin_reply(int mgmt_sk, uint16_t index, bdaddr_t *bdaddr,
+static int mgmt_pin_reply(int mgmt_sk, uint16_t index,
+ struct mgmt_addr_info *addr,
const char *pin, size_t len)
{
struct mgmt_cp_pin_code_reply cp;
memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, bdaddr);
+ memcpy(&cp.addr, addr, sizeof(cp.addr));
cp.pin_len = len;
memcpy(cp.pin_code, pin, len);
printf("hci%u PIN Negative Reply successful\n", id);
}
-static int mgmt_pin_neg_reply(int mgmt_sk, uint16_t index, bdaddr_t *bdaddr)
+static int mgmt_pin_neg_reply(int mgmt_sk, uint16_t index,
+ struct mgmt_addr_info *addr)
{
struct mgmt_cp_pin_code_neg_reply cp;
memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, bdaddr);
+ memcpy(&cp.addr, addr, sizeof(cp.addr));
return mgmt_send_cmd(mgmt_sk, MGMT_OP_PIN_CODE_NEG_REPLY, index,
&cp, sizeof(cp), pin_neg_rsp, NULL);
if (monitor) {
char addr[18];
- ba2str(&ev->bdaddr, addr);
+ ba2str(&ev->addr.bdaddr, addr);
printf("hci%u %s request PIN\n", index, addr);
}
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);
+ return mgmt_pin_neg_reply(mgmt_sk, index, &ev->addr);
pin_len = strlen(pin);
if (pin[pin_len - 1] == '\n') {
pin_len--;
}
- return mgmt_pin_reply(mgmt_sk, index, &ev->bdaddr, pin, pin_len);
+ return mgmt_pin_reply(mgmt_sk, index, &ev->addr, pin, pin_len);
}
static void confirm_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
struct mgmt_cp_user_confirm_reply cp;
memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, bdaddr);
+ bacpy(&cp.addr.bdaddr, bdaddr);
return mgmt_send_cmd(mgmt_sk, MGMT_OP_USER_CONFIRM_REPLY, index,
&cp, sizeof(cp), confirm_rsp, NULL);
struct mgmt_cp_user_confirm_reply cp;
memset(&cp, 0, sizeof(cp));
- bacpy(&cp.bdaddr, bdaddr);
+ bacpy(&cp.addr.bdaddr, bdaddr);
return mgmt_send_cmd(mgmt_sk, MGMT_OP_USER_CONFIRM_NEG_REPLY, index,
&cp, sizeof(cp), confirm_neg_rsp, NULL);
return -EINVAL;
}
- ba2str(&ev->bdaddr, addr);
+ ba2str(&ev->addr.bdaddr, addr);
val = bt_get_le32(&ev->value);
if (monitor)
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);
+ return mgmt_confirm_neg_reply(mgmt_sk, index, &ev->addr.bdaddr);
rsp_len = strlen(rsp);
if (rsp[rsp_len - 1] == '\n') {
}
if (rsp[0] == 'y' || rsp[0] == 'Y')
- return mgmt_confirm_reply(mgmt_sk, index, &ev->bdaddr);
+ return mgmt_confirm_reply(mgmt_sk, index, &ev->addr.bdaddr);
else
- return mgmt_confirm_neg_reply(mgmt_sk, index, &ev->bdaddr);
+ return mgmt_confirm_neg_reply(mgmt_sk, index, &ev->addr.bdaddr);
}
static int mgmt_handle_event(int mgmt_sk, uint16_t ev, uint16_t index,
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);
+ return mgmt_connected(mgmt_sk, index, data, len);
case MGMT_EV_DEVICE_DISCONNECTED:
- return mgmt_connected(mgmt_sk, index, false, data, len);
+ return mgmt_disconnected(mgmt_sk, index, data, len);
case MGMT_EV_CONNECT_FAILED:
return mgmt_conn_failed(mgmt_sk, index, data, len);
case MGMT_EV_AUTH_FAILED:
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:
monitor = true;
}
+static void version_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_version *rp = rsp;
+
+ if (status != 0) {
+ fprintf(stderr, "Reading mgmt version failed with status"
+ " 0x%02x (%s)\n", status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ fprintf(stderr, "Too small version reply (%u bytes)\n", len);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("MGMT Version %u, revision %u\n", rp->version,
+ bt_get_le16(&rp->revision));
+
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_version(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE,
+ NULL, 0, version_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send read_version cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void commands_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_commands *rp = rsp;
+ uint16_t num_commands, num_events, *opcode;
+ size_t expected_len;
+ int i;
+
+ if (status != 0) {
+ fprintf(stderr, "Reading supported commands failed with status"
+ " 0x%02x (%s)\n", status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ fprintf(stderr, "Too small commands reply (%u bytes)\n", len);
+ exit(EXIT_FAILURE);
+ }
+
+ num_commands = bt_get_le16(&rp->num_commands);
+ num_events = bt_get_le16(&rp->num_events);
+
+ expected_len = sizeof(*rp) + num_commands * sizeof(uint16_t) +
+ num_events * sizeof(uint16_t);
+
+ if (len < expected_len) {
+ fprintf(stderr, "Too small commands reply (%u != %zu)\n",
+ len, expected_len);
+ exit(EXIT_FAILURE);
+ }
+
+ opcode = rp->opcodes;
+
+ printf("%u commands:\n", num_commands);
+ for (i = 0; i < num_commands; i++) {
+ uint16_t op = bt_get_le16(opcode++);
+ printf("\t%s (0x%04x)\n", mgmt_opstr(op), op);
+ }
+
+ printf("%u events:\n", num_events);
+ for (i = 0; i < num_events; i++) {
+ uint16_t ev = bt_get_le16(opcode++);
+ printf("\t%s (0x%04x)\n", mgmt_evstr(ev), ev);
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_commands(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_READ_COMMANDS, MGMT_INDEX_NONE,
+ NULL, 0, commands_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send read_commands cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
static void info_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
void *rsp, uint16_t len, void *user_data)
{
cmd_setting(mgmt_sk, index, MGMT_OP_SET_PAIRABLE, argc, argv);
}
+static void cmd_linksec(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ cmd_setting(mgmt_sk, index, MGMT_OP_SET_LINK_SECURITY, argc, argv);
+}
+
+static void cmd_ssp(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ cmd_setting(mgmt_sk, index, MGMT_OP_SET_SSP, argc, argv);
+}
+
+static void cmd_hs(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ cmd_setting(mgmt_sk, index, MGMT_OP_SET_HS, argc, argv);
+}
+
+static void cmd_le(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ cmd_setting(mgmt_sk, index, MGMT_OP_SET_LE, 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));
+ struct mgmt_ev_class_of_dev_changed *rp = rsp;
+
+ if (len == 0 && status != 0) {
+ fprintf(stderr, "%s failed, status 0x%02x (%s)\n",
+ mgmt_opstr(op), status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ fprintf(stderr, "Unexpected %s len %u\n", mgmt_opstr(op), len);
exit(EXIT_FAILURE);
}
- printf("hci%u class changed\n", id);
+ printf("%s succeeded. Class 0x%02x%02x%02x\n", mgmt_opstr(op),
+ rp->class_of_dev[2], rp->class_of_dev[1], rp->class_of_dev[0]);
+
exit(EXIT_SUCCESS);
}
struct mgmt_rp_disconnect *rp = rsp;
char addr[18];
- if (status != 0) {
+ if (len == 0 && status != 0) {
fprintf(stderr, "Disconnect failed with status 0x%02x (%s)\n",
status, mgmt_errstr(status));
exit(EXIT_FAILURE);
exit(EXIT_FAILURE);
}
- ba2str(&rp->bdaddr, addr);
+ ba2str(&rp->addr.bdaddr, addr);
- if (rp->status == 0) {
+ if (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));
+ addr, status, mgmt_errstr(status));
exit(EXIT_FAILURE);
}
}
exit(EXIT_FAILURE);
}
- str2ba(argv[1], &cp.bdaddr);
+ str2ba(argv[1], &cp.addr.bdaddr);
if (index == MGMT_INDEX_NONE)
index = 0;
discovery = true;
}
+static void find_usage(void)
+{
+ printf("Usage: btmgmt find [-l|-b]>\n");
+}
+
+static struct option find_options[] = {
+ { "help", 0, 0, 'h' },
+ { "le-only", 1, 0, 'l' },
+ { "bredr-only", 1, 0, 'b' },
+ { 0, 0, 0, 0 }
+};
+
static void cmd_find(int mgmt_sk, uint16_t index, int argc, char **argv)
{
struct mgmt_cp_start_discovery cp;
uint8_t type;
+ int opt;
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);
+ hci_set_bit(BDADDR_BREDR, &type);
+ hci_set_bit(BDADDR_LE_PUBLIC, &type);
+ hci_set_bit(BDADDR_LE_RANDOM, &type);
+
+ while ((opt = getopt_long(argc, argv, "+lbh", find_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'l':
+ hci_clear_bit(BDADDR_BREDR, &type);
+ hci_set_bit(BDADDR_LE_PUBLIC, &type);
+ hci_set_bit(BDADDR_LE_RANDOM, &type);
+ break;
+ case 'b':
+ hci_set_bit(BDADDR_BREDR, &type);
+ hci_clear_bit(BDADDR_LE_PUBLIC, &type);
+ hci_clear_bit(BDADDR_LE_RANDOM, &type);
+ break;
+ case 'h':
+ default:
+ find_usage();
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
memset(&cp, 0, sizeof(cp));
cp.type = type;
struct mgmt_cp_set_local_name cp;
if (argc < 2) {
- printf("Usage: btmgmt %s <name>\n", argv[0]);
+ printf("Usage: btmgmt %s <name> [shortname]\n", argv[0]);
exit(EXIT_FAILURE);
}
memset(&cp, 0, sizeof(cp));
strncpy((char *) cp.name, argv[1], HCI_MAX_NAME_LENGTH);
+ if (argc > 2)
+ strncpy((char *) cp.short_name, argv[2],
+ MGMT_MAX_SHORT_NAME_LENGTH);
if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_LOCAL_NAME, index,
&cp, sizeof(cp), name_rsp, NULL) < 0) {
struct mgmt_rp_pair_device *rp = rsp;
char addr[18];
- if (status != 0) {
+ if (len == 0 && status != 0) {
fprintf(stderr, "Pairing failed with status 0x%02x (%s)\n",
status, mgmt_errstr(status));
exit(EXIT_FAILURE);
ba2str(&rp->addr.bdaddr, addr);
- if (rp->status != 0) {
+ if (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));
+ addr, typestr(rp->addr.type), status,
+ mgmt_errstr(status));
exit(EXIT_FAILURE);
}
{
struct mgmt_cp_pair_device cp;
uint8_t cap = 0x01;
- uint8_t type = MGMT_ADDR_BREDR;
+ uint8_t type = BDADDR_BREDR;
int opt;
while ((opt = getopt_long(argc, argv, "+c:t:h", pair_options,
}
}
-static void remove_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+static void unpair_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;
+ struct mgmt_rp_unpair_device *rp = rsp;
char addr[18];
- if (status != 0) {
- fprintf(stderr, "Remove keys failed with status 0x%02x (%s)\n",
+ if (len == 0 && status != 0) {
+ fprintf(stderr, "Unpair device failed. 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);
+ fprintf(stderr, "Unexpected unpair_device_rsp len %u\n", len);
exit(EXIT_FAILURE);
}
- ba2str(&rp->bdaddr, addr);
+ ba2str(&rp->addr.bdaddr, addr);
- if (rp->status != 0) {
+ if (status != 0) {
fprintf(stderr,
- "Removing keys for %s failed. status 0x%02x (%s)\n",
- addr, rp->status, mgmt_errstr(rp->status));
+ "Unpairing %s failed. status 0x%02x (%s)\n",
+ addr, status, mgmt_errstr(status));
exit(EXIT_FAILURE);
}
- printf("Removed keys for %s\n", addr);
+ printf("%s unpaired\n", addr);
exit(EXIT_SUCCESS);
}
-static void cmd_remove(int mgmt_sk, uint16_t index, int argc, char **argv)
+static void cmd_unpair(int mgmt_sk, uint16_t index, int argc, char **argv)
{
- struct mgmt_cp_remove_keys cp;
+ struct mgmt_cp_unpair_device cp;
if (argc < 2) {
printf("Usage: btmgmt %s <remote address>\n", argv[0]);
index = 0;
memset(&cp, 0, sizeof(cp));
- str2ba(argv[1], &cp.bdaddr);
+ str2ba(argv[1], &cp.addr.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");
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_UNPAIR_DEVICE, index, &cp,
+ sizeof(cp), unpair_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send unpair_device cmd\n");
exit(EXIT_FAILURE);
}
}
}
}
+static void block_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ struct mgmt_addr_info *rp = rsp;
+ char addr[18];
+
+ if (len == 0 && status != 0) {
+ fprintf(stderr, "%s failed, status 0x%02x (%s)\n",
+ mgmt_opstr(op), status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ fprintf(stderr, "Unexpected %s len %u\n", mgmt_opstr(op), len);
+ exit(EXIT_FAILURE);
+ }
+
+ ba2str(&rp->bdaddr, addr);
+
+ if (status != 0) {
+ fprintf(stderr, "%s %s (%s) failed. status 0x%02x (%s)\n",
+ mgmt_opstr(op), addr, typestr(rp->type),
+ status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("%s %s succeeded\n", mgmt_opstr(op), addr);
+
+ exit(EXIT_SUCCESS);
+}
+
+static void block_usage(void)
+{
+ printf("Usage: btmgmt block [-t type] <remote address>\n");
+}
+
+static struct option block_options[] = {
+ { "help", 0, 0, 'h' },
+ { "type", 1, 0, 't' },
+ { 0, 0, 0, 0 }
+};
+
+static void cmd_block(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_block_device cp;
+ uint8_t type = BDADDR_BREDR;
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "+t:h", block_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 't':
+ type = strtol(optarg, NULL, 0);
+ break;
+ case 'h':
+ default:
+ block_usage();
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ block_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;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_BLOCK_DEVICE, index,
+ &cp, sizeof(cp), block_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send block_device cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void unblock_usage(void)
+{
+ printf("Usage: btmgmt unblock [-t type] <remote address>\n");
+}
+
+static void cmd_unblock(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_unblock_device cp;
+ uint8_t type = BDADDR_BREDR;
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "+t:h", block_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 't':
+ type = strtol(optarg, NULL, 0);
+ break;
+ case 'h':
+ default:
+ unblock_usage();
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ unblock_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;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_UNBLOCK_DEVICE, index,
+ &cp, sizeof(cp), block_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send unblock_device cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void uuid_to_uuid128(uuid_t *uuid128, const uuid_t *uuid)
+{
+ if (uuid->type == SDP_UUID16)
+ sdp_uuid16_to_uuid128(uuid128, uuid);
+ else if (uuid->type == SDP_UUID32)
+ sdp_uuid32_to_uuid128(uuid128, uuid);
+ else
+ memcpy(uuid128, uuid, sizeof(*uuid));
+}
+
+static void cmd_add_uuid(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_add_uuid cp;
+ uint128_t uint128;
+ uuid_t uuid, uuid128;
+
+ if (argc < 3) {
+ printf("UUID and service hint needed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (bt_string2uuid(&uuid, argv[1]) < 0) {
+ printf("Invalid UUID: %s\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&cp, 0, sizeof(cp));
+
+ uuid_to_uuid128(&uuid128, &uuid);
+ ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+ htob128(&uint128, (uint128_t *) cp.uuid);
+
+ cp.svc_hint = atoi(argv[2]);
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_ADD_UUID, index,
+ &cp, sizeof(cp), class_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send add_uuid cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_remove_uuid(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_remove_uuid cp;
+ uint128_t uint128;
+ uuid_t uuid, uuid128;
+
+ if (argc < 2) {
+ printf("UUID needed\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (bt_string2uuid(&uuid, argv[1]) < 0) {
+ printf("Invalid UUID: %s\n", argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&cp, 0, sizeof(cp));
+
+ uuid_to_uuid128(&uuid128, &uuid);
+ ntoh128((uint128_t *) uuid128.value.uuid128.data, &uint128);
+ htob128(&uint128, (uint128_t *) cp.uuid);
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_REMOVE_UUID, index,
+ &cp, sizeof(cp), class_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send remove_uuid cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_clr_uuids(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ char *uuid_any = "00000000-0000-0000-0000-000000000000";
+ char *rm_argv[] = { "rm-uuid", uuid_any, NULL };
+
+ cmd_remove_uuid(mgmt_sk, index, 2, rm_argv);
+}
+
+static void did_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, "Set Device ID failed with status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Device ID successfully set\n");
+
+ exit(EXIT_SUCCESS);
+}
+
+static void did_usage(void)
+{
+ printf("Usage: btmgmt did <source>:<vendor>:<product>:<version>\n");
+ printf(" possible source values: bluetooth, usb\n");
+}
+
+static void cmd_did(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_set_device_id cp;
+ uint16_t vendor, product, version , source;
+ int result;
+
+ if (argc < 2) {
+ did_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ result = sscanf(argv[1], "bluetooth:%4hx:%4hx:%4hx", &vendor, &product,
+ &version);
+ if (result == 3) {
+ source = 0x0001;
+ goto done;
+ }
+
+ result = sscanf(argv[1], "usb:%4hx:%4hx:%4hx", &vendor, &product,
+ &version);
+ if (result == 3) {
+ source = 0x0002;
+ goto done;
+ }
+
+ did_usage();
+ exit(EXIT_FAILURE);
+
+done:
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp.source = htobs(source);
+ cp.vendor = htobs(vendor);
+ cp.product = htobs(product);
+ cp.version = htobs(version);
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_DEVICE_ID, index,
+ &cp, sizeof(cp), did_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send set_dev_class 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" },
+ { "version", cmd_version, "Get the MGMT Version" },
+ { "commands", cmd_commands, "List supported commands" },
{ "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" },
+ { "linksec", cmd_linksec, "Toggle link level security" },
+ { "ssp", cmd_ssp, "Toggle SSP mode" },
+ { "hs", cmd_hs, "Toggle HS Support" },
+ { "le", cmd_le, "Toggle LE Support" },
{ "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)" },
+ { "unpair", cmd_unpair, "Unpair device" },
{ "keys", cmd_keys, "Load Keys" },
+ { "block", cmd_block, "Block Device" },
+ { "unblock", cmd_unblock, "Unblock Device" },
+ { "add-uuid", cmd_add_uuid, "Add UUID" },
+ { "rm-uuid", cmd_add_uuid, "Remove UUID" },
+ { "clr-uuids", cmd_clr_uuids, "Clear UUIDs", },
+ { "did", cmd_did, "Set Device ID", },
{ NULL, NULL, 0 }
};
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+
+#define BT_H4_CMD_PKT 0x01
+#define BT_H4_ACL_PKT 0x02
+#define BT_H4_SCO_PKT 0x03
+#define BT_H4_EVT_PKT 0x04
+
+struct bt_hci_cmd_hdr {
+ uint16_t opcode;
+ uint8_t plen;
+} __attribute__ ((packed));
+
+struct bt_hci_evt_hdr {
+ uint8_t evt;
+ uint8_t plen;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_NOP 0x0000
+
+#define BT_HCI_CMD_INQUIRY 0x0401
+struct bt_hci_cmd_inquiry {
+ uint8_t lap[3];
+ uint8_t length;
+ uint8_t num_rsp;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_INQUIRY_CANCEL 0x0402
+
+#define BT_HCI_CMD_CREATE_CONN 0x0405
+struct bt_hci_cmd_create_conn {
+ uint8_t bdaddr[6];
+ uint16_t pkt_type;
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_mode;
+ uint16_t clock_offset;
+ uint8_t role_switch;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_DISCONNECT 0x0406
+struct bt_hci_cmd_disconnect {
+ uint16_t handle;
+ uint8_t reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ADD_SCO_CONN 0x0407
+struct bt_hci_cmd_add_sco_conn {
+ uint16_t handle;
+ uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_CREATE_CONN_CANCEL 0x0408
+struct bt_hci_cmd_create_conn_cancel {
+ uint8_t bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ACCEPT_CONN_REQUEST 0x0409
+struct bt_hci_cmd_accept_conn_request {
+ uint8_t bdaddr[6];
+ uint8_t role;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REJECT_CONN_REQUEST 0x040a
+struct bt_hci_cmd_reject_conn_request {
+ uint8_t bdaddr[6];
+ uint8_t reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_CHANGE_CONN_PKT_TYPE 0x040f
+struct bt_hci_cmd_change_conn_pkt_type {
+ uint16_t handle;
+ uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REMOTE_NAME_REQUEST 0x0419
+struct bt_hci_cmd_remote_name_request {
+ uint8_t bdaddr[6];
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_mode;
+ uint16_t clock_offset;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_REMOTE_NAME_REQUEST_CANCEL 0x041a
+struct bt_hci_cmd_remote_name_request_cancel {
+ uint8_t bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_REMOTE_FEATURES 0x041b
+struct bt_hci_cmd_read_remote_features {
+ uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_REMOTE_EXT_FEATURES 0x041c
+struct bt_hci_cmd_read_remote_ext_features {
+ uint16_t handle;
+ uint8_t page;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_REMOTE_VERSION 0x041d
+struct bt_hci_cmd_read_remote_version {
+ uint16_t handle;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_DEFAULT_LINK_POLICY 0x080e
+struct bt_hci_rsp_read_default_link_policy {
+ uint8_t status;
+ uint16_t policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_DEFAULT_LINK_POLICY 0x080f
+struct bt_hci_cmd_write_default_link_policy {
+ uint16_t policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SET_EVENT_MASK 0x0c01
+struct bt_hci_cmd_set_event_mask {
+ uint8_t mask[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_RESET 0x0c03
+
+#define BT_HCI_CMD_SET_EVENT_FILTER 0x0c05
+struct bt_hci_cmd_set_event_filter {
+ uint8_t type;
+ uint8_t cond_type;
+ uint8_t cond[0];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_STORED_LINK_KEY 0x0c0d
+struct bt_hci_cmd_read_stored_link_key {
+ uint8_t bdaddr[6];
+ uint8_t read_all;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_stored_link_key {
+ uint8_t status;
+ uint16_t max_num_keys;
+ uint16_t num_keys;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_STORED_LINK_KEY 0x0c11
+struct bt_hci_cmd_write_stored_link_key {
+ uint8_t num_keys;
+} __attribute__ ((packed));
+struct bt_hci_rsp_write_stored_link_key {
+ uint8_t status;
+ uint8_t num_keys;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_DELETE_STORED_LINK_KEY 0x0c12
+struct bt_hci_cmd_delete_stored_link_key {
+ uint8_t bdaddr[6];
+ uint8_t delete_all;
+} __attribute__ ((packed));
+struct bt_hci_rsp_delete_stored_link_key {
+ uint8_t status;
+ uint16_t num_keys;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LOCAL_NAME 0x0c13
+struct bt_hci_cmd_write_local_name {
+ uint8_t name[248];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_NAME 0x0c14
+struct bt_hci_rsp_read_local_name {
+ uint8_t status;
+ uint8_t name[248];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_CONN_ACCEPT_TIMEOUT 0x0c15
+struct bt_hci_rsp_read_conn_accept_timeout {
+ uint8_t status;
+ uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_CONN_ACCEPT_TIMEOUT 0x0c16
+struct bt_hci_cmd_write_conn_accept_timeout {
+ uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_PAGE_TIMEOUT 0x0c17
+struct bt_hci_rsp_read_page_timeout {
+ uint8_t status;
+ uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_PAGE_TIMEOUT 0x0c18
+struct bt_hci_cmd_write_page_timeout {
+ uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_SCAN_ENABLE 0x0c19
+struct bt_hci_rsp_read_scan_enable {
+ uint8_t status;
+ uint8_t enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_SCAN_ENABLE 0x0c1a
+struct bt_hci_cmd_write_scan_enable {
+ uint8_t enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_AUTH_ENABLE 0x0c1f
+struct bt_hci_rsp_read_auth_enable {
+ uint8_t status;
+ uint8_t enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_AUTH_ENABLE 0x0c20
+struct bt_hci_cmd_write_auth_enable {
+ uint8_t enable;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_CLASS_OF_DEV 0x0c23
+struct bt_hci_rsp_read_class_of_dev {
+ uint8_t status;
+ uint8_t dev_class[3];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_CLASS_OF_DEV 0x0c24
+struct bt_hci_cmd_write_class_of_dev {
+ uint8_t dev_class[3];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_VOICE_SETTING 0x0c25
+struct bt_hci_rsp_read_voice_setting {
+ uint8_t status;
+ uint16_t setting;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_VOICE_SETTING 0x0c26
+struct bt_hci_cmd_write_voice_setting {
+ uint16_t setting;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_INQUIRY_MODE 0x0c44
+struct bt_hci_rsp_read_inquiry_mode {
+ uint8_t status;
+ uint8_t mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_INQUIRY_MODE 0x0c45
+struct bt_hci_cmd_write_inquiry_mode {
+ uint8_t mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_AFH_ASSESS_MODE 0x0c48
+struct bt_hci_rsp_read_afh_assess_mode {
+ uint8_t status;
+ uint8_t mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_AFH_ASSESS_MODE 0x0c49
+struct bt_hci_cmd_write_afh_assess_mode {
+ uint8_t mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_EXT_INQUIRY_RSP 0x0c51
+struct bt_hci_rsp_read_ext_inquiry_rsp {
+ uint8_t status;
+ uint8_t fec;
+ uint8_t data[240];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_EXT_INQUIRY_RSP 0x0c52
+struct bt_hci_cmd_write_ext_inquiry_rsp {
+ uint8_t fec;
+ uint8_t data[240];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_SIMPLE_PAIRING_MODE 0x0c55
+struct bt_hci_rsp_read_simple_pairing_mode {
+ uint8_t status;
+ uint8_t mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_SIMPLE_PAIRING_MODE 0x0c56
+struct bt_hci_cmd_write_simple_pairing_mode {
+ uint8_t mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_INQUIRY_RSP_TX_POWER 0x0c58
+struct bt_hci_rsp_read_inquiry_rsp_tx_power {
+ uint8_t status;
+ int8_t level;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LE_HOST_SUPPORTED 0x0c6c
+struct bt_hci_rsp_read_le_host_supported {
+ uint8_t status;
+ uint8_t supported;
+ uint8_t simultaneous;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LE_HOST_SUPPORTED 0x0c6d
+struct bt_hci_cmd_write_le_host_supported {
+ uint8_t supported;
+ uint8_t simultaneous;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_VERSION 0x1001
+struct bt_hci_rsp_read_local_version {
+ uint8_t status;
+ uint8_t hci_ver;
+ uint16_t hci_rev;
+ uint8_t lmp_ver;
+ uint16_t manufacturer;
+ uint16_t lmp_subver;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_COMMANDS 0x1002
+struct bt_hci_rsp_read_local_commands {
+ uint8_t status;
+ uint8_t commands[64];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_FEATURES 0x1003
+struct bt_hci_rsp_read_local_features {
+ uint8_t status;
+ uint8_t features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_LOCAL_EXT_FEATURES 0x1004
+struct bt_hci_cmd_read_local_ext_features {
+ uint8_t page;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_local_ext_features {
+ uint8_t status;
+ uint8_t page;
+ uint8_t max_page;
+ uint8_t features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_BUFFER_SIZE 0x1005
+struct bt_hci_rsp_read_buffer_size {
+ uint8_t status;
+ uint16_t acl_mtu;
+ uint8_t sco_mtu;
+ uint16_t acl_max_pkt;
+ uint16_t sco_max_pkt;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_COUNTRY_CODE 0x1007
+struct bt_hci_rsp_read_country_code {
+ uint8_t status;
+ uint8_t code;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_BD_ADDR 0x1009
+struct bt_hci_rsp_read_bd_addr {
+ uint8_t status;
+ uint8_t bdaddr[6];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_DATA_BLOCK_SIZE 0x100a
+struct bt_hci_rsp_read_data_block_size {
+ uint8_t status;
+ uint16_t max_acl_len;
+ uint16_t block_len;
+ uint16_t num_blocks;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_EVENT_MASK 0x2001
+struct bt_hci_cmd_le_set_event_mask {
+ uint8_t mask[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_BUFFER_SIZE 0x2002
+struct bt_hci_rsp_le_read_buffer_size {
+ uint8_t status;
+ uint16_t le_mtu;
+ uint8_t le_max_pkt;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_LOCAL_FEATURES 0x2003
+struct bt_hci_rsp_le_read_local_features {
+ uint8_t status;
+ uint8_t features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_SCAN_PARAMETERS 0x200b
+struct bt_hci_cmd_le_set_scan_parameters {
+ uint8_t type;
+ uint16_t interval;
+ uint16_t window;
+ uint8_t own_addr_type;
+ uint8_t filter_policy;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_SET_SCAN_ENABLE 0x200c
+struct bt_hci_cmd_le_set_scan_enable {
+ uint8_t enable;
+ uint8_t filter_dup;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_LE_READ_SUPPORTED_STATES 0x201c
+struct bt_hci_rsp_le_read_supported_states {
+ uint8_t status;
+ uint8_t states[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_INQUIRY_COMPLETE 0x01
+struct bt_hci_evt_inquiry_complete {
+ uint8_t status;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_INQUIRY_RESULT 0x02
+struct bt_hci_evt_inquiry_result {
+ uint8_t num_resp;
+ uint8_t bdaddr[6];
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t pscan_mode;
+ uint8_t dev_class[3];
+ uint8_t clock_offset;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CONN_COMPLETE 0x03
+struct bt_hci_evt_conn_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t bdaddr[6];
+ uint8_t link_type;
+ uint8_t encr_mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CONN_REQUEST 0x04
+struct bt_hci_evt_conn_request {
+ uint8_t bdaddr[6];
+ uint8_t dev_class[3];
+ uint8_t link_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_DISCONNECT_COMPLETE 0x05
+struct bt_hci_evt_disconnect_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t reason;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_NAME_REQUEST_COMPLETE 0x07
+struct bt_hci_evt_remote_name_req_complete {
+ uint8_t status;
+ uint8_t bdaddr[6];
+ uint8_t name[248];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_FEATURES_COMPLETE 0x0b
+struct bt_hci_evt_remote_features_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_VERSION_COMPLETE 0x0c
+struct bt_hci_evt_remote_version_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t lmp_ver;
+ uint16_t manufacturer;
+ uint16_t lmp_subver;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CMD_COMPLETE 0x0e
+struct bt_hci_evt_cmd_complete {
+ uint8_t ncmd;
+ uint16_t opcode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CMD_STATUS 0x0f
+struct bt_hci_evt_cmd_status {
+ uint8_t status;
+ uint8_t ncmd;
+ uint16_t opcode;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_NUM_COMPLETED_PACKETS 0x13
+struct bt_hci_evt_num_completed_packets {
+ uint8_t num_handles;
+ uint16_t handle;
+ uint16_t count;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_CONN_PKT_TYPE_CHANGED 0x1d
+struct bt_hci_evt_conn_pkt_type_changed {
+ uint8_t status;
+ uint16_t handle;
+ uint16_t pkt_type;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI 0x22
+struct bt_hci_evt_inquiry_result_with_rssi {
+ uint8_t num_resp;
+ uint8_t bdaddr[6];
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t dev_class[3];
+ uint16_t clock_offset;
+ int8_t rssi;
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_REMOTE_EXT_FEATURES_COMPLETE 0x23
+struct bt_hci_evt_remote_ext_features_complete {
+ uint8_t status;
+ uint16_t handle;
+ uint8_t page;
+ uint8_t max_page;
+ uint8_t features[8];
+} __attribute__ ((packed));
+
+#define BT_HCI_EVT_EXT_INQUIRY_RESULT 0x2f
+struct bt_hci_evt_ext_inquiry_result {
+ uint8_t num_resp;
+ uint8_t bdaddr[6];
+ uint8_t pscan_rep_mode;
+ uint8_t pscan_period_mode;
+ uint8_t dev_class[3];
+ uint16_t clock_offset;
+ int8_t rssi;
+ uint8_t data[240];
+} __attribute__ ((packed));
+
+#define BT_HCI_ERR_SUCCESS 0x00
+#define BT_HCI_ERR_UNKNOWN_COMMAND 0x01
+#define BT_HCI_ERR_UNKNOWN_CONN_ID 0x02
+#define BT_HCI_ERR_HARDWARE_FAILURE 0x03
+#define BT_HCI_ERR_PAGE_TIMEOUT 0x04
+#define BT_HCI_ERR_INVALID_PARAMETERS 0x12
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#include "btsnoop.h"
+
+static inline uint64_t ntoh64(uint64_t n)
+{
+ uint64_t h;
+ uint64_t tmp = ntohl(n & 0x00000000ffffffff);
+
+ h = ntohl(n >> 32);
+ h |= tmp << 32;
+
+ return h;
+}
+
+#define hton64(x) ntoh64(x)
+
+struct btsnoop_hdr {
+ uint8_t id[8]; /* Identification Pattern */
+ uint32_t version; /* Version Number = 1 */
+ uint32_t type; /* Datalink Type */
+} __attribute__ ((packed));
+#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
+
+struct btsnoop_pkt {
+ uint32_t size; /* Original Length */
+ uint32_t len; /* Included Length */
+ uint32_t flags; /* Packet Flags */
+ uint32_t drops; /* Cumulative Drops */
+ uint64_t ts; /* Timestamp microseconds */
+ uint8_t data[0]; /* Packet Data */
+} __attribute__ ((packed));
+#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
+
+static const uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e,
+ 0x6f, 0x6f, 0x70, 0x00 };
+
+static const uint32_t btsnoop_version = 1;
+static const uint32_t btsnoop_type = 1001;
+
+static int btsnoop_fd = -1;
+static uint16_t btsnoop_index = 0xffff;
+
+void btsnoop_open(const char *path)
+{
+ if (btsnoop_fd >= 0)
+ return;
+
+ btsnoop_fd = open(path, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+}
+
+void btsnoop_write(struct timeval *tv, uint16_t index, uint32_t flags,
+ const void *data, uint16_t size)
+{
+ struct btsnoop_hdr hdr;
+ struct btsnoop_pkt pkt;
+ uint64_t ts;
+ ssize_t written;
+
+ if (!tv)
+ return;
+
+ if (btsnoop_fd < 0)
+ return;
+
+ if (btsnoop_index == 0xffff) {
+ memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+ hdr.version = htonl(btsnoop_version);
+ hdr.type = htonl(btsnoop_type);
+
+ written = write(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE);
+ if (written < 0)
+ return;
+
+ btsnoop_index = index;
+ }
+
+ if (index != btsnoop_index)
+ return;
+
+ ts = (tv->tv_sec - 946684800ll) * 1000000ll + tv->tv_usec;
+
+ pkt.size = htonl(size);
+ pkt.len = htonl(size);
+ pkt.flags = htonl(flags);
+ pkt.drops = htonl(0);
+ pkt.ts = hton64(ts + 0x00E03AB44A676000ll);
+
+ written = write(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
+ if (written < 0)
+ return;
+
+ if (data && size > 0) {
+ written = write(btsnoop_fd, data, size);
+ if (written < 0)
+ return;
+ }
+}
+
+void btsnoop_close(void)
+{
+ if (btsnoop_fd < 0)
+ return;
+
+ close(btsnoop_fd);
+ btsnoop_fd = -1;
+
+ btsnoop_index = 0xffff;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <sys/time.h>
+
+void btsnoop_open(const char *path);
+void btsnoop_write(struct timeval *tv, uint16_t index, uint32_t flags,
+ const void *data, uint16_t size);
+void btsnoop_close(void);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/mgmt.h>
+
+#include "mainloop.h"
+#include "packet.h"
+#include "control.h"
+
+struct control_data {
+ uint16_t channel;
+ int fd;
+};
+
+static void free_data(void *user_data)
+{
+ struct control_data *data = user_data;
+
+ close(data->fd);
+
+ free(data);
+}
+
+static void mgmt_index_added(uint16_t len, const void *buf)
+{
+ printf("@ Index Added\n");
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_index_removed(uint16_t len, const void *buf)
+{
+ printf("@ Index Removed\n");
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_controller_error(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_controller_error *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Controller Error control\n");
+ return;
+ }
+
+ printf("@ Controller Error: 0x%2.2x\n", ev->error_code);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+#ifndef NELEM
+#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+static const char *settings_str[] = {
+ "powered", "connectable", "fast-connectable", "discoverable",
+ "pairable", "link-security", "ssp", "br/edr", "hs", "le"
+};
+
+static void mgmt_new_settings(uint16_t len, const void *buf)
+{
+ uint32_t settings;
+ unsigned int i;
+
+ if (len < 4) {
+ printf("* Malformed New Settings control\n");
+ return;
+ }
+
+ settings = bt_get_le32(buf);
+
+ printf("@ New Settings: 0x%4.4x\n", settings);
+
+ printf("%-12c", ' ');
+ for (i = 0; i < NELEM(settings_str); i++) {
+ if (settings & (1 << i))
+ printf("%s ", settings_str[i]);
+ }
+ printf("\n");
+
+ buf += 4;
+ len -= 4;
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_class_of_dev_changed(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_class_of_dev_changed *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Class of Device Changed control\n");
+ return;
+ }
+
+ printf("@ Class of Device Changed: 0x%2.2x%2.2x%2.2x\n",
+ ev->class_of_dev[2],
+ ev->class_of_dev[1],
+ ev->class_of_dev[0]);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_local_name_changed(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_local_name_changed *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Local Name Changed control\n");
+ return;
+ }
+
+ printf("@ Local Name Changed: %s (%s)\n", ev->name, ev->short_name);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_new_link_key(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_new_link_key *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed New Link Key control\n");
+ return;
+ }
+
+ ba2str(&ev->key.addr.bdaddr, str);
+
+ printf("@ New Link Key: %s (%d)\n", str, ev->key.addr.type);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_new_long_term_key(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_new_long_term_key *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed New Long Term Key control\n");
+ return;
+ }
+
+ ba2str(&ev->key.addr.bdaddr, str);
+
+ printf("@ New Long Term Key: %s (%d)\n", str, ev->key.addr.type);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_device_connected(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_device_connected *ev = buf;
+ uint32_t flags;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Device Connected control\n");
+ return;
+ }
+
+ flags = btohs(ev->flags);
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ Device Connected: %s (%d) flags 0x%4.4x\n",
+ str, ev->addr.type, flags);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_device_disconnected(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_device_disconnected *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Device Disconnected control\n");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ Device Disconnected: %s (%d)\n", str, ev->addr.type);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_connect_failed(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_connect_failed *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Connect Failed control\n");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ Connect Failed: %s (%d) status 0x%2.2x\n",
+ str, ev->addr.type, ev->status);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_pin_code_request(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_pin_code_request *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed PIN Code Request control\n");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ PIN Code Request: %s (%d) secure 0x%2.2x\n",
+ str, ev->addr.type, ev->secure);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_user_confirm_request(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_user_confirm_request *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed User Confirmation Request control\n");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ User Confirmation Request: %s (%d) hint %d value %d\n",
+ str, ev->addr.type, ev->confirm_hint, ev->value);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_user_passkey_request(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_user_passkey_request *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed User Passkey Request control\n");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ PIN User Passkey Request: %s (%d)\n", str, ev->addr.type);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_auth_failed(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_auth_failed *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Authentication Failed control\n");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ Authentication Failed: %s (%d) status 0x%2.2x\n",
+ str, ev->addr.type, ev->status);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_device_found(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_device_found *ev = buf;
+ uint32_t flags;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Device Found control\n");
+ return;
+ }
+
+ flags = btohs(ev->flags);
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ Device Found: %s (%d) rssi %d flags 0x%4.4x\n",
+ str, ev->addr.type, ev->rssi, flags);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_discovering(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_discovering *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Discovering control\n");
+ return;
+ }
+
+ printf("@ Discovering: 0x%2.2x (%d)\n", ev->discovering, ev->type);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_device_blocked(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_device_blocked *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Device Blocked control\n");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ Device Blocked: %s (%d)\n", str, ev->addr.type);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_device_unblocked(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_device_unblocked *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Device Unblocked control\n");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ Device Unblocked: %s (%d)\n", str, ev->addr.type);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_device_unpaired(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_device_unpaired *ev = buf;
+ char str[18];
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Device Unpaired control\n");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("@ Device Unpaired: %s (%d)\n", str, ev->addr.type);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+void control_message(uint16_t opcode, const void *data, uint16_t size)
+{
+ switch (opcode) {
+ case MGMT_EV_INDEX_ADDED:
+ mgmt_index_added(size, data);
+ break;
+ case MGMT_EV_INDEX_REMOVED:
+ mgmt_index_removed(size, data);
+ break;
+ case MGMT_EV_CONTROLLER_ERROR:
+ mgmt_controller_error(size, data);
+ break;
+ case MGMT_EV_NEW_SETTINGS:
+ mgmt_new_settings(size, data);
+ break;
+ case MGMT_EV_CLASS_OF_DEV_CHANGED:
+ mgmt_class_of_dev_changed(size, data);
+ break;
+ case MGMT_EV_LOCAL_NAME_CHANGED:
+ mgmt_local_name_changed(size, data);
+ break;
+ case MGMT_EV_NEW_LINK_KEY:
+ mgmt_new_link_key(size, data);
+ break;
+ case MGMT_EV_NEW_LONG_TERM_KEY:
+ mgmt_new_long_term_key(size, data);
+ break;
+ case MGMT_EV_DEVICE_CONNECTED:
+ mgmt_device_connected(size, data);
+ break;
+ case MGMT_EV_DEVICE_DISCONNECTED:
+ mgmt_device_disconnected(size, data);
+ break;
+ case MGMT_EV_CONNECT_FAILED:
+ mgmt_connect_failed(size, data);
+ break;
+ case MGMT_EV_PIN_CODE_REQUEST:
+ mgmt_pin_code_request(size, data);
+ break;
+ case MGMT_EV_USER_CONFIRM_REQUEST:
+ mgmt_user_confirm_request(size, data);
+ break;
+ case MGMT_EV_USER_PASSKEY_REQUEST:
+ mgmt_user_passkey_request(size, data);
+ break;
+ case MGMT_EV_AUTH_FAILED:
+ mgmt_auth_failed(size, data);
+ break;
+ case MGMT_EV_DEVICE_FOUND:
+ mgmt_device_found(size, data);
+ break;
+ case MGMT_EV_DISCOVERING:
+ mgmt_discovering(size, data);
+ break;
+ case MGMT_EV_DEVICE_BLOCKED:
+ mgmt_device_blocked(size, data);
+ break;
+ case MGMT_EV_DEVICE_UNBLOCKED:
+ mgmt_device_unblocked(size, data);
+ break;
+ case MGMT_EV_DEVICE_UNPAIRED:
+ mgmt_device_unpaired(size, data);
+ break;
+ default:
+ printf("* Unknown control (code %d len %d)\n", opcode, size);
+ packet_hexdump(data, size);
+ break;
+ }
+}
+
+static void data_callback(int fd, uint32_t events, void *user_data)
+{
+ struct control_data *data = user_data;
+ unsigned char buf[HCI_MAX_FRAME_SIZE];
+ unsigned char control[32];
+ struct mgmt_hdr hdr;
+ struct msghdr msg;
+ struct iovec iov[2];
+
+ if (events & (EPOLLERR | EPOLLHUP)) {
+ mainloop_remove_fd(fd);
+ return;
+ }
+
+ iov[0].iov_base = &hdr;
+ iov[0].iov_len = MGMT_HDR_SIZE;
+ iov[1].iov_base = buf;
+ iov[1].iov_len = sizeof(buf);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ while (1) {
+ struct cmsghdr *cmsg;
+ struct timeval *tv = NULL;
+ uint16_t opcode, index, pktlen;
+ ssize_t len;
+
+ len = recvmsg(fd, &msg, MSG_DONTWAIT);
+ if (len < 0)
+ break;
+
+ if (len < MGMT_HDR_SIZE)
+ break;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET)
+ continue;
+
+ if (cmsg->cmsg_type == SCM_TIMESTAMP)
+ tv = (struct timeval *) CMSG_DATA(cmsg);
+ }
+
+ opcode = btohs(hdr.opcode);
+ index = btohs(hdr.index);
+ pktlen = btohs(hdr.len);
+
+ switch (data->channel) {
+ case HCI_CHANNEL_CONTROL:
+ packet_control(tv, index, opcode, buf, pktlen);
+ break;
+ case HCI_CHANNEL_MONITOR:
+ packet_monitor(tv, index, opcode, buf, pktlen);
+ break;
+ }
+ }
+}
+
+static int open_socket(uint16_t channel)
+{
+ struct sockaddr_hci addr;
+ int fd, opt = 1;
+
+ fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (fd < 0) {
+ perror("Failed to open channel");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_family = AF_BLUETOOTH;
+ addr.hci_dev = HCI_DEV_NONE;
+ addr.hci_channel = channel;
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ if (errno == EINVAL) {
+ /* Fallback to hcidump support */
+ close(fd);
+ return -1;
+ }
+ perror("Failed to bind channel");
+ close(fd);
+ return -1;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) < 0) {
+ perror("Failed to enable timestamps");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int open_channel(uint16_t channel)
+{
+ struct control_data *data;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return -1;
+
+ memset(data, 0, sizeof(*data));
+ data->channel = channel;
+
+ data->fd = open_socket(channel);
+ if (data->fd < 0) {
+ free(data);
+ return -1;
+ }
+
+ mainloop_add_fd(data->fd, EPOLLIN, data_callback, data, free_data);
+
+ return 0;
+}
+
+int control_tracing(void)
+{
+ if (open_channel(HCI_CHANNEL_MONITOR) < 0)
+ return -1;
+
+ open_channel(HCI_CHANNEL_CONTROL);
+
+ return 0;
+}
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2004-2011 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
*
*/
-#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 <stdint.h>
+
+int control_tracing(void);
+
+void control_message(uint16_t opcode, const void *data, uint16_t size);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "mainloop.h"
+#include "packet.h"
+#include "hcidump.h"
+
+struct hcidump_data {
+ uint16_t index;
+ int fd;
+};
+
+static void free_data(void *user_data)
+{
+ struct hcidump_data *data = user_data;
+
+ close(data->fd);
+
+ free(data);
+}
+
+static int open_hci_dev(uint16_t index)
+{
+ struct sockaddr_hci addr;
+ struct hci_filter flt;
+ int fd, opt = 1;
+
+ fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (fd < 0) {
+ perror("Failed to open channel");
+ return -1;
+ }
+
+ /* Setup filter */
+ hci_filter_clear(&flt);
+ hci_filter_all_ptypes(&flt);
+ hci_filter_all_events(&flt);
+
+ if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+ perror("Failed to set HCI filter");
+ close(fd);
+ return -1;
+ }
+
+ if (setsockopt(fd, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
+ perror("Failed to enable HCI data direction info");
+ close(fd);
+ return -1;
+ }
+
+ if (setsockopt(fd, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
+ perror("Failed to enable HCI time stamps");
+ close(fd);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_family = AF_BLUETOOTH;
+ addr.hci_dev = index;
+ addr.hci_channel = HCI_CHANNEL_RAW;
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Failed to bind channel");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static void device_callback(int fd, uint32_t events, void *user_data)
+{
+ struct hcidump_data *data = user_data;
+ unsigned char buf[HCI_MAX_FRAME_SIZE];
+ unsigned char control[64];
+ struct msghdr msg;
+ struct iovec iov;
+
+ if (events & (EPOLLERR | EPOLLHUP)) {
+ mainloop_remove_fd(fd);
+ return;
+ }
+
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ while (1) {
+ struct cmsghdr *cmsg;
+ struct timeval *tv = NULL;
+ int *dir = NULL;
+ ssize_t len;
+
+ len = recvmsg(fd, &msg, MSG_DONTWAIT);
+ if (len < 0)
+ break;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_HCI)
+ continue;
+
+ switch (cmsg->cmsg_type) {
+ case HCI_DATA_DIR:
+ dir = (int *) CMSG_DATA(cmsg);
+ break;
+ case HCI_CMSG_TSTAMP:
+ tv = (struct timeval *) CMSG_DATA(cmsg);
+ break;
+ }
+ }
+
+ if (!dir || len < 1)
+ continue;
+
+ switch (buf[0]) {
+ case HCI_COMMAND_PKT:
+ packet_hci_command(tv, data->index, buf + 1, len - 1);
+ break;
+ case HCI_EVENT_PKT:
+ packet_hci_event(tv, data->index, buf + 1, len - 1);
+ break;
+ case HCI_ACLDATA_PKT:
+ packet_hci_acldata(tv, data->index, !!(*dir),
+ buf + 1, len - 1);
+ break;
+ case HCI_SCODATA_PKT:
+ packet_hci_scodata(tv, data->index, !!(*dir),
+ buf + 1, len - 1);
+ break;
+ }
+ }
+}
+
+static void open_device(uint16_t index)
+{
+ struct hcidump_data *data;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return;
+
+ memset(data, 0, sizeof(*data));
+ data->index = index;
+
+ data->fd = open_hci_dev(index);
+ if (data->fd < 0) {
+ free(data);
+ return;
+ }
+
+ mainloop_add_fd(data->fd, EPOLLIN, device_callback, data, free_data);
+}
+
+static void device_info(int fd, uint16_t index, uint8_t *type, uint8_t *bus,
+ bdaddr_t *bdaddr, char *name)
+{
+ struct hci_dev_info di;
+
+ memset(&di, 0, sizeof(di));
+ di.dev_id = index;
+
+ if (ioctl(fd, HCIGETDEVINFO, (void *) &di) < 0) {
+ perror("Failed to get device information");
+ return;
+ }
+
+ *type = di.type >> 4;
+ *bus = di.type & 0x0f;
+
+ bacpy(bdaddr, &di.bdaddr);
+ memcpy(name, di.name, 8);
+}
+
+static void device_list(int fd, int max_dev)
+{
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ int i;
+
+ dl = malloc(max_dev * sizeof(*dr) + sizeof(*dl));
+ if (!dl) {
+ perror("Failed to allocate device list memory");
+ return;
+ }
+
+ memset(dl, 0, max_dev * sizeof(*dr) + sizeof(*dl));
+ dl->dev_num = max_dev;
+
+ dr = dl->dev_req;
+
+ if (ioctl(fd, HCIGETDEVLIST, (void *) dl) < 0) {
+ perror("Failed to get device list");
+ goto done;
+ }
+
+ for (i = 0; i < dl->dev_num; i++, dr++) {
+ struct timeval tmp_tv, *tv = NULL;
+ uint8_t type = 0xff, bus = 0xff;
+ char str[18], name[8] = "";
+ bdaddr_t bdaddr;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ if (!gettimeofday(&tmp_tv, NULL))
+ tv = &tmp_tv;
+
+ device_info(fd, dr->dev_id, &type, &bus, &bdaddr, name);
+ ba2str(&bdaddr, str);
+ packet_new_index(tv, dr->dev_id, str, type, bus, name);
+ open_device(dr->dev_id);
+ }
+
+done:
+ free(dl);
+}
+
+static int open_stack_internal(void)
+{
+ struct sockaddr_hci addr;
+ struct hci_filter flt;
+ int fd, opt = 1;
+
+ fd = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
+ if (fd < 0) {
+ perror("Failed to open channel");
+ return -1;
+ }
+
+ /* Setup filter */
+ hci_filter_clear(&flt);
+ hci_filter_set_ptype(HCI_EVENT_PKT, &flt);
+ hci_filter_set_event(EVT_STACK_INTERNAL, &flt);
+
+ if (setsockopt(fd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+ perror("Failed to set HCI filter");
+ close(fd);
+ return -1;
+ }
+
+ if (setsockopt(fd, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
+ perror("Failed to enable HCI time stamps");
+ close(fd);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_family = AF_BLUETOOTH;
+ addr.hci_dev = HCI_DEV_NONE;
+ addr.hci_channel = HCI_CHANNEL_RAW;
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Failed to bind channel");
+ close(fd);
+ return -1;
+ }
+
+ device_list(fd, HCI_MAX_DEV);
+
+ return fd;
+}
+
+static void stack_internal_callback(int fd, uint32_t events, void *user_data)
+{
+ unsigned char buf[HCI_MAX_FRAME_SIZE];
+ unsigned char control[32];
+ struct msghdr msg;
+ struct iovec iov;
+ struct cmsghdr *cmsg;
+ ssize_t len;
+ hci_event_hdr *eh;
+ evt_stack_internal *si;
+ evt_si_device *sd;
+ struct timeval *tv = NULL;
+ uint8_t type = 0xff, bus = 0xff;
+ char str[18], name[8] = "";
+ bdaddr_t bdaddr;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ if (events & (EPOLLERR | EPOLLHUP)) {
+ mainloop_remove_fd(fd);
+ return;
+ }
+
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ len = recvmsg(fd, &msg, MSG_DONTWAIT);
+ if (len < 0)
+ return;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_HCI)
+ continue;
+
+ switch (cmsg->cmsg_type) {
+ case HCI_CMSG_TSTAMP:
+ tv = (struct timeval *) CMSG_DATA(cmsg);
+ break;
+ }
+ }
+
+ if (len < 1 + HCI_EVENT_HDR_SIZE + EVT_STACK_INTERNAL_SIZE +
+ EVT_SI_DEVICE_SIZE)
+ return;
+
+ if (buf[0] != HCI_EVENT_PKT)
+ return;
+
+ eh = (hci_event_hdr *) (buf + 1);
+ if (eh->evt != EVT_STACK_INTERNAL)
+ return;
+
+ si = (evt_stack_internal *) (buf + 1 + HCI_EVENT_HDR_SIZE);
+ if (si->type != EVT_SI_DEVICE)
+ return;
+
+ sd = (evt_si_device *) &si->data;
+
+ switch (sd->event) {
+ case HCI_DEV_REG:
+ device_info(fd, sd->dev_id, &type, &bus, &bdaddr, name);
+ ba2str(&bdaddr, str);
+ packet_new_index(tv, sd->dev_id, str, type, bus, name);
+ open_device(sd->dev_id);
+ break;
+ case HCI_DEV_UNREG:
+ ba2str(&bdaddr, str);
+ packet_del_index(tv, sd->dev_id, str);
+ break;
+ }
+}
+
+int hcidump_tracing(void)
+{
+ struct hcidump_data *data;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return -1;
+
+ memset(data, 0, sizeof(*data));
+ data->index = HCI_DEV_NONE;
+
+ data->fd = open_stack_internal();
+ if (data->fd < 0) {
+ free(data);
+ return -1;
+ }
+
+ mainloop_add_fd(data->fd, EPOLLIN, stack_internal_callback,
+ data, free_data);
+
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int hcidump_tracing(void);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include "mainloop.h"
+#include "packet.h"
+#include "control.h"
+#include "hcidump.h"
+#include "btsnoop.h"
+
+static void signal_callback(int signum, void *user_data)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ mainloop_quit();
+ break;
+ }
+}
+
+static void usage(void)
+{
+ printf("btmon - Bluetooth monitor\n"
+ "Usage:\n");
+ printf("\tbtmon [options]\n");
+ printf("options:\n"
+ "\t-b, --btsnoop <file> Save dump in btsnoop format\n"
+ "\t-h, --help Show help options\n");
+}
+
+static const struct option main_options[] = {
+ { "btsnoop", required_argument, NULL, 'b' },
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { }
+};
+
+int main(int argc, char *argv[])
+{
+ unsigned long filter_mask = 0;
+ sigset_t mask;
+
+ mainloop_init();
+
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "bvh", main_options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'b':
+ btsnoop_open(optarg);
+ break;
+ case 'v':
+ printf("%s\n", VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+ filter_mask |= PACKET_FILTER_SHOW_INDEX;
+ filter_mask |= PACKET_FILTER_SHOW_TIME;
+ filter_mask |= PACKET_FILTER_SHOW_ACL_DATA;
+
+ packet_set_filter(filter_mask);
+
+ printf("Bluetooth monitor ver %s\n", VERSION);
+
+ if (control_tracing() < 0) {
+ if (hcidump_tracing() < 0)
+ return EXIT_FAILURE;
+ }
+
+ return mainloop_run();
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2002-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 <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <sys/timerfd.h>
+#include <sys/epoll.h>
+
+#include "mainloop.h"
+
+#define MAX_EPOLL_EVENTS 10
+
+static int epoll_fd;
+static int epoll_terminate;
+
+struct mainloop_data {
+ int fd;
+ uint32_t events;
+ mainloop_event_func callback;
+ mainloop_destroy_func destroy;
+ void *user_data;
+};
+
+#define MAX_MAINLOOP_ENTRIES 128
+
+static struct mainloop_data *mainloop_list[MAX_MAINLOOP_ENTRIES];
+
+struct timeout_data {
+ int fd;
+ mainloop_timeout_func callback;
+ mainloop_destroy_func destroy;
+ void *user_data;
+};
+
+struct signal_data {
+ int fd;
+ sigset_t mask;
+ mainloop_signal_func callback;
+ mainloop_destroy_func destroy;
+ void *user_data;
+};
+
+static struct signal_data *signal_data;
+
+void mainloop_init(void)
+{
+ unsigned int i;
+
+ epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+
+ for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++)
+ mainloop_list[i] = NULL;
+
+ epoll_terminate = 0;
+}
+
+void mainloop_quit(void)
+{
+ epoll_terminate = 1;
+}
+
+static void signal_callback(int fd, uint32_t events, void *user_data)
+{
+ struct signal_data *data = user_data;
+ struct signalfd_siginfo si;
+ ssize_t result;
+
+ if (events & (EPOLLERR | EPOLLHUP)) {
+ mainloop_quit();
+ return;
+ }
+
+ result = read(fd, &si, sizeof(si));
+ if (result != sizeof(si))
+ return;
+
+ if (data->callback)
+ data->callback(si.ssi_signo, data->user_data);
+}
+
+int mainloop_run(void)
+{
+ unsigned int i;
+
+ if (signal_data) {
+ if (sigprocmask(SIG_BLOCK, &signal_data->mask, NULL) < 0)
+ return 1;
+
+ signal_data->fd = signalfd(-1, &signal_data->mask,
+ SFD_NONBLOCK | SFD_CLOEXEC);
+ if (signal_data->fd < 0)
+ return 1;
+
+ if (mainloop_add_fd(signal_data->fd, EPOLLIN,
+ signal_callback, signal_data, NULL) < 0) {
+ close(signal_data->fd);
+ return 1;
+ }
+ }
+
+ while (!epoll_terminate) {
+ struct epoll_event events[MAX_EPOLL_EVENTS];
+ int n, nfds;
+
+ nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, -1);
+ if (nfds < 0)
+ continue;
+
+ for (n = 0; n < nfds; n++) {
+ struct mainloop_data *data = events[n].data.ptr;
+
+ data->callback(data->fd, events[n].events,
+ data->user_data);
+ }
+ }
+
+ if (signal_data) {
+ mainloop_remove_fd(signal_data->fd);
+ close(signal_data->fd);
+
+ if (signal_data->destroy)
+ signal_data->destroy(signal_data->user_data);
+ }
+
+ for (i = 0; i < MAX_MAINLOOP_ENTRIES; i++) {
+ struct mainloop_data *data = mainloop_list[i];
+
+ mainloop_list[i] = NULL;
+
+ if (data) {
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
+
+ if (data->destroy)
+ data->destroy(data->user_data);
+
+ free(data);
+ }
+ }
+
+ close(epoll_fd);
+ epoll_fd = 0;
+
+ return 0;
+}
+
+int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
+ void *user_data, mainloop_destroy_func destroy)
+{
+ struct mainloop_data *data;
+ struct epoll_event ev;
+ int err;
+
+ if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1 || !callback)
+ return -EINVAL;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return -ENOMEM;
+
+ memset(data, 0, sizeof(*data));
+ data->fd = fd;
+ data->events = events;
+ data->callback = callback;
+ data->destroy = destroy;
+ data->user_data = user_data;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = events;
+ ev.data.ptr = data;
+
+ err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev);
+ if (err < 0) {
+ free(data);
+ return err;
+ }
+
+ mainloop_list[fd] = data;
+
+ return 0;
+}
+
+int mainloop_modify_fd(int fd, uint32_t events)
+{
+ struct mainloop_data *data;
+ struct epoll_event ev;
+ int err;
+
+ if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1)
+ return -EINVAL;
+
+ data = mainloop_list[fd];
+ if (!data)
+ return -ENXIO;
+
+ if (data->events == events)
+ return 0;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = events;
+ ev.data.ptr = data;
+
+ err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, data->fd, &ev);
+ if (err < 0)
+ return err;
+
+ data->events = events;
+
+ return 0;
+}
+
+int mainloop_remove_fd(int fd)
+{
+ struct mainloop_data *data;
+ int err;
+
+ if (fd < 0 || fd > MAX_MAINLOOP_ENTRIES - 1)
+ return -EINVAL;
+
+ data = mainloop_list[fd];
+ if (!data)
+ return -ENXIO;
+
+ mainloop_list[fd] = NULL;
+
+ err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, data->fd, NULL);
+
+ if (data->destroy)
+ data->destroy(data->user_data);
+
+ free(data);
+
+ return err;
+}
+
+static void timeout_destroy(void *user_data)
+{
+ struct timeout_data *data = user_data;
+
+ close(data->fd);
+ data->fd = -1;
+
+ if (data->destroy)
+ data->destroy(data->user_data);
+}
+
+static void timeout_callback(int fd, uint32_t events, void *user_data)
+{
+ struct timeout_data *data = user_data;
+ uint64_t expired;
+ ssize_t result;
+
+ if (events & (EPOLLERR | EPOLLHUP))
+ return;
+
+ result = read(data->fd, &expired, sizeof(expired));
+ if (result != sizeof(expired))
+ return;
+
+ if (data->callback)
+ data->callback(data->fd, data->user_data);
+}
+
+static inline int timeout_set(int fd, unsigned int seconds)
+{
+ struct itimerspec itimer;
+
+ memset(&itimer, 0, sizeof(itimer));
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_nsec = 0;
+ itimer.it_value.tv_sec = seconds;
+ itimer.it_value.tv_nsec = 0;
+
+ return timerfd_settime(fd, 0, &itimer, NULL);
+}
+
+int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback,
+ void *user_data, mainloop_destroy_func destroy)
+{
+ struct timeout_data *data;
+
+ if (!callback)
+ return -EINVAL;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return -ENOMEM;
+
+ memset(data, 0, sizeof(*data));
+ data->callback = callback;
+ data->destroy = destroy;
+ data->user_data = user_data;
+
+ data->fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+ if (data->fd < 0) {
+ free(data);
+ return -EIO;
+ }
+
+ if (seconds > 0) {
+ if (timeout_set(data->fd, seconds) < 0) {
+ close(data->fd);
+ free(data);
+ return -EIO;
+ }
+ }
+
+ if (mainloop_add_fd(data->fd, EPOLLIN | EPOLLONESHOT,
+ timeout_callback, data, timeout_destroy) < 0) {
+ close(data->fd);
+ free(data);
+ return -EIO;
+ }
+
+ return data->fd;
+}
+
+int mainloop_modify_timeout(int id, unsigned int seconds)
+{
+ if (seconds > 0) {
+ if (timeout_set(id, seconds) < 0)
+ return -EIO;
+ }
+
+ if (mainloop_modify_fd(id, EPOLLIN | EPOLLONESHOT) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+int mainloop_remove_timeout(int id)
+{
+ return mainloop_remove_fd(id);
+}
+
+int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback,
+ void *user_data, mainloop_destroy_func destroy)
+{
+ struct signal_data *data;
+
+ if (!mask || !callback)
+ return -EINVAL;
+
+ data = malloc(sizeof(*data));
+ if (!data)
+ return -ENOMEM;
+
+ memset(data, 0, sizeof(*data));
+ data->callback = callback;
+ data->destroy = destroy;
+ data->user_data = user_data;
+
+ data->fd = -1;
+ memcpy(&data->mask, mask, sizeof(sigset_t));
+
+ free(signal_data);
+ signal_data = data;
+
+ return 0;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2002-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
+ *
+ */
+
+#include <signal.h>
+#include <sys/epoll.h>
+
+typedef void (*mainloop_destroy_func) (void *user_data);
+
+typedef void (*mainloop_event_func) (int fd, uint32_t events, void *user_data);
+typedef void (*mainloop_timeout_func) (int id, void *user_data);
+typedef void (*mainloop_signal_func) (int signum, void *user_data);
+
+void mainloop_init(void);
+void mainloop_quit(void);
+int mainloop_run(void);
+
+int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
+ void *user_data, mainloop_destroy_func destroy);
+int mainloop_modify_fd(int fd, uint32_t events);
+int mainloop_remove_fd(int fd);
+
+int mainloop_add_timeout(unsigned int seconds, mainloop_timeout_func callback,
+ void *user_data, mainloop_destroy_func destroy);
+int mainloop_modify_timeout(int fd, unsigned int seconds);
+int mainloop_remove_timeout(int id);
+
+int mainloop_set_signal(sigset_t *mask, mainloop_signal_func callback,
+ void *user_data, mainloop_destroy_func destroy);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "control.h"
+#include "btsnoop.h"
+#include "packet.h"
+
+static unsigned long filter_mask = 0;
+
+void packet_set_filter(unsigned long filter)
+{
+ filter_mask = filter;
+}
+
+static void print_channel_header(struct timeval *tv, uint16_t index,
+ uint16_t channel)
+{
+ if (filter_mask & PACKET_FILTER_SHOW_INDEX) {
+ switch (channel) {
+ case HCI_CHANNEL_CONTROL:
+ printf("{hci%d} ", index);
+ break;
+ case HCI_CHANNEL_MONITOR:
+ printf("[hci%d] ", index);
+ break;
+ }
+ }
+
+ if (tv) {
+ time_t t = tv->tv_sec;
+ struct tm tm;
+
+ localtime_r(&t, &tm);
+
+ if (filter_mask & PACKET_FILTER_SHOW_DATE)
+ printf("%04d-%02d-%02d ", tm.tm_year + 1900,
+ tm.tm_mon + 1, tm.tm_mday);
+
+ if (filter_mask & PACKET_FILTER_SHOW_TIME)
+ printf("%02d:%02d:%02d.%06lu ", tm.tm_hour,
+ tm.tm_min, tm.tm_sec, tv->tv_usec);
+ }
+}
+
+static void print_header(struct timeval *tv, uint16_t index)
+{
+ print_channel_header(tv, index, HCI_CHANNEL_MONITOR);
+}
+
+void packet_hexdump(const unsigned char *buf, uint16_t len)
+{
+ static const char hexdigits[] = "0123456789abcdef";
+ char str[68];
+ uint16_t i;
+
+ if (!len)
+ return;
+
+ for (i = 0; i < len; i++) {
+ str[((i % 16) * 3) + 0] = hexdigits[buf[i] >> 4];
+ str[((i % 16) * 3) + 1] = hexdigits[buf[i] & 0xf];
+ str[((i % 16) * 3) + 2] = ' ';
+ str[(i % 16) + 49] = isprint(buf[i]) ? buf[i] : '.';
+
+ if ((i + 1) % 16 == 0) {
+ str[47] = ' ';
+ str[48] = ' ';
+ str[65] = '\0';
+ printf("%-12c%s\n", ' ', str);
+ str[0] = ' ';
+ }
+ }
+
+ if (i % 16 > 0) {
+ uint16_t j;
+ for (j = (i % 16); j < 16; j++) {
+ str[(j * 3) + 0] = ' ';
+ str[(j * 3) + 1] = ' ';
+ str[(j * 3) + 2] = ' ';
+ str[j + 49] = ' ';
+ }
+ str[47] = ' ';
+ str[48] = ' ';
+ str[65] = '\0';
+ printf("%-12c%s\n", ' ', str);
+ }
+}
+
+void packet_control(struct timeval *tv, uint16_t index, uint16_t opcode,
+ const void *data, uint16_t size)
+{
+ print_channel_header(tv, index, HCI_CHANNEL_CONTROL);
+
+ control_message(opcode, data, size);
+}
+
+#define MONITOR_NEW_INDEX 0
+#define MONITOR_DEL_INDEX 1
+#define MONITOR_COMMAND_PKT 2
+#define MONITOR_EVENT_PKT 3
+#define MONITOR_ACL_TX_PKT 4
+#define MONITOR_ACL_RX_PKT 5
+#define MONITOR_SCO_TX_PKT 6
+#define MONITOR_SCO_RX_PKT 7
+
+struct monitor_new_index {
+ uint8_t type;
+ uint8_t bus;
+ bdaddr_t bdaddr;
+ char name[8];
+} __attribute__((packed));
+
+#define MONITOR_NEW_INDEX_SIZE 16
+
+#define MONITOR_DEL_INDEX_SIZE 0
+
+#define MAX_INDEX 16
+
+static struct monitor_new_index index_list[MAX_INDEX];
+
+void packet_monitor(struct timeval *tv, uint16_t index, uint16_t opcode,
+ const void *data, uint16_t size)
+{
+ const struct monitor_new_index *ni;
+ char str[18];
+
+ switch (opcode) {
+ case MONITOR_NEW_INDEX:
+ ni = data;
+
+ if (index < MAX_INDEX)
+ memcpy(&index_list[index], ni, MONITOR_NEW_INDEX_SIZE);
+
+ ba2str(&ni->bdaddr, str);
+ packet_new_index(tv, index, str, ni->type, ni->bus, ni->name);
+ break;
+ case MONITOR_DEL_INDEX:
+ if (index < MAX_INDEX)
+ ba2str(&index_list[index].bdaddr, str);
+ else
+ ba2str(BDADDR_ANY, str);
+
+ packet_del_index(tv, index, str);
+ break;
+ case MONITOR_COMMAND_PKT:
+ packet_hci_command(tv, index, data, size);
+ break;
+ case MONITOR_EVENT_PKT:
+ packet_hci_event(tv, index, data, size);
+ break;
+ case MONITOR_ACL_TX_PKT:
+ packet_hci_acldata(tv, index, false, data, size);
+ break;
+ case MONITOR_ACL_RX_PKT:
+ packet_hci_acldata(tv, index, true, data, size);
+ break;
+ case MONITOR_SCO_TX_PKT:
+ packet_hci_scodata(tv, index, false, data, size);
+ break;
+ case MONITOR_SCO_RX_PKT:
+ packet_hci_scodata(tv, index, true, data, size);
+ break;
+ default:
+ print_header(tv, index);
+ printf("* Unknown packet (code %d len %d)\n", opcode, size);
+ packet_hexdump(data, size);
+ break;
+ }
+}
+
+static const struct {
+ uint16_t opcode;
+ const char *str;
+} opcode2str_table[] = {
+ /* OGF 1 - Link Control */
+ { 0x0401, "Inquiry" },
+ { 0x0402, "Inquiry Cancel" },
+ { 0x0403, "Periodic Inquiry Mode" },
+ { 0x0404, "Exit Periodic Inquiry Mode" },
+ { 0x0405, "Create Connection" },
+ { 0x0406, "Disconnect" },
+ { 0x0407, "Add SCO Connection" },
+ { 0x0408, "Create Connection Cancel" },
+ { 0x0409, "Accept Connection Request" },
+ { 0x040a, "Reject Connection Request" },
+ { 0x040b, "Link Key Request Reply" },
+ { 0x040c, "Link Key Request Negative Reply" },
+ { 0x040d, "PIN Code Request Reply" },
+ { 0x040e, "PIN Code Request Negative Reply" },
+ { 0x040f, "Change Connection Packet Type" },
+ /* reserved command */
+ { 0x0411, "Authentication Requested" },
+ /* reserved command */
+ { 0x0413, "Set Connection Encryption" },
+ /* reserved command */
+ { 0x0415, "Change Connection Link Key" },
+ /* reserved command */
+ { 0x0417, "Master Link Key" },
+ /* reserved command */
+ { 0x0419, "Remote Name Request" },
+ { 0x041a, "Remote Name Request Cancel" },
+ { 0x041b, "Read Remote Supported Features" },
+ { 0x041c, "Read Remote Extended Features" },
+ { 0x041d, "Read Remote Version Information" },
+ /* reserved command */
+ { 0x041f, "Read Clock Offset" },
+ { 0x0420, "Read LMP Handle" },
+ /* reserved commands */
+ { 0x0428, "Setup Synchronous Connection" },
+ { 0x0429, "Accept Synchronous Connection" },
+ { 0x042a, "Reject Synchronous Connection" },
+ { 0x042b, "IO Capability Request Reply" },
+ { 0x042c, "User Confirmation Request Reply" },
+ { 0x042d, "User Confirmation Request Neg Reply" },
+ { 0x042e, "User Passkey Request Reply" },
+ { 0x042f, "User Passkey Request Negative Reply" },
+ { 0x0430, "Remote OOB Data Request Reply" },
+ /* reserved commands */
+ { 0x0433, "Remote OOB Data Request Neg Reply" },
+ { 0x0434, "IO Capability Request Negative Reply"},
+ { 0x0435, "Create Physical Link" },
+ { 0x0436, "Accept Physical Link" },
+ { 0x0437, "Disconnect Physical Link" },
+ { 0x0438, "Create Logical Link" },
+ { 0x0439, "Accept Logical Link" },
+ { 0x043a, "Disconnect Logical Link" },
+ { 0x043b, "Logical Link Cancel" },
+ { 0x043c, "Flow Specifcation Modify" },
+
+ /* OGF 2 - Link Policy */
+ { 0x0801, "Holde Mode" },
+ /* reserved command */
+ { 0x0803, "Sniff Mode" },
+ { 0x0804, "Exit Sniff Mode" },
+ { 0x0805, "Park State" },
+ { 0x0806, "Exit Park State" },
+ { 0x0807, "QoS Setup" },
+ /* reserved command */
+ { 0x0809, "Role Discovery" },
+ /* reserved command */
+ { 0x080b, "Switch Role" },
+ { 0x080c, "Read Link Policy Settings" },
+ { 0x080d, "Write Link Policy Settings" },
+ { 0x080e, "Read Default Link Policy Settings" },
+ { 0x080f, "Write Default Link Policy Settings" },
+ { 0x0810, "Flow Specification" },
+ { 0x0811, "Sniff Subrating" },
+
+ /* OGF 3 - Host Control */
+ { 0x0c01, "Set Event Mask" },
+ /* reserved command */
+ { 0x0c03, "Reset" },
+ /* reserved command */
+ { 0x0c05, "Set Event Filter" },
+ /* reserved commands */
+ { 0x0c08, "Flush" },
+ { 0x0c09, "Read PIN Type" },
+ { 0x0c0a, "Write PIN Type" },
+ { 0x0c0b, "Create New Unit Key" },
+ /* reserved command */
+ { 0x0c0d, "Read Stored Link Key" },
+ /* reserved commands */
+ { 0x0c11, "Write Stored Link Key" },
+ { 0x0c12, "Delete Stored Link Key" },
+ { 0x0c13, "Write Local Name" },
+ { 0x0c14, "Read Local Name" },
+ { 0x0c15, "Read Connection Accept Timeout" },
+ { 0x0c16, "Write Connection Accept Timeout" },
+ { 0x0c17, "Read Page Timeout" },
+ { 0x0c18, "Write Page Timeout" },
+ { 0x0c19, "Read Scan Enable" },
+ { 0x0c1a, "Write Scan Enable" },
+ { 0x0c1b, "Read Page Scan Activity" },
+ { 0x0c1c, "Write Page Scan Activity" },
+ { 0x0c1d, "Read Inquiry Scan Activity" },
+ { 0x0c1e, "Write Inquiry Scan Activity" },
+ { 0x0c1f, "Read Authentication Enable" },
+ { 0x0c20, "Write Authentication Enable" },
+ { 0x0c21, "Read Encryption Mode" },
+ { 0x0c22, "Write Encryption Mode" },
+ { 0x0c23, "Read Class of Device" },
+ { 0x0c24, "Write Class of Device" },
+ { 0x0c25, "Read Voice Setting" },
+ { 0x0c26, "Write Voice Setting" },
+ { 0x0c27, "Read Automatic Flush Timeout" },
+ { 0x0c28, "Write Automatic Flush Timeout" },
+ { 0x0c29, "Read Num Broadcast Retransmissions" },
+ { 0x0c2a, "Write Num Broadcast Retransmissions" },
+ { 0x0c2b, "Read Hold Mode Activity" },
+ { 0x0c2c, "Write Hold Mode Activity" },
+ { 0x0c2d, "Read Transmit Power Level" },
+ { 0x0c2e, "Read Sync Flow Control Enable" },
+ { 0x0c2f, "Write Sync Flow Control Enable" },
+ /* reserved command */
+ { 0x0c31, "Set Host Controller To Host Flow" },
+ /* reserved command */
+ { 0x0c33, "Host Buffer Size" },
+ /* reserved command */
+ { 0x0c35, "Host Number of Completed Packets" },
+ { 0x0c36, "Read Link Supervision Timeout" },
+ { 0x0c37, "Write Link Supervision Timeout" },
+ { 0x0c38, "Read Number of Supported IAC" },
+ { 0x0c39, "Read Current IAC LAP" },
+ { 0x0c3a, "Write Current IAC LAP" },
+ { 0x0c3b, "Read Page Scan Period Mode" },
+ { 0x0c3c, "Write Page Scan Period Mode" },
+ { 0x0c3d, "Read Page Scan Mode" },
+ { 0x0c3e, "Write Page Scan Mode" },
+ { 0x0c3f, "Set AFH Host Channel Classification" },
+ /* reserved commands */
+ { 0x0c42, "Read Inquiry Scan Type" },
+ { 0x0c43, "Write Inquiry Scan Type" },
+ { 0x0c44, "Read Inquiry Mode" },
+ { 0x0c45, "Write Inquiry Mode" },
+ { 0x0c46, "Read Page Scan Type" },
+ { 0x0c47, "Write Page Scan Type" },
+ { 0x0c48, "Read AFH Channel Assessment Mode" },
+ { 0x0c49, "Write AFH Channel Assessment Mode" },
+ /* reserved commands */
+ { 0x0c51, "Read Extended Inquiry Response" },
+ { 0x0c52, "Write Extended Inquiry Response" },
+ { 0x0c53, "Refresh Encryption Key" },
+ /* reserved command */
+ { 0x0c55, "Read Simple Pairing Mode" },
+ { 0x0c56, "Write Simple Pairing Mode" },
+ { 0x0c57, "Read Local OOB Data" },
+ { 0x0c58, "Read Inquiry Response TX Power Level"},
+ { 0x0c59, "Write Inquiry Transmit Power Level" },
+ { 0x0c5a, "Read Default Erroneous Reporting" },
+ { 0x0c5b, "Write Default Erroneous Reporting" },
+ /* reserved commands */
+ { 0x0c5f, "Enhanced Flush" },
+ /* reserved command */
+ { 0x0c61, "Read Logical Link Accept Timeout" },
+ { 0x0c62, "Write Logical Link Accept Timeout" },
+ { 0x0c63, "Set Event Mask Page 2" },
+ { 0x0c64, "Read Location Data" },
+ { 0x0c65, "Write Location Data" },
+ { 0x0c66, "Read Flow Control Mode" },
+ { 0x0c67, "Write Flow Control Mode" },
+ { 0x0c68, "Read Enhanced Transmit Power Level" },
+ { 0x0c69, "Read Best Effort Flush Timeout" },
+ { 0x0c6a, "Write Best Effort Flush Timeout" },
+ { 0x0c6b, "Short Range Mode" },
+ { 0x0c6c, "Read LE Host Supported" },
+ { 0x0c6d, "Write LE Host Supported" },
+
+ /* OGF 4 - Information Parameter */
+ { 0x1001, "Read Local Version Information" },
+ { 0x1002, "Read Local Supported Commands" },
+ { 0x1003, "Read Local Supported Features" },
+ { 0x1004, "Read Local Extended Features" },
+ { 0x1005, "Read Buffer Size" },
+ /* reserved command */
+ { 0x1007, "Read Country Code" },
+ /* reserved command */
+ { 0x1009, "Read BD ADDR" },
+ { 0x100a, "Read Data Block Size" },
+
+ /* OGF 5 - Status Parameter */
+ { 0x1401, "Read Failed Contact Counter" },
+ { 0x1402, "Reset Failed Contact Counter" },
+ { 0x1403, "Read Link Quality" },
+ /* reserved command */
+ { 0x1405, "Read RSSI" },
+ { 0x1406, "Read AFH Channel Map" },
+ { 0x1407, "Read Clock" },
+ { 0x1408, "Read Encryption Key Size" },
+ { 0x1409, "Read Local AMP Info" },
+ { 0x140a, "Read Local AMP ASSOC" },
+ { 0x140b, "Write Remote AMP ASSOC" },
+
+ /* OGF 8 - LE Control */
+ { 0x2001, "LE Set Event Mask" },
+ { 0x2002, "LE Read Buffer Size" },
+ { 0x2003, "LE Read Local Supported Features" },
+ /* reserved command */
+ { 0x2005, "LE Set Random Address" },
+ { 0x2006, "LE Set Advertising Parameters" },
+ { 0x2007, "LE Read Advertising Channel TX Power"},
+ { 0x2008, "LE Set Advertising Data" },
+ { 0x2009, "LE Set Scan Response Data" },
+ { 0x200a, "LE Set Advertise Enable" },
+ { 0x200b, "LE Set Scan Parameters" },
+ { 0x200c, "LE Set Scan Enable" },
+ { 0x200d, "LE Create Connection" },
+ { 0x200e, "LE Create Connection Cancel" },
+ { 0x200f, "LE Read White List Size" },
+ { 0x2010, "LE Clear White List" },
+ { 0x2011, "LE Add Device To White List" },
+ { 0x2012, "LE Remove Device From White List" },
+ { 0x2013, "LE Connection Update" },
+ { 0x2014, "LE Set Host Channel Classification" },
+ { 0x2015, "LE Read Channel Map" },
+ { 0x2016, "LE Read Remote Used Features" },
+ { 0x2017, "LE Encrypt" },
+ { 0x2018, "LE Rand" },
+ { 0x2019, "LE Start Encryption" },
+ { 0x201a, "LE Long Term Key Request Reply" },
+ { 0x201b, "LE Long Term Key Request Neg Reply" },
+ { 0x201c, "LE Read Supported States" },
+ { 0x201d, "LE Receiver Test" },
+ { 0x201e, "LE Transmitter Test" },
+ { 0x201f, "LE Test End" },
+ { }
+};
+
+static const char *opcode2str(uint16_t opcode)
+{
+ int i;
+
+ for (i = 0; opcode2str_table[i].str; i++) {
+ if (opcode2str_table[i].opcode == opcode)
+ return opcode2str_table[i].str;
+ }
+
+ return "Unknown";
+}
+
+static const struct {
+ uint8_t event;
+ const char *str;
+} event2str_table[] = {
+ { 0x01, "Inquiry Complete" },
+ { 0x02, "Inquiry Result" },
+ { 0x03, "Connect Complete" },
+ { 0x04, "Connect Request" },
+ { 0x05, "Disconn Complete" },
+ { 0x06, "Auth Complete" },
+ { 0x07, "Remote Name Req Complete" },
+ { 0x08, "Encrypt Change" },
+ { 0x09, "Change Connection Link Key Complete" },
+ { 0x0a, "Master Link Key Complete" },
+ { 0x0b, "Read Remote Supported Features" },
+ { 0x0c, "Read Remote Version Complete" },
+ { 0x0d, "QoS Setup Complete" },
+ { 0x0e, "Command Complete" },
+ { 0x0f, "Command Status" },
+ { 0x10, "Hardware Error" },
+ { 0x11, "Flush Occurred" },
+ { 0x12, "Role Change" },
+ { 0x13, "Number of Completed Packets" },
+ { 0x14, "Mode Change" },
+ { 0x15, "Return Link Keys" },
+ { 0x16, "PIN Code Request" },
+ { 0x17, "Link Key Request" },
+ { 0x18, "Link Key Notification" },
+ { 0x19, "Loopback Command" },
+ { 0x1a, "Data Buffer Overflow" },
+ { 0x1b, "Max Slots Change" },
+ { 0x1c, "Read Clock Offset Complete" },
+ { 0x1d, "Connection Packet Type Changed" },
+ { 0x1e, "QoS Violation" },
+ { 0x1f, "Page Scan Mode Change" },
+ { 0x20, "Page Scan Repetition Mode Change" },
+ { 0x21, "Flow Specification Complete" },
+ { 0x22, "Inquiry Result with RSSI" },
+ { 0x23, "Read Remote Extended Features" },
+ /* reserved events */
+ { 0x2c, "Synchronous Connect Complete" },
+ { 0x2d, "Synchronous Connect Changed" },
+ { 0x2e, "Sniff Subrate" },
+ { 0x2f, "Extended Inquiry Result" },
+ { 0x30, "Encryption Key Refresh Complete" },
+ { 0x31, "IO Capability Request" },
+ { 0x32, "IO Capability Response" },
+ { 0x33, "User Confirmation Request" },
+ { 0x34, "User Passkey Request" },
+ { 0x35, "Remote OOB Data Request" },
+ { 0x36, "Simple Pairing Complete" },
+ /* reserved event */
+ { 0x38, "Link Supervision Timeout Change" },
+ { 0x39, "Enhanced Flush Complete" },
+ /* reserved event */
+ { 0x3b, "User Passkey Notification" },
+ { 0x3c, "Keypress Notification" },
+ { 0x3d, "Remote Host Supported Features" },
+ { 0x3e, "LE Meta Event" },
+ /* reserved event */
+ { 0x40, "Physical Link Complete" },
+ { 0x41, "Channel Selected" },
+ { 0x42, "Disconn Physical Link Complete" },
+ { 0x43, "Physical Link Loss Early Warning" },
+ { 0x44, "Physical Link Recovery" },
+ { 0x45, "Logical Link Complete" },
+ { 0x46, "Disconn Logical Link Complete" },
+ { 0x47, "Flow Spec Modify Complete" },
+ { 0x48, "Number Of Completed Data Blocks" },
+ { 0x49, "AMP Start Test" },
+ { 0x4a, "AMP Test End" },
+ { 0x4b, "AMP Receiver Report" },
+ { 0x4c, "Short Range Mode Change Complete" },
+ { 0x4d, "AMP Status Change" },
+ { 0xfe, "Testing" },
+ { 0xff, "Vendor" },
+ { }
+};
+
+static const char *event2str(uint8_t event)
+{
+ int i;
+
+ for (i = 0; event2str_table[i].str; i++) {
+ if (event2str_table[i].event == event)
+ return event2str_table[i].str;
+ }
+
+ return "Unknown";
+}
+
+void packet_new_index(struct timeval *tv, uint16_t index, const char *label,
+ uint8_t type, uint8_t bus, const char *name)
+{
+ print_header(tv, index);
+
+ printf("= New Index: %s (%s,%s,%s)\n", label,
+ hci_typetostr(type), hci_bustostr(bus), name);
+}
+
+void packet_del_index(struct timeval *tv, uint16_t index, const char *label)
+{
+ print_header(tv, index);
+
+ printf("= Delete Index: %s\n", label);
+}
+
+void packet_hci_command(struct timeval *tv, uint16_t index,
+ const void *data, uint16_t size)
+{
+ const hci_command_hdr *hdr = data;
+ uint16_t opcode = btohs(hdr->opcode);
+ uint16_t ogf = cmd_opcode_ogf(opcode);
+ uint16_t ocf = cmd_opcode_ocf(opcode);
+
+ btsnoop_write(tv, index, 0x02, data, size);
+
+ print_header(tv, index);
+
+ if (size < HCI_COMMAND_HDR_SIZE) {
+ printf("* Malformed HCI Command packet\n");
+ return;
+ }
+
+ printf("< HCI Command: %s (0x%2.2x|0x%4.4x) plen %d\n",
+ opcode2str(opcode), ogf, ocf, hdr->plen);
+
+ data += HCI_COMMAND_HDR_SIZE;
+ size -= HCI_COMMAND_HDR_SIZE;
+
+ packet_hexdump(data, size);
+}
+
+void packet_hci_event(struct timeval *tv, uint16_t index,
+ const void *data, uint16_t size)
+{
+ const hci_event_hdr *hdr = data;
+
+ btsnoop_write(tv, index, 0x03, data, size);
+
+ print_header(tv, index);
+
+ if (size < HCI_EVENT_HDR_SIZE) {
+ printf("* Malformed HCI Event packet\n");
+ return;
+ }
+
+ printf("> HCI Event: %s (0x%2.2x) plen %d\n",
+ event2str(hdr->evt), hdr->evt, hdr->plen);
+
+ data += HCI_EVENT_HDR_SIZE;
+ size -= HCI_EVENT_HDR_SIZE;
+
+ packet_hexdump(data, size);
+}
+
+void packet_hci_acldata(struct timeval *tv, uint16_t index, bool in,
+ const void *data, uint16_t size)
+{
+ const hci_acl_hdr *hdr = data;
+ uint16_t handle = btohs(hdr->handle);
+ uint16_t dlen = btohs(hdr->dlen);
+ uint8_t flags = acl_flags(handle);
+
+ btsnoop_write(tv, index, in ? 0x01 : 0x00, data, size);
+
+ print_header(tv, index);
+
+ if (size < HCI_ACL_HDR_SIZE) {
+ printf("* Malformed ACL Data %s packet\n", in ? "RX" : "TX");
+ return;
+ }
+
+ printf("%c ACL Data: handle %d flags 0x%2.2x dlen %d\n",
+ in ? '>' : '<', acl_handle(handle), flags, dlen);
+
+ data += HCI_ACL_HDR_SIZE;
+ size -= HCI_ACL_HDR_SIZE;
+
+ if (filter_mask & PACKET_FILTER_SHOW_ACL_DATA)
+ packet_hexdump(data, size);
+}
+
+void packet_hci_scodata(struct timeval *tv, uint16_t index, bool in,
+ const void *data, uint16_t size)
+{
+ const hci_sco_hdr *hdr = data;
+ uint16_t handle = btohs(hdr->handle);
+ uint8_t flags = acl_flags(handle);
+
+ print_header(tv, index);
+
+ if (size < HCI_SCO_HDR_SIZE) {
+ printf("* Malformed SCO Data %s packet\n", in ? "RX" : "TX");
+ return;
+ }
+
+ printf("%c SCO Data: handle %d flags 0x%2.2x dlen %d\n",
+ in ? '>' : '<', acl_handle(handle), flags, hdr->dlen);
+
+ data += HCI_SCO_HDR_SIZE;
+ size -= HCI_SCO_HDR_SIZE;
+
+ if (filter_mask & PACKET_FILTER_SHOW_SCO_DATA)
+ packet_hexdump(data, size);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdbool.h>
+#include <sys/time.h>
+
+#define PACKET_FILTER_SHOW_INDEX (1 << 0)
+#define PACKET_FILTER_SHOW_DATE (1 << 1)
+#define PACKET_FILTER_SHOW_TIME (1 << 2)
+#define PACKET_FILTER_SHOW_ACL_DATA (1 << 3)
+#define PACKET_FILTER_SHOW_SCO_DATA (1 << 4)
+
+void packet_set_filter(unsigned long filter);
+
+void packet_hexdump(const unsigned char *buf, uint16_t len);
+
+void packet_control(struct timeval *tv, uint16_t index, uint16_t opcode,
+ const void *data, uint16_t size);
+void packet_monitor(struct timeval *tv, uint16_t index, uint16_t opcode,
+ const void *data, uint16_t size);
+
+void packet_new_index(struct timeval *tv, uint16_t index, const char *label,
+ uint8_t type, uint8_t bus, const char *name);
+void packet_del_index(struct timeval *tv, uint16_t index, const char *label);
+
+void packet_hci_command(struct timeval *tv, uint16_t index,
+ const void *data, uint16_t size);
+void packet_hci_event(struct timeval *tv, uint16_t index,
+ const void *data, uint16_t size);
+void packet_hci_acldata(struct timeval *tv, uint16_t index, bool in,
+ const void *data, uint16_t size);
+void packet_hci_scodata(struct timeval *tv, uint16_t index, bool in,
+ const void *data, uint16_t size);
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/bnep.h>
+#include <bluetooth/uuid.h>
#include <glib.h>
*
*/
-#define PANU_UUID "00001115-0000-1000-8000-00805f9b34fb"
-#define NAP_UUID "00001116-0000-1000-8000-00805f9b34fb"
-#define GN_UUID "00001117-0000-1000-8000-00805f9b34fb"
-#define BNEP_SVC_UUID "0000000f-0000-1000-8000-00805f9b34fb"
-
int bnep_init(void);
int bnep_cleanup(void);
#include <gdbus.h>
#include "log.h"
-#include "glib-compat.h"
#include "btio.h"
#include "dbus-common.h"
#include "adapter.h"
#include "connection.h"
#define NETWORK_PEER_INTERFACE "org.bluez.Network"
+#define CON_SETUP_RETRIES 3
+#define CON_SETUP_TO 9
typedef enum {
CONNECTED,
guint watch; /* Disconnect watch */
guint dc_id;
struct network_peer *peer;
+ guint attempt_cnt;
+ guint timeout_source;
};
struct __service_16 {
{
DBusMessage *reply;
+ if (nc->timeout_source > 0) {
+ g_source_remove(nc->timeout_source);
+ nc->timeout_source = 0;
+ }
+
if (nc->watch) {
g_dbus_remove_watch(connection, nc->watch);
nc->watch = 0;
if (cond & G_IO_NVAL)
return FALSE;
+ g_source_remove(nc->timeout_source);
+ nc->timeout_source = 0;
+
if (cond & (G_IO_HUP | G_IO_ERR)) {
error("Hangup or error on l2cap server socket");
goto failed;
return FALSE;
}
-static int bnep_connect(struct network_conn *nc)
+static int bnep_send_conn_req(struct network_conn *nc)
{
struct bnep_setup_conn_req *req;
struct __service_16 *s;
- struct timeval timeo;
unsigned char pkt[BNEP_MTU];
int fd;
s->dst = htons(nc->id);
s->src = htons(BNEP_SVC_PANU);
- memset(&timeo, 0, sizeof(timeo));
- timeo.tv_sec = 30;
-
fd = g_io_channel_unix_get_fd(nc->io);
- setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+ if (write(fd, pkt, sizeof(*req) + sizeof(*s)) < 0) {
+ int err = -errno;
+ error("bnep connection req send failed: %s", strerror(errno));
+ return err;
+ }
+
+ nc->attempt_cnt++;
+
+ return 0;
+}
+
+static gboolean bnep_conn_req_to(gpointer user_data)
+{
+ struct network_conn *nc;
+
+ nc = user_data;
+ if (nc->attempt_cnt == CON_SETUP_RETRIES) {
+ error("Too many bnep connection attempts");
+ } else {
+ error("bnep connection setup TO, retrying...");
+ if (!bnep_send_conn_req(nc))
+ return TRUE;
+ }
+
+ cancel_connection(nc, "bnep setup failed");
+
+ return FALSE;
+}
+
+static int bnep_connect(struct network_conn *nc)
+{
+ int err;
+
+ nc->attempt_cnt = 0;
+ if ((err = bnep_send_conn_req(nc)))
+ return err;
- if (send(fd, pkt, sizeof(*req) + sizeof(*s), 0) < 0)
- return -errno;
+ nc->timeout_source = g_timeout_add_seconds(CON_SETUP_TO,
+ bnep_conn_req_to, nc);
g_io_add_watch(nc->io, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) bnep_setup_cb, nc);
BT_IO_OPT_SOURCE_BDADDR, &peer->src,
BT_IO_OPT_DEST_BDADDR, &peer->dst,
BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_OMTU, BNEP_MTU,
BT_IO_OPT_IMTU, BNEP_MTU,
BT_IO_OPT_INVALID);
peer_free(peer);
}
-static GDBusMethodTable connection_methods[] = {
- { "Connect", "s", "s", connection_connect,
- G_DBUS_METHOD_FLAG_ASYNC },
- { "Disconnect", "", "", connection_disconnect },
- { "GetProperties", "", "a{sv}",connection_get_properties },
+static const GDBusMethodTable connection_methods[] = {
+ { GDBUS_ASYNC_METHOD("Connect",
+ NULL, NULL, connection_connect) },
+ { GDBUS_METHOD("Disconnect",
+ NULL, NULL, connection_disconnect) },
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ connection_get_properties) },
{ }
};
-static GDBusSignalTable connection_signals[] = {
- { "PropertyChanged", "sv" },
+static const GDBusSignalTable connection_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ }
};
#include <bluetooth/bluetooth.h>
#include <bluetooth/bnep.h>
#include <bluetooth/sdp.h>
+#include <bluetooth/uuid.h>
#include <glib.h>
#include <gdbus.h>
}
/*
- * There is one socket to handle the incomming connections. NAP,
+ * There is one socket to handle the incoming connections. NAP,
* GN and PANU servers share the same PSM. The initial BNEP message
* (setup connection request) contains the destination service
* field that defines which service the source is connecting to.
#include <bluetooth/bnep.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include <netinet/in.h>
#include <glib.h>
#include "error.h"
#include "sdpd.h"
#include "btio.h"
-#include "glib-compat.h"
#include "common.h"
#include "server.h"
}
#endif
-static void add_lang_attr(sdp_record_t *r)
-{
- sdp_lang_attr_t base_lang;
- sdp_list_t *langs = 0;
-
- /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
- base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
- base_lang.encoding = 106;
- base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
- langs = sdp_list_append(0, &base_lang);
- sdp_set_lang_attr(r, langs);
- sdp_list_free(langs, 0);
-}
-
static sdp_record_t *server_record_new(const char *name, uint16_t id)
{
sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
aproto = sdp_list_append(NULL, apseq);
sdp_set_access_protos(record, aproto);
- add_lang_attr(record);
+ sdp_add_lang_attr(record);
sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC,
SDP_UINT16, &security_desc);
switch (req->uuid_size) {
case 2: /* UUID16 */
- *dst_role = ntohs(bt_get_unaligned((uint16_t *) dest));
- *src_role = ntohs(bt_get_unaligned((uint16_t *) source));
+ *dst_role = bt_get_be16(dest);
+ *src_role = bt_get_be16(source);
break;
case 4: /* UUID32 */
case 16: /* UUID128 */
- *dst_role = ntohl(bt_get_unaligned((uint32_t *) dest));
- *src_role = ntohl(bt_get_unaligned((uint32_t *) source));
+ *dst_role = bt_get_be32(dest);
+ *src_role = bt_get_be32(source);
break;
default:
return BNEP_CONN_INVALID_SVC;
{
struct network_server *ns = NULL;
struct network_session *session = NULL;
- gboolean connected = FALSE;
char address[20] = {0};
GError *gerr = NULL;
const char* paddr = address;
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__
- gboolean connected = TRUE;
-#endif
if (cond & G_IO_NVAL)
return FALSE;
adapter_free(na);
}
-static GDBusMethodTable server_methods[] = {
- { "Register", "ss", "", register_server },
- { "Unregister", "s", "", unregister_server },
+static const GDBusMethodTable server_methods[] = {
+ { GDBUS_METHOD("Register",
+ GDBUS_ARGS({ "uuid", "s" }, { "bridge", "s" }), NULL,
+ register_server) },
+ { GDBUS_METHOD("Unregister",
+ GDBUS_ARGS({ "uuid", "s" }), NULL,
+ unregister_server) },
{ }
};
#ifdef __TIZEN_PATCH__
static GDBusSignalTable server_signals[] = {
- { "PeerConnected", "ss" },
- { "PeerDisconnected", "ss" },
+ { GDBUS_SIGNAL("PeerConnected",
+ GDBUS_ARGS({ "device", "s" }, { "address", "s" })) },
+ { GDBUS_SIGNAL("PeerDisconnected",
+ GDBUS_ARGS({ "device", "s" }, { "address", "s" })) },
{ }
};
#endif
void server_exit(void);
int server_register(struct btd_adapter *adapter);
int server_unregister(struct btd_adapter *adapter);
-
-int server_find_data(const char *path, const char *pattern);
-Index: bluez-4.98/Makefile.tools
+Index: bluez-4.99/Makefile.tools
===================================================================
---- bluez-4.98.orig/Makefile.tools 2012-03-14 18:30:05.595203885 +0900
-+++ bluez-4.98/Makefile.tools 2012-03-14 19:39:16.815200856 +0900
-@@ -60,7 +60,7 @@
+--- bluez-4.99.org/Makefile.tools 2012-05-07 19:36:32.000000000 +0530
++++ bluez-4.99/Makefile.tools 2012-05-07 19:37:09.000000000 +0530
+@@ -75,7 +75,7 @@
attrib/gattrib.c btio/btio.c \
attrib/gatttool.h attrib/interactive.c \
- attrib/utils.c
+ attrib/utils.c src/log.c
-attrib_gatttool_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @READLINE_LIBS@
+attrib_gatttool_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @READLINE_LIBS@ -lncurses
endif
-
+
dist_man_MANS += tools/rfcomm.1 tools/l2ping.8 \
Name: bluez
Summary: Bluetooth utilities
-Version: 4.98
-Release: 1
+Version: 4.101
+Release: 2
Group: Applications/System
License: GPLv2+
URL: http://www.bluez.org/
Source0: http://www.kernel.org/pub/linux/bluetooth/%{name}-%{version}.tar.gz
Patch1 : bluez-ncurses.patch
Requires: dbus >= 0.60
-Requires: usbutils
Requires: pciutils
BuildRequires: pkgconfig(dbus-1)
-BuildRequires: pkgconfig(alsa)
-BuildRequires: pkgconfig(udev)
-BuildRequires: pkgconfig(sndfile)
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: flex
BuildRequires: bison
%build
export CFLAGS="${CFLAGS} -D__TIZEN_PATCH__ -D__BROADCOM_PATCH__ "
-%ifnarch %{arm}
-export LDFLAGS="${LDFLAGS} -lncurses -Wl,--as-needed "
-%endif
+export LDFLAGS=" -lncurses -Wl,--as-needed "
%reconfigure --disable-static \
--sysconfdir=%{_prefix}/etc \
--localstatedir=/opt/var \
--enable-pcmcia=no \
--enable-hid2hci=no \
--enable-alsa=no \
- --enable-gstreamer \
+ --enable-gstreamer=no \
--disable-dfutool \
--disable-cups \
- --disable-tests \
- --disable-udevrules \
+ --enable-health \
--enable-dbusoob \
- --with-telephony=tizen
+ --with-telephony=tizen
-make
+make %{?jobs:-j%jobs}
%install
rm -rf %{buildroot}
%{_bindir}/hcitool
%dir %{_libdir}/bluetooth/plugins
%dir /opt/var/lib/bluetooth
-%exclude /lib/udev/rules.d/97-bluetooth.rules
+%{_datadir}/dbus-1/system-services/org.bluez.service
%files -n libbluetooth3
%{_includedir}/bluetooth/*
%{_libdir}/libbluetooth.so
%{_libdir}/pkgconfig/bluez.pc
-
-
#define MACHINE_INFO_FILE "machine-info"
static GIOChannel *inotify = NULL;
-static int watch_fd = -1;
+static int watch_d = -1;
/* This file is part of systemd's hostnamed functionality:
* http://0pointer.de/public/systemd-man/machine-info.html
mask |= IN_MOVED_FROM;
mask |= IN_MOVED_TO;
- watch_fd = inotify_add_watch(inot_fd, MACHINE_INFO_DIR, mask);
- if (watch_fd < 0) {
+ watch_d = inotify_add_watch(inot_fd, MACHINE_INFO_DIR, mask);
+ if (watch_d < 0) {
error("Failed to setup watch for '%s'", MACHINE_INFO_DIR);
close(inot_fd);
return 0;
static void adaptername_exit(void)
{
- if (watch_fd >= 0)
- close(watch_fd);
+ if (watch_d >= 0 && inotify != NULL) {
+ int inot_fd = g_io_channel_unix_get_fd(inotify);
+ inotify_rm_watch(inot_fd, watch_d);
+ }
+
if (inotify != NULL) {
g_io_channel_shutdown(inotify, FALSE, NULL);
g_io_channel_unref(inotify);
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 const GDBusMethodTable oob_methods[] = {
+ { GDBUS_METHOD("AddRemoteData",
+ GDBUS_ARGS({ "address", "s" }, { "hash", "ay" },
+ { "randomizer", "ay" }), NULL,
+ add_remote_data) },
+ { GDBUS_METHOD("RemoveRemoteData",
+ GDBUS_ARGS({ "address", "s" }), NULL,
+ remove_remote_data) },
+ { GDBUS_ASYNC_METHOD("ReadLocalData",
+ NULL, GDBUS_ARGS({ "hash", "ay" },
+ { "randomizer", "ay" }),
+ read_local_data) },
+ { }
};
static int oob_probe(struct btd_adapter *adapter)
#include "gattrib.h"
#include "gatt-service.h"
#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
#include "attrib-server.h"
/* FIXME: Not defined by SIG? UUID128? */
return -1;
}
-static uint8_t battery_state_read(struct attribute *a, gpointer user_data)
+static uint8_t battery_state_read(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
{
struct btd_adapter *adapter = user_data;
uint8_t value;
static gboolean register_battery_service(struct btd_adapter *adapter)
{
- return gatt_service_add(adapter, GATT_PRIM_SVC_UUID,
- BATTERY_STATE_SVC_UUID,
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, BATTERY_STATE_SVC_UUID);
+
+ return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
/* battery state characteristic */
GATT_OPT_CHR_UUID, BATTERY_STATE_UUID,
GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ |
bt_uuid_t uuid;
int len;
- start_handle = attrib_db_find_avail(adapter->adapter, svc_size);
+ bt_uuid16_create(&uuid, THERM_HUMIDITY_SVC_UUID);
+ start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
if (start_handle == 0) {
error("Not enough free handles to register service");
return;
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,
+ attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
atval, len);
- g_assert(h - start_handle == svc_size);
+ g_assert(h - start_handle + 1 == svc_size);
/* Add an SDP record for the above service */
sdp_handle = attrib_create_sdp(adapter->adapter, start_handle,
bt_uuid_t uuid;
int len;
- start_handle = attrib_db_find_avail(adapter->adapter, svc_size);
+ bt_uuid16_create(&uuid, MANUFACTURER_SVC_UUID);
+ start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
if (start_handle == 0) {
error("Not enough free handles to register service");
return;
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,
+ attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
atval, len);
- g_assert(h - start_handle == svc_size);
+ g_assert(h - start_handle + 1 == svc_size);
range[0] = start_handle;
range[1] = start_handle + svc_size - 1;
bt_uuid_t uuid;
int len;
- start_handle = attrib_db_find_avail(adapter->adapter, svc_size);
+ bt_uuid16_create(&uuid, MANUFACTURER_SVC_UUID);
+ start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
if (start_handle == 0) {
error("Not enough free handles to register service");
return;
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,
+ attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
atval, len);
- g_assert(h - start_handle == svc_size);
+ g_assert(h - start_handle + 1 == svc_size);
range[0] = start_handle;
range[1] = start_handle + svc_size - 1;
uint8_t atval[256];
bt_uuid_t uuid;
- start_handle = attrib_db_find_avail(adapter->adapter, svc_size);
+ bt_uuid16_create(&uuid, VENDOR_SPECIFIC_SVC_UUID);
+ start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
if (start_handle == 0) {
error("Not enough free handles to register service");
return;
atval[3] = 0x64;
atval[4] = 0x6F;
atval[5] = 0x72;
- attrib_db_add(adapter->adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
atval, 6);
- g_assert(h - start_handle == svc_size);
+ g_assert(h - start_handle + 1 == svc_size);
range[0] = start_handle;
range[1] = start_handle + svc_size - 1;
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;
+ uint128_t prim_weight_uuid, char_weight_uuid;
uint16_t start_handle, h;
const int svc_size = 6;
uint32_t sdp_handle;
int len;
btoh128(&char_weight_uuid_btorder, &char_weight_uuid);
-
- start_handle = attrib_db_find_avail(adapter->adapter, svc_size);
+ btoh128(&prim_weight_uuid_btorder, &prim_weight_uuid);
+ bt_uuid128_create(&uuid, prim_weight_uuid);
+ start_handle = attrib_db_find_avail(adapter->adapter, &uuid, svc_size);
if (start_handle == 0) {
error("Not enough free handles to register service");
return;
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,
+ attrib_db_add(adapter->adapter, h, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
atval, len);
-
- g_assert(h - start_handle == svc_size);
+ g_assert(h - start_handle + 1 == svc_size);
/* Add an SDP record for the above service */
sdp_handle = attrib_create_sdp(adapter->adapter, start_handle,
static int gatt_example_init(void)
{
- if (!main_opts.attrib_server) {
- DBG("Attribute server is disabled");
+ if (!main_opts.gatt_enabled) {
+ DBG("GATT is disabled");
return -ENOTSUP;
}
static void gatt_example_exit(void)
{
- if (!main_opts.attrib_server)
+ if (!main_opts.gatt_enabled)
return;
btd_unregister_adapter_driver(&gatt_example_adapter_driver);
#include <glib.h>
-#include "glib-compat.h"
#include "hcid.h"
#include "sdpd.h"
#include "btio.h"
struct hci_version ver;
+ uint16_t did_source;
uint16_t did_vendor;
uint16_t did_product;
uint16_t did_version;
GSList *need_name;
guint stop_scan_id;
+
+ uint16_t discoverable_timeout;
+ guint discoverable_id;
} *devs = NULL;
static int found_dev_rssi_cmp(gconstpointer a, gconstpointer b)
return 0;
}
-static int hciops_set_discoverable(int index, gboolean discoverable)
+static int hciops_set_discoverable(int index, gboolean discoverable,
+ uint16_t timeout)
{
struct dev_info *dev = &devs[index];
uint8_t mode;
1, &mode) < 0)
return -errno;
+ dev->discoverable_timeout = timeout;
+
return 0;
}
}
if (dev->features[3] & LMP_RSSI_INQ)
- events[4] |= 0x04; /* Inquiry Result with RSSI */
+ events[4] |= 0x02; /* Inquiry Result with RSSI */
if (dev->features[5] & LMP_SNIFF_SUBR)
events[5] |= 0x20; /* Sniff Subrating */
return 0;
}
+static void update_ext_inquiry_response(int index)
+{
+ struct dev_info *dev = &devs[index];
+ write_ext_inquiry_response_cp cp;
+
+ DBG("hci%d", index);
+
+ if (!(dev->features[6] & LMP_EXT_INQ))
+ return;
+
+ if (dev->ssp_mode == 0)
+ return;
+
+ if (dev->cache_enable)
+ return;
+
+ memset(&cp, 0, sizeof(cp));
+
+ eir_create(dev->name, dev->tx_power, dev->did_vendor, dev->did_product,
+ dev->did_version, dev->did_source, dev->uuids,
+ cp.data);
+
+ if (memcmp(cp.data, dev->eir, sizeof(cp.data)) == 0)
+ return;
+
+ memcpy(dev->eir, cp.data, sizeof(cp.data));
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
+ OCF_WRITE_EXT_INQUIRY_RESPONSE,
+ WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE, &cp) < 0)
+ error("Unable to write EIR data: %s (%d)",
+ strerror(errno), errno);
+}
+
+static int hciops_set_name(int index, const char *name)
+{
+ struct dev_info *dev = &devs[index];
+ change_local_name_cp cp;
+
+ DBG("hci%d, name %s", index, name);
+
+ memset(&cp, 0, sizeof(cp));
+ strncpy((char *) cp.name, name, sizeof(cp.name));
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME,
+ CHANGE_LOCAL_NAME_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ memcpy(dev->name, cp.name, 248);
+ update_ext_inquiry_response(index);
+
+ return 0;
+}
+
+static int write_class(int index, uint32_t class)
+{
+ struct dev_info *dev = &devs[index];
+ write_class_of_dev_cp cp;
+
+ DBG("hci%d class 0x%06x", index, class);
+
+ memcpy(cp.dev_class, &class, 3);
+
+ if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV,
+ WRITE_CLASS_OF_DEV_CP_SIZE, &cp) < 0)
+ return -errno;
+
+ dev->pending_cod = class;
+
+ return 0;
+}
+
+static int hciops_set_dev_class(int index, uint8_t major, uint8_t minor)
+{
+ struct dev_info *dev = &devs[index];
+ int err;
+
+ DBG("hci%d major %u minor %u", index, major, minor);
+
+ /* Update only the major and minor class bits keeping remaining bits
+ * intact*/
+ dev->wanted_cod &= 0xffe000;
+ dev->wanted_cod |= ((major & 0x1f) << 8) | minor;
+
+ if (dev->wanted_cod == dev->current_cod ||
+ dev->cache_enable || dev->pending_cod)
+ return 0;
+
+ DBG("Changing Major/Minor class to 0x%06x", dev->wanted_cod);
+
+ err = write_class(index, dev->wanted_cod);
+ if (err < 0)
+ error("Adapter class update failed: %s (%d)",
+ strerror(-err), -err);
+
+ return err;
+}
+
static gboolean init_adapter(int index)
{
struct dev_info *dev = &devs[index];
struct btd_adapter *adapter = NULL;
gboolean existing_adapter = dev->registered;
- uint8_t mode, on_mode;
+ uint8_t mode, on_mode, major, minor;
gboolean pairable, discoverable;
+ const char *name;
+ uint16_t discoverable_timeout;
if (!dev->registered) {
- adapter = btd_manager_register_adapter(index);
+ adapter = btd_manager_register_adapter(index, TRUE);
if (adapter)
dev->registered = TRUE;
} else {
if (adapter == NULL)
return FALSE;
- btd_adapter_get_mode(adapter, &mode, &on_mode, &pairable);
+ btd_adapter_get_mode(adapter, &mode, &on_mode,
+ &discoverable_timeout,
+ &pairable);
if (existing_adapter)
mode = on_mode;
}
start_adapter(index);
+
+ name = btd_adapter_get_name(adapter);
+ if (name)
+ hciops_set_name(index, name);
+
+ btd_adapter_get_class(adapter, &major, &minor);
+ hciops_set_dev_class(index, major, minor);
+
btd_adapter_start(adapter);
discoverable = (mode == MODE_DISCOVERABLE);
- hciops_set_discoverable(index, discoverable);
+ hciops_set_discoverable(index, discoverable, discoverable_timeout);
hciops_set_pairable(index, pairable);
if (dev->already_up)
}
static int hciops_set_did(int index, uint16_t vendor, uint16_t product,
- uint16_t version)
+ uint16_t version, uint16_t source)
{
struct dev_info *dev = &devs[index];
dev->did_vendor = vendor;
dev->did_product = product;
dev->did_version = version;
+ dev->did_source = source;
return 0;
}
DBG("local auth 0x%02x and remote auth 0x%02x",
conn->loc_auth, conn->rem_auth);
- if (key_type == 0x06) {
+ if (key_type == HCI_LK_CHANGED_COMBINATION) {
/* Some buggy controller combinations generate a changed
* combination key for legacy pairing even when there's no
* previous key */
if (conn->rem_auth == 0xff && old_key_type == 0xff)
- key_type = 0x00;
+ key_type = HCI_LK_COMBINATION;
else if (old_key_type != 0xff)
key_type = old_key_type;
else
key_info->type = key_type;
/* Skip the storage check if this is a debug key */
- if (key_type == 0x03)
+ if (key_type == HCI_LK_DEBUG_COMBINATION)
goto done;
/* Store the link key persistently if one of the following is true:
* If none of the above match only keep the link key around for
* this connection and set the temporary flag for the device.
*/
- if (key_type < 0x03 || (key_type == 0x06 && old_key_type != 0xff) ||
+ if (key_type < HCI_LK_DEBUG_COMBINATION ||
+ (key_type == HCI_LK_CHANGED_COMBINATION
+ && old_key_type != HCI_LK_INVALID) ||
(conn->loc_auth > 0x01 && conn->rem_auth > 0x01) ||
(conn->loc_auth == 0x02 || conn->loc_auth == 0x03) ||
(conn->rem_auth == 0x02 || conn->rem_auth == 0x03)) {
/* Simple Pairing handling */
-static int hciops_confirm_reply(int index, bdaddr_t *bdaddr, gboolean success)
+static int hciops_confirm_reply(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ gboolean success)
{
struct dev_info *dev = &devs[index];
user_confirm_reply_cp cp;
/* Wait 5 milliseconds before doing auto-accept */
usleep(5000);
- if (hciops_confirm_reply(index, &req->bdaddr, TRUE) < 0)
+ if (hciops_confirm_reply(index, &req->bdaddr,
+ BDADDR_BREDR, TRUE) < 0)
goto fail;
return;
init_adapter(index);
}
-static void update_ext_inquiry_response(int index)
-{
- struct dev_info *dev = &devs[index];
- write_ext_inquiry_response_cp cp;
-
- DBG("hci%d", index);
-
- if (!(dev->features[6] & LMP_EXT_INQ))
- return;
-
- if (dev->ssp_mode == 0)
- return;
-
- if (dev->cache_enable)
- return;
-
- memset(&cp, 0, sizeof(cp));
-
- 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;
-
- memcpy(dev->eir, cp.data, sizeof(cp.data));
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL,
- OCF_WRITE_EXT_INQUIRY_RESPONSE,
- WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE, &cp) < 0)
- error("Unable to write EIR data: %s (%d)",
- strerror(errno), errno);
-}
-
static void update_name(int index, const char *name)
{
struct btd_adapter *adapter;
cs_inquiry_evt(index, evt->status);
}
-static void read_scan_complete(int index, uint8_t status, void *ptr)
-{
- struct btd_adapter *adapter;
- read_scan_enable_rp *rp = ptr;
-
- DBG("hci%d status %u", index, status);
-
- adapter = manager_find_adapter_by_id(index);
- if (!adapter) {
- error("Unable to find matching adapter");
- return;
- }
-
- adapter_mode_changed(adapter, rp->enable);
-}
-
-static int write_class(int index, uint32_t class)
+static gboolean discoverable_timeout_handler(gpointer user_data)
{
- struct dev_info *dev = &devs[index];
- write_class_of_dev_cp cp;
-
- DBG("hci%d class 0x%06x", index, class);
-
- memcpy(cp.dev_class, &class, 3);
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_WRITE_CLASS_OF_DEV,
- WRITE_CLASS_OF_DEV_CP_SIZE, &cp) < 0)
- return -errno;
+ struct dev_info *dev = user_data;
- dev->pending_cod = class;
+ hciops_set_discoverable(dev->id, FALSE, 0);
- return 0;
+ return FALSE;
}
/* Limited Discoverable bit mask in CoD */
return write_class(index, dev->wanted_cod);
}
+static void reset_discoverable_timeout(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ if (dev->discoverable_id > 0) {
+ g_source_remove(dev->discoverable_id);
+ dev->discoverable_id = 0;
+ }
+}
+
+static void set_discoverable_timeout(int index)
+{
+ struct dev_info *dev = &devs[index];
+
+ reset_discoverable_timeout(index);
+
+ if (dev->discoverable_timeout == 0) {
+ hciops_set_limited_discoverable(index, FALSE);
+ return;
+ }
+
+ /* Set limited discoverable if pairable and interval between 0 to 60
+ sec */
+ if (dev->pairable && dev->discoverable_timeout <= 60)
+ hciops_set_limited_discoverable(index, TRUE);
+ else
+ hciops_set_limited_discoverable(index, FALSE);
+
+ dev->discoverable_id = g_timeout_add_seconds(dev->discoverable_timeout,
+ discoverable_timeout_handler,
+ dev);
+}
+
+static void read_scan_complete(int index, uint8_t status, void *ptr)
+{
+ struct btd_adapter *adapter;
+ read_scan_enable_rp *rp = ptr;
+
+ DBG("hci%d status %u", index, status);
+
+ switch (rp->enable) {
+ case (SCAN_PAGE | SCAN_INQUIRY):
+ case SCAN_INQUIRY:
+ set_discoverable_timeout(index);
+ break;
+ default:
+ reset_discoverable_timeout(index);
+ hciops_set_limited_discoverable(index, FALSE);
+ }
+
+ adapter = manager_find_adapter_by_id(index);
+ if (!adapter) {
+ error("Unable to find matching adapter");
+ return;
+ }
+
+ adapter_mode_changed(adapter, rp->enable);
+}
+
static void write_class_complete(int index, uint8_t status)
{
struct dev_info *dev = &devs[index];
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)
+static void dev_found(struct dev_info *info, bdaddr_t *dba, uint8_t bdaddr_type,
+ uint8_t *cod, int8_t rssi, uint8_t cfm_name,
+ uint8_t *eir, size_t eir_len)
{
struct found_dev *dev;
GSList *match;
else
dev->name_state = NAME_NOT_NEEDED;
+ if (cod && !eir_has_data_type(eir, eir_len, EIR_CLASS_OF_DEV))
+ eir_len = eir_append_data(eir, eir_len, EIR_CLASS_OF_DEV,
+ cod, 3);
+
info->found_devs = g_slist_prepend(info->found_devs, dev);
event:
- btd_event_device_found(&info->bdaddr, dba, type, cod, rssi, cfm_name,
+ btd_event_device_found(&info->bdaddr, dba, bdaddr_type, rssi, cfm_name,
eir, eir_len);
}
for (i = 0; i < num; i++) {
inquiry_info *info = ptr;
- uint32_t class = info->dev_class[0] |
- (info->dev_class[1] << 8) |
- (info->dev_class[2] << 16);
+ uint8_t eir[5];
- dev_found(dev, &info->bdaddr, ADDR_TYPE_BREDR, class, 0, 1,
- NULL, 0);
+ memset(eir, 0, sizeof(eir));
+ dev_found(dev, &info->bdaddr, BDADDR_BREDR, info->dev_class,
+ 0, 1, eir, 0);
ptr += INQUIRY_INFO_SIZE;
}
}
{
struct dev_info *dev = &devs[index];
uint8_t num = *(uint8_t *) ptr++;
+ uint8_t eir[5];
int i;
if (!num)
if ((plen - 1) / num == INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE) {
for (i = 0; i < num; i++) {
inquiry_info_with_rssi_and_pscan_mode *info = ptr;
- uint32_t class = info->dev_class[0]
- | (info->dev_class[1] << 8)
- | (info->dev_class[2] << 16);
- dev_found(dev, &info->bdaddr, ADDR_TYPE_BREDR, class,
- info->rssi, 1, NULL, 0);
+ memset(eir, 0, sizeof(eir));
+ dev_found(dev, &info->bdaddr, BDADDR_BREDR,
+ info->dev_class, info->rssi,
+ 1, eir, 0);
ptr += INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE;
}
} else {
for (i = 0; i < num; i++) {
inquiry_info_with_rssi *info = ptr;
- uint32_t class = info->dev_class[0]
- | (info->dev_class[1] << 8)
- | (info->dev_class[2] << 16);
- dev_found(dev, &info->bdaddr, ADDR_TYPE_BREDR, class,
- info->rssi, 1, NULL, 0);
+ memset(eir, 0, sizeof(eir));
+ dev_found(dev, &info->bdaddr, BDADDR_BREDR,
+ info->dev_class, info->rssi,
+ 1, eir, 0);
ptr += INQUIRY_INFO_WITH_RSSI_SIZE;
}
}
for (i = 0; i < num; i++) {
extended_inquiry_info *info = ptr;
- uint32_t class = info->dev_class[0]
- | (info->dev_class[1] << 8)
- | (info->dev_class[2] << 16);
+ uint8_t eir[sizeof(info->data) + 5];
gboolean cfm_name;
+ size_t eir_len;
+
+ eir_len = eir_length(info->data, sizeof(info->data));
- if (eir_has_complete_name(info->data, sizeof(info->data)))
+ memset(eir, 0, sizeof(eir));
+ memcpy(eir, info->data, eir_len);
+
+ if (eir_has_data_type(eir, eir_len, EIR_NAME_COMPLETE))
cfm_name = FALSE;
else
cfm_name = TRUE;
- dev_found(dev, &info->bdaddr, ADDR_TYPE_BREDR, class,
- info->rssi, cfm_name,
- info->data, sizeof(info->data));
+ dev_found(dev, &info->bdaddr, BDADDR_BREDR, info->dev_class,
+ info->rssi, cfm_name, eir, eir_len);
ptr += EXTENDED_INQUIRY_INFO_SIZE;
}
}
conn = get_connection(dev, &evt->bdaddr);
conn->handle = btohs(evt->handle);
- btd_event_conn_complete(&dev->bdaddr, &evt->bdaddr);
+ btd_event_conn_complete(&dev->bdaddr, &evt->bdaddr, BDADDR_BREDR,
+ NULL, NULL);
if (conn->secmode3)
bonding_complete(dev, conn, 0);
free(str);
}
+static inline uint8_t le_addr_type(uint8_t bdaddr_type)
+{
+ switch (bdaddr_type) {
+ case LE_RANDOM_ADDRESS:
+ return BDADDR_LE_RANDOM;
+ case LE_PUBLIC_ADDRESS:
+ default:
+ return BDADDR_LE_PUBLIC;
+ }
+}
+
static inline void le_conn_complete(int index, void *ptr)
{
struct dev_info *dev = &devs[index];
char filename[PATH_MAX];
char local_addr[18], peer_addr[18], *str;
struct bt_conn *conn;
+ uint8_t bdaddr_type;
if (evt->status) {
btd_event_conn_failed(&dev->bdaddr, &evt->peer_bdaddr,
conn = get_connection(dev, &evt->peer_bdaddr);
conn->handle = btohs(evt->handle);
- btd_event_conn_complete(&dev->bdaddr, &evt->peer_bdaddr);
+ bdaddr_type = le_addr_type(evt->peer_bdaddr_type);
+ btd_event_conn_complete(&dev->bdaddr, &evt->peer_bdaddr, bdaddr_type,
+ NULL, NULL);
/* check if the remote version needs be requested */
ba2str(&dev->bdaddr, local_addr);
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];
info = (le_advertising_info *) &meta->data[1];
rssi = *(info->data + info->length);
- dev_found(dev, &info->bdaddr, le_addr_type(info->bdaddr_type), 0, rssi,
- 0, info->data, info->length);
+ dev_found(dev, &info->bdaddr, le_addr_type(info->bdaddr_type), NULL,
+ rssi, 0, info->data, info->length);
num_reports--;
rssi = *(info->data + info->length);
dev_found(dev, &info->bdaddr, le_addr_type(info->bdaddr_type),
- 0, rssi, 0, info->data, info->length);
+ NULL, rssi, 0, info->data, info->length);
}
}
devs[index].pending_cod = 0;
devs[index].cache_enable = TRUE;
devs[index].discov_state = DISCOV_HALTED;
+ reset_discoverable_timeout(index);
if (!devs[index].pending) {
struct btd_adapter *adapter;
return err;
}
-static int hciops_set_dev_class(int index, uint8_t major, uint8_t minor)
-{
- struct dev_info *dev = &devs[index];
- int err;
-
- DBG("hci%d major %u minor %u", index, major, minor);
-
- /* Update only the major and minor class bits keeping remaining bits
- * intact*/
- dev->wanted_cod &= 0xffe000;
- dev->wanted_cod |= ((major & 0x1f) << 8) | minor;
-
- if (dev->wanted_cod == dev->current_cod ||
- dev->cache_enable || dev->pending_cod)
- return 0;
-
- DBG("Changing Major/Minor class to 0x%06x", dev->wanted_cod);
-
- err = write_class(index, dev->wanted_cod);
- if (err < 0)
- error("Adapter class update failed: %s (%d)",
- strerror(-err), -err);
-
- return err;
-}
-
static int start_inquiry(int index, uint8_t length)
{
struct dev_info *dev = &devs[index];
return le_set_scan_enable(index, 0);
}
-static int hciops_set_name(int index, const char *name)
-{
- struct dev_info *dev = &devs[index];
- change_local_name_cp cp;
-
- DBG("hci%d, name %s", index, name);
-
- memset(&cp, 0, sizeof(cp));
- strncpy((char *) cp.name, name, sizeof(cp.name));
-
- if (hci_send_cmd(dev->sk, OGF_HOST_CTL, OCF_CHANGE_LOCAL_NAME,
- CHANGE_LOCAL_NAME_CP_SIZE, &cp) < 0)
- return -errno;
-
- memcpy(dev->name, cp.name, 248);
- update_ext_inquiry_response(index);
-
- return 0;
-}
-
static int cancel_resolve_name(int index)
{
struct dev_info *info = &devs[index];
return 0;
}
-static int hciops_block_device(int index, bdaddr_t *bdaddr)
+static int hciops_block_device(int index, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
{
struct dev_info *dev = &devs[index];
char addr[18];
return 0;
}
-static int hciops_unblock_device(int index, bdaddr_t *bdaddr)
+static int hciops_unblock_device(int index, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
{
struct dev_info *dev = &devs[index];
char addr[18];
return 0;
}
-static int hciops_disconnect(int index, bdaddr_t *bdaddr)
+static int hciops_disconnect(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type)
{
DBG("hci%d", index);
return disconnect_addr(index, bdaddr, HCI_OE_USER_ENDED_CONNECTION);
}
-static int hciops_remove_bonding(int index, bdaddr_t *bdaddr)
+static int hciops_remove_bonding(int index, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
{
struct dev_info *dev = &devs[index];
delete_stored_link_key_cp cp;
return err;
}
-static int hciops_passkey_reply(int index, bdaddr_t *bdaddr, uint32_t passkey)
+static int hciops_passkey_reply(int index, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type, uint32_t passkey)
{
struct dev_info *dev = &devs[index];
char addr[18];
static int hciops_load_keys(int index, GSList *keys, gboolean debug_keys)
{
struct dev_info *dev = &devs[index];
+ GSList *l;
DBG("hci%d keys %d debug_keys %d", index, g_slist_length(keys),
debug_keys);
if (dev->keys != NULL)
return -EEXIST;
- dev->keys = keys;
+ for (l = keys; l; l = l->next) {
+ struct link_key_info *orig, *dup;
+
+ orig = l->data;
+
+ dup = g_memdup(orig, sizeof(*orig));
+
+ dev->keys = g_slist_prepend(dev->keys, dup);
+ }
+
dev->debug_keys = debug_keys;
return 0;
{
struct dev_info *dev = &devs[index];
- dev->io_capability = io_capability;
+ /* hciops is not to be used for SMP pairing for LE devices. So
+ * change the IO capability from KeyboardDisplay to DisplayYesNo
+ * in case it is set. */
+ dev->io_capability = (io_capability == 0x04) ? 0x01 : io_capability;
return 0;
}
bonding_complete(dev, conn, HCI_UNSPECIFIED_ERROR);
}
-static int hciops_create_bonding(int index, bdaddr_t *bdaddr, uint8_t io_cap)
+static int hciops_create_bonding(int index, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type, uint8_t io_cap)
{
struct dev_info *dev = &devs[index];
BtIOSecLevel sec_level;
if (conn->io != NULL)
return -EBUSY;
- conn->loc_cap = io_cap;
+ /* hciops is not to be used for SMP pairing for LE devices. So
+ * change the IO capability from KeyboardDisplay to DisplayYesNo
+ * in case it is set. */
+ conn->loc_cap = (io_cap == 0x04 ? 0x01 : io_cap);
/* If our IO capability is NoInputNoOutput use medium security
* level (i.e. don't require MITM protection) else use high
}
static int hciops_confirm_name(int index, bdaddr_t *bdaddr,
- gboolean name_known)
+ uint8_t bdaddr_type, gboolean name_known)
{
struct dev_info *info = &devs[index];
struct found_dev *dev;
return 0;
}
+static int hciops_load_ltks(int index, GSList *keys)
+{
+ return -ENOSYS;
+}
+
static struct btd_adapter_ops hci_ops = {
.setup = hciops_setup,
.cleanup = hciops_cleanup,
.set_powered = hciops_set_powered,
.set_discoverable = hciops_set_discoverable,
.set_pairable = hciops_set_pairable,
- .set_limited_discoverable = hciops_set_limited_discoverable,
.start_discovery = hciops_start_discovery,
.stop_discovery = hciops_stop_discovery,
.set_name = hciops_set_name,
.add_remote_oob_data = hciops_add_remote_oob_data,
.remove_remote_oob_data = hciops_remove_remote_oob_data,
.confirm_name = hciops_confirm_name,
+ .load_ltks = hciops_load_ltks,
};
static int hciops_init(void)
goto done;
}
- dbus_error_init(&derr);
if (dbus_message_get_args(reply, &derr,
DBUS_TYPE_UINT32, &radio_states,
DBUS_TYPE_INVALID) == FALSE) {
#include "device.h"
#include "event.h"
#include "oob.h"
+#include "eir.h"
#define MGMT_BUF_SIZE 1024
+struct pending_uuid {
+ uuid_t uuid;
+ uint8_t svc_hint;
+};
+
static int max_index = -1;
static struct controller_info {
gboolean valid;
uint32_t current_settings;
uint8_t dev_class[3];
GSList *connections;
+ uint8_t discov_type;
+
+ gboolean pending_uuid;
+ GSList *pending_uuids;
+
+ gboolean pending_class;
+ uint8_t major;
+ uint8_t minor;
+
+ gboolean pending_powered;
+ gboolean pending_cod_change;
} *controllers = NULL;
static int mgmt_sock = -1;
struct mgmt_rp_read_version *rp = buf;
if (len < sizeof(*rp)) {
- error("Too small read version complete event");
- return;
+ error("Too small read version complete event"
+ " (probably an old kernel)");
+ abort();
}
mgmt_revision = btohs(bt_get_unaligned(&rp->revision));
DBG("version %u revision %u", mgmt_version, mgmt_revision);
+ if (mgmt_version < 1) {
+ error("Version 1 of mgmt needed (kernel has version %u)",
+ mgmt_version);
+ abort();
+ }
+
memset(&hdr, 0, sizeof(hdr));
hdr.opcode = htobs(MGMT_OP_READ_INDEX_LIST);
hdr.index = htobs(MGMT_INDEX_NONE);
static void add_controller(uint16_t index)
{
+ struct controller_info *info;
+
if (index > max_index) {
size_t size = sizeof(struct controller_info) * (index + 1);
max_index = index;
controllers = g_realloc(controllers, size);
}
- memset(&controllers[index], 0, sizeof(struct controller_info));
+ info = &controllers[index];
+
+ memset(info, 0, sizeof(*info));
- controllers[index].valid = TRUE;
+ info->valid = TRUE;
DBG("Added controller %u", index);
}
btd_manager_unregister_adapter(index);
+ g_slist_free_full(controllers[index].pending_uuids, g_free);
+ controllers[index].pending_uuids = NULL;
+
memset(&controllers[index], 0, sizeof(struct controller_info));
DBG("Removed controller %u", index);
return mgmt_set_mode(index, MGMT_OP_SET_CONNECTABLE, connectable);
}
-static int mgmt_set_discoverable(int index, gboolean discoverable)
+static int mgmt_set_discoverable(int index, gboolean discoverable,
+ uint16_t timeout)
{
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_discoverable)];
struct mgmt_hdr *hdr = (void *) buf;
hdr->len = htobs(sizeof(*cp));
cp->val = discoverable;
+ cp->timeout = timeout;
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
return -errno;
return mode;
}
-static int mgmt_update_powered(struct btd_adapter *adapter, uint32_t settings)
+static void update_settings(struct btd_adapter *adapter, uint32_t settings)
{
+ struct controller_info *info;
gboolean pairable;
uint8_t on_mode;
- uint16_t index;
+ uint16_t index, discoverable_timeout;
- if (!mgmt_powered(settings)) {
- btd_adapter_stop(adapter);
- return 0;
- }
-
- btd_adapter_start(adapter);
+ DBG("new settings %x", settings);
- btd_adapter_get_mode(adapter, NULL, &on_mode, &pairable);
+ btd_adapter_get_mode(adapter, NULL, &on_mode, &discoverable_timeout,
+ &pairable);
index = adapter_get_dev_id(adapter);
- if (on_mode == MODE_DISCOVERABLE && !mgmt_discoverable(settings))
- mgmt_set_discoverable(index, TRUE);
- else if (on_mode == MODE_CONNECTABLE && !mgmt_connectable(settings))
+ info = &controllers[index];
+
+ if (on_mode == MODE_DISCOVERABLE && !mgmt_discoverable(settings)) {
+ if(!mgmt_connectable(settings))
+ mgmt_set_connectable(index, TRUE);
+ mgmt_set_discoverable(index, TRUE, discoverable_timeout);
+ } else if (on_mode == MODE_CONNECTABLE && !mgmt_connectable(settings)) {
mgmt_set_connectable(index, TRUE);
- else
+ } else if (mgmt_powered(settings)) {
adapter_mode_changed(adapter, create_mode(settings));
+ }
if (mgmt_pairable(settings) != pairable)
mgmt_set_pairable(index, pairable);
+ if (mgmt_ssp(info->supported_settings) && !mgmt_ssp(settings))
+ mgmt_set_mode(index, MGMT_OP_SET_SSP, 1);
+
+ if (mgmt_low_energy(info->supported_settings) &&
+ !mgmt_low_energy(settings))
+ mgmt_set_mode(index, MGMT_OP_SET_LE, 1);
+}
+
+static int mgmt_update_powered(struct btd_adapter *adapter,
+ struct controller_info *info,
+ uint32_t settings)
+{
+ if (!mgmt_powered(settings)) {
+ btd_adapter_stop(adapter);
+ g_slist_free_full(info->pending_uuids, g_free);
+ info->pending_uuids = NULL;
+ info->pending_uuid = FALSE;
+ info->pending_class = FALSE;
+ info->pending_cod_change = FALSE;
+ return 0;
+ }
+
+ btd_adapter_start(adapter);
+
+ update_settings(adapter, settings);
+
return 0;
}
uint32_t settings, *ev = buf;
struct controller_info *info;
struct btd_adapter *adapter;
+ gboolean old_power, new_power, old_pairable, new_pairable;
if (len < sizeof(*ev)) {
error("Too small new settings event");
settings = bt_get_le32(ev);
- if (mgmt_powered(settings) != mgmt_powered(info->current_settings))
- mgmt_update_powered(adapter, settings);
- else if (mode_changed(settings, info->current_settings))
+ old_power = mgmt_powered(info->current_settings);
+ new_power = mgmt_powered(settings);
+
+ if (new_power != old_power)
+ mgmt_update_powered(adapter, info, settings);
+ else if (new_power && mode_changed(settings, info->current_settings))
adapter_mode_changed(adapter, create_mode(settings));
- if (mgmt_pairable(settings) != mgmt_pairable(info->current_settings))
+ old_pairable = mgmt_pairable(info->current_settings);
+ new_pairable = mgmt_pairable(settings);
+
+ /* Check for pairable change, except when powered went from True
+ * to False (in which case we always get all settings as False) */
+ if ((!old_power || new_power) && new_pairable != old_pairable)
btd_adapter_pairable_changed(adapter, mgmt_pairable(settings));
info->current_settings = settings;
struct controller_info *info;
if (len != sizeof(*ev)) {
- error("new_key event size mismatch (%zu != %zu)",
+ error("mgmt_new_link_key event size mismatch (%zu != %zu)",
len, sizeof(*ev));
return;
}
info = &controllers[index];
if (ev->store_hint)
- btd_event_link_key_notify(&info->bdaddr, &ev->key.bdaddr,
+ btd_event_link_key_notify(&info->bdaddr, &ev->key.addr.bdaddr,
ev->key.val, ev->key.type,
ev->key.pin_len);
- bonding_complete(info, &ev->key.bdaddr, 0);
+ bonding_complete(info, &ev->key.addr.bdaddr, 0);
}
static void mgmt_device_connected(int sk, uint16_t index, void *buf, size_t len)
{
- struct mgmt_addr_info *ev = buf;
+ struct mgmt_ev_device_connected *ev = buf;
+ struct eir_data eir_data;
struct controller_info *info;
+ uint16_t eir_len;
char addr[18];
if (len < sizeof(*ev)) {
return;
}
- ba2str(&ev->bdaddr, addr);
+ eir_len = bt_get_le16(&ev->eir_len);
+ if (len < sizeof(*ev) + eir_len) {
+ error("Too small device_connected event");
+ return;
+ }
- DBG("hci%u device %s connected", index, addr);
+ ba2str(&ev->addr.bdaddr, addr);
+
+ DBG("hci%u device %s connected eir_len %u", index, addr, eir_len);
if (index > max_index) {
error("Unexpected index %u in device_connected event", index);
info = &controllers[index];
- btd_event_conn_complete(&info->bdaddr, &ev->bdaddr);
+ memset(&eir_data, 0, sizeof(eir_data));
+ if (eir_len > 0)
+ eir_parse(&eir_data, ev->eir, eir_len);
+
+ btd_event_conn_complete(&info->bdaddr, &ev->addr.bdaddr,
+ ev->addr.type,
+ eir_data.name,
+ eir_data.dev_class);
+
+ eir_data_free(&eir_data);
}
static void mgmt_device_disconnected(int sk, uint16_t index, void *buf,
hdr->index = htobs(index);
cp = (void *) &buf[sizeof(*hdr)];
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = BDADDR_BREDR;
buf_len = sizeof(*hdr) + sizeof(*cp);
} else {
hdr->index = htobs(index);
cp = (void *) &buf[sizeof(*hdr)];
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = BDADDR_BREDR;
cp->pin_len = pin_len;
memcpy(cp->pin_code, pin, pin_len);
return;
}
- ba2str(&ev->bdaddr, addr);
+ ba2str(&ev->addr.bdaddr, addr);
DBG("hci%u %s", index, addr);
info = &controllers[index];
- err = btd_event_request_pin(&info->bdaddr, &ev->bdaddr, ev->secure);
+ err = btd_event_request_pin(&info->bdaddr, &ev->addr.bdaddr,
+ ev->secure);
if (err < 0) {
error("btd_event_request_pin: %s", strerror(-err));
- mgmt_pincode_reply(index, &ev->bdaddr, NULL, 0);
+ mgmt_pincode_reply(index, &ev->addr.bdaddr, NULL, 0);
}
}
-static int mgmt_confirm_reply(int index, bdaddr_t *bdaddr, gboolean success)
+static int mgmt_confirm_reply(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ gboolean success)
{
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_user_confirm_reply)];
struct mgmt_hdr *hdr = (void *) buf;
hdr->index = htobs(index);
cp = (void *) &buf[sizeof(*hdr)];
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = bdaddr_type;
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
return -errno;
return 0;
}
-static int mgmt_passkey_reply(int index, bdaddr_t *bdaddr, uint32_t passkey)
+static int mgmt_passkey_reply(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ uint32_t passkey)
{
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_user_passkey_reply)];
struct mgmt_hdr *hdr = (void *) buf;
hdr->len = htobs(sizeof(*cp));
cp = (void *) &buf[sizeof(*hdr)];
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = bdaddr_type;
buf_len = sizeof(*hdr) + sizeof(*cp);
} else {
hdr->len = htobs(sizeof(*cp));
cp = (void *) &buf[sizeof(*hdr)];
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = bdaddr_type;
cp->passkey = htobl(passkey);
buf_len = sizeof(*hdr) + sizeof(*cp);
return;
}
- ba2str(&ev->bdaddr, addr);
+ ba2str(&ev->addr.bdaddr, addr);
DBG("hci%u %s", index, addr);
info = &controllers[index];
- err = btd_event_user_passkey(&info->bdaddr, &ev->bdaddr);
+ err = btd_event_user_passkey(&info->bdaddr, &ev->addr.bdaddr);
if (err < 0) {
error("btd_event_user_passkey: %s", strerror(-err));
- mgmt_passkey_reply(index, &ev->bdaddr, INVALID_PASSKEY);
+ mgmt_passkey_reply(index, &ev->addr.bdaddr, ev->addr.type,
+ INVALID_PASSKEY);
}
}
struct confirm_data {
int index;
bdaddr_t bdaddr;
+ uint8_t type;
};
static gboolean confirm_accept(gpointer user_data)
if (data->index > max_index || !info->valid)
return FALSE;
- mgmt_confirm_reply(data->index, &data->bdaddr, TRUE);
+ mgmt_confirm_reply(data->index, &data->bdaddr, data->type, TRUE);
return FALSE;
}
return;
}
- ba2str(&ev->bdaddr, addr);
+ ba2str(&ev->addr.bdaddr, addr);
DBG("hci%u %s confirm_hint %u", index, addr, ev->confirm_hint);
data = g_new0(struct confirm_data, 1);
data->index = index;
- bacpy(&data->bdaddr, &ev->bdaddr);
+ bacpy(&data->bdaddr, &ev->addr.bdaddr);
+ data->type = ev->addr.type;
g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, 1,
confirm_accept, data, g_free);
info = &controllers[index];
- err = btd_event_user_confirm(&info->bdaddr, &ev->bdaddr,
+ err = btd_event_user_confirm(&info->bdaddr, &ev->addr.bdaddr,
btohl(ev->value));
if (err < 0) {
error("btd_event_user_confirm: %s", strerror(-err));
- mgmt_confirm_reply(index, &ev->bdaddr, FALSE);
+ mgmt_confirm_reply(index, &ev->addr.bdaddr, ev->addr.type,
+ FALSE);
}
}
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_add_uuid)];
struct mgmt_hdr *hdr = (void *) buf;
struct mgmt_cp_add_uuid *cp = (void *) &buf[sizeof(*hdr)];
+ struct controller_info *info = &controllers[index];
uuid_t uuid128;
uint128_t uint128;
DBG("index %d", index);
+ if (info->pending_uuid) {
+ struct pending_uuid *pending = g_new0(struct pending_uuid, 1);
+
+ memcpy(&pending->uuid, uuid, sizeof(*uuid));
+ pending->svc_hint = svc_hint;
+
+ info->pending_uuids = g_slist_append(info->pending_uuids,
+ pending);
+ return 0;
+ }
+
uuid_to_uuid128(&uuid128, uuid);
memset(buf, 0, sizeof(buf));
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
return -errno;
+ info->pending_uuid = TRUE;
+
return 0;
}
index = btohs(bt_get_unaligned(&rp->index[i]));
add_controller(index);
- get_connections(sk, index);
- clear_uuids(index);
+ read_info(sk, index);
}
}
static int mgmt_set_powered(int index, gboolean powered)
{
- DBG("index %d powered %d", index, powered);
+ struct controller_info *info = &controllers[index];
+
+ DBG("index %d powered %d pending_uuid %u", index, powered,
+ info->pending_uuid);
+
+ if (powered) {
+ if (info->pending_uuid) {
+ info->pending_powered = TRUE;
+ return 0;
+ }
+ } else {
+ info->pending_powered = FALSE;
+ }
+
return mgmt_set_mode(index, MGMT_OP_SET_POWERED, powered);
}
+static int mgmt_set_name(int index, const char *name)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_local_name)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_set_local_name *cp = (void *) &buf[sizeof(*hdr)];
+
+ DBG("index %d, name %s", index, name);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_SET_LOCAL_NAME);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ strncpy((char *) cp->name, name, sizeof(cp->name) - 1);
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int mgmt_set_dev_class(int index, uint8_t major, uint8_t minor)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_dev_class)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_set_dev_class *cp = (void *) &buf[sizeof(*hdr)];
+ struct controller_info *info = &controllers[index];
+
+ DBG("index %d major %u minor %u", index, major, minor);
+
+ if (info->pending_uuid) {
+ info->major = major;
+ info->minor = minor;
+ info->pending_class = TRUE;
+ return 0;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_SET_DEV_CLASS);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ cp->major = major;
+ cp->minor = minor;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
static void read_info_complete(int sk, uint16_t index, void *buf, size_t len)
{
struct mgmt_rp_read_info *rp = buf;
struct controller_info *info;
struct btd_adapter *adapter;
- uint8_t mode;
+ const char *name;
+ uint8_t mode, major, minor;
char addr[18];
if (len < sizeof(*rp)) {
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);
+ clear_uuids(index);
+
+ adapter = btd_manager_register_adapter(index,
+ mgmt_powered(info->current_settings));
if (adapter == NULL) {
error("mgmtops: unable to register adapter");
return;
}
- btd_adapter_get_mode(adapter, &mode, NULL, NULL);
- if (mode == MODE_OFF) {
+ update_settings(adapter, info->current_settings);
+
+ name = btd_adapter_get_name(adapter);
+
+ DBG("mgmtops setting name %s", name);
+
+ if (name)
+ mgmt_set_name(index, name);
+ else
+ adapter_name_changed(adapter, (char *) rp->name);
+
+ btd_adapter_get_class(adapter, &major, &minor);
+ mgmt_set_dev_class(index, major, minor);
+
+ btd_adapter_get_mode(adapter, &mode, NULL, NULL, NULL);
+ if (mode == MODE_OFF && mgmt_powered(info->current_settings)) {
mgmt_set_powered(index, FALSE);
return;
}
- if (mgmt_powered(info->current_settings))
- mgmt_update_powered(adapter, info->current_settings);
- else
+ if (mode != MODE_OFF && !mgmt_powered(info->current_settings))
mgmt_set_powered(index, TRUE);
-
- adapter_name_changed(adapter, (char *) rp->name);
+ else {
+ get_connections(sk, index);
+ btd_adapter_start(adapter);
+ }
btd_adapter_unref(adapter);
}
-static void disconnect_complete(int sk, uint16_t index, void *buf, size_t len)
+static void disconnect_complete(int sk, uint16_t index, uint8_t status,
+ void *buf, size_t len)
{
struct mgmt_rp_disconnect *rp = buf;
struct controller_info *info;
return;
}
- ba2str(&rp->bdaddr, addr);
+ ba2str(&rp->addr.bdaddr, addr);
- if (rp->status != 0) {
- error("Disconnecting %s failed with status %u",
- addr, rp->status);
+ if (status != 0) {
+ error("Disconnecting %s failed with status %u", addr, status);
return;
}
info = &controllers[index];
- btd_event_disconn_complete(&info->bdaddr, &rp->bdaddr);
+ btd_event_disconn_complete(&info->bdaddr, &rp->addr.bdaddr);
- bonding_complete(info, &rp->bdaddr, HCI_CONNECTION_TERMINATED);
+ bonding_complete(info, &rp->addr.bdaddr, HCI_CONNECTION_TERMINATED);
}
-static void pair_device_complete(int sk, uint16_t index, void *buf, size_t len)
+static void pair_device_complete(int sk, uint16_t index, uint8_t status,
+ void *buf, size_t len)
{
struct mgmt_rp_pair_device *rp = buf;
struct controller_info *info;
ba2str(&rp->addr.bdaddr, addr);
- DBG("hci%d %s pairing complete status %u", index, addr, rp->status);
+ DBG("hci%d %s pairing complete status %u", index, addr, status);
if (index > max_index) {
error("Unexpected index %u in pair_device complete", index);
info = &controllers[index];
- bonding_complete(info, &rp->addr.bdaddr, rp->status);
+ bonding_complete(info, &rp->addr.bdaddr, status);
}
static void get_connections_complete(int sk, uint16_t index, void *buf,
bdaddr_t *bdaddr = g_memdup(&rp->addr[i], sizeof(bdaddr_t));
info->connections = g_slist_append(info->connections, bdaddr);
}
-
- read_info(sk, index);
}
static void set_local_name_complete(int sk, uint16_t index, void *buf,
struct btd_adapter *adapter;
if (len != sizeof(*rp)) {
- error("Wrong read_local_oob_data_complete event size");
+ error("read_local_oob_data_complete event size mismatch "
+ "(%zu != %zu)", len, sizeof(*rp));
return;
}
oob_read_local_data_complete(adapter, rp->hash, rp->randomizer);
}
+static void start_discovery_complete(int sk, uint16_t index, uint8_t status,
+ void *buf, size_t len)
+{
+ uint8_t *type = buf;
+ struct btd_adapter *adapter;
+
+ if (len != sizeof(*type)) {
+ error("start_discovery_complete event size mismatch "
+ "(%zu != %zu)", len, sizeof(*type));
+ return;
+ }
+
+ DBG("hci%u type %u status %u", index, *type, status);
+
+ if (index > max_index) {
+ error("Invalid index %u in start_discovery_complete", index);
+ return;
+ }
+
+ if (!status)
+ return;
+
+ adapter = manager_find_adapter_by_id(index);
+ if (adapter)
+ /* Start discovery failed, inform upper layers. */
+ adapter_set_discovering(adapter, FALSE);
+}
+
static void read_local_oob_data_failed(int sk, uint16_t index)
{
struct btd_adapter *adapter;
oob_read_local_data_complete(adapter, NULL, NULL);
}
+static void handle_pending_uuids(uint16_t index)
+{
+ struct controller_info *info;
+ struct pending_uuid *pending;
+
+ DBG("index %d", index);
+
+ info = &controllers[index];
+
+ info->pending_uuid = FALSE;
+
+ if (g_slist_length(info->pending_uuids) == 0) {
+ if (info->pending_class) {
+ info->pending_class = FALSE;
+ mgmt_set_dev_class(index, info->major, info->minor);
+ }
+
+ if (info->pending_powered) {
+ info->pending_powered = FALSE;
+ mgmt_set_powered(index, TRUE);
+ }
+
+ return;
+ }
+
+ pending = info->pending_uuids->data;
+
+ mgmt_add_uuid(index, &pending->uuid, pending->svc_hint);
+
+ info->pending_uuids = g_slist_remove(info->pending_uuids, pending);
+ g_free(pending);
+}
+
+static void mgmt_add_uuid_complete(int sk, uint16_t index, void *buf,
+ size_t len)
+{
+ DBG("index %d", index);
+
+ if (index > max_index) {
+ error("Unexpected index %u in add_uuid_complete event", index);
+ return;
+ }
+
+ handle_pending_uuids(index);
+}
+
static void mgmt_cmd_complete(int sk, uint16_t index, void *buf, size_t len)
{
struct mgmt_ev_cmd_complete *ev = buf;
case MGMT_OP_SET_PAIRABLE:
mgmt_new_settings(sk, index, ev->data, len);
break;
+ case MGMT_OP_SET_SSP:
+ DBG("set_ssp complete");
+ break;
+ case MGMT_OP_SET_LE:
+ DBG("set_le complete");
+ break;
case MGMT_OP_ADD_UUID:
- DBG("add_uuid complete");
+ mgmt_add_uuid_complete(sk, index, ev->data, len);
break;
case MGMT_OP_REMOVE_UUID:
DBG("remove_uuid complete");
case MGMT_OP_LOAD_LINK_KEYS:
DBG("load_link_keys complete");
break;
- case MGMT_OP_REMOVE_KEYS:
- DBG("remove_keys complete");
+ case MGMT_OP_CANCEL_PAIR_DEVICE:
+ DBG("cancel_pair_device complete");
+ break;
+ case MGMT_OP_UNPAIR_DEVICE:
+ DBG("unpair_device complete");
break;
case MGMT_OP_DISCONNECT:
DBG("disconnect complete");
- disconnect_complete(sk, index, ev->data, len);
+ disconnect_complete(sk, index, ev->status, ev->data, len);
break;
case MGMT_OP_GET_CONNECTIONS:
get_connections_complete(sk, index, ev->data, len);
DBG("set_io_capability complete");
break;
case MGMT_OP_PAIR_DEVICE:
- pair_device_complete(sk, index, ev->data, len);
+ pair_device_complete(sk, index, ev->status, ev->data, len);
break;
case MGMT_OP_USER_CONFIRM_REPLY:
DBG("user_confirm_reply complete");
DBG("set_fast_connectable complete");
break;
case MGMT_OP_START_DISCOVERY:
- DBG("start_discovery complete");
+ start_discovery_complete(sk, index, ev->status, ev->data, len);
break;
case MGMT_OP_STOP_DISCOVERY:
DBG("stop_discovery complete");
break;
+ case MGMT_OP_SET_DEVICE_ID:
+ DBG("set_did complete");
+ break;
default:
error("Unknown command complete for opcode %u", opcode);
break;
}
}
+static void mgmt_add_uuid_busy(int sk, uint16_t index)
+{
+ struct controller_info *info;
+
+ DBG("index %d", index);
+
+ info = &controllers[index];
+ info->pending_cod_change = TRUE;
+}
+
static void mgmt_cmd_status(int sk, uint16_t index, void *buf, size_t len)
{
struct mgmt_ev_cmd_status *ev = buf;
opcode = btohs(bt_get_unaligned(&ev->opcode));
- DBG("status %u opcode %u (index %u)", ev->status, opcode, index);
+ if (!ev->status) {
+ DBG("%s (0x%04x) cmd_status %u", mgmt_opstr(opcode), opcode,
+ ev->status);
+ return;
+ }
switch (opcode) {
case MGMT_OP_READ_LOCAL_OOB_DATA:
read_local_oob_data_failed(sk, index);
break;
+ case MGMT_OP_ADD_UUID:
+ if (ev->status == MGMT_STATUS_BUSY) {
+ mgmt_add_uuid_busy(sk, index);
+ return;
+ }
+ break;
}
+
+ error("hci%u: %s (0x%04x) failed: %s (0x%02x)", index,
+ mgmt_opstr(opcode), opcode, mgmt_errstr(ev->status),
+ ev->status);
}
static void mgmt_controller_error(int sk, uint16_t index, void *buf, size_t len)
info = &controllers[index];
- bonding_complete(info, &ev->bdaddr, ev->status);
+ bonding_complete(info, &ev->addr.bdaddr, ev->status);
}
static void mgmt_local_name_changed(int sk, uint16_t index, void *buf, size_t len)
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];
+ uint32_t flags;
+ uint16_t eir_len;
uint8_t *eir;
- uint32_t cls;
+ gboolean confirm_name;
- if (len != sizeof(*ev)) {
- error("mgmt_device_found length %zu instead of expected %zu",
- len, sizeof(*ev));
+ if (len < sizeof(*ev)) {
+ error("mgmt_device_found too short (%zu bytes)", len);
+ return;
+ }
+
+ eir_len = bt_get_le16(&ev->eir_len);
+ if (len != sizeof(*ev) + eir_len) {
+ error("mgmt_device_found event size mismatch (%zu != %zu)",
+ len, sizeof(*ev) + eir_len);
return;
}
info = &controllers[index];
- cls = ev->dev_class[0] | (ev->dev_class[1] << 8) |
- (ev->dev_class[2] << 16);
-
- if (ev->eir[0] == 0)
+ if (eir_len == 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);
-}
+ flags = btohl(ev->flags);
-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;
- }
+ ba2str(&ev->addr.bdaddr, addr);
+ DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u",
+ index, addr, ev->rssi, flags, eir_len);
- if (index > max_index) {
- error("Unexpected index %u in remote_name event", index);
- return;
- }
+ if (flags & MGMT_DEV_FOUND_LEGACY_PAIRING)
+ btd_event_set_legacy_pairing(&info->bdaddr, &ev->addr.bdaddr,
+ TRUE);
+ else
+ btd_event_set_legacy_pairing(&info->bdaddr, &ev->addr.bdaddr,
+ FALSE);
- info = &controllers[index];
+ confirm_name = (flags & MGMT_DEV_FOUND_CONFIRM_NAME);
- 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);
+ btd_event_device_found(&info->bdaddr, &ev->addr.bdaddr,
+ ev->addr.type,
+ ev->rssi, confirm_name,
+ eir, eir_len);
}
static void mgmt_discovering(int sk, uint16_t index, void *buf, size_t len)
{
- struct mgmt_mode *ev = buf;
+ struct mgmt_ev_discovering *ev = buf;
struct controller_info *info;
struct btd_adapter *adapter;
return;
}
- DBG("Controller %u discovering %u", index, ev->val);
+ DBG("Controller %u type %u discovering %u", index,
+ ev->type, ev->discovering);
if (index > max_index) {
error("Unexpected index %u in discovering event", index);
if (!adapter)
return;
- adapter_set_discovering(adapter, ev->val);
+ adapter_set_discovering(adapter, ev->discovering);
}
static void mgmt_device_blocked(int sk, uint16_t index, void *buf, size_t len)
return;
}
- ba2str(&ev->bdaddr, addr);
+ ba2str(&ev->addr.bdaddr, addr);
DBG("Device blocked, index %u, addr %s", index, addr);
if (index > max_index) {
info = &controllers[index];
- btd_event_device_blocked(&info->bdaddr, &ev->bdaddr);
+ btd_event_device_blocked(&info->bdaddr, &ev->addr.bdaddr);
}
static void mgmt_device_unblocked(int sk, uint16_t index, void *buf, size_t len)
return;
}
- ba2str(&ev->bdaddr, addr);
+ ba2str(&ev->addr.bdaddr, addr);
DBG("Device unblocked, index %u, addr %s", index, addr);
if (index > max_index) {
info = &controllers[index];
- btd_event_device_unblocked(&info->bdaddr, &ev->bdaddr);
+ btd_event_device_unblocked(&info->bdaddr, &ev->addr.bdaddr);
+}
+
+static void mgmt_device_unpaired(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct controller_info *info;
+ struct mgmt_ev_device_unpaired *ev = buf;
+ char addr[18];
+
+ if (len < sizeof(*ev)) {
+ error("Too small mgmt_device_unpaired event packet");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, addr);
+ DBG("Device upaired, index %u, addr %s", index, addr);
+
+ if (index > max_index) {
+ error("Unexpected index %u in device_unpaired event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ btd_event_device_unpaired(&info->bdaddr, &ev->addr.bdaddr);
+}
+
+static void mgmt_new_ltk(int sk, uint16_t index, void *buf, size_t len)
+{
+ struct mgmt_ev_new_long_term_key *ev = buf;
+ struct controller_info *info;
+
+ if (len != sizeof(*ev)) {
+ error("mgmt_new_ltk event size mismatch (%zu != %zu)",
+ len, sizeof(*ev));
+ return;
+ }
+
+ DBG("Controller %u new LTK authenticated %u enc_size %u", index,
+ ev->key.authenticated, ev->key.enc_size);
+
+ if (index > max_index) {
+ error("Unexpected index %u in new_key event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ if (ev->store_hint) {
+ btd_event_ltk_notify(&info->bdaddr, &ev->key.addr.bdaddr,
+ ev->key.addr.type, ev->key.val, ev->key.master,
+ ev->key.authenticated, ev->key.enc_size,
+ ev->key.ediv, ev->key.rand);
+ }
+
+ if (ev->key.master)
+ bonding_complete(info, &ev->key.addr.bdaddr, 0);
+}
+
+static void mgmt_cod_changed(int sk, uint16_t index)
+{
+ struct controller_info *info;
+
+ DBG("index %d", index);
+
+ if (index > max_index) {
+ error("Unexpected index %u in mgmt_cod_changed event", index);
+ return;
+ }
+
+ info = &controllers[index];
+
+ if (info->pending_cod_change) {
+ info->pending_cod_change = FALSE;
+ handle_pending_uuids(index);
+ }
}
static gboolean mgmt_event(GIOChannel *io, GIOCondition cond, gpointer user_data)
case MGMT_EV_NEW_SETTINGS:
mgmt_new_settings(sk, index, buf + MGMT_HDR_SIZE, len);
break;
+ case MGMT_EV_CLASS_OF_DEV_CHANGED:
+ mgmt_cod_changed(sk, index);
+ break;
case MGMT_EV_NEW_LINK_KEY:
mgmt_new_link_key(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_UNBLOCKED:
mgmt_device_unblocked(sk, index, buf + MGMT_HDR_SIZE, len);
break;
+ case MGMT_EV_DEVICE_UNPAIRED:
+ mgmt_device_unpaired(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;
+ case MGMT_EV_NEW_LONG_TERM_KEY:
+ mgmt_new_ltk(sk, index, buf + MGMT_HDR_SIZE, len);
+ break;
default:
error("Unknown Management opcode %u (index %u)", opcode, index);
break;
}
}
-static int mgmt_set_dev_class(int index, uint8_t major, uint8_t minor)
-{
- char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_dev_class)];
- struct mgmt_hdr *hdr = (void *) buf;
- struct mgmt_cp_set_dev_class *cp = (void *) &buf[sizeof(*hdr)];
-
- DBG("index %d major %u minor %u", index, major, minor);
-
- memset(buf, 0, sizeof(buf));
- hdr->opcode = htobs(MGMT_OP_SET_DEV_CLASS);
- hdr->len = htobs(sizeof(*cp));
- hdr->index = htobs(index);
-
- cp->major = major;
- cp->minor = minor;
-
- if (write(mgmt_sock, buf, sizeof(buf)) < 0)
- return -errno;
-
- return 0;
-}
-
-static int mgmt_set_limited_discoverable(int index, gboolean limited)
-{
- DBG("index %d limited %d", index, limited);
- return -ENOSYS;
-}
-
static int mgmt_start_discovery(int index)
{
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)];
+ struct controller_info *info = &controllers[index];
DBG("index %d", index);
+ info->discov_type = 0;
+
+ if (mgmt_bredr(info->current_settings))
+ hci_set_bit(BDADDR_BREDR, &info->discov_type);
+
+ if (mgmt_low_energy(info->current_settings)) {
+ hci_set_bit(BDADDR_LE_PUBLIC, &info->discov_type);
+ hci_set_bit(BDADDR_LE_RANDOM, &info->discov_type);
+ }
+
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);
+ cp->type = info->discov_type;
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
return -errno;
static int mgmt_stop_discovery(int index)
{
- struct mgmt_hdr hdr;
-
- DBG("index %d", index);
-
- memset(&hdr, 0, sizeof(hdr));
- hdr.opcode = htobs(MGMT_OP_STOP_DISCOVERY);
- hdr.index = htobs(index);
-
- if (write(mgmt_sock, &hdr, sizeof(hdr)) < 0)
- return -errno;
-
- return 0;
-}
-
-static int mgmt_set_name(int index, const char *name)
-{
- char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_local_name)];
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_start_discovery)];
struct mgmt_hdr *hdr = (void *) buf;
- struct mgmt_cp_set_local_name *cp = (void *) &buf[sizeof(*hdr)];
+ struct mgmt_cp_start_discovery *cp = (void *) &buf[sizeof(*hdr)];
+ struct controller_info *info = &controllers[index];
- DBG("index %d, name %s", index, name);
+ DBG("index %d", index);
memset(buf, 0, sizeof(buf));
- hdr->opcode = htobs(MGMT_OP_SET_LOCAL_NAME);
+ hdr->opcode = htobs(MGMT_OP_STOP_DISCOVERY);
hdr->len = htobs(sizeof(*cp));
hdr->index = htobs(index);
- strncpy((char *) cp->name, name, sizeof(cp->name) - 1);
+ cp->type = info->discov_type;
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
return -errno;
return 0;
}
-static int mgmt_block_device(int index, bdaddr_t *bdaddr)
+static int mgmt_block_device(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type)
{
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_block_device)];
struct mgmt_hdr *hdr = (void *) buf;
hdr->index = htobs(index);
cp = (void *) &buf[sizeof(*hdr)];
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = bdaddr_type;
buf_len = sizeof(*hdr) + sizeof(*cp);
return 0;
}
-static int mgmt_unblock_device(int index, bdaddr_t *bdaddr)
+static int mgmt_unblock_device(int index, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
{
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_unblock_device)];
struct mgmt_hdr *hdr = (void *) buf;
hdr->index = htobs(index);
cp = (void *) &buf[sizeof(*hdr)];
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = bdaddr_type;
buf_len = sizeof(*hdr) + sizeof(*cp);
return 0;
}
-static int mgmt_disconnect(int index, bdaddr_t *bdaddr)
+static int mgmt_disconnect(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type)
{
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_disconnect)];
struct mgmt_hdr *hdr = (void *) buf;
hdr->len = htobs(sizeof(*cp));
hdr->index = htobs(index);
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = bdaddr_type;
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
error("write: %s (%d)", strerror(errno), errno);
return 0;
}
-static int mgmt_remove_bonding(int index, bdaddr_t *bdaddr)
+static int mgmt_unpair_device(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type)
{
- char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_remove_keys)];
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_unpair_device)];
struct mgmt_hdr *hdr = (void *) buf;
- struct mgmt_cp_remove_keys *cp = (void *) &buf[sizeof(*hdr)];
+ struct mgmt_cp_unpair_device *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_KEYS);
+ hdr->opcode = htobs(MGMT_OP_UNPAIR_DEVICE);
hdr->len = htobs(sizeof(*cp));
hdr->index = htobs(index);
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = bdaddr_type;
cp->disconnect = 1;
if (write(mgmt_sock, buf, sizeof(buf)) < 0)
}
static int mgmt_set_did(int index, uint16_t vendor, uint16_t product,
- uint16_t version)
+ uint16_t version, uint16_t source)
{
- DBG("index %d vendor %u product %u version %u",
- index, vendor, product, version);
- return -ENOSYS;
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_device_id)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_set_device_id *cp = (void *) &buf[sizeof(*hdr)];
+
+ DBG("index %d source %x vendor %x product %x version %x",
+ index, source, vendor, product, version);
+
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_SET_DEVICE_ID);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ cp->source = htobs(source);
+ cp->vendor = htobs(vendor);
+ cp->product = htobs(product);
+ cp->version = htobs(version);
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
}
static int mgmt_disable_cod_cache(int index)
if (buf == NULL)
return -ENOMEM;
- memset(buf, 0, sizeof(buf));
-
hdr = (void *) buf;
hdr->opcode = htobs(MGMT_OP_LOAD_LINK_KEYS);
hdr->len = htobs(cp_size);
for (l = keys, key = cp->keys; l != NULL; l = g_slist_next(l), key++) {
struct link_key_info *info = l->data;
- bacpy(&key->bdaddr, &info->bdaddr);
+ bacpy(&key->addr.bdaddr, &info->bdaddr);
+ key->addr.type = BDADDR_BREDR;
key->type = info->type;
memcpy(key->val, info->key, 16);
key->pin_len = info->pin_len;
return 0;
}
-static int mgmt_create_bonding(int index, bdaddr_t *bdaddr, uint8_t io_cap)
+static int mgmt_create_bonding(int index, bdaddr_t *bdaddr, uint8_t addr_type, uint8_t io_cap)
{
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_pair_device)];
struct mgmt_hdr *hdr = (void *) buf;
hdr->index = htobs(index);
bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = addr_type;
cp->io_cap = io_cap;
if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
static int mgmt_cancel_bonding(int index, bdaddr_t *bdaddr)
{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_addr_info)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_addr_info *cp = (void *) &buf[sizeof(*hdr)];
char addr[18];
ba2str(bdaddr, addr);
DBG("hci%d bdaddr %s", index, addr);
- return -ENOSYS;
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_CANCEL_PAIR_DEVICE);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ bacpy(&cp->bdaddr, bdaddr);
+
+ if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
}
static int mgmt_read_local_oob_data(int index)
hdr->index = htobs(index);
hdr->len = htobs(sizeof(*cp));
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
memcpy(cp->hash, hash, 16);
memcpy(cp->randomizer, randomizer, 16);
hdr->index = htobs(index);
hdr->len = htobs(sizeof(*cp));
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.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)
+static int mgmt_confirm_name(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ gboolean name_known)
{
char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_confirm_name)];
struct mgmt_hdr *hdr = (void *) buf;
hdr->index = htobs(index);
hdr->len = htobs(sizeof(*cp));
- bacpy(&cp->bdaddr, bdaddr);
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = bdaddr_type;
cp->name_known = name_known;
if (write(mgmt_sock, &buf, sizeof(buf)) < 0)
return 0;
}
+static int mgmtops_load_ltks(int index, GSList *keys)
+{
+ char *buf;
+ struct mgmt_hdr *hdr;
+ struct mgmt_cp_load_long_term_keys *cp;
+ struct mgmt_ltk_info *key;
+ size_t key_count, cp_size;
+ GSList *l;
+ int err;
+
+ key_count = g_slist_length(keys);
+
+ DBG("index %d keys %zu", index, key_count);
+
+ cp_size = sizeof(*cp) + (key_count * sizeof(*key));
+
+ buf = g_try_malloc0(sizeof(*hdr) + cp_size);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ hdr = (void *) buf;
+ hdr->opcode = htobs(MGMT_OP_LOAD_LONG_TERM_KEYS);
+ hdr->len = htobs(cp_size);
+ hdr->index = htobs(index);
+
+ cp = (void *) (buf + sizeof(*hdr));
+ cp->key_count = htobs(key_count);
+
+ for (l = keys, key = cp->keys; l != NULL; l = g_slist_next(l), key++) {
+ struct smp_ltk_info *info = l->data;
+
+ bacpy(&key->addr.bdaddr, &info->bdaddr);
+ key->addr.type = info->bdaddr_type;
+ memcpy(key->val, info->val, sizeof(info->val));
+ memcpy(key->rand, info->rand, sizeof(info->rand));
+ memcpy(&key->ediv, &info->ediv, sizeof(key->ediv));
+ key->authenticated = info->authenticated;
+ key->master = info->master;
+ key->enc_size = info->enc_size;
+ }
+
+ if (write(mgmt_sock, buf, sizeof(*hdr) + cp_size) < 0)
+ err = -errno;
+ else
+ err = 0;
+
+ g_free(buf);
+
+ return err;
+}
+
static struct btd_adapter_ops mgmt_ops = {
.setup = mgmt_setup,
.cleanup = mgmt_cleanup,
.set_powered = mgmt_set_powered,
.set_discoverable = mgmt_set_discoverable,
.set_pairable = mgmt_set_pairable,
- .set_limited_discoverable = mgmt_set_limited_discoverable,
.start_discovery = mgmt_start_discovery,
.stop_discovery = mgmt_stop_discovery,
.set_name = mgmt_set_name,
.unblock_device = mgmt_unblock_device,
.get_conn_list = mgmt_get_conn_list,
.disconnect = mgmt_disconnect,
- .remove_bonding = mgmt_remove_bonding,
+ .remove_bonding = mgmt_unpair_device,
.pincode_reply = mgmt_pincode_reply,
.confirm_reply = mgmt_confirm_reply,
.passkey_reply = mgmt_passkey_reply,
.add_remote_oob_data = mgmt_add_remote_oob_data,
.remove_remote_oob_data = mgmt_remove_remote_oob_data,
.confirm_name = mgmt_confirm_name,
+ .load_ltks = mgmtops_load_ltks,
};
static int mgmt_init(void)
#include <bluetooth/rfcomm.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include <glib.h>
#define PNATD "/usr/bin/phonet-at"
#define DUN_CHANNEL 1
-#define DUN_UUID "00001103-0000-1000-8000-00805F9B34FB"
#define TTY_TIMEOUT 100
#define TTY_TRIES 10
return;
}
- if (btd_request_authorization(&server->bda, &client->bda, DUN_UUID,
+ if (btd_request_authorization(&server->bda, &client->bda, DUN_GW_UUID,
auth_cb, user_data) < 0) {
error("Requesting DUN authorization failed");
return;
return dbus_message_new_method_return(msg);
}
-static GDBusMethodTable service_methods[] = {
- { "AddRecord", "s", "u", add_service_record },
- { "UpdateRecord", "us", "", update_service_record },
- { "RemoveRecord", "u", "", remove_service_record },
- { "RequestAuthorization","su", "", request_authorization,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "CancelAuthorization", "", "", cancel_authorization },
+static const GDBusMethodTable service_methods[] = {
+ { GDBUS_METHOD("AddRecord",
+ GDBUS_ARGS({ "record", "s" }),
+ GDBUS_ARGS({ "handle", "u" }),
+ add_service_record) },
+ { GDBUS_METHOD("UpdateRecord",
+ GDBUS_ARGS({ "handle", "u" }, { "record", "s" }), NULL,
+ update_service_record) },
+ { GDBUS_METHOD("RemoveRecord",
+ GDBUS_ARGS({ "handle", "u" }), NULL,
+ remove_service_record) },
+ { GDBUS_ASYNC_METHOD("RequestAuthorization",
+ GDBUS_ARGS({ "address", "s" }, { "handle", "u"}), NULL,
+ request_authorization) },
+ { GDBUS_METHOD("CancelAuthorization",
+ NULL, NULL, cancel_authorization) },
{ }
};
#endif
#include <bluetooth/bluetooth.h>
+#include <glib.h>
#include "plugin.h"
#include "adapter.h"
*/
static ssize_t wii_pincb(struct btd_adapter *adapter, struct btd_device *device,
- char *pinbuf)
+ char *pinbuf, gboolean *display)
{
uint16_t vendor, product;
bdaddr_t sba, dba;
- char addr[18];
+ char addr[18], name[25];
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) {
+
+ device_get_name(device, name, sizeof(name));
+ name[sizeof(name) - 1] = 0;
+
+ if (g_str_equal(name, "Nintendo RVL-CNT-01") ||
+ (vendor == 0x057e && product == 0x0306)) {
DBG("Forcing fixed pin on detected wiimote %s", addr);
memcpy(pinbuf, &sba, 6);
return 6;
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments 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 <glib.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
+#include "gatt-service.h"
+#include "attrib-server.h"
+#include "device.h"
+#include "attio.h"
+#include "dbus-common.h"
+#include "reporter.h"
+#include "immalert.h"
+
+struct imm_alert_adapter {
+ struct btd_adapter *adapter;
+ DBusConnection *conn;
+ GSList *connected_devices;
+};
+
+struct connected_device {
+ struct btd_device *device;
+ struct imm_alert_adapter *adapter;
+ uint8_t alert_level;
+ guint callback_id;
+};
+
+static GSList *imm_alert_adapters;
+
+static int imdevice_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct connected_device *condev = a;
+ const struct btd_device *device = b;
+
+ if (condev->device == device)
+ return 0;
+
+ return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct imm_alert_adapter *ia, struct btd_device *device)
+{
+ GSList *l = g_slist_find_custom(ia->connected_devices, device,
+ imdevice_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static int imadapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct imm_alert_adapter *imadapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (imadapter->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct imm_alert_adapter *
+find_imm_alert_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(imm_alert_adapters, adapter,
+ imadapter_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+const char *imm_alert_get_level(struct btd_device *device)
+{
+ struct imm_alert_adapter *imadapter;
+ struct connected_device *condev;
+
+ if (!device)
+ return get_alert_level_string(NO_ALERT);
+
+ imadapter = find_imm_alert_adapter(device_get_adapter(device));
+ if (!imadapter)
+ return get_alert_level_string(NO_ALERT);
+
+ condev = find_connected_device(imadapter, device);
+ if (!condev)
+ return get_alert_level_string(NO_ALERT);
+
+ return get_alert_level_string(condev->alert_level);
+}
+
+static void imm_alert_emit_alert_signal(struct connected_device *condev,
+ uint8_t alert_level)
+{
+ struct imm_alert_adapter *adapter;
+ const char *path, *alert_level_str;
+
+ if (!condev)
+ return;
+
+ adapter = condev->adapter;
+ path = device_get_path(condev->device);
+ alert_level_str = get_alert_level_string(alert_level);
+
+ DBG("alert %s remote %s", alert_level_str, path);
+
+ emit_property_changed(adapter->conn, path,
+ PROXIMITY_REPORTER_INTERFACE, "ImmediateAlertLevel",
+ DBUS_TYPE_STRING, &alert_level_str);
+}
+
+static void imm_alert_remove_condev(struct connected_device *condev)
+{
+ struct imm_alert_adapter *ia;
+
+ if (!condev)
+ return;
+
+ ia = condev->adapter;
+
+ if (condev->callback_id && condev->device)
+ btd_device_remove_attio_callback(condev->device,
+ condev->callback_id);
+
+ if (condev->device)
+ btd_device_unref(condev->device);
+
+ ia->connected_devices = g_slist_remove(ia->connected_devices, condev);
+ g_free(condev);
+}
+
+/* condev can be NULL */
+static void imm_alert_disc_cb(gpointer user_data)
+{
+ struct connected_device *condev = user_data;
+
+ if (!condev)
+ return;
+
+ DBG("immediate alert remove device %p", condev->device);
+
+ imm_alert_emit_alert_signal(condev, NO_ALERT);
+ imm_alert_remove_condev(condev);
+}
+
+static uint8_t imm_alert_alert_lvl_write(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ uint8_t value;
+ struct imm_alert_adapter *ia = user_data;
+ struct connected_device *condev = NULL;
+
+ if (!device)
+ goto set_error;
+
+ condev = find_connected_device(ia, device);
+
+ if (a->len == 0) {
+ DBG("Illegal alert level length");
+ goto set_error;
+ }
+
+ value = a->data[0];
+ if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+ DBG("Illegal alert value");
+ goto set_error;
+ }
+
+ /* Register a disconnect cb if the alert level is non-zero */
+ if (value != NO_ALERT && !condev) {
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = ia;
+ condev->callback_id = btd_device_add_attio_callback(device,
+ NULL, imm_alert_disc_cb, condev);
+ ia->connected_devices = g_slist_append(ia->connected_devices,
+ condev);
+ DBG("added connected dev %p", device);
+ }
+
+ if (value != NO_ALERT) {
+ condev->alert_level = value;
+ imm_alert_emit_alert_signal(condev, value);
+ }
+
+ /*
+ * Emit NO_ALERT if the alert level was non-zero before. This is
+ * guaranteed when there's a condev.
+ */
+ if (value == NO_ALERT && condev)
+ imm_alert_disc_cb(condev);
+
+ DBG("alert level set to %d by device %p", value, device);
+ return 0;
+
+set_error:
+ error("Set immediate alert level for dev %p", device);
+ /* remove alerts by erroneous devices */
+ imm_alert_disc_cb(condev);
+ return ATT_ECODE_IO;
+}
+
+void imm_alert_register(struct btd_adapter *adapter, DBusConnection *conn)
+{
+ gboolean svc_added;
+ bt_uuid_t uuid;
+ struct imm_alert_adapter *imadapter;
+
+ bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID);
+
+ imadapter = g_new0(struct imm_alert_adapter, 1);
+ imadapter->adapter = adapter;
+ imadapter->conn = dbus_connection_ref(conn);
+
+ imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter);
+
+ /* Immediate Alert Service */
+ svc_added = gatt_service_add(adapter,
+ GATT_PRIM_SVC_UUID, &uuid,
+ /* Alert level characteristic */
+ GATT_OPT_CHR_UUID, ALERT_LEVEL_CHR_UUID,
+ GATT_OPT_CHR_PROPS,
+ ATT_CHAR_PROPER_WRITE_WITHOUT_RESP,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+ imm_alert_alert_lvl_write, imadapter,
+ GATT_OPT_INVALID);
+
+ if (!svc_added) {
+ imm_alert_unregister(adapter);
+ return;
+ }
+
+ DBG("Immediate Alert service added");
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+ struct connected_device *condev = data;
+
+ imm_alert_remove_condev(condev);
+}
+
+void imm_alert_unregister(struct btd_adapter *adapter)
+{
+ struct imm_alert_adapter *imadapter;
+
+ imadapter = find_imm_alert_adapter(adapter);
+ if (!imadapter)
+ return;
+
+ g_slist_foreach(imadapter->connected_devices, remove_condev_list_item,
+ NULL);
+ dbus_connection_unref(imadapter->conn);
+
+ imm_alert_adapters = g_slist_remove(imm_alert_adapters, imadapter);
+ g_free(imadapter);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments 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
+ *
+ */
+
+void imm_alert_register(struct btd_adapter *adapter, DBusConnection *conn);
+void imm_alert_unregister(struct btd_adapter *adapter);
+const char *imm_alert_get_level(struct btd_device *device);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments 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 <glib.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "att-database.h"
+#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "gatt-service.h"
+#include "attrib-server.h"
+#include "device.h"
+#include "attio.h"
+#include "dbus-common.h"
+#include "reporter.h"
+#include "linkloss.h"
+
+#define BLUEZ_SERVICE "org.bluez"
+
+struct link_loss_adapter {
+ struct btd_adapter *adapter;
+ uint16_t alert_lvl_value_handle;
+ DBusConnection *conn;
+ GSList *connected_devices;
+};
+
+struct connected_device {
+ struct btd_device *device;
+ struct link_loss_adapter *adapter;
+ uint8_t alert_level;
+ guint callback_id;
+ guint local_disc_id;
+};
+
+static GSList *link_loss_adapters;
+
+static int lldevice_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct connected_device *llcondev = a;
+ const struct btd_device *device = b;
+
+ if (llcondev->device == device)
+ return 0;
+
+ return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct link_loss_adapter *la, struct btd_device *device)
+{
+ GSList *l = g_slist_find_custom(la->connected_devices, device,
+ lldevice_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static int lladapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct link_loss_adapter *lladapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (lladapter->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct link_loss_adapter *
+find_link_loss_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(link_loss_adapters, adapter,
+ lladapter_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+const char *link_loss_get_alert_level(struct btd_device *device)
+{
+ struct link_loss_adapter *lladapter;
+ struct connected_device *condev;
+
+ if (!device)
+ return get_alert_level_string(NO_ALERT);
+
+ lladapter = find_link_loss_adapter(device_get_adapter(device));
+ if (!lladapter)
+ return get_alert_level_string(NO_ALERT);
+
+ condev = find_connected_device(lladapter, device);
+ if (!condev)
+ return get_alert_level_string(NO_ALERT);
+
+ return get_alert_level_string(condev->alert_level);
+}
+
+static void link_loss_emit_alert_signal(struct connected_device *condev)
+{
+ struct link_loss_adapter *adapter = condev->adapter;
+ const char *alert_level_str, *path;
+
+ if (!condev->device)
+ return;
+
+ path = device_get_path(condev->device);
+ alert_level_str = get_alert_level_string(condev->alert_level);
+
+ DBG("alert %s remote %s", alert_level_str, path);
+
+ emit_property_changed(adapter->conn, path,
+ PROXIMITY_REPORTER_INTERFACE, "LinkLossAlertLevel",
+ DBUS_TYPE_STRING, &alert_level_str);
+}
+
+static uint8_t link_loss_alert_lvl_read(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ struct link_loss_adapter *la = user_data;
+ struct connected_device *condev;
+ uint8_t alert_level = NO_ALERT;
+
+ if (!device)
+ goto out;
+
+ condev = find_connected_device(la, device);
+ if (!condev)
+ goto out;
+
+ alert_level = condev->alert_level;
+
+out:
+ DBG("return alert level %d for dev %p", alert_level, device);
+
+ /* update the alert level according to the requesting device */
+ attrib_db_update(la->adapter, a->handle, NULL, &alert_level,
+ sizeof(alert_level), NULL);
+
+ return 0;
+}
+
+/* condev can be NULL */
+static void link_loss_remove_condev(struct connected_device *condev)
+{
+ struct link_loss_adapter *la;
+
+ if (!condev)
+ return;
+
+ la = condev->adapter;
+
+ if (condev->callback_id && condev->device)
+ btd_device_remove_attio_callback(condev->device,
+ condev->callback_id);
+
+ if (condev->local_disc_id && condev->device)
+ device_remove_disconnect_watch(condev->device,
+ condev->local_disc_id);
+
+ if (condev->device)
+ btd_device_unref(condev->device);
+
+ la->connected_devices = g_slist_remove(la->connected_devices, condev);
+ g_free(condev);
+}
+
+static void link_loss_disc_cb(gpointer user_data)
+{
+ struct connected_device *condev = user_data;
+
+ DBG("alert loss disconnect device %p", condev->device);
+
+ /* if an alert-level is set, emit a signal */
+ if (condev->alert_level != NO_ALERT)
+ link_loss_emit_alert_signal(condev);
+
+ /* we are open for more changes now */
+ link_loss_remove_condev(condev);
+}
+
+static void link_loss_local_disc(struct btd_device *device,
+ gboolean removal, void *user_data)
+{
+ struct connected_device *condev = user_data;
+
+ /* no need to alert on this device - we requested disconnection */
+ link_loss_remove_condev(condev);
+
+ DBG("alert level zeroed for locally disconnecting dev %p", device);
+}
+
+static uint8_t link_loss_alert_lvl_write(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ uint8_t value;
+ struct link_loss_adapter *la = user_data;
+ struct connected_device *condev = NULL;
+
+ if (!device)
+ goto set_error;
+
+ /* condev might remain NULL here if nothing is found */
+ condev = find_connected_device(la, device);
+
+ if (a->len == 0) {
+ DBG("Illegal alert level length");
+ goto set_error;
+ }
+
+ value = a->data[0];
+ if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+ DBG("Illegal alert value");
+ goto set_error;
+ }
+
+ /* Register a disconnect cb if the alert level is non-zero */
+ if (value != NO_ALERT && !condev) {
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = la;
+ condev->callback_id = btd_device_add_attio_callback(device,
+ NULL, link_loss_disc_cb, condev);
+ condev->local_disc_id = device_add_disconnect_watch(device,
+ link_loss_local_disc, condev, NULL);
+
+ la->connected_devices = g_slist_append(la->connected_devices,
+ condev);
+ } else if (value == NO_ALERT && condev) {
+ link_loss_remove_condev(condev);
+ condev = NULL;
+ }
+
+ DBG("alert level set to %d by device %p", value, device);
+
+ if (condev)
+ condev->alert_level = value;
+
+ return 0;
+
+set_error:
+ error("Set link loss alert level for dev %p", device);
+ /* reset alert level on erroneous devices */
+ link_loss_remove_condev(condev);
+ return ATT_ECODE_IO;
+}
+
+void link_loss_register(struct btd_adapter *adapter, DBusConnection *conn)
+{
+ gboolean svc_added;
+ bt_uuid_t uuid;
+ struct link_loss_adapter *lladapter;
+
+ bt_uuid16_create(&uuid, LINK_LOSS_SVC_UUID);
+
+ lladapter = g_new0(struct link_loss_adapter, 1);
+ lladapter->adapter = adapter;
+ lladapter->conn = dbus_connection_ref(conn);
+
+ link_loss_adapters = g_slist_append(link_loss_adapters, lladapter);
+
+ /* Link Loss Service */
+ svc_added = gatt_service_add(adapter,
+ GATT_PRIM_SVC_UUID, &uuid,
+ /* Alert level characteristic */
+ GATT_OPT_CHR_UUID, ALERT_LEVEL_CHR_UUID,
+ GATT_OPT_CHR_PROPS,
+ ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_WRITE,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ link_loss_alert_lvl_read, lladapter,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+ link_loss_alert_lvl_write, lladapter,
+ GATT_OPT_CHR_VALUE_GET_HANDLE,
+ &lladapter->alert_lvl_value_handle,
+ GATT_OPT_INVALID);
+
+ if (!svc_added)
+ goto err;
+
+ DBG("Link Loss service added");
+ return;
+
+err:
+ error("Error adding Link Loss service");
+ link_loss_unregister(adapter);
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+ struct connected_device *condev = data;
+
+ link_loss_remove_condev(condev);
+}
+
+void link_loss_unregister(struct btd_adapter *adapter)
+{
+ struct link_loss_adapter *lladapter;
+ lladapter = find_link_loss_adapter(adapter);
+ if (!lladapter)
+ return;
+
+ g_slist_foreach(lladapter->connected_devices, remove_condev_list_item,
+ NULL);
+ dbus_connection_unref(lladapter->conn);
+
+ link_loss_adapters = g_slist_remove(link_loss_adapters, lladapter);
+ g_free(lladapter);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments 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
+ *
+ */
+
+void link_loss_register(struct btd_adapter *adapter, DBusConnection *conn);
+void link_loss_unregister(struct btd_adapter *adapter);
+const char *link_loss_get_alert_level(struct btd_device *device);
#endif
#include <errno.h>
-
+#include <stdint.h>
#include <glib.h>
#include <gdbus.h>
#include "log.h"
#include "plugin.h"
#include "manager.h"
+#include "hcid.h"
static DBusConnection *connection = NULL;
static GKeyFile *config = NULL;
static int proximity_init(void)
{
+ if (!main_opts.gatt_enabled) {
+ DBG("GATT is disabled");
+ return -ENOTSUP;
+ }
connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
if (connection == NULL)
static void proximity_exit(void)
{
+ if (!main_opts.gatt_enabled)
+ return;
+
if (config)
g_key_file_free(config);
#include "adapter.h"
#include "device.h"
#include "att.h"
+#include "gattrib.h"
+#include "gatt.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 = {
static gint primary_uuid_cmp(gconstpointer a, gconstpointer b)
{
- const struct att_primary *prim = a;
+ const struct gatt_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;
+ struct gatt_primary *linkloss, *txpower, *immediate;
GSList *l, *primaries;
primaries = btd_device_get_primaries(device);
}
static struct btd_device_driver monitor_driver = {
- .name = "Proximity GATT Driver",
+ .name = "Proximity GATT Monitor Driver",
.uuids = BTD_UUIDS(IMMEDIATE_ALERT_UUID, LINK_LOSS_UUID, TX_POWER_UUID),
.probe = attio_device_probe,
.remove = attio_device_remove,
};
+static struct btd_adapter_driver reporter_server_driver = {
+ .name = "Proximity GATT Reporter Driver",
+ .probe = reporter_init,
+ .remove = reporter_exit,
+};
+
static void load_config_file(GKeyFile *config)
{
char **list;
load_config_file(config);
- /* TODO: Register Proximity Monitor/Reporter drivers */
+ connection = dbus_connection_ref(conn);
+
ret = btd_register_device_driver(&monitor_driver);
if (ret < 0)
- return ret;
+ goto fail_monitor;
- connection = dbus_connection_ref(conn);
+ ret = btd_register_adapter_driver(&reporter_server_driver);
+ if (ret < 0)
+ goto fail_reporter;
+
+ return 0;
- return reporter_init();
+fail_reporter:
+ btd_unregister_device_driver(&monitor_driver);
+
+fail_monitor:
+ dbus_connection_unref(connection);
+ return ret;
}
void proximity_manager_exit(void)
{
- reporter_exit();
btd_unregister_device_driver(&monitor_driver);
+ btd_unregister_adapter_driver(&reporter_server_driver);
dbus_connection_unref(connection);
}
#include "monitor.h"
#include "textfile.h"
-#define PROXIMITY_INTERFACE "org.bluez.Proximity"
+#define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor"
#define ALERT_LEVEL_CHR_UUID 0x2A06
#define POWER_LEVEL_CHR_UUID 0x2A07
gpointer user_data)
{
struct monitor *monitor = user_data;
- struct att_char *chr;
+ struct gatt_char *chr;
uint8_t value = str2level(monitor->linklosslevel);
if (status) {
gpointer user_data)
{
struct monitor *monitor = user_data;
- struct att_char *chr;
+ struct gatt_char *chr;
if (status) {
error("Discover Tx Power handle: %s", att_ecode2str(status));
gpointer user_data)
{
struct monitor *monitor = user_data;
- struct att_char *chr;
+ struct gatt_char *chr;
if (status) {
error("Discover Immediate Alert handle: %s",
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 const GDBusMethodTable monitor_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { GDBUS_ASYNC_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+ set_property) },
{ }
};
-static GDBusSignalTable monitor_signals[] = {
- { "PropertyChanged", "sv" },
+static const GDBusSignalTable monitor_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ }
};
}
int monitor_register(DBusConnection *conn, struct btd_device *device,
- struct att_primary *linkloss, struct att_primary *txpower,
- struct att_primary *immediate, struct enabled *enabled)
+ struct gatt_primary *linkloss, struct gatt_primary *txpower,
+ struct gatt_primary *immediate, struct enabled *enabled)
{
const char *path = device_get_path(device);
struct monitor *monitor;
if (linkloss && enabled->linkloss) {
monitor->linkloss = g_new0(struct att_range, 1);
- monitor->linkloss->start = linkloss->start;
- monitor->linkloss->end = linkloss->end;
+ monitor->linkloss->start = linkloss->range.start;
+ monitor->linkloss->end = linkloss->range.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->txpower->start = txpower->range.start;
+ monitor->txpower->end = txpower->range.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->immediate->start = immediate->range.start;
+ monitor->immediate->end = immediate->range.end;
}
monitor->enabled.findme = enabled->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);
+ struct gatt_primary *linkloss, struct gatt_primary *txpower,
+ struct gatt_primary *immediate, struct enabled *enabled);
void monitor_unregister(DBusConnection *conn, struct btd_device *device);
#include <glib.h>
#include <bluetooth/uuid.h>
#include <adapter.h>
+#include <errno.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
#include "log.h"
+#include "dbus-common.h"
+#include "error.h"
+#include "device.h"
#include "hcid.h"
-#include "att.h"
#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
#include "attrib-server.h"
#include "reporter.h"
+#include "linkloss.h"
+#include "immalert.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
+#define BLUEZ_SERVICE "org.bluez"
-enum {
- NO_ALERT = 0x00,
- MILD_ALERT = 0x01,
- HIGH_ALERT = 0x02,
+struct reporter_adapter {
+ DBusConnection *conn;
+ struct btd_adapter *adapter;
+ GSList *devices;
};
-static uint16_t tx_power_handle;
+static GSList *reporter_adapters;
-static void register_link_loss(void)
+static int radapter_cmp(gconstpointer a, gconstpointer b)
{
- 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;
- }
+ const struct reporter_adapter *radapter = a;
+ const struct btd_adapter *adapter = b;
- DBG("start_handle=0x%04x", start_handle);
+ if (radapter->adapter == adapter)
+ return 0;
- h = start_handle;
+ return -1;
+}
- /* 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);
+static struct reporter_adapter *
+find_reporter_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(reporter_adapters, adapter,
+ radapter_cmp);
+ if (!l)
+ return NULL;
- /* 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);
+ return l->data;
+}
- /* 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);
+const char *get_alert_level_string(uint8_t level)
+{
+ switch (level) {
+ case NO_ALERT:
+ return "none";
+ case MILD_ALERT:
+ return "mild";
+ case HIGH_ALERT:
+ return "high";
+ }
- g_assert(h - start_handle == svc_size);
+ return "unknown";
}
-static void register_tx_power(void)
+static void register_tx_power(struct btd_adapter *adapter)
{
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);
+ bt_uuid16_create(&uuid, TX_POWER_SVC_UUID);
+ start_handle = attrib_db_find_avail(adapter, &uuid, svc_size);
if (start_handle == 0) {
error("Not enough free handles to register service");
return;
/* 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);
+ attrib_db_add(adapter, 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);
+ attrib_db_add(adapter, 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);
+ attrib_db_add(adapter, 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);
+ attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2);
g_assert(h - start_handle == svc_size);
}
-static void register_immediate_alert(void)
+static DBusMessage *get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
{
- uint16_t start_handle, h;
- const int svc_size = 3;
- uint8_t atval[256];
- bt_uuid_t uuid;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *reply = NULL;
+ const char *linkloss_level, *immalert_level;
+ struct btd_device *device = data;
- /* 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;
- }
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
- DBG("start_handle=0x%04x", start_handle);
+ linkloss_level = link_loss_get_alert_level(device);
+ immalert_level = imm_alert_get_level(device);
- h = start_handle;
+ dbus_message_iter_init_append(reply, &iter);
- /* 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);
+ if (!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))
+ goto err;
- /* 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);
+ dict_append_entry(&dict, "LinkLossAlertLevel", DBUS_TYPE_STRING,
+ &linkloss_level);
+ dict_append_entry(&dict, "ImmediateAlertLevel", DBUS_TYPE_STRING,
+ &immalert_level);
- /* 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);
+ if (!dbus_message_iter_close_container(&iter, &dict))
+ goto err;
- g_assert(h - start_handle == svc_size);
+ return reply;
+
+err:
+ if (reply)
+ dbus_message_unref(reply);
+ return btd_error_failed(msg, "not enough memory");
+}
+
+static const GDBusMethodTable reporter_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { }
+};
+
+static const GDBusSignalTable reporter_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
+};
+
+static void unregister_reporter_device(gpointer data, gpointer user_data)
+{
+ struct btd_device *device = data;
+ struct reporter_adapter *radapter = user_data;
+ const char *path = device_get_path(device);
+
+ DBG("unregister on device %s", path);
+
+ g_dbus_unregister_interface(radapter->conn, path,
+ PROXIMITY_REPORTER_INTERFACE);
+
+ radapter->devices = g_slist_remove(radapter->devices, device);
+ btd_device_unref(device);
+}
+
+static void register_reporter_device(struct btd_device *device,
+ struct reporter_adapter *radapter)
+{
+ const char *path = device_get_path(device);
+
+ DBG("register on device %s", path);
+
+ g_dbus_register_interface(radapter->conn, path,
+ PROXIMITY_REPORTER_INTERFACE,
+ reporter_methods, reporter_signals,
+ NULL, device, NULL);
+
+ btd_device_ref(device);
+ radapter->devices = g_slist_prepend(radapter->devices, device);
}
-int reporter_init(void)
+static int reporter_device_probe(struct btd_device *device, GSList *uuids)
{
- if (!main_opts.attrib_server) {
- DBG("Attribute server is disabled");
+ struct reporter_adapter *radapter;
+ struct btd_adapter *adapter = device_get_adapter(device);
+
+ radapter = find_reporter_adapter(adapter);
+ if (!radapter)
return -1;
+
+ register_reporter_device(device, radapter);
+ return 0;
+}
+
+static void reporter_device_remove(struct btd_device *device)
+{
+ struct reporter_adapter *radapter;
+ struct btd_adapter *adapter = device_get_adapter(device);
+
+ radapter = find_reporter_adapter(adapter);
+ if (!radapter)
+ return;
+
+ unregister_reporter_device(device, radapter);
+}
+
+/* device driver for tracking remote GATT client devices */
+static struct btd_device_driver reporter_device_driver = {
+ .name = "Proximity GATT Reporter Device Tracker Driver",
+ .uuids = BTD_UUIDS(GATT_UUID),
+ .probe = reporter_device_probe,
+ .remove = reporter_device_remove,
+};
+
+int reporter_init(struct btd_adapter *adapter)
+{
+ struct reporter_adapter *radapter;
+ DBusConnection *conn;
+
+ if (!main_opts.gatt_enabled) {
+ DBG("GATT is disabled");
+ return -ENOTSUP;
}
- DBG("Proximity Reporter");
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn)
+ return -1;
+
+ radapter = g_new0(struct reporter_adapter, 1);
+ radapter->adapter = adapter;
+ radapter->conn = conn;
+
+ link_loss_register(adapter, radapter->conn);
+ register_tx_power(adapter);
+ imm_alert_register(adapter, radapter->conn);
- register_link_loss();
- register_tx_power();
- register_immediate_alert();
+ btd_register_device_driver(&reporter_device_driver);
+
+ reporter_adapters = g_slist_prepend(reporter_adapters, radapter);
+ DBG("Proximity Reporter for adapter %p", adapter);
return 0;
}
-void reporter_exit(void)
+void reporter_exit(struct btd_adapter *adapter)
{
+ struct reporter_adapter *radapter = find_reporter_adapter(adapter);
+ if (!radapter)
+ return;
+
+ btd_unregister_device_driver(&reporter_device_driver);
+
+ g_slist_foreach(radapter->devices, unregister_reporter_device,
+ radapter);
+
+ link_loss_unregister(adapter);
+ imm_alert_unregister(adapter);
+ dbus_connection_unref(radapter->conn);
+
+ reporter_adapters = g_slist_remove(reporter_adapters, radapter);
+ g_free(radapter);
}
*
*/
-int reporter_init(void);
-void reporter_exit(void);
+#define PROXIMITY_REPORTER_INTERFACE "org.bluez.ProximityReporter"
+
+#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,
+};
+
+int reporter_init(struct btd_adapter *adapter);
+void reporter_exit(struct btd_adapter *adapter);
+
+const char *get_alert_level_string(uint8_t level);
#include <config.h>
#endif
-#include <errno.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
-#include <gdbus.h>
-
#include "log.h"
#include "adapter.h"
-#include "device.h"
#include "manager.h"
#include "server.h"
#include <config.h>
#endif
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdint.h>
#include <glib.h>
#include <gdbus.h>
#define SAP_DUMMY_PATH "/org/bluez/test"
enum {
- SIM_DISCONNECTED= 0x00,
- SIM_CONNECTED = 0x01,
- SIM_POWERED_OFF = 0x02,
- SIM_MISSING = 0x03
+ 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 void *sap_data = NULL; /* SAP server private data. */
static gboolean ongoing_call_status = FALSE;
static int max_msg_size_supported = 512;
sap_connect_rsp(sap_device, SAP_STATUS_CONNECTION_FAILED,
maxmsgsize);
return;
- } else if (max_msg_size_supported > maxmsgsize) {
+ }
+
+ 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) {
+ }
+
+ 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) {
+ }
+
+ 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);
}
+
+ 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)
SAP_RESULT_ERROR_NOT_ACCESSIBLE, NULL, 0);
else
sap_transfer_apdu_rsp(sap_device, SAP_RESULT_OK,
- (uint8_t*)&apdu, sizeof(apdu));
+ (uint8_t *)&apdu, sizeof(apdu));
}
void sap_transfer_atr_req(void *sap_device)
NULL, 0);
else
sap_transfer_atr_rsp(sap_device, SAP_RESULT_OK,
- (uint8_t*)&atr, sizeof(atr));
+ (uint8_t *)&atr, sizeof(atr));
}
void sap_power_sim_off_req(void *sap_device)
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;
+ ongoing_call_status = FALSE;
} else if (!ongoing_call_status && ongoing) {
/* An ongoing call has started.*/
- ongoing_call_status = ongoing;
+ ongoing_call_status = TRUE;
}
DBG("OngoingCall status set to %d", ongoing_call_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},
+static const GDBusMethodTable dummy_methods[] = {
+ { GDBUS_METHOD("OngoingCall",
+ GDBUS_ARGS({ "ongoing", "b" }), NULL,
+ ongoing_call) },
+ { GDBUS_METHOD("MaxMessageSize",
+ GDBUS_ARGS({ "size", "u" }), NULL,
+ max_msg_size) },
+ { GDBUS_METHOD("DisconnectImmediate", NULL, NULL,
+ disconnect_immediate) },
+ { GDBUS_METHOD("CardStatus",
+ GDBUS_ARGS({ "status", "" }), NULL,
+ card_status) },
{ }
};
NULL, NULL) == FALSE) {
error("sap-dummy interface %s init failed on path %s",
SAP_DUMMY_IFACE, SAP_DUMMY_PATH);
+ dbus_connection_unref(connection);
+ connection = NULL;
return -1;
}
void sap_exit(void)
{
+ g_dbus_unregister_interface(connection, SAP_DUMMY_PATH,
+ SAP_DUMMY_IFACE);
+
dbus_connection_unref(connection);
connection = NULL;
}
u8500.sap_data = NULL;
}
-static void recv_sim_ready()
+static void recv_sim_ready(void)
{
sap_info("sim is ready. Try to connect again");
#include <config.h>
#endif
-#include <stdio.h>
#include <errno.h>
#include <glib.h>
-#include <netinet/in.h>
-#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include "adapter.h"
#include "btio.h"
#include "server.h"
#define SAP_SERVER_INTERFACE "org.bluez.SimAccess"
-#define SAP_UUID "0000112D-0000-1000-8000-00805F9B34FB"
#define SAP_SERVER_CHANNEL 8
-#define PADDING4(x) ((4 - (x & 0x03)) & 0x03)
+#define PADDING4(x) ((4 - ((x) & 0x03)) & 0x03)
#define PARAMETER_SIZE(x) (sizeof(struct sap_parameter) + x + PADDING4(x))
#define SAP_NO_REQ 0xFF
};
struct sap_server {
- bdaddr_t src;
char *path;
uint32_t record_id;
GIOChannel *listen_io;
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_COMMAND_APDU)
- if ( msg->param->id != SAP_PARAM_ID_COMMAND_APDU7816)
+ if (msg->param->id != SAP_PARAM_ID_COMMAND_APDU7816)
return -EBADMSG;
if (msg->param->len == 0x00)
aproto = sdp_list_append(NULL, apseq);
sdp_set_access_protos(record, aproto);
- sdp_set_info_attr(record, "SIM Access Server",
- NULL, NULL);
+ sdp_set_info_attr(record, "SIM Access Server", NULL, NULL);
sdp_data_free(ch);
sdp_list_free(proto[0], NULL);
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;
return -EINVAL;
DBG("state %d pr 0x%02x result 0x%02x", conn->state,
- conn->processing_req, result);
+ conn->processing_req, result);
if (conn->processing_req != SAP_RESET_SIM_REQ)
return 0;
return -EINVAL;
DBG("state %d pr 0x%02x result 0x%02x", conn->state,
- conn->processing_req, result);
+ conn->processing_req, result);
if (conn->processing_req != SAP_TRANSFER_CARD_READER_STATUS_REQ)
return 0;
return -EINVAL;
DBG("state %d pr 0x%02x result 0x%02x", conn->state,
- conn->processing_req, result);
+ conn->processing_req, result);
if (conn->processing_req != SAP_SET_TRANSPORT_PROTOCOL_REQ)
return 0;
return -EINVAL;
DBG("state %d pr 0x%02x sc 0x%02x", conn->state, conn->processing_req,
- status_change);
+ status_change);
/* Might be need to change state to connected after ongoing call.*/
if (conn->state == SAP_STATE_CONNECT_MODEM_BUSY &&
static void sap_io_destroy(void *data)
{
struct sap_connection *conn = data;
+ gboolean connected = FALSE;
DBG("conn %p", conn);
- if (conn && conn->io) {
- gboolean connected = FALSE;
+ if (!conn || !conn->io)
+ return;
- stop_guard_timer(conn);
+ 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,
+ 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);
+ 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);
- }
+ sap_conn_remove(conn);
}
static void sap_connect_cb(GIOChannel *io, GError *gerr, gpointer data)
conn = g_try_new0(struct sap_connection, 1);
if (!conn) {
- error("Can't allocate memory for incomming SAP connection.");
+ error("Can't allocate memory for incoming SAP connection.");
g_io_channel_shutdown(io, TRUE, NULL);
return;
}
return;
}
- DBG("Authorizing incomming SAP connection from %s", dstaddr);
+ DBG("Authorizing incoming SAP connection from %s", dstaddr);
}
static inline DBusMessage *message_failed(DBusMessage *msg,
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",
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
"There is no active connection");
return dbus_message_new_method_return(msg);
return reply;
}
-static GDBusMethodTable server_methods[] = {
- {"GetProperties", "", "a{sv}", get_properties},
- {"Disconnect", "", "", disconnect},
+static const GDBusMethodTable server_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { GDBUS_METHOD("Disconnect", NULL, NULL, disconnect) },
{ }
};
-static GDBusSignalTable server_signals[] = {
- { "PropertyChanged", "sv"},
+static const GDBusSignalTable server_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ }
};
return -ENOMEM;
}
- bacpy(&server->src, src);
server->path = g_strdup(path);
record = create_sap_record(SAP_SERVER_CHANNEL);
goto sdp_err;
}
- if (add_record_to_server(&server->src, record) < 0) {
+ if (add_record_to_server(src, record) < 0) {
error("Adding SAP SDP record to the SDP server failed.");
sdp_record_free(record);
goto sdp_err;
io = bt_io_listen(BT_IO_RFCOMM, NULL, connect_confirm_cb, server,
NULL, &gerr,
- BT_IO_OPT_SOURCE_BDADDR, &server->src,
+ BT_IO_OPT_SOURCE_BDADDR, src,
BT_IO_OPT_CHANNEL, SAP_SERVER_CHANNEL,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
BT_IO_OPT_MASTER, TRUE,
#define SBC_SB_4 0x00
#define SBC_SB_8 0x01
-/* Data endianess */
+/* Data endianness */
#define SBC_LE 0x00
#define SBC_BE 0x01
#define SBC_X_BUFFER_SIZE 328
#ifdef __GNUC__
-#define SBC_ALWAYS_INLINE __attribute__((always_inline))
+#define SBC_ALWAYS_INLINE inline __attribute__((always_inline))
#else
#define SBC_ALWAYS_INLINE inline
#endif
+++ /dev/null
-# Run helper every time a Bluetooth device appears
-# On remove actions, bluetoothd should go away by itself
-ACTION=="add", SUBSYSTEM=="bluetooth", RUN+="@prefix@/sbin/bluetoothd --udev"
-ACTION=="change", SUBSYSTEM=="bluetooth", RUN+="@prefix@/sbin/bluetoothd --udev"
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
#include <bluetooth/rfcomm.h>
+#include <bluetooth/uuid.h>
#include <glib.h>
#include <gdbus.h>
#include "sdpd.h"
#include "glib-helper.h"
-#define RFCOMM_UUID_STR "00000003-0000-1000-8000-00805F9B34FB"
-
static DBusConnection *connection = NULL;
static int serial_probe(struct btd_device *device, const char *uuid)
#include "../src/dbus-common.h"
#include "log.h"
-#include "glib-compat.h"
#include "glib-helper.h"
#include "sdp-client.h"
#include "btio.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 */
BT_IO_OPT_SOURCE_BDADDR, &device->src,
BT_IO_OPT_DEST_BDADDR, &device->dst,
BT_IO_OPT_CHANNEL, port->channel,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_INVALID);
if (!port->io) {
error("%s", gerr->message);
BT_IO_OPT_SOURCE_BDADDR, &device->src,
BT_IO_OPT_DEST_BDADDR, &device->dst,
BT_IO_OPT_CHANNEL, port->channel,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_INVALID);
if (port->io == NULL)
return -EIO;
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;
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
-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 },
+static const GDBusMethodTable port_methods[] = {
+ { GDBUS_ASYNC_METHOD("Connect",
+ GDBUS_ARGS({ "pattern", "s" }), GDBUS_ARGS({ "tty", "s" }),
+ port_connect) },
+ { GDBUS_ASYNC_METHOD("ConnectFD",
+ GDBUS_ARGS({ "pattern", "s" }), GDBUS_ARGS({ "fd", "s" }),
+ port_connect) },
+ { GDBUS_METHOD("Disconnect",
+ GDBUS_ARGS({ "device", "s" }), NULL,
+ port_disconnect) },
{ }
};
g_free(prx);
}
-static void add_lang_attr(sdp_record_t *r)
-{
- sdp_lang_attr_t base_lang;
- sdp_list_t *langs = 0;
-
- /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
- base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
- base_lang.encoding = 106;
- base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
- langs = sdp_list_append(0, &base_lang);
- sdp_set_lang_attr(r, langs);
- sdp_list_free(langs, 0);
-}
-
static sdp_record_t *proxy_record_new(const char *uuid128, uint8_t channel)
{
sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id;
aproto = sdp_list_append(NULL, apseq);
sdp_set_access_protos(record, aproto);
- add_lang_attr(record);
+ sdp_add_lang_attr(record);
sdp_set_info_attr(record, "Serial Proxy", NULL, "Serial Proxy");
return dbus_message_new_method_return(msg);
}
-static GDBusMethodTable proxy_methods[] = {
- { "Enable", "", "", proxy_enable },
- { "Disable", "", "", proxy_disable },
- { "GetInfo", "", "a{sv}",proxy_get_info },
- { "SetSerialParameters", "syys", "", proxy_set_serial_params },
+static const GDBusMethodTable proxy_methods[] = {
+ { GDBUS_METHOD("Enable", NULL, NULL, proxy_enable) },
+ { GDBUS_METHOD("Disable", NULL, NULL, proxy_disable) },
+ { GDBUS_METHOD("GetInfo",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ proxy_get_info) },
+ { GDBUS_METHOD("SetSerialParameters",
+ GDBUS_ARGS({ "rate", "s" }, { "data", "y" },
+ { "stop", "y" }, { "parity", "s" }),
+ NULL, proxy_set_serial_params) },
{ },
};
g_free(adapter);
}
-static GDBusMethodTable manager_methods[] = {
- { "CreateProxy", "ss", "s", create_proxy },
- { "ListProxies", "", "as", list_proxies },
- { "RemoveProxy", "s", "", remove_proxy },
+static const GDBusMethodTable manager_methods[] = {
+ { GDBUS_METHOD("CreateProxy",
+ GDBUS_ARGS({ "pattern", "s" },
+ { "address", "s" }),
+ GDBUS_ARGS({ "path", "s" }),
+ create_proxy) },
+ { GDBUS_METHOD("ListProxies",
+ NULL, GDBUS_ARGS({ "paths", "as" }),
+ list_proxies) },
+ { GDBUS_METHOD("RemoveProxy",
+ GDBUS_ARGS({ "path", "s" }), NULL,
+ remove_proxy) },
{ },
};
-static GDBusSignalTable manager_signals[] = {
- { "ProxyCreated", "s" },
- { "ProxyRemoved", "s" },
+static const GDBusSignalTable manager_signals[] = {
+ { GDBUS_SIGNAL("ProxyCreated", GDBUS_ARGS({ "path", "s" })) },
+ { GDBUS_SIGNAL("ProxyRemoved", GDBUS_ARGS({ "path", "s" })) },
{ }
};
#include "device.h"
#include "dbus-common.h"
#include "error.h"
-#include "glib-compat.h"
#include "glib-helper.h"
#include "agent.h"
#include "storage.h"
#include "gattrib.h"
#include "att.h"
+#include "gatt.h"
#include "attrib-server.h"
#include "eir.h"
#define IO_CAPABILITY_DISPLAYYESNO 0x01
#define IO_CAPABILITY_KEYBOARDONLY 0x02
#define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03
+#define IO_CAPABILITY_KEYBOARDDISPLAY 0x04
#define IO_CAPABILITY_INVALID 0xFF
#define check_address(address) bachk(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) */
guint pairable_timeout_id; /* pairable timeout id */
}
int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
- uint8_t minor)
+ uint8_t minor)
{
- return adapter_ops->set_dev_class(adapter->dev_id, major, minor);
+ return adapter_ops->set_dev_class(adapter->dev_id, major, minor);
}
static const char *mode2str(uint8_t mode)
return MODE_UNKNOWN;
}
-static void adapter_set_limited_discoverable(struct btd_adapter *adapter,
- gboolean limited)
-{
- DBG("%s", limited ? "TRUE" : "FALSE");
-
- adapter_ops->set_limited_discoverable(adapter->dev_id, limited);
-}
-
-static void adapter_remove_discov_timeout(struct btd_adapter *adapter)
-{
- if (!adapter)
- return;
-
- if (adapter->discov_timeout_id == 0)
- return;
-
- g_source_remove(adapter->discov_timeout_id);
- adapter->discov_timeout_id = 0;
-}
-
-static gboolean discov_timeout_handler(gpointer user_data)
-{
- struct btd_adapter *adapter = user_data;
-
- adapter->discov_timeout_id = 0;
-
- adapter_ops->set_discoverable(adapter->dev_id, FALSE);
-
- return FALSE;
-}
-
-static void adapter_set_discov_timeout(struct btd_adapter *adapter,
- guint interval)
-{
- if (adapter->discov_timeout_id) {
- g_source_remove(adapter->discov_timeout_id);
- adapter->discov_timeout_id = 0;
- }
-
- if (interval == 0) {
- adapter_set_limited_discoverable(adapter, FALSE);
- return;
- }
-
- /* Set limited discoverable if pairable and interval between 0 to 60
- sec */
- if (adapter->pairable && interval <= 60)
- adapter_set_limited_discoverable(adapter, TRUE);
- else
- adapter_set_limited_discoverable(adapter, FALSE);
-
- adapter->discov_timeout_id = g_timeout_add_seconds(interval,
- discov_timeout_handler,
- adapter);
-}
-
static struct session_req *session_ref(struct session_req *req)
{
req->refcount++;
int err;
if (mode == MODE_CONNECTABLE)
- err = adapter_ops->set_discoverable(adapter->dev_id, FALSE);
+ err = adapter_ops->set_discoverable(adapter->dev_id, FALSE, 0);
else
- err = adapter_ops->set_discoverable(adapter->dev_id, TRUE);
-
- if (err < 0)
- return err;
-
- if (mode == MODE_CONNECTABLE)
- return 0;
-
- adapter_remove_discov_timeout(adapter);
-
- if (adapter->discov_timeout)
- adapter_set_discov_timeout(adapter, adapter->discov_timeout);
+ err = adapter_ops->set_discoverable(adapter->dev_id, TRUE,
+ adapter->discov_timeout);
- return 0;
+ return err;
}
static struct session_req *find_session_by_msg(GSList *list, const DBusMessage *msg)
for (l = all, le = NULL; l; l = l->next) {
struct remote_dev_info *dev = l->data;
- if (dev->type == ADDR_TYPE_BREDR) {
+ if (dev->bdaddr_type == BDADDR_BREDR) {
dev_info_free(dev);
continue;
}
return;
}
- adapter_ops->stop_discovery(adapter->dev_id);
+ if (adapter->up)
+ adapter_ops->stop_discovery(adapter->dev_id);
}
static void session_remove(struct session_req *req)
return dbus_message_new_method_return(msg);
if (adapter->scan_mode & SCAN_INQUIRY)
- adapter_set_discov_timeout(adapter, timeout);
+ adapter_ops->set_discoverable(adapter->dev_id, TRUE, timeout);
adapter->discov_timeout = timeout;
adapter->dev_class = new_class;
- if (main_opts.attrib_server) {
+ if (main_opts.gatt_enabled) {
/* Removes service class */
class[1] = class[1] & 0x1f;
attrib_gap_set(adapter, GATT_CHARAC_APPEARANCE, class, 2);
ADAPTER_INTERFACE, "Name",
DBUS_TYPE_STRING, &name);
- if (main_opts.attrib_server)
+ if (main_opts.gatt_enabled)
attrib_gap_set(adapter, GATT_CHARAC_DEVICE_NAME,
(const uint8_t *) name, strlen(name));
}
int err = adapter_ops->set_name(adapter->dev_id, maxname);
if (err < 0)
return err;
+ } else {
+ g_free(adapter->name);
+ adapter->name = g_strdup(maxname);
}
write_local_name(&adapter->bdaddr, maxname);
static struct btd_device *adapter_create_device(DBusConnection *conn,
struct btd_adapter *adapter,
const char *address,
- addr_type_t type)
+ uint8_t bdaddr_type)
{
struct btd_device *device;
const char *path;
DBG("%s", address);
- device = device_create(conn, adapter, address, type);
+ device = device_create(conn, adapter, address, bdaddr_type);
if (!device)
return NULL;
return device;
return adapter_create_device(conn, adapter, address,
- ADDR_TYPE_BREDR);
+ BDADDR_BREDR);
}
static gboolean discovery_cb(gpointer user_data)
static DBusMessage *set_property(DBusConnection *conn,
DBusMessage *msg, void *data)
{
- struct btd_adapter *adapter = data;
DBusMessageIter iter;
DBusMessageIter sub;
const char *property;
- char srcaddr[18];
-
- ba2str(&adapter->bdaddr, srcaddr);
if (!dbus_message_iter_init(msg, &iter))
return btd_error_invalid_args(msg);
DBusMessageIter array_iter;
const gchar *dev_path;
- if (!dbus_message_has_signature(msg, DBUS_TYPE_INVALID_AS_STRING))
- return btd_error_invalid_args(msg);
-
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
struct remote_dev_info *dev;
struct btd_device *device;
bdaddr_t addr;
- addr_type_t type;
+ uint8_t bdaddr_type;
str2ba(address, &addr);
dev = adapter_search_found_devices(adapter, &addr);
if (dev)
- type = dev->type;
+ bdaddr_type = dev->bdaddr_type;
else
- type = ADDR_TYPE_BREDR;
+ bdaddr_type = BDADDR_BREDR;
- device = adapter_create_device(conn, adapter, address, type);
+ device = adapter_create_device(conn, adapter, address, bdaddr_type);
if (!device && err)
*err = -ENOMEM;
return IO_CAPABILITY_KEYBOARDONLY;
if (g_str_equal(capability, "NoInputNoOutput"))
return IO_CAPABILITY_NOINPUTNOOUTPUT;
+ if (g_str_equal(capability, "KeyboardDisplay"))
+ return IO_CAPABILITY_KEYBOARDDISPLAY;
return IO_CAPABILITY_INVALID;
}
return btd_error_failed(msg, strerror(-err));
}
- if (device_is_bredr(device))
- 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;
+ return device_create_bonding(device, conn, msg, agent_path, cap);
}
static gint device_path_cmp(struct btd_device *device, const gchar *path)
void *data)
{
const char *path, *name, *capability;
- struct agent *agent;
struct btd_adapter *adapter = data;
uint8_t cap;
name = dbus_message_get_sender(msg);
- agent = agent_create(adapter, name, path, cap,
+ adapter->agent = agent_create(adapter, name, path, cap,
(agent_remove_cb) agent_removed, adapter);
- if (!agent)
- return btd_error_failed(msg, "Failed to create a new agent");
-
- adapter->agent = agent;
DBG("Agent registered for hci%d at %s:%s", adapter->dev_id, name,
path);
return dbus_message_new_method_return(msg);
}
-static GDBusMethodTable adapter_methods[] = {
- { "GetProperties", "", "a{sv}",get_properties },
- { "SetProperty", "sv", "", set_property,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "RequestSession", "", "", request_session,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "ReleaseSession", "", "", release_session },
- { "StartDiscovery", "", "", adapter_start_discovery },
- { "StopDiscovery", "", "", adapter_stop_discovery,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "ListDevices", "", "ao", list_devices,
- G_DBUS_METHOD_FLAG_DEPRECATED},
- { "CreateDevice", "s", "o", create_device,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "CreatePairedDevice", "sos", "o", create_paired_device,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "CancelDeviceCreation","s", "", cancel_device_creation,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "RemoveDevice", "o", "", remove_device,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "FindDevice", "s", "o", find_device },
- { "RegisterAgent", "os", "", register_agent },
- { "UnregisterAgent", "o", "", unregister_agent },
+static const GDBusMethodTable adapter_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { GDBUS_ASYNC_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+ set_property) },
+ { GDBUS_ASYNC_METHOD("RequestSession", NULL, NULL,
+ request_session) },
+ { GDBUS_METHOD("ReleaseSession", NULL, NULL,
+ release_session) },
+ { GDBUS_METHOD("StartDiscovery", NULL, NULL,
+ adapter_start_discovery) },
+ { GDBUS_ASYNC_METHOD("StopDiscovery", NULL, NULL,
+ adapter_stop_discovery) },
+ { GDBUS_DEPRECATED_METHOD("ListDevices",
+ NULL, GDBUS_ARGS({ "devices", "ao" }),
+ list_devices) },
+ { GDBUS_ASYNC_METHOD("CreateDevice",
+ GDBUS_ARGS({ "address", "s" }),
+ GDBUS_ARGS({ "device", "o" }),
+ create_device) },
+ { GDBUS_ASYNC_METHOD("CreatePairedDevice",
+ GDBUS_ARGS({ "address", "s" }, { "agent", "o" },
+ { "capability", "s" }),
+ GDBUS_ARGS({ "device", "o" }),
+ create_paired_device) },
+ { GDBUS_ASYNC_METHOD("CancelDeviceCreation",
+ GDBUS_ARGS({ "address", "s" }), NULL,
+ cancel_device_creation) },
+ { GDBUS_ASYNC_METHOD("RemoveDevice",
+ GDBUS_ARGS({ "device", "o" }), NULL,
+ remove_device) },
+ { GDBUS_METHOD("FindDevice",
+ GDBUS_ARGS({ "address", "s" }),
+ GDBUS_ARGS({ "device", "o" }),
+ find_device) },
+ { GDBUS_METHOD("RegisterAgent",
+ GDBUS_ARGS({ "agent", "o" },
+ { "capability", "s" }), NULL,
+ register_agent) },
+ { GDBUS_METHOD("UnregisterAgent",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ unregister_agent) },
{ }
};
-static GDBusSignalTable adapter_signals[] = {
- { "PropertyChanged", "sv" },
- { "DeviceCreated", "o" },
- { "DeviceRemoved", "o" },
- { "DeviceFound", "sa{sv}" },
- { "DeviceDisappeared", "s" },
+static const GDBusSignalTable adapter_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { GDBUS_SIGNAL("DeviceCreated",
+ GDBUS_ARGS({ "device", "o" })) },
+ { GDBUS_SIGNAL("DeviceRemoved",
+ GDBUS_ARGS({ "device", "o" })) },
+ { GDBUS_SIGNAL("DeviceFound",
+ GDBUS_ARGS({ "address", "s" },
+ { "values", "a{sv}" })) },
+ { GDBUS_SIGNAL("DeviceDisappeared",
+ GDBUS_ARGS({ "address", "s" })) },
{ }
};
key, (GCompareFunc) device_address_cmp))
return;
- device = device_create(connection, adapter, key, ADDR_TYPE_BREDR);
+ device = device_create(connection, adapter, key, BDADDR_BREDR);
if (!device)
return;
GSList *keys;
};
+static int str2buf(const char *str, uint8_t *buf, size_t blen)
+{
+ int i, dlen;
+
+ if (str == NULL)
+ return -EINVAL;
+
+ memset(buf, 0, blen);
+
+ dlen = MIN((strlen(str) / 2), blen);
+
+ for (i = 0; i < dlen; i++)
+ sscanf(str + (i * 2), "%02hhX", &buf[i]);
+
+ return 0;
+}
+
static struct link_key_info *get_key_info(const char *addr, const char *value)
{
struct link_key_info *info;
char tmp[3];
long int l;
- int i;
if (strlen(value) < 36) {
error("Unexpectedly short (%zu) link key line", strlen(value));
str2ba(addr, &info->bdaddr);
- memset(tmp, 0, sizeof(tmp));
-
- for (i = 0; i < 16; i++) {
- memcpy(tmp, value + (i * 2), 2);
- info->key[i] = (uint8_t) strtol(tmp, NULL, 16);
- }
+ str2buf(value, info->key, sizeof(info->key));
memcpy(tmp, value + 33, 2);
info->type = (uint8_t) strtol(tmp, NULL, 10);
return info;
}
+static struct smp_ltk_info *get_ltk_info(const char *addr, uint8_t bdaddr_type,
+ const char *value)
+{
+ struct smp_ltk_info *ltk;
+ char *ptr;
+ int i, ret;
+
+ if (strlen(value) < 60) {
+ error("Unexpectedly short (%zu) LTK", strlen(value));
+ return NULL;
+ }
+
+ ltk = g_new0(struct smp_ltk_info, 1);
+
+ str2ba(addr, <k->bdaddr);
+
+ ltk->bdaddr_type = bdaddr_type;
+
+ str2buf(value, ltk->val, sizeof(ltk->val));
+
+ ptr = (char *) value + 2 * sizeof(ltk->val) + 1;
+
+ ret = sscanf(ptr, " %hhd %hhd %hhd %hd %n",
+ <k->authenticated, <k->master, <k->enc_size,
+ <k->ediv, &i);
+ if (ret < 2) {
+ g_free(ltk);
+ return NULL;
+ }
+ ptr += i;
+
+ str2buf(ptr, ltk->rand, sizeof(ltk->rand));
+
+ return ltk;
+}
+
static void create_stored_device_from_linkkeys(char *key, char *value,
void *user_data)
{
(GCompareFunc) device_address_cmp))
return;
- device = device_create(connection, adapter, key, ADDR_TYPE_BREDR);
+ device = device_create(connection, adapter, key, BDADDR_BREDR);
+ if (device) {
+ device_set_temporary(device, FALSE);
+ adapter->devices = g_slist_append(adapter->devices, device);
+ }
+}
+
+static void create_stored_device_from_ltks(char *key, char *value,
+ void *user_data)
+{
+ struct adapter_keys *keys = user_data;
+ struct btd_adapter *adapter = keys->adapter;
+ struct btd_device *device;
+ struct smp_ltk_info *info;
+ char address[18], srcaddr[18];
+ uint8_t bdaddr_type;
+ bdaddr_t src;
+
+ if (sscanf(key, "%17s#%hhu", address, &bdaddr_type) < 2)
+ return;
+
+ info = get_ltk_info(address, bdaddr_type, value);
+ if (info == NULL)
+ return;
+
+ keys->keys = g_slist_append(keys->keys, info);
+
+ if (g_slist_find_custom(adapter->devices, address,
+ (GCompareFunc) device_address_cmp))
+ return;
+
+ adapter_get_address(adapter, &src);
+ ba2str(&src, srcaddr);
+
+ if (g_strcmp0(srcaddr, address) == 0)
+ return;
+
+ device = device_create(connection, adapter, address, bdaddr_type);
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, ADDR_TYPE_BREDR);
+ device = device_create(connection, adapter, key, BDADDR_BREDR);
if (device) {
device_set_temporary(device, FALSE);
adapter->devices = g_slist_append(adapter->devices, device);
return NULL;
for (i = 0; services[i]; i++) {
- struct att_primary *prim;
+ struct gatt_primary *prim;
int ret;
- prim = g_new0(struct att_primary, 1);
+ prim = g_new0(struct gatt_primary, 1);
- ret = sscanf(services[i], "%04hX#%04hX#%s", &prim->start,
- &prim->end, prim->uuid);
+ ret = sscanf(services[i], "%04hX#%04hX#%s", &prim->range.start,
+ &prim->range.end, prim->uuid);
if (ret < 3) {
g_free(prim);
return l;
}
-static void create_stored_device_from_primary(char *key, char *value,
+static void create_stored_device_from_primaries(char *key, char *value,
void *user_data)
{
struct btd_adapter *adapter = user_data;
struct btd_device *device;
GSList *services, *uuids, *l;
+ char address[18];
+ uint8_t bdaddr_type;
+
+ if (sscanf(key, "%17s#%hhu", address, &bdaddr_type) < 2)
+ return;
if (g_slist_find_custom(adapter->devices,
- key, (GCompareFunc) device_address_cmp))
+ address, (GCompareFunc) device_address_cmp))
return;
- /* FIXME: Get the correct LE addr type (public/random) */
- device = device_create(connection, adapter, key, ADDR_TYPE_LE_PUBLIC);
+ device = device_create(connection, adapter, address, bdaddr_type);
if (!device)
return;
return;
for (l = services, uuids = NULL; l; l = l->next) {
- struct att_primary *prim = l->data;
+ struct gatt_primary *prim = l->data;
uuids = g_slist_append(uuids, prim->uuid);
}
g_slist_free(uuids);
}
+static void smp_key_free(void *data)
+{
+ struct smp_ltk_info *info = data;
+
+ g_free(info);
+}
+
static void load_devices(struct btd_adapter *adapter)
{
char filename[PATH_MAX + 1];
textfile_foreach(filename, create_stored_device_from_profiles,
adapter);
- create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "primary");
- textfile_foreach(filename, create_stored_device_from_primary,
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "primaries");
+ textfile_foreach(filename, create_stored_device_from_primaries,
adapter);
create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "linkkeys");
err = adapter_ops->load_keys(adapter->dev_id, keys.keys,
main_opts.debug_keys);
- if (err < 0) {
+ if (err < 0)
error("Unable to load keys to adapter_ops: %s (%d)",
strerror(-err), -err);
- g_slist_free_full(keys.keys, g_free);
- }
+
+ g_slist_free_full(keys.keys, g_free);
+ keys.keys = NULL;
+
+ create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "longtermkeys");
+ textfile_foreach(filename, create_stored_device_from_ltks, &keys);
+
+ err = adapter_ops->load_ltks(adapter->dev_id, keys.keys);
+ if (err < 0)
+ error("Unable to load keys to adapter_ops: %s (%d)",
+ strerror(-err), -err);
+ g_slist_free_full(keys.keys, smp_key_free);
+ keys.keys = NULL;
create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "blocked");
textfile_foreach(filename, create_stored_device_from_blocked, adapter);
}
-int btd_adapter_block_address(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+int btd_adapter_block_address(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
{
- return adapter_ops->block_device(adapter->dev_id, bdaddr);
+ return adapter_ops->block_device(adapter->dev_id, bdaddr, bdaddr_type);
}
-int btd_adapter_unblock_address(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+int btd_adapter_unblock_address(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
{
- return adapter_ops->unblock_device(adapter->dev_id, bdaddr);
+ return adapter_ops->unblock_device(adapter->dev_id, bdaddr,
+ bdaddr_type);
}
static void clear_blocked(struct btd_adapter *adapter)
{
int err;
- err = adapter_ops->unblock_device(adapter->dev_id, BDADDR_ANY);
+ err = adapter_ops->unblock_device(adapter->dev_id, BDADDR_ANY, 0);
if (err < 0)
error("Clearing blocked list failed: %s (%d)",
strerror(-err), -err);
struct btd_adapter_driver *driver = user_data;
int err;
- if (!adapter->up)
- return;
-
if (driver->probe == NULL)
return;
}
void btd_adapter_get_mode(struct btd_adapter *adapter, uint8_t *mode,
- uint8_t *on_mode, gboolean *pairable)
+ uint8_t *on_mode,
+ uint16_t *discoverable_timeout,
+ gboolean *pairable)
{
char str[14], address[18];
if (on_mode)
*on_mode = get_mode(&adapter->bdaddr, "on");
+ if (discoverable_timeout)
+ *discoverable_timeout = get_discoverable_timeout(address);
+
if (pairable)
*pairable = adapter->pairable;
}
+void btd_adapter_get_class(struct btd_adapter *adapter, uint8_t *major,
+ uint8_t *minor)
+{
+ uint8_t cls[3];
+
+ if (read_local_class(&adapter->bdaddr, cls) < 0) {
+ uint32_t class = htobl(main_opts.class);
+ memcpy(cls, &class, 3);
+ }
+
+ *major = cls[1];
+ *minor = cls[0];
+}
+
+const char *btd_adapter_get_name(struct btd_adapter *adapter)
+{
+ return adapter->name;
+}
+
void btd_adapter_start(struct btd_adapter *adapter)
{
char address[18];
- uint8_t cls[3];
gboolean powered;
ba2str(&adapter->bdaddr, address);
adapter->up = TRUE;
adapter->discov_timeout = get_discoverable_timeout(address);
adapter->pairable_timeout = get_pairable_timeout(address);
- adapter->mode = MODE_CONNECTABLE;
adapter->off_timer = 0;
- /* 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);
- memcpy(cls, &class, 3);
- }
-
- btd_adapter_set_class(adapter, cls[1], cls[0]);
+ if (adapter->scan_mode & SCAN_INQUIRY)
+ adapter->mode = MODE_DISCOVERABLE;
+ else
+ adapter->mode = MODE_CONNECTABLE;
powered = TRUE;
emit_property_changed(connection, adapter->path,
{
gboolean prop_false = FALSE;
- /* cancel pending timeout */
- if (adapter->discov_timeout_id) {
- g_source_remove(adapter->discov_timeout_id);
- adapter->discov_timeout_id = 0;
- }
-
/* check pending requests */
reply_pending_requests(adapter);
+ adapter->up = FALSE;
+
stop_discovery(adapter);
if (adapter->disc_sessions) {
emit_property_changed(connection, adapter->path, ADAPTER_INTERFACE,
"Powered", DBUS_TYPE_BOOLEAN, &prop_false);
- adapter->up = FALSE;
+ adapter->discovering = FALSE;
adapter->scan_mode = SCAN_DISABLED;
adapter->mode = MODE_OFF;
adapter->off_requested = FALSE;
g_free(path);
}
-gboolean adapter_init(struct btd_adapter *adapter)
+gboolean adapter_init(struct btd_adapter *adapter, gboolean up)
{
- /* adapter_ops makes sure that newly registered adapters always
- * start off as powered */
- adapter->up = TRUE;
+ adapter->up = up;
adapter->allow_name_changes = TRUE;
sdp_init_services_list(&adapter->bdaddr);
- if (main_opts.attrib_server)
+ if (main_opts.gatt_enabled)
btd_adapter_gatt_server_start(adapter);
load_drivers(adapter);
g_slist_free(adapter->devices);
unload_drivers(adapter);
- if (main_opts.attrib_server)
+ if (main_opts.gatt_enabled)
btd_adapter_gatt_server_stop(adapter);
g_slist_free(adapter->pin_callbacks);
if (!adapter_has_discov_sessions(adapter) || adapter->discov_suspended)
return;
- DBG("hci%u enabling timer, disc_sessions %u", adapter->dev_id,
+ DBG("hci%u restarting discovery, disc_sessions %u", adapter->dev_id,
g_slist_length(adapter->disc_sessions));
- adapter->discov_id = g_timeout_add_seconds(main_opts.discov_interval,
- discovery_cb, adapter);
+ adapter->discov_id = g_idle_add(discovery_cb, adapter);
}
static void suspend_discovery(struct btd_adapter *adapter)
} else
alias = g_strdup(dev->alias);
- if (dev->type != ADDR_TYPE_BREDR) {
+ if (dev->bdaddr_type != BDADDR_BREDR) {
gboolean broadcaster;
+ uint16_t app;
if (dev->flags & (EIR_LIM_DISC | EIR_GEN_DISC))
broadcaster = FALSE;
dev->legacy = FALSE;
+ if (read_remote_appearance(&adapter->bdaddr, &dev->bdaddr,
+ dev->bdaddr_type, &app) == 0)
+ icon = gap_appearance_to_icon(app);
+ else
+ icon = 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,
}
static struct remote_dev_info *found_device_new(const bdaddr_t *bdaddr,
- addr_type_t type, const char *name,
+ uint8_t bdaddr_type, const char *name,
const char *alias, uint32_t class,
gboolean legacy, int flags)
{
dev = g_new0(struct remote_dev_info, 1);
bacpy(&dev->bdaddr, bdaddr);
- dev->type = type;
+ dev->bdaddr_type = bdaddr_type;
dev->name = g_strdup(name);
dev->alias = g_strdup(alias);
dev->class = class;
}
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,
+ bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ int8_t rssi, uint8_t confirm_name,
uint8_t *data, uint8_t data_len)
{
struct remote_dev_info *dev;
struct eir_data eir_data;
char *alias, *name;
gboolean legacy, name_known;
+ uint32_t dev_class;
int err;
memset(&eir_data, 0, sizeof(eir_data));
return;
}
+ dev_class = eir_data.dev_class[0] | (eir_data.dev_class[1] << 8) |
+ (eir_data.dev_class[2] << 16);
+ if (dev_class != 0)
+ write_remote_class(&adapter->bdaddr, bdaddr, dev_class);
+
+ if (eir_data.appearance != 0)
+ write_remote_appearance(&adapter->bdaddr, bdaddr, bdaddr_type,
+ eir_data.appearance);
+
if (eir_data.name != NULL && eir_data.name_complete)
write_device_name(&adapter->bdaddr, bdaddr, eir_data.name);
if (dev) {
adapter->oor_devices = g_slist_remove(adapter->oor_devices,
dev);
+
+ /* If an existing device had no name but the newly received EIR
+ * data has (complete or not), we want to present it to the
+ * user. */
+ if (dev->name == NULL && eir_data.name != NULL) {
+ dev->name = g_strdup(eir_data.name);
+ goto done;
+ }
+
if (dev->rssi != rssi)
goto done;
name = read_stored_data(&adapter->bdaddr, bdaddr, "names");
- if (type == ADDR_TYPE_BREDR) {
+ if (bdaddr_type == BDADDR_BREDR) {
legacy = pairing_is_legacy(&adapter->bdaddr, bdaddr, data,
name);
}
if (confirm_name)
- adapter_ops->confirm_name(adapter->dev_id, bdaddr, name_known);
+ adapter_ops->confirm_name(adapter->dev_id, bdaddr, bdaddr_type,
+ name_known);
alias = read_stored_data(&adapter->bdaddr, bdaddr, "aliases");
- dev = found_device_new(bdaddr, type, name, alias, class, legacy,
- eir_data.flags);
+ dev = found_device_new(bdaddr, bdaddr_type, name, alias, dev_class,
+ legacy, eir_data.flags);
free(name);
free(alias);
if (adapter->scan_mode == scan_mode)
return;
- adapter_remove_discov_timeout(adapter);
-
switch (scan_mode) {
case SCAN_DISABLED:
adapter->mode = MODE_OFF;
adapter->mode = MODE_DISCOVERABLE;
discoverable = TRUE;
pairable = adapter->pairable;
- if (adapter->discov_timeout != 0)
- adapter_set_discov_timeout(adapter,
- adapter->discov_timeout);
break;
- case SCAN_INQUIRY:
- /* Address the scenario where a low-level application like
- * hciconfig changed the scan mode */
- if (adapter->discov_timeout != 0)
- adapter_set_discov_timeout(adapter,
- adapter->discov_timeout);
-
- /* ignore, this event should not be sent */
default:
/* ignore, reserved */
return;
ADAPTER_INTERFACE, "Pairable",
DBUS_TYPE_BOOLEAN, &pairable);
- if (!discoverable)
- adapter_set_limited_discoverable(adapter, FALSE);
-
emit_property_changed(connection, path,
ADAPTER_INTERFACE, "Discoverable",
DBUS_TYPE_BOOLEAN, &discoverable);
/* Device connected? */
if (!g_slist_find(adapter->connections, device))
- return -ENOTCONN;
+ error("Authorization request for non-connected device!?");
if (adapter->auth_idle_id)
return -EBUSY;
agent = device_get_agent(device);
if (!agent) {
+ warn("Can't find device agent");
g_free(auth);
return -EPERM;
}
}
ssize_t btd_adapter_get_pin(struct btd_adapter *adapter, struct btd_device *dev,
- char *pin_buf)
+ char *pin_buf, gboolean *display)
{
GSList *l;
btd_adapter_pin_cb_t cb;
for (l = adapter->pin_callbacks; l != NULL; l = g_slist_next(l)) {
cb = l->data;
- ret = cb(adapter, dev, pin_buf);
+ ret = cb(adapter, dev, pin_buf, display);
if (ret > 0)
return ret;
}
timeout, clock, accuracy);
}
-int btd_adapter_disconnect_device(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+int btd_adapter_disconnect_device(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr, uint8_t bdaddr_type)
+
{
- return adapter_ops->disconnect(adapter->dev_id, bdaddr);
+ return adapter_ops->disconnect(adapter->dev_id, bdaddr, bdaddr_type);
}
-int btd_adapter_remove_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr)
+int btd_adapter_remove_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
{
- return adapter_ops->remove_bonding(adapter->dev_id, bdaddr);
+ return adapter_ops->remove_bonding(adapter->dev_id, bdaddr,
+ bdaddr_type);
}
int btd_adapter_pincode_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
}
int btd_adapter_confirm_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- gboolean success)
+ uint8_t bdaddr_type, gboolean success)
{
- return adapter_ops->confirm_reply(adapter->dev_id, bdaddr, success);
+ return adapter_ops->confirm_reply(adapter->dev_id, bdaddr, bdaddr_type,
+ success);
}
int btd_adapter_passkey_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- uint32_t passkey)
+ uint8_t bdaddr_type, uint32_t passkey)
{
- return adapter_ops->passkey_reply(adapter->dev_id, bdaddr, passkey);
+ return adapter_ops->passkey_reply(adapter->dev_id, bdaddr, bdaddr_type,
+ passkey);
}
int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
}
int btd_adapter_set_did(struct btd_adapter *adapter, uint16_t vendor,
- uint16_t product, uint16_t version)
+ uint16_t product, uint16_t version,
+ uint16_t source)
{
- return adapter_ops->set_did(adapter->dev_id, vendor, product, version);
+ return adapter_ops->set_did(adapter->dev_id, vendor, product, version,
+ source);
}
int adapter_create_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- uint8_t io_cap)
+ uint8_t addr_type, uint8_t io_cap)
{
suspend_discovery(adapter);
- return adapter_ops->create_bonding(adapter->dev_id, bdaddr, io_cap);
+ return adapter_ops->create_bonding(adapter->dev_id, bdaddr,
+ addr_type, io_cap);
}
int adapter_cancel_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr)
/* Invalid SSP passkey value used to indicate negative replies */
#define INVALID_PASSKEY 0xffffffff
-typedef enum {
- ADDR_TYPE_BREDR,
- ADDR_TYPE_LE_PUBLIC,
- ADDR_TYPE_LE_RANDOM,
-} addr_type_t;
-
struct btd_adapter;
struct link_key_info {
uint8_t pin_len;
};
+struct smp_ltk_info {
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ uint8_t authenticated;
+ uint8_t master;
+ uint8_t enc_size;
+ uint16_t ediv;
+ uint8_t rand[8];
+ uint8_t val[16];
+};
+
struct remote_dev_info {
bdaddr_t bdaddr;
- addr_type_t type;
+ uint8_t bdaddr_type;
int8_t rssi;
uint32_t class;
char *name;
char **uuids;
size_t uuid_count;
GSList *services;
- uint8_t bdaddr_type;
uint8_t flags;
};
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);
+ uint8_t *on_mode,
+ uint16_t *discoverable_timeout,
+ gboolean *pairable);
+void btd_adapter_get_class(struct btd_adapter *adapter, uint8_t *major,
+ uint8_t *minor);
+const char *btd_adapter_get_name(struct btd_adapter *adapter);
struct btd_device *adapter_get_device(DBusConnection *conn,
struct btd_adapter *adapter, const char *address);
gboolean remove_storage);
struct btd_adapter *adapter_create(DBusConnection *conn, int id);
-gboolean adapter_init(struct btd_adapter *adapter);
+gboolean adapter_init(struct btd_adapter *adapter, gboolean up);
void adapter_remove(struct btd_adapter *adapter);
void adapter_set_allow_name_changes(struct btd_adapter *adapter,
gboolean allow_name_changes);
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);
struct remote_dev_info *adapter_search_found_devices(struct btd_adapter *adapter,
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,
+ bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ 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 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);
+ struct btd_device *dev, char *out, gboolean *display);
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);
+ char *pin_buf, gboolean *display);
typedef void (*bt_hci_result_t) (uint8_t status, gpointer user_data);
int (*setup) (void);
void (*cleanup) (void);
int (*set_powered) (int index, gboolean powered);
- int (*set_discoverable) (int index, gboolean discoverable);
+ int (*set_discoverable) (int index, gboolean discoverable,
+ uint16_t timeout);
int (*set_pairable) (int index, gboolean pairable);
- int (*set_limited_discoverable) (int index, gboolean limited);
int (*start_discovery) (int index);
int (*stop_discovery) (int index);
int (*read_clock) (int index, bdaddr_t *bdaddr, int which, int timeout,
uint32_t *clock, uint16_t *accuracy);
int (*read_bdaddr) (int index, bdaddr_t *bdaddr);
- int (*block_device) (int index, bdaddr_t *bdaddr);
- int (*unblock_device) (int index, bdaddr_t *bdaddr);
+ int (*block_device) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type);
+ int (*unblock_device) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type);
int (*get_conn_list) (int index, GSList **conns);
- int (*disconnect) (int index, bdaddr_t *bdaddr);
- int (*remove_bonding) (int index, bdaddr_t *bdaddr);
+ int (*disconnect) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type);
+ int (*remove_bonding) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type);
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 (*confirm_reply) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ gboolean success);
+ int (*passkey_reply) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ uint32_t passkey);
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,
- uint16_t version);
+ uint16_t version, uint16_t source);
int (*add_uuid) (int index, uuid_t *uuid, uint8_t svc_hint);
int (*remove_uuid) (int index, uuid_t *uuid);
int (*disable_cod_cache) (int index);
int (*restore_powered) (int index);
int (*load_keys) (int index, GSList *keys, gboolean debug_keys);
int (*set_io_capability) (int index, uint8_t io_capability);
- int (*create_bonding) (int index, bdaddr_t *bdaddr, uint8_t io_cap);
+ int (*create_bonding) (int index, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type, 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 (*confirm_name) (int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ gboolean name_known);
+ int (*load_ltks) (int index, GSList *keys);
};
int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority);
int which, int timeout, uint32_t *clock,
uint16_t *accuracy);
-int btd_adapter_block_address(struct btd_adapter *adapter, bdaddr_t *bdaddr);
-int btd_adapter_unblock_address(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+int btd_adapter_block_address(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type);
+int btd_adapter_unblock_address(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type);
int btd_adapter_disconnect_device(struct btd_adapter *adapter,
- bdaddr_t *bdaddr);
+ bdaddr_t *bdaddr, uint8_t bdaddr_type);
-int btd_adapter_remove_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr);
+int btd_adapter_remove_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type);
int btd_adapter_pincode_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
const char *pin, size_t pin_len);
int btd_adapter_confirm_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- gboolean success);
+ uint8_t bdaddr_type, gboolean success);
int btd_adapter_passkey_reply(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- uint32_t passkey);
+ uint8_t bdaddr_type, uint32_t passkey);
int btd_adapter_encrypt_link(struct btd_adapter *adapter, bdaddr_t *bdaddr,
bt_hci_result_t cb, gpointer user_data);
int btd_adapter_set_did(struct btd_adapter *adapter, uint16_t vendor,
- uint16_t product, uint16_t version);
+ uint16_t product, uint16_t version,
+ uint16_t source);
int adapter_create_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- uint8_t io_cap);
+ uint8_t bdaddr_type, uint8_t io_cap);
int adapter_cancel_bonding(struct btd_adapter *adapter, bdaddr_t *bdaddr);
AGENT_REQUEST_CONFIRMATION,
AGENT_REQUEST_PINCODE,
AGENT_REQUEST_AUTHORIZE,
- AGENT_REQUEST_CONFIRM_MODE
+ AGENT_REQUEST_CONFIRM_MODE,
+ AGENT_REQUEST_DISPLAY_PINCODE,
} agent_request_type_t;
struct agent {
if (agent->request) {
DBusError err;
agent_pincode_cb pincode_cb;
+ agent_passkey_cb passkey_cb;
agent_cb cb;
dbus_error_init(&err);
pincode_cb = agent->request->cb;
pincode_cb(agent, &err, NULL, agent->request->user_data);
break;
+ case AGENT_REQUEST_PASSKEY:
+ passkey_cb = agent->request->cb;
+ passkey_cb(agent, &err, 0, agent->request->user_data);
+ break;
default:
cb = agent->request->cb;
cb(agent, &err, agent->request->user_data);
error("Agent replied with an error: %s, %s",
err.name, err.message);
-#ifdef __TIZEN_PATCH__
- if (strcmp(err.message, "CanceledbyUser") == 0)
- {
- set_cancel_from_authentication_req(req->user_data);
- }
-#endif
cb(agent, &err, req->user_data);
if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
goto done;
}
- dbus_error_init(&err);
if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
error("Wrong reply signature: %s", err.message);
cb(agent, &err, req->user_data);
error("Agent %s replied with an error: %s, %s",
agent->path, err.name, err.message);
-#ifdef __TIZEN_PATCH__
- if (strcmp(err.message, "CanceledbyUser") == 0)
- {
- set_cancel_from_authentication_req(req->user_data);
- }
-#endif
-
cb(agent, &err, NULL, req->user_data);
dbus_error_free(&err);
goto done;
}
- dbus_error_init(&err);
if (!dbus_message_get_args(message, &err,
DBUS_TYPE_STRING, &pin,
DBUS_TYPE_INVALID)) {
len = strlen(pin);
- dbus_error_init(&err);
if (len > 16 || len < 1) {
error("Invalid PIN length (%zu) from agent", len);
dbus_set_error_const(&err, "org.bluez.Error.InvalidArgs",
return 0;
failed:
-#ifdef __TIZEN_PATCH__
agent_request_free(req, FALSE);
-#else
- g_free(req);
-#endif
return err;
}
if (dbus_set_error_from_message(&err, message)) {
error("Agent replied with an error: %s, %s",
err.name, err.message);
-#ifdef __TIZEN_PATCH__
- if (strcmp(err.message, "CanceledbyUser") == 0)
- {
- set_cancel_from_authentication_req(req->user_data);
- }
-#endif
-
cb(agent, &err, 0, req->user_data);
dbus_error_free(&err);
goto done;
}
- dbus_error_init(&err);
if (!dbus_message_get_args(message, &err,
DBUS_TYPE_UINT32, &passkey,
DBUS_TYPE_INVALID)) {
DBUS_TYPE_INVALID);
if (!g_dbus_send_message(connection, message)) {
-#ifndef __TIZEN_PATCH__
- dbus_message_unref(message);
-#endif
error("D-Bus send failed");
return -1;
}
return 0;
}
+static void display_pincode_reply(DBusPendingCall *call, void *user_data)
+{
+ struct agent_request *req = user_data;
+ struct agent *agent = req->agent;
+ DBusMessage *message;
+ DBusError err;
+ agent_cb cb = req->cb;
+
+ /* clear agent->request early; our callback will likely try
+ * another request */
+ agent->request = NULL;
+
+ /* steal_reply will always return non-NULL since the callback
+ * is only called after a reply has been received */
+ message = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_set_error_from_message(&err, message)) {
+ error("Agent replied with an error: %s, %s",
+ err.name, err.message);
+
+ cb(agent, &err, req->user_data);
+
+ if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
+ agent_cancel(agent);
+ dbus_message_unref(message);
+ dbus_error_free(&err);
+ return;
+ }
+
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
+ error("Wrong reply signature: %s", err.message);
+ cb(agent, &err, req->user_data);
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ cb(agent, NULL, req->user_data);
+done:
+ dbus_message_unref(message);
+
+ agent_request_free(req, TRUE);
+}
+
+static int display_pincode_request_new(struct agent_request *req,
+ const char *device_path,
+ const char *pincode)
+{
+ struct agent *agent = req->agent;
+
+ req->msg = dbus_message_new_method_call(agent->name, agent->path,
+ "org.bluez.Agent", "DisplayPinCode");
+ if (req->msg == NULL) {
+ error("Couldn't allocate D-Bus message");
+ return -ENOMEM;
+ }
+
+ dbus_message_append_args(req->msg,
+ DBUS_TYPE_OBJECT_PATH, &device_path,
+ DBUS_TYPE_STRING, &pincode,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(connection, req->msg,
+ &req->call, REQUEST_TIMEOUT) == FALSE) {
+ error("D-Bus send failed");
+ return -EIO;
+ }
+
+ dbus_pending_call_set_notify(req->call, display_pincode_reply,
+ req, NULL);
+
+ return 0;
+}
+
+int agent_display_pincode(struct agent *agent, struct btd_device *device,
+ const char *pincode, agent_cb cb,
+ void *user_data, GDestroyNotify destroy)
+{
+ struct agent_request *req;
+ const gchar *dev_path = device_get_path(device);
+ int err;
+
+ if (agent->request)
+ return -EBUSY;
+
+ DBG("Calling Agent.DisplayPinCode: name=%s, path=%s, pincode=%s",
+ agent->name, agent->path, pincode);
+
+ req = agent_request_new(agent, AGENT_REQUEST_DISPLAY_PINCODE, cb,
+ user_data, destroy);
+
+ err = display_pincode_request_new(req, dev_path, pincode);
+ if (err < 0)
+ goto failed;
+
+ agent->request = req;
+
+ return 0;
+
+failed:
+ agent_request_free(req, FALSE);
+ return err;
+}
+
uint8_t agent_get_io_capability(struct agent *agent)
{
return agent->capability;
int agent_display_passkey(struct agent *agent, struct btd_device *device,
uint32_t passkey);
+int agent_display_pincode(struct agent *agent, struct btd_device *device,
+ const char *pincode, agent_cb cb,
+ void *user_data, GDestroyNotify destroy);
+
int agent_cancel(struct agent *agent);
gboolean agent_is_busy(struct agent *agent, void *user_data);
void agent_init(void);
void agent_exit(void);
-
#include "log.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 "att.h"
+#include "gatt.h"
+#include "att-database.h"
#include "storage.h"
#include "attrib-server.h"
GIOChannel *le_io;
uint32_t gatt_sdp_handle;
uint32_t gap_sdp_handle;
- GSList *database;
+ GList *database;
GSList *clients;
uint16_t name_handle;
uint16_t appearance_handle;
guint id;
gboolean encrypted;
struct gatt_server *server;
+ guint cleanup_id;
+ struct btd_device *device;
};
struct group_elem {
static void channel_free(struct gatt_channel *channel)
{
- g_attrib_unref(channel->attrib);
+ if (channel->cleanup_id)
+ g_source_remove(channel->cleanup_id);
+
+ if (channel->device)
+ btd_device_unref(channel->device);
+
+ 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);
+ g_list_free_full(server->database, attrib_free);
if (server->l2cap_io != NULL) {
g_io_channel_unref(server->l2cap_io);
char addr[18];
ba2str(bdaddr, addr);
- error("Not GATT adapter found for address %s", addr);
+ error("No GATT server found in %s", addr);
return NULL;
}
return attrib1->handle - attrib2->handle;
}
-static struct attribute *find_primary_range(struct gatt_server *server,
- uint16_t start, uint16_t *end)
+static struct attribute *find_svc_range(struct gatt_server *server,
+ uint16_t start, uint16_t *end)
{
struct attribute *attrib;
guint h = start;
- GSList *l;
+ GList *l;
if (end == NULL)
return NULL;
- l = g_slist_find_custom(server->database, GUINT_TO_POINTER(h),
+ l = g_list_find_custom(server->database, GUINT_TO_POINTER(h),
handle_cmp);
if (!l)
return NULL;
attrib = l->data;
- if (bt_uuid_cmp(&attrib->uuid, &prim_uuid) != 0)
+ if (bt_uuid_cmp(&attrib->uuid, &prim_uuid) != 0 &&
+ bt_uuid_cmp(&attrib->uuid, &snd_uuid) != 0)
return NULL;
*end = start;
uuid_t svc, gap_uuid;
bdaddr_t addr;
- a = find_primary_range(server, handle, &end);
+ a = find_svc_range(server, handle, &end);
if (a == NULL)
return 0;
DBG("handle=0x%04x", handle);
- if (g_slist_find_custom(server->database, GUINT_TO_POINTER(h),
+ if (g_list_find_custom(server->database, GUINT_TO_POINTER(h),
handle_cmp))
return NULL;
a->read_reqs = read_reqs;
a->write_reqs = write_reqs;
- server->database = g_slist_insert_sorted(server->database, a,
+ server->database = g_list_insert_sorted(server->database, a,
attribute_cmp);
return a;
struct att_data_list *adl;
struct attribute *a;
struct group_elem *cur, *old = NULL;
- GSList *l, *groups, *database;
+ GSList *l, *groups;
+ GList *dl, *database;
uint16_t length, last_handle, last_size = 0;
uint8_t status;
int i;
last_handle = end;
database = channel->server->database;
- for (l = database, groups = NULL, cur = NULL; l; l = l->next) {
+ for (dl = database, groups = NULL, cur = NULL; dl; dl = dl->next) {
- a = l->data;
+ a = dl->data;
if (a->handle < start)
continue;
a->read_reqs);
if (status == 0x00 && a->read_cb)
- status = a->read_cb(a, a->cb_user_data);
+ status = a->read_cb(a, channel->device,
+ a->cb_user_data);
if (status) {
g_slist_free_full(groups, g_free);
return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ, start,
ATT_ECODE_ATTR_NOT_FOUND, pdu, len);
- if (l == NULL)
+ if (dl == NULL)
cur->end = a->handle;
else
cur->end = last_handle;
uint8_t *pdu, int len)
{
struct att_data_list *adl;
- GSList *l, *types, *database;
+ GSList *l, *types;
+ GList *dl, *database;
struct attribute *a;
uint16_t num, length;
uint8_t status;
ATT_ECODE_INVALID_HANDLE, pdu, len);
database = channel->server->database;
- for (l = database, length = 0, types = NULL; l; l = l->next) {
+ for (dl = database, length = 0, types = NULL; dl; dl = dl->next) {
- a = l->data;
+ a = dl->data;
if (a->handle < start)
continue;
a->read_reqs);
if (status == 0x00 && a->read_cb)
- status = a->read_cb(a, a->cb_user_data);
+ status = a->read_cb(a, channel->device,
+ a->cb_user_data);
if (status) {
g_slist_free(types);
{
struct attribute *a;
struct att_data_list *adl;
- GSList *l, *info, *database;
+ GSList *l, *info;
+ GList *dl, *database;
uint8_t format, last_type = BT_UUID_UNSPEC;
uint16_t length, num;
int i;
ATT_ECODE_INVALID_HANDLE, pdu, len);
database = channel->server->database;
- for (l = database, info = NULL, num = 0; l; l = l->next) {
- a = l->data;
+ for (dl = database, info = NULL, num = 0; dl; dl = dl->next) {
+ a = dl->data;
if (a->handle < start)
continue;
{
struct attribute *a;
struct att_range *range;
- GSList *l, *matches, *database;
+ GSList *matches;
+ GList *dl, *database;
int len;
if (start > end || start == 0x0000)
/* Searching first requested handle number */
database = channel->server->database;
- for (l = database, matches = NULL, range = NULL; l; l = l->next) {
- a = l->data;
+ for (dl = database, matches = NULL, range = NULL; dl; dl = dl->next) {
+ a = dl->data;
if (a->handle < start)
continue;
{
struct attribute *a;
uint8_t status;
- GSList *l;
+ GList *l;
uint16_t cccval;
+ uint8_t bdaddr_type;
guint h = handle;
- l = g_slist_find_custom(channel->server->database,
+ l = g_list_find_custom(channel->server->database,
GUINT_TO_POINTER(h), handle_cmp);
if (!l)
return enc_error_resp(ATT_OP_READ_REQ, handle,
a = l->data;
+ bdaddr_type = device_get_addr_type(channel->device);
+
if (bt_uuid_cmp(&ccc_uuid, &a->uuid) == 0 &&
- read_device_ccc(&channel->src, &channel->dst,
- handle, &cccval) == 0) {
+ read_device_ccc(&channel->src, &channel->dst, bdaddr_type,
+ handle, &cccval) == 0) {
uint8_t config[2];
att_put_u16(cccval, config);
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);
+ status = a->read_cb(a, channel->device, a->cb_user_data);
if (status)
return enc_error_resp(ATT_OP_READ_REQ, handle, status, pdu,
{
struct attribute *a;
uint8_t status;
- GSList *l;
+ GList *l;
uint16_t cccval;
+ uint8_t bdaddr_type;
guint h = handle;
- l = g_slist_find_custom(channel->server->database,
+ l = g_list_find_custom(channel->server->database,
GUINT_TO_POINTER(h), handle_cmp);
if (!l)
return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
ATT_ECODE_INVALID_OFFSET, pdu, len);
+ bdaddr_type = device_get_addr_type(channel->device);
+
if (bt_uuid_cmp(&ccc_uuid, &a->uuid) == 0 &&
- read_device_ccc(&channel->src, &channel->dst,
- handle, &cccval) == 0) {
+ read_device_ccc(&channel->src, &channel->dst, bdaddr_type,
+ handle, &cccval) == 0) {
uint8_t config[2];
att_put_u16(cccval, config);
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);
+ status = a->read_cb(a, channel->device, a->cb_user_data);
if (status)
return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle, status,
{
struct attribute *a;
uint8_t status;
- GSList *l;
+ GList *l;
guint h = handle;
- l = g_slist_find_custom(channel->server->database,
+ l = g_list_find_custom(channel->server->database,
GUINT_TO_POINTER(h), handle_cmp);
if (!l)
return enc_error_resp(ATT_OP_WRITE_REQ, handle,
value, vlen, NULL);
if (a->write_cb) {
- status = a->write_cb(a, a->cb_user_data);
+ status = a->write_cb(a, channel->device,
+ 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);
+ uint8_t bdaddr_type = device_get_addr_type(channel->device);
+
+ write_device_ccc(&channel->src, &channel->dst, bdaddr_type,
+ handle, cccval);
}
return enc_write_resp(pdu, len);
static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu,
uint8_t *pdu, int len)
{
- guint old_mtu = channel->mtu;
+ GError *gerr = NULL;
+ GIOChannel *io;
+ uint16_t imtu;
if (mtu < ATT_DEFAULT_LE_MTU)
- channel->mtu = ATT_DEFAULT_LE_MTU;
- else
- channel->mtu = MIN(mtu, channel->mtu);
+ return enc_error_resp(ATT_OP_MTU_REQ, 0,
+ ATT_ECODE_REQ_NOT_SUPP, pdu, len);
+
+ io = g_attrib_get_channel(channel->attrib);
- bt_io_set(channel->server->le_io, BT_IO_L2CAP, NULL,
- BT_IO_OPT_OMTU, channel->mtu,
+ bt_io_get(io, BT_IO_L2CAP, &gerr,
+ BT_IO_OPT_IMTU, &imtu,
BT_IO_OPT_INVALID);
- return enc_mtu_resp(old_mtu, pdu, len);
+ if (gerr)
+ return enc_error_resp(ATT_OP_MTU_REQ, 0,
+ ATT_ECODE_UNLIKELY, pdu, len);
+
+ channel->mtu = MIN(mtu, imtu);
+ g_attrib_set_mtu(channel->attrib, channel->mtu);
+
+ return enc_mtu_resp(imtu, pdu, len);
}
-static void channel_disconnect(void *user_data)
+static void channel_remove(struct gatt_channel *channel)
{
- struct gatt_channel *channel = user_data;
-
channel->server->clients = g_slist_remove(channel->server->clients,
channel);
channel_free(channel);
}
+static gboolean channel_watch_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ channel_remove(user_data);
+
+ return FALSE;
+}
+
static void channel_handler(const uint8_t *ipdu, uint16_t len,
gpointer user_data)
{
break;
case ATT_OP_HANDLE_CNF:
return;
+ case ATT_OP_HANDLE_IND:
+ case ATT_OP_HANDLE_NOTIFY:
+ /* The attribute client is already handling these */
+ return;
case ATT_OP_READ_MULTI_REQ:
case ATT_OP_PREP_WRITE_REQ:
case ATT_OP_EXEC_WRITE_REQ:
NULL, NULL, NULL);
}
-guint attrib_channel_attach(GAttrib *attrib, gboolean out)
+guint attrib_channel_attach(GAttrib *attrib)
{
struct gatt_server *server;
struct btd_device *device;
GError *gerr = NULL;
char addr[18];
uint16_t cid;
+ guint mtu = 0;
io = g_attrib_get_channel(attrib);
BT_IO_OPT_SOURCE_BDADDR, &channel->src,
BT_IO_OPT_DEST_BDADDR, &channel->dst,
BT_IO_OPT_CID, &cid,
- BT_IO_OPT_OMTU, &channel->mtu,
+ BT_IO_OPT_IMTU, &mtu,
BT_IO_OPT_INVALID);
if (gerr) {
error("bt_io_get: %s", gerr->message);
}
server = find_gatt_server(&channel->src);
- if (server == NULL)
+ if (server == NULL) {
+ char src[18];
+
+ ba2str(&channel->src, src);
+ error("No GATT server found in %s", src);
+ g_free(channel);
return 0;
+ }
channel->server = server;
ba2str(&channel->dst, addr);
- device = adapter_find_device(server->adapter, addr);
- if (device_is_bonded(device) == FALSE)
+ device = adapter_find_device(server->adapter, addr);
+ if (device == NULL || 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 != ATT_CID)
+ if (cid != ATT_CID) {
channel->le = FALSE;
- else
+ channel->mtu = mtu;
+ } else {
channel->le = TRUE;
-
+ channel->mtu = ATT_DEFAULT_LE_MTU;
+ }
channel->attrib = g_attrib_ref(attrib);
channel->id = g_attrib_register(channel->attrib, GATTRIB_ALL_REQS,
channel_handler, channel, NULL);
- if (out == FALSE)
- g_attrib_set_disconnect_function(channel->attrib,
- channel_disconnect, channel);
+ channel->cleanup_id = g_io_add_watch(io, G_IO_HUP, channel_watch_cb,
+ channel);
+
+ channel->device = btd_device_ref(device);
server->clients = g_slist_append(server->clients, channel);
channel = l->data;
g_attrib_unregister(channel->attrib, channel->id);
-
- channel_disconnect(channel);
+ channel_remove(channel);
return TRUE;
}
}
attrib = g_attrib_new(io);
- attrib_channel_attach(attrib, FALSE);
- g_io_channel_unref(io);
+ attrib_channel_attach(attrib);
g_attrib_unref(attrib);
}
remove_record_from_server(sdp_handle);
}
-uint16_t attrib_db_find_avail(struct btd_adapter *adapter, uint16_t nitems)
+static uint16_t find_uuid16_avail(struct btd_adapter *adapter, uint16_t nitems)
{
struct gatt_server *server;
uint16_t handle;
GSList *l;
-
- g_assert(nitems > 0);
+ GList *dl;
l = g_slist_find_custom(servers, adapter, adapter_cmp);
if (l == NULL)
return 0;
server = l->data;
+ if (server->database == NULL)
+ return 0x0001;
- for (l = server->database, handle = 0; l; l = l->next) {
- struct attribute *a = l->data;
+ for (dl = server->database, handle = 0x0001; dl; dl = dl->next) {
+ struct attribute *a = dl->data;
- if (handle && (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+ if ((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;
+ if (a->len == 16 && (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
+ bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)) {
+ /* 128 bit UUID service definition */
+ return 0;
+ }
+
if (a->handle == 0xffff)
return 0;
return 0;
}
+static uint16_t find_uuid128_avail(struct btd_adapter *adapter, uint16_t nitems)
+{
+ uint16_t handle = 0, end = 0xffff;
+ struct gatt_server *server;
+ GList *dl;
+ GSList *l;
+
+ l = g_slist_find_custom(servers, adapter, adapter_cmp);
+ if (l == NULL)
+ return 0;
+
+ server = l->data;
+ if (server->database == NULL)
+ return 0xffff - nitems + 1;
+
+ for (dl = g_list_last(server->database); dl; dl = dl->prev) {
+ struct attribute *a = dl->data;
+
+ if (handle == 0)
+ handle = a->handle;
+
+ if (bt_uuid_cmp(&a->uuid, &prim_uuid) != 0 &&
+ bt_uuid_cmp(&a->uuid, &snd_uuid) != 0)
+ continue;
+
+ if (end - handle >= nitems)
+ return end - nitems + 1;
+
+ if (a->len == 2) {
+ /* 16 bit UUID service definition */
+ return 0;
+ }
+
+ if (a->handle == 0x0001)
+ return 0;
+
+ end = a->handle - 1;
+ handle = 0;
+ }
+
+ if (end - 0x0001 >= nitems)
+ return end - nitems + 1;
+
+ return 0;
+}
+
+uint16_t attrib_db_find_avail(struct btd_adapter *adapter, bt_uuid_t *svc_uuid,
+ uint16_t nitems)
+{
+ g_assert(nitems > 0);
+
+ if (svc_uuid->type == BT_UUID16)
+ return find_uuid16_avail(adapter, nitems);
+ else if (svc_uuid->type == BT_UUID128)
+ return find_uuid128_avail(adapter, nitems);
+ else {
+ char uuidstr[MAX_LEN_UUID_STR];
+
+ bt_uuid_to_string(svc_uuid, uuidstr, MAX_LEN_UUID_STR);
+ error("Service uuid: %s is neither a 16-bit nor a 128-bit uuid",
+ uuidstr);
+ return 0;
+ }
+}
+
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)
struct gatt_server *server;
struct attribute *a;
GSList *l;
+ GList *dl;
guint h = handle;
l = g_slist_find_custom(servers, adapter, adapter_cmp);
DBG("handle=0x%04x", handle);
- l = g_slist_find_custom(server->database, GUINT_TO_POINTER(h),
+ dl = g_list_find_custom(server->database, GUINT_TO_POINTER(h),
handle_cmp);
- if (!l)
+ if (dl == NULL)
return -ENOENT;
- a = l->data;
+ a = dl->data;
a->data = g_try_realloc(a->data, len);
- if (a->data == NULL)
+ if (len && a->data == NULL)
return -ENOMEM;
a->len = len;
struct gatt_server *server;
struct attribute *a;
GSList *l;
+ GList *dl;
guint h = handle;
l = g_slist_find_custom(servers, adapter, adapter_cmp);
DBG("handle=0x%04x", handle);
- l = g_slist_find_custom(server->database, GUINT_TO_POINTER(h),
+ dl = g_list_find_custom(server->database, GUINT_TO_POINTER(h),
handle_cmp);
- if (!l)
+ if (dl == NULL)
return -ENOENT;
- a = l->data;
- server->database = g_slist_remove(server->database, a);
+ a = dl->data;
+ server->database = g_list_remove(server->database, a);
g_free(a->data);
g_free(a);
*
*/
-uint16_t attrib_db_find_avail(struct btd_adapter *adapter, uint16_t nitems);
+uint16_t attrib_db_find_avail(struct btd_adapter *adapter, bt_uuid_t *svc_uuid,
+ 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);
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);
+guint attrib_channel_attach(GAttrib *attrib);
gboolean attrib_channel_detach(GAttrib *attrib, guint id);
-<?xml version="1.0"?><!--*-nxml-*-->
+<!-- This configuration file specifies the required security policies
+ for Bluetooth core daemon to work. -->
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
-
<busconfig>
- <policy user="root">
- <allow own="org.projectx.bluetooth"/>
- <allow send_interface="org.projectx.bluetooth"/>
- <allow send_destination="org.projectx.bluetooth"/>
- <allow own="org.bluez.frwk_agent"/>
- <allow send_interface="org.bluez.frwk_agent"/>
- <allow send_destination="org.bluez.frwk_agent"/>
- <allow own="org.bluez.Agent"/>
- <allow send_interface="org.bluez.Agent"/>
- <allow send_destination="org.bluez.Agent"/>
- <allow own="org.bluez.Adapter"/>
- <allow send_interface="org.bluez.Adapter"/>
- <allow send_destination="org.bluez.Adapter"/>
- <allow own="org.bluez.Manager"/>
- <allow send_interface="org.bluez.Manager"/>
- <allow send_destination="org.bluez.Manager"/>
- <allow own="org.bluez.Device"/>
- <allow send_interface="org.bluez.Device"/>
- <allow send_destination="org.bluez.Device"/>
- <allow own="org.bluez.Serial"/>
- <allow send_interface="org.bluez.Serial"/>
- <allow send_destination="org.bluez.Serial"/>
- <allow own="org.bluez.SerialProxyManager"/>
- <allow send_interface="org.bluez.SerialProxyManager"/>
- <allow send_destination="org.bluez.SerialProxyManager"/>
- <allow own="org.bluez.SerialProxy"/>
- <allow send_interface="org.bluez.SerialProxy"/>
- <allow send_destination="org.bluez.SerialProxy"/>
- <allow own="org.bluez.NetworkServer"/>
- <allow send_interface="org.bluez.NetworkServer"/>
- <allow send_destination="org.bluez.NetworkServer"/>
- <allow own="org.bluez.Network"/>
- <allow send_interface="org.bluez.Network"/>
- <allow send_destination="org.bluez.Network"/>
- <allow own="org.bluez.AudioSink"/>
- <allow send_interface="org.bluez.AudioSink"/>
- <allow send_destination="org.bluez.AudioSink"/>
- <allow own="org.bluez.Input"/>
- <allow send_interface="org.bluez.Input"/>
- <allow send_destination="org.bluez.Input"/>
- </policy>
- <policy group="bt_use">
- <allow send_interface="org.projectx.bluetooth"/>
- <allow send_destination="org.projectx.bluetooth"/>
- <allow send_interface="org.bluez.frwk_agent"/>
- <allow send_destination="org.bluez.frwk_agent"/>
- <allow send_interface="org.bluez.Agent"/>
- <allow send_destination="org.bluez.Agent"/>
- <allow send_interface="org.bluez.Adapter"/>
- <allow send_destination="org.bluez.Adapter"/>
- <allow send_interface="org.bluez.Manager"/>
- <allow send_destination="org.bluez.Manager"/>
- <allow send_interface="org.bluez.Device"/>
- <allow send_destination="org.bluez.Device"/>
- <allow send_interface="org.bluez.Serial"/>
- <allow send_destination="org.bluez.Serial"/>
- <allow send_interface="org.bluez.SerialProxyManager"/>
- <allow send_destination="org.bluez.SerialProxyManager"/>
- <allow send_interface="org.bluez.SerialProxy"/>
- <allow send_destination="org.bluez.SerialProxy"/>
- <allow send_interface="org.bluez.NetworkServer"/>
- <allow send_destination="org.bluez.NetworkServer"/>
- <allow send_interface="org.bluez.Network"/>
- <allow send_destination="org.bluez.Network"/>
- <allow send_interface="org.bluez.AudioSink"/>
- <allow send_destination="org.bluez.AudioSink"/>
- <allow send_interface="org.bluez.Input"/>
- <allow send_destination="org.bluez.Input"/>
- </policy>
- <policy context="default">
- <deny send_interface="org.projectx.bluetooth"/>
- <deny send_destination="org.projectx.bluetooth"/>
- <deny send_interface="org.bluez.frwk_agent"/>
- <deny send_destination="org.bluez.frwk_agent"/>
- <deny send_interface="org.bluez.Agent"/>
- <deny send_destination="org.bluez.Agent"/>
- <deny send_interface="org.bluez.Adapter"/>
- <deny send_destination="org.bluez.Adapter"/>
- <deny send_interface="org.bluez.Manager"/>
- <deny send_destination="org.bluez.Manager"/>
- <deny send_interface="org.bluez.Device"/>
- <deny send_destination="org.bluez.Device"/>
- <deny send_interface="org.bluez.Serial"/>
- <deny send_destination="org.bluez.Serial"/>
- <deny send_interface="org.bluez.SerialProxyManager"/>
- <deny send_destination="org.bluez.SerialProxyManager"/>
- <deny send_interface="org.bluez.SerialProxy"/>
- <deny send_destination="org.bluez.SerialProxy"/>
- <deny send_interface="org.bluez.NetworkServer"/>
- <deny send_destination="org.bluez.NetworkServer"/>
- <deny send_interface="org.bluez.Network"/>
- <deny send_destination="org.bluez.Network"/>
- <deny send_interface="org.bluez.AudioSink"/>
- <deny send_destination="org.bluez.AudioSink"/>
- <deny send_interface="org.bluez.Input"/>
- <deny send_destination="org.bluez.Input"/>
- </policy>
+
+ <!-- ../system.conf have denied everything, so we just punch some holes -->
+
+ <policy user="root">
+ <allow own="org.bluez"/>
+ <allow send_destination="org.bluez"/>
+ <allow send_interface="org.bluez.Agent"/>
+ <allow send_interface="org.bluez.HandsfreeAgent"/>
+ <allow send_interface="org.bluez.MediaEndpoint"/>
+ <allow send_interface="org.bluez.MediaPlayer"/>
+ <allow send_interface="org.bluez.Watcher"/>
+ <allow send_interface="org.bluez.ThermometerWatcher"/>
+ </policy>
+
+ <policy at_console="true">
+ <allow send_destination="org.bluez"/>
+ </policy>
+
+ <!-- allow users of lp group (printing subsystem) to
+ communicate with bluetoothd -->
+ <policy group="lp">
+ <allow send_destination="org.bluez"/>
+ </policy>
+
+ <policy context="default">
+ <deny send_destination="org.bluez"/>
+ </policy>
+
</busconfig>
[Unit]
Description=Bluetooth service
-After=syslog.target
[Service]
Type=dbus
BusName=org.bluez
ExecStart=@prefix@/sbin/bluetoothd -n
-StandardOutput=syslog
[Install]
WantedBy=bluetooth.target
+Alias=dbus-org.bluez.service
return NULL;
}
+
+const char *gap_appearance_to_icon(uint16_t appearance)
+{
+ switch ((appearance & 0xffc0) >> 6) {
+ case 0x01:
+ return "phone";
+ case 0x02:
+ return "computer";
+ case 0x05:
+ return "video-display";
+ case 0x0a:
+ return "multimedia-player";
+ case 0x0b:
+ return "scanner";
+ case 0x0f: /* HID Generic */
+ switch (appearance & 0x3f) {
+ case 0x01:
+ return "input-keyboard";
+ case 0x02:
+ return "input-mouse";
+ case 0x03:
+ case 0x04:
+ return "input-gaming";
+ case 0x05:
+ return "input-tablet";
+ case 0x08:
+ return "scanner";
+ }
+ break;
+ }
+
+ return NULL;
+}
DBusConnection *get_dbus_connection(void);
const char *class_to_icon(uint32_t class);
+const char *gap_appearance_to_icon(uint16_t appearance);
#include <gdbus.h>
#include "log.h"
-#include "textfile.h"
#include "att.h"
#include "hcid.h"
#include "device.h"
#include "dbus-common.h"
#include "error.h"
-#include "glib-compat.h"
#include "glib-helper.h"
#include "sdp-client.h"
#include "gatt.h"
/* When all services should trust a remote device */
#define GLOBAL_TRUST "[all]"
+#define APPEARANCE_CHR_UUID 0x2a01
+
struct btd_disconnect_data {
guint id;
disconnect_watch watch;
struct bonding_req {
DBusConnection *conn;
DBusMessage *msg;
- GIOChannel *io;
guint listener_id;
#ifdef __TIZEN_PATCH__
- guint cancel_by_user;
+ gboolean cancel_by_user;
#endif
struct btd_device *device;
};
struct agent *agent;
struct btd_device *device;
uint32_t passkey;
+ char *pincode;
gboolean secure;
};
struct browse_req {
DBusConnection *conn;
DBusMessage *msg;
- GIOChannel *io;
- GAttrib *attrib;
struct btd_device *device;
GSList *match_uuids;
GSList *profiles_added;
gpointer user_data;
};
+typedef void (*attio_error_cb) (const GError *gerr, gpointer user_data);
+typedef void (*attio_success_cb) (gpointer user_data);
+
+struct att_callbacks {
+ attio_error_cb error; /* Callback for error */
+ attio_success_cb success; /* Callback for success */
+ gpointer user_data;
+};
+
struct btd_device {
bdaddr_t bdaddr;
- addr_type_t type;
+ uint8_t bdaddr_type;
gchar *path;
char name[MAX_NAME_LENGTH + 1];
char *alias;
+ uint16_t vendor_src;
uint16_t vendor;
uint16_t product;
uint16_t version;
gboolean authorizing;
gint ref;
+
+ GIOChannel *att_io;
+ guint cleanup_id;
};
static uint16_t uuid_list[] = {
static GSList *device_drivers = NULL;
-static void browse_request_free(struct browse_req *req, gboolean shutdown)
+static void browse_request_free(struct browse_req *req)
{
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)
g_free(req);
}
+static void att_cleanup(struct btd_device *device)
+{
+ if (device->attachid) {
+ attrib_channel_detach(device->attrib, device->attachid);
+ device->attachid = 0;
+ }
+
+ if (device->cleanup_id) {
+ g_source_remove(device->cleanup_id);
+ device->cleanup_id = 0;
+ }
+
+ if (device->att_io) {
+ g_io_channel_shutdown(device->att_io, FALSE, NULL);
+ g_io_channel_unref(device->att_io);
+ device->att_io = NULL;
+ }
+
+ if (device->attrib) {
+ g_attrib_unref(device->attrib);
+ device->attrib = NULL;
+ }
+}
+
static void browse_request_cancel(struct browse_req *req)
{
struct btd_device *device = req->device;
bt_cancel_discovery(&src, &device->bdaddr);
+ att_cleanup(device);
+
device->browse = NULL;
- browse_request_free(req, TRUE);
+ browse_request_free(req);
}
static void device_free(gpointer user_data)
g_slist_free_full(device->attios, g_free);
g_slist_free_full(device->attios_offline, g_free);
- g_attrib_unref(device->attrib);
+ att_cleanup(device);
if (device->tmp_records)
sdp_list_free(device->tmp_records,
DBG("%p", device);
+ if (device->authr)
+ g_free(device->authr->pincode);
g_free(device->authr);
g_free(device->path);
g_free(device->alias);
gboolean device_is_bredr(struct btd_device *device)
{
- return (device->type == ADDR_TYPE_BREDR);
+ return (device->bdaddr_type == BDADDR_BREDR);
}
gboolean device_is_le(struct btd_device *device)
{
- return (device->type != ADDR_TYPE_BREDR);
+ return (device->bdaddr_type != BDADDR_BREDR);
}
gboolean device_is_paired(struct btd_device *device)
bdaddr_t src;
char name[MAX_NAME_LENGTH + 1], srcaddr[18], dstaddr[18];
char **str;
- const char *ptr;
+ const char *ptr, *icon = NULL;
dbus_bool_t boolean;
uint32_t class;
+ uint16_t app;
int i;
GSList *l;
/* Class */
if (read_remote_class(&src, &device->bdaddr, &class) == 0) {
- const char *icon = class_to_icon(class);
+ icon = class_to_icon(class);
dict_append_entry(&dict, "Class", DBUS_TYPE_UINT32, &class);
+ } else if (read_remote_appearance(&src, &device->bdaddr,
+ device->bdaddr_type, &app) == 0)
+ /* Appearance */
+ icon = gap_appearance_to_icon(app);
- if (icon)
- dict_append_entry(&dict, "Icon",
- DBUS_TYPE_STRING, &icon);
- }
+ dict_append_entry(&dict, "Icon", DBUS_TYPE_STRING, &icon);
/* Vendor */
if (device->vendor)
dict_append_entry(&dict, "Vendor", DBUS_TYPE_UINT16,
&device->vendor);
+ /* Vendor Source*/
+ if (device->vendor_src)
+ dict_append_entry(&dict, "VendorSource", DBUS_TYPE_UINT16,
+ &device->vendor_src);
+
/* Product */
if (device->product)
dict_append_entry(&dict, "Product", DBUS_TYPE_UINT16,
&device->product);
/* Version */
- if (device->product)
+ if (device->version)
dict_append_entry(&dict, "Version", DBUS_TYPE_UINT16,
&device->version);
device->disconn_timer = 0;
- btd_adapter_disconnect_device(device->adapter, &device->bdaddr);
+ btd_adapter_disconnect_device(device->adapter, &device->bdaddr,
+ device->bdaddr_type);
return FALSE;
}
g_slist_foreach(device->drivers, (GFunc) driver_remove, device);
if (!update_only)
- err = btd_adapter_block_address(device->adapter, &device->bdaddr);
+ err = btd_adapter_block_address(device->adapter,
+ &device->bdaddr, device->bdaddr_type);
if (err < 0)
return err;
return 0;
if (!update_only)
- err = btd_adapter_unblock_address(device->adapter, &device->bdaddr);
+ err = btd_adapter_unblock_address(device->adapter,
+ &device->bdaddr, device->bdaddr_type);
if (err < 0)
return err;
return NULL;
}
-static GDBusMethodTable device_methods[] = {
- { "GetProperties", "", "a{sv}", get_properties },
- { "SetProperty", "sv", "", set_property },
- { "DiscoverServices", "s", "a{us}", discover_services,
- G_DBUS_METHOD_FLAG_ASYNC},
- { "CancelDiscovery", "", "", cancel_discover },
- { "Disconnect", "", "", disconnect,
- G_DBUS_METHOD_FLAG_ASYNC},
+static const GDBusMethodTable device_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+ set_property) },
+ { GDBUS_ASYNC_METHOD("DiscoverServices",
+ GDBUS_ARGS({ "pattern", "s" }),
+ GDBUS_ARGS({ "services", "a{us}" }),
+ discover_services) },
+ { GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) },
+ { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, disconnect) },
{ }
};
-static GDBusSignalTable device_signals[] = {
- { "PropertyChanged", "sv" },
- { "DisconnectRequested", "" },
+static const GDBusSignalTable device_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { GDBUS_SIGNAL("DisconnectRequested", NULL) },
{ }
};
DBUS_TYPE_UINT16, &value);
}
+static void device_set_vendor_src(struct btd_device *device, uint16_t value)
+{
+ DBusConnection *conn = get_dbus_connection();
+
+ if (device->vendor_src == value)
+ return;
+
+ device->vendor_src = value;
+
+ emit_property_changed(conn, device->path, DEVICE_INTERFACE,
+ "VendorSource", DBUS_TYPE_UINT16, &value);
+}
+
static void device_set_product(struct btd_device *device, uint16_t value)
{
DBusConnection *conn = get_dbus_connection();
struct btd_device *device_create(DBusConnection *conn,
struct btd_adapter *adapter,
- const gchar *address, addr_type_t type)
+ const gchar *address, uint8_t bdaddr_type)
{
gchar *address_up;
struct btd_device *device;
str2ba(address, &device->bdaddr);
device->adapter = adapter;
- device->type = type;
+ device->bdaddr_type = bdaddr_type;
adapter_get_address(adapter, &src);
ba2str(&src, srcaddr);
read_device_name(srcaddr, address, device->name);
device_set_bonded(device, TRUE);
}
+ if (device_is_le(device) && has_longtermkeys(&src, &device->bdaddr,
+ device->bdaddr_type)) {
+ device_set_paired(device, TRUE);
+ device_set_bonded(device, TRUE);
+ }
+
if (read_device_id(srcaddr, address, NULL, &vendor, &product, &version)
== 0) {
device_set_vendor(device, vendor);
return device->vendor;
}
+uint16_t btd_device_get_vendor_src(struct btd_device *device)
+{
+ return device->vendor_src;
+}
+
uint16_t btd_device_get_product(struct btd_device *device)
{
return device->product;
static void device_remove_stored(struct btd_device *device)
{
bdaddr_t src;
- char addr[18];
+ char key[20];
DBusConnection *conn = get_dbus_connection();
adapter_get_address(device->adapter, &src);
- ba2str(&device->bdaddr, addr);
+ ba2str(&device->bdaddr, key);
+
+ /* key: address only */
+ delete_entry(&src, "profiles", key);
+ delete_entry(&src, "trusts", key);
if (device_is_bonded(device)) {
- delete_entry(&src, "linkkeys", addr);
- delete_entry(&src, "aliases", addr);
+ delete_entry(&src, "linkkeys", key);
+ delete_entry(&src, "aliases", key);
+
+ /* key: address#type */
+ sprintf(&key[17], "#%hhu", device->bdaddr_type);
+
+ delete_entry(&src, "longtermkeys", key);
+
device_set_bonded(device, FALSE);
device->paired = FALSE;
- btd_adapter_remove_bonding(device->adapter, &device->bdaddr);
+ btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
+ device->bdaddr_type);
}
- delete_entry(&src, "profiles", addr);
- delete_entry(&src, "trusts", addr);
+
delete_all_records(&src, &device->bdaddr);
- delete_device_service(&src, &device->bdaddr);
+ delete_device_service(&src, &device->bdaddr, device->bdaddr_type);
if (device->blocked)
device_unblock(conn, device, TRUE, FALSE);
for (l = profiles; l; l = l->next) {
const char *profile_uuid = l->data;
const sdp_record_t *rec;
- struct att_primary *prim;
+ struct gatt_primary *prim;
uint16_t start = 0, end = 0, psm = 0;
uuid_t prim_uuid;
if (!gatt_parse_record(rec, &prim_uuid, &psm, &start, &end))
continue;
- prim = g_new0(struct att_primary, 1);
- prim->start = start;
- prim->end = end;
+ prim = g_new0(struct gatt_primary, 1);
+ prim->range.start = start;
+ prim->range.end = end;
sdp_uuid2strn(&prim_uuid, prim->uuid, sizeof(prim->uuid));
prim_list = g_slist_append(prim_list, prim);
}
device->browse = NULL;
- browse_request_free(req, FALSE);
+ browse_request_free(req);
}
static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
services = g_string_new(NULL);
for (l = primary_list; l; l = l->next) {
- struct att_primary *primary = l->data;
+ struct gatt_primary *primary = l->data;
char service[64];
memset(service, 0, sizeof(service));
snprintf(service, sizeof(service), "%04X#%04X#%s ",
- primary->start, primary->end, primary->uuid);
+ primary->range.start, primary->range.end, primary->uuid);
services = g_string_append(services, service);
}
adapter_get_address(adapter, &sba);
device_get_address(device, &dba, NULL);
- write_device_services(&sba, &dba, str);
+ write_device_services(&sba, &dba, device->bdaddr_type, str);
g_free(str);
}
static gboolean att_connect(gpointer user_data);
-static void attrib_disconnected(gpointer user_data)
+static gboolean attrib_disconnected_cb(GIOChannel *io, GIOCondition cond,
+ 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);
+ if (device->browse)
+ goto done;
+
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;
+ if (device->auto_connect == FALSE || err != ETIMEDOUT)
+ goto done;
device->auto_id = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT_IDLE,
AUTO_CONNECTION_INTERVAL,
att_connect, device,
att_connect_dispatched);
+
+done:
+ att_cleanup(device);
+
+ return FALSE;
+}
+
+static void appearance_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct btd_device *device = user_data;
+ struct btd_adapter *adapter = device->adapter;
+ struct att_data_list *list = NULL;
+ uint16_t app;
+ bdaddr_t src;
+ uint8_t *atval;
+
+ if (status != 0) {
+ DBG("Read characteristics by UUID failed: %s\n",
+ att_ecode2str(status));
+ goto done;
+ }
+
+ list = dec_read_by_type_resp(pdu, plen);
+ if (list == NULL)
+ goto done;
+
+ if (list->len != 4) {
+ DBG("Appearance value: invalid data");
+ goto done;
+ }
+
+ /* A device shall have only one instance of the
+ Appearance characteristic. */
+ atval = list->data[0] + 2; /* skip handle value */
+ app = att_get_u16(atval);
+
+ adapter_get_address(adapter, &src);
+ write_remote_appearance(&src, &device->bdaddr, device->bdaddr_type,
+ app);
+
+done:
+ att_data_list_free(list);
+ if (device->attios == NULL && device->attios_offline == NULL)
+ att_cleanup(device);
}
static void primary_cb(GSList *services, guint8 status, gpointer user_data)
{
struct browse_req *req = user_data;
struct btd_device *device = req->device;
+ struct gatt_primary *gap_prim = NULL;
GSList *l, *uuids = NULL;
- gboolean shutdown;
if (status) {
- DBusMessage *reply;
- reply = btd_error_failed(req->msg, att_ecode2str(status));
- g_dbus_send_message(req->conn, reply);
- shutdown = TRUE;
+ if (req->msg) {
+ DBusMessage *reply;
+ reply = btd_error_failed(req->msg,
+ att_ecode2str(status));
+ g_dbus_send_message(req->conn, reply);
+ }
goto done;
}
device_set_temporary(device, FALSE);
for (l = services; l; l = l->next) {
- struct att_primary *prim = l->data;
+ struct gatt_primary *prim = l->data;
+
+ if (strcmp(prim->uuid, GAP_SVC_UUID) == 0)
+ gap_prim = prim;
+
uuids = g_slist_append(uuids, prim->uuid);
}
- /*
- * 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);
+ if (gap_prim) {
+ /* Read appearance characteristic */
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, APPEARANCE_CHR_UUID);
+
+ gatt_read_char_by_uuid(device->attrib, gap_prim->range.start,
+ gap_prim->range.end, &uuid, appearance_cb, device);
+ } else if (device->attios == NULL && device->attios_offline == NULL)
+ att_cleanup(device);
g_slist_free(uuids);
services_changed(device);
- create_device_reply(device, req);
+ if (req->msg)
+ create_device_reply(device, req);
store_services(device);
- shutdown = FALSE;
done:
device->browse = NULL;
- browse_request_free(req, shutdown);
+ browse_request_free(req);
}
static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
{
- struct btd_device *device = user_data;
- struct browse_req *req = device->browse;
+ struct att_callbacks *attcb = user_data;
+ struct btd_device *device = attcb->user_data;
GAttrib *attrib;
- if (gerr) {
- DBusMessage *reply;
+ g_io_channel_unref(device->att_io);
+ device->att_io = NULL;
+ if (gerr) {
DBG("%s", gerr->message);
- if (req) {
- reply = btd_error_failed(req->msg, gerr->message);
- g_dbus_send_message(req->conn, reply);
+ if (attcb->error)
+ attcb->error(gerr, user_data);
- 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;
+ goto done;
}
attrib = g_attrib_new(io);
- device->attachid = attrib_channel_attach(attrib, TRUE);
+ device->attachid = attrib_channel_attach(attrib);
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);
- }
+ device->attrib = attrib;
+ device->cleanup_id = g_io_add_watch(io, G_IO_HUP,
+ attrib_disconnected_cb, device);
+
+ if (attcb->success)
+ attcb->success(user_data);
+done:
+ g_free(attcb);
+}
+
+static void att_error_cb(const GError *gerr, gpointer user_data)
+{
+ struct att_callbacks *attcb = user_data;
+ struct btd_device *device = attcb->user_data;
+
+ if (device->auto_connect == FALSE)
+ return;
+
+ device->auto_id = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT_IDLE,
+ AUTO_CONNECTION_INTERVAL,
+ att_connect, device,
+ att_connect_dispatched);
+
+ DBG("Enabling automatic connections");
+}
+
+static void att_success_cb(gpointer user_data)
+{
+ struct att_callbacks *attcb = user_data;
+ struct btd_device *device = attcb->user_data;
+
+ if (device->attios == NULL)
+ return;
+
+ 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;
+ struct att_callbacks *attcb;
GIOChannel *io;
GError *gerr = NULL;
char addr[18];
DBG("Connection attempt to: %s", addr);
+ attcb = g_new0(struct att_callbacks, 1);
+ attcb->error = att_error_cb;
+ attcb->success = att_success_cb;
+ attcb->user_data = device;
+
if (device_is_bredr(device)) {
io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
- device, NULL, &gerr,
+ attcb, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, &sba,
BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
BT_IO_OPT_PSM, ATT_PSM,
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);
+ attcb, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &sba,
+ BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+ BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
+ 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);
+ g_free(attcb);
return FALSE;
}
- g_io_channel_unref(io);
+ device->att_io = io;
return FALSE;
}
+static void att_browse_error_cb(const GError *gerr, gpointer user_data)
+{
+ struct att_callbacks *attcb = user_data;
+ struct btd_device *device = attcb->user_data;
+ struct browse_req *req = device->browse;
+
+ if (req->msg) {
+ DBusMessage *reply;
+
+ reply = btd_error_failed(req->msg, gerr->message);
+ g_dbus_send_message(req->conn, reply);
+ }
+
+ device->browse = NULL;
+ browse_request_free(req);
+}
+
+static void att_browse_cb(gpointer user_data)
+{
+ struct att_callbacks *attcb = user_data;
+ struct btd_device *device = attcb->user_data;
+
+ gatt_discover_primary(device->attrib, NULL, primary_cb,
+ device->browse);
+}
+
int device_browse_primary(struct btd_device *device, DBusConnection *conn,
DBusMessage *msg, gboolean secure)
{
struct btd_adapter *adapter = device->adapter;
+ struct att_callbacks *attcb;
struct browse_req *req;
BtIOSecLevel sec_level;
bdaddr_t src;
if (device->browse)
return -EBUSY;
+ /* FIXME: GATT service updates (implemented in update_services() for
+ * SDP) are not supported yet. It will be supported once client side
+ * "Services Changed" characteristic handling is implemented. */
+ if (device->primaries) {
+ error("Could not update GATT services");
+ return -ENOSYS;
+ }
+
req = g_new0(struct browse_req, 1);
req->device = btd_device_ref(device);
-
adapter_get_address(adapter, &src);
+ device->browse = req;
+
+ if (device->attrib) {
+ gatt_discover_primary(device->attrib, NULL, primary_cb, req);
+ goto done;
+ }
+
sec_level = secure ? BT_IO_SEC_HIGH : BT_IO_SEC_LOW;
- req->io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
- device, NULL, NULL,
+ attcb = g_new0(struct att_callbacks, 1);
+ attcb->error = att_browse_error_cb;
+ attcb->success = att_browse_cb;
+ attcb->user_data = device;
+
+ device->att_io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
+ attcb, NULL, NULL,
BT_IO_OPT_SOURCE_BDADDR, &src,
BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+ BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
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, FALSE);
+ if (device->att_io == NULL) {
+ device->browse = NULL;
+ browse_request_free(req);
+ g_free(attcb);
return -EIO;
}
+done:
+ if (conn == NULL)
+ conn = get_dbus_connection();
+
req->conn = dbus_connection_ref(conn);
- device->browse = req;
if (msg) {
const char *sender = dbus_message_get_sender(msg);
err = bt_search_service(&src, &device->bdaddr, &uuid, cb, req, NULL);
if (err < 0) {
- browse_request_free(req, FALSE);
+ browse_request_free(req);
return err;
}
}
void device_get_address(struct btd_device *device, bdaddr_t *bdaddr,
- addr_type_t *type)
+ uint8_t *bdaddr_type)
{
bacpy(bdaddr, &device->bdaddr);
- if (type != NULL)
- *type = device->type;
+ if (bdaddr_type != NULL)
+ *bdaddr_type = device->bdaddr_type;
+}
+
+void device_set_addr_type(struct btd_device *device, uint8_t bdaddr_type)
+{
+ if (device == NULL)
+ return;
+
+ device->bdaddr_type = bdaddr_type;
+}
+
+uint8_t device_get_addr_type(struct btd_device *device)
+{
+ return device->bdaddr_type;
}
const gchar *device_get_path(struct btd_device *device)
{
struct btd_device *device = user_data;
- device_browse_sdp(device, NULL, NULL, NULL, TRUE);
+ if (device_is_bredr(device))
+ device_browse_sdp(device, NULL, NULL, NULL, TRUE);
+ else
+ device_browse_primary(device, NULL, NULL, FALSE);
device->discov_timer = 0;
if (bonding->conn)
dbus_connection_unref(bonding->conn);
- if (bonding->io)
- g_io_channel_unref(bonding->io);
-
device = bonding->device;
g_free(bonding);
return;
if (!value)
- btd_adapter_remove_bonding(device->adapter, &device->bdaddr);
+ btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
+ device->bdaddr_type);
device->paired = value;
{
struct bonding_req *bonding;
const char *name = dbus_message_get_sender(msg);
- struct agent *agent;
char addr[18];
ba2str(&device->bdaddr, addr);
if (!agent_path)
goto proceed;
- agent = agent_create(device->adapter, name, agent_path,
+ device->agent = agent_create(device->adapter, name, agent_path,
capability,
device_agent_removed,
device);
- if (!agent) {
- error("Unable to create a new agent");
- return NULL;
- }
-
- device->agent = agent;
DBG("Temporary agent registered for %s at %s:%s",
addr, name, agent_path);
device_request_disconnect(device, NULL);
}
}
-#ifdef __TIZEN_PATCH__
-void set_cancel_from_authentication_req(void *user_data)
-{
- struct authentication_req *auth = (struct authentication_req *)user_data;
-
- if (auth && auth->device && auth->device->bonding)
- {
- auth->device->bonding->cancel_by_user = 1;
- }
-}
-#endif
DBusMessage *device_create_bonding(struct btd_device *device,
DBusConnection *conn,
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;
int err;
- 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);
+ if (device_is_bonded(device))
return btd_error_already_exists(msg);
+
+ if (device_is_le(device)) {
+ struct att_callbacks *attcb;
+ GError *gerr = NULL;
+ bdaddr_t sba;
+
+ adapter_get_address(adapter, &sba);
+
+ attcb = g_new0(struct att_callbacks, 1);
+ attcb->user_data = device;
+
+ device->att_io = bt_io_connect(BT_IO_L2CAP, att_connect_cb,
+ attcb, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &sba,
+ BT_IO_OPT_DEST_BDADDR, &device->bdaddr,
+ BT_IO_OPT_DEST_TYPE, device->bdaddr_type,
+ BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+
+ if (device->att_io == NULL) {
+ DBusMessage *reply = btd_error_failed(msg,
+ gerr->message);
+
+ error("Bonding bt_io_connect(): %s", gerr->message);
+ g_error_free(gerr);
+ g_free(attcb);
+ return reply;
+ }
}
- err = adapter_create_bonding(adapter, &device->bdaddr, capability);
+ err = adapter_create_bonding(adapter, &device->bdaddr,
+ device->bdaddr_type, capability);
if (err < 0)
return btd_error_failed(msg, strerror(-err));
bonding = bonding_request_new(conn, msg, device, agent_path,
capability);
- if (!bonding) {
- adapter_cancel_bonding(adapter, &device->bdaddr);
- return NULL;
- }
bonding->listener_id = g_dbus_add_disconnect_watch(conn,
dbus_message_get_sender(msg),
{
struct authentication_req *auth = device->authr;
- if (auth && auth->type == AUTH_TYPE_NOTIFY && auth->agent)
+ if (auth && (auth->type == AUTH_TYPE_NOTIFY_PASSKEY
+ || auth->type == AUTH_TYPE_NOTIFY_PINCODE) && auth->agent)
agent_cancel(auth->agent);
}
static void device_auth_req_free(struct btd_device *device)
{
+ if (device->authr)
+ g_free(device->authr->pincode);
g_free(device->authr);
device->authr = NULL;
}
DBG("bonding %p status 0x%02x", bonding, status);
- if (auth && auth->type == AUTH_TYPE_NOTIFY && auth->agent)
+ if (auth && (auth->type == AUTH_TYPE_NOTIFY_PASSKEY
+ || auth->type == AUTH_TYPE_NOTIFY_PINCODE) && auth->agent)
agent_cancel(auth->agent);
if (status) {
device->discov_timer = 0;
}
- device_browse_sdp(device, bonding->conn, bonding->msg,
- NULL, FALSE);
+ if (device_is_bredr(device))
+ device_browse_sdp(device, bonding->conn, bonding->msg,
+ NULL, FALSE);
+ else
+ device_browse_primary(device, bonding->conn,
+ bonding->msg, FALSE);
bonding_request_free(bonding);
} else {
device_cancel_authentication(device, FALSE);
#ifdef __TIZEN_PATCH__
- if (bonding->cancel_by_user)
- {
+ if (bonding->cancel_by_user) {
info("Bonding Cancel by user");
reply = new_authentication_return(bonding->msg, 0x2a);
- }
- else
+ } else
#endif
reply = new_authentication_return(bonding->msg, status);
g_dbus_send_message(bonding->conn, reply);
struct btd_device *device = auth->device;
struct btd_adapter *adapter = device_get_adapter(device);
struct agent *adapter_agent = adapter_get_agent(adapter);
+#ifdef __TIZEN_PATCH__
+ struct bonding_req *bonding = device->bonding;
+
+ if (err == NULL)
+ goto done;
+ if (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err->name)) {
+#else
if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
-
+#endif
if (auth->agent == adapter_agent || adapter_agent == NULL)
goto done;
return;
}
+#ifdef __TIZEN_PATCH__
+ if (bonding)
+ bonding->cancel_by_user = TRUE;
+#endif
+
done:
/* No need to reply anything if the authentication already failed */
if (auth->cb == NULL)
struct btd_device *device = auth->device;
struct btd_adapter *adapter = device_get_adapter(device);
struct agent *adapter_agent = adapter_get_agent(adapter);
+#ifdef __TIZEN_PATCH__
+ struct bonding_req *bonding = device->bonding;
+ if (err == NULL)
+ goto done;
+
+ if (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err->name)) {
+#else
if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
-
+#endif
if (auth->agent == adapter_agent || adapter_agent == NULL)
goto done;
return;
}
+#ifdef __TIZEN_PATCH__
+ if (bonding)
+ bonding->cancel_by_user = TRUE;
+#endif
+
done:
/* No need to reply anything if the authentication already failed */
if (auth->cb == NULL)
struct btd_device *device = auth->device;
struct btd_adapter *adapter = device_get_adapter(device);
struct agent *adapter_agent = adapter_get_agent(adapter);
+#ifdef __TIZEN_PATCH__
+ struct bonding_req *bonding = device->bonding;
+ if (err == NULL)
+ goto done;
+
+ if (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err->name)) {
+#else
if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
+#endif
if (auth->agent == adapter_agent || adapter_agent == NULL)
goto done;
return;
}
+#ifdef __TIZEN_PATCH__
+ if (bonding)
+ bonding->cancel_by_user = TRUE;
+#endif
+
done:
/* No need to reply anything if the authentication already failed */
if (auth->cb == NULL)
device->authr->agent = NULL;
}
+static void display_pincode_cb(struct agent *agent, DBusError *err, void *data)
+{
+ 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);
+#ifdef __TIZEN_PATCH__
+ struct bonding_req *bonding = device->bonding;
+
+ if (err == NULL)
+ goto done;
+
+ if (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err->name)) {
+#else
+ if (err && (g_str_equal(DBUS_ERROR_UNKNOWN_METHOD, err->name) ||
+ g_str_equal(DBUS_ERROR_NO_REPLY, err->name))) {
+#endif
+
+ /* Request a pincode if we fail to display one */
+ if (auth->agent == adapter_agent || adapter_agent == NULL) {
+ if (agent_request_pincode(agent, device, pincode_cb,
+ auth->secure, auth, NULL) < 0)
+ goto done;
+ return;
+ }
+
+ if (agent_display_pincode(adapter_agent, device, auth->pincode,
+ display_pincode_cb, auth, NULL) < 0)
+ goto done;
+
+ auth->agent = adapter_agent;
+ return;
+ }
+#ifdef __TIZEN_PATCH__
+ if (bonding)
+ bonding->cancel_by_user = TRUE;
+#endif
+
+done:
+ /* No need to reply anything if the authentication already failed */
+ if (auth->cb == NULL)
+ return;
+
+ ((agent_pincode_cb) auth->cb)(agent, err, auth->pincode, device);
+
+ g_free(device->authr->pincode);
+ device->authr->pincode = NULL;
+ device->authr->cb = NULL;
+ device->authr->agent = NULL;
+}
+
int device_request_authentication(struct btd_device *device, auth_type_t type,
- uint32_t passkey, gboolean secure, void *cb)
+ void *data, 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;
auth, NULL);
break;
case AUTH_TYPE_CONFIRM:
- err = agent_request_confirmation(agent, device, passkey,
+ auth->passkey = *((uint32_t *) data);
+ err = agent_request_confirmation(agent, device, auth->passkey,
confirm_cb, auth, NULL);
break;
- case AUTH_TYPE_NOTIFY:
- err = agent_display_passkey(agent, device, passkey);
+ case AUTH_TYPE_NOTIFY_PASSKEY:
+ auth->passkey = *((uint32_t *) data);
+ err = agent_display_passkey(agent, device, auth->passkey);
+ break;
+ case AUTH_TYPE_NOTIFY_PINCODE:
+ auth->pincode = g_strdup((const char *) data);
+ err = agent_display_pincode(agent, device, auth->pincode,
+ display_pincode_cb, auth, NULL);
break;
default:
err = -EINVAL;
case AUTH_TYPE_PASSKEY:
((agent_passkey_cb) auth->cb)(agent, &err, 0, device);
break;
- case AUTH_TYPE_NOTIFY:
+ case AUTH_TYPE_NOTIFY_PASSKEY:
/* User Notify doesn't require any reply */
break;
+ case AUTH_TYPE_NOTIFY_PINCODE:
+ ((agent_pincode_cb) auth->cb)(agent, &err, NULL, device);
+ break;
}
dbus_error_free(&err);
device->attios_offline = g_slist_append(device->attios_offline,
attio);
g_idle_add(notify_attios, device);
- } else {
+ return attio->id;
+ }
+
+ device->attios = g_slist_append(device->attios, attio);
+
+ if (device->auto_id == 0)
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;
}
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->auto_id) {
+ g_source_remove(device->auto_id);
+ device->auto_id = 0;
}
- if (device->attrib) {
- g_attrib_unref(device->attrib);
- device->attrib = NULL;
- }
+ att_cleanup(device);
return TRUE;
}
+
+void device_set_pnpid(struct btd_device *device, uint8_t vendor_id_src,
+ uint16_t vendor_id, uint16_t product_id,
+ uint16_t product_ver)
+{
+ device_set_vendor(device, vendor_id);
+ device_set_vendor_src(device, vendor_id_src);
+ device_set_product(device, product_id);
+ device_set_version(device, product_ver);
+}
AUTH_TYPE_PINCODE,
AUTH_TYPE_PASSKEY,
AUTH_TYPE_CONFIRM,
- AUTH_TYPE_NOTIFY,
+ AUTH_TYPE_NOTIFY_PASSKEY,
+ AUTH_TYPE_NOTIFY_PINCODE,
} auth_type_t;
struct btd_device *device_create(DBusConnection *conn,
struct btd_adapter *adapter,
- const char *address, addr_type_t type);
+ const char *address, uint8_t bdaddr_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);
uint16_t btd_device_get_vendor(struct btd_device *device);
+uint16_t btd_device_get_vendor_src(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);
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,
- addr_type_t *type);
+ uint8_t *bdaddr_type);
+void device_set_addr_type(struct btd_device *device, uint8_t bdaddr_type);
+uint8_t device_get_addr_type(struct btd_device *device);
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);
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);
-#ifdef __TIZEN_PATCH__
-void set_cancel_from_authentication_req(void *user_data);
-#endif
DBusMessage *device_create_bonding(struct btd_device *device,
DBusConnection *conn, DBusMessage *msg,
const char *agent_path, uint8_t capability);
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, gboolean secure, void *cb);
+ void *data, 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);
gboolean update_only);
int device_unblock(DBusConnection *conn, struct btd_device *device,
gboolean silent, gboolean update_only);
+void device_set_pnpid(struct btd_device *device, uint8_t vendor_id_src,
+ uint16_t vendor_id, uint16_t product_id,
+ uint16_t product_ver);
#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);
while (len < eir_len - 1) {
uint8_t field_len = eir_data[0];
- uint8_t name_len;
+ uint8_t data_len, *data = &eir_data[2];
/* Check for the end of EIR */
if (field_len == 0)
len += field_len + 1;
- /* Bail out if got incorrect length */
- if (len > eir_len) {
- eir_data_free(eir);
- return -EINVAL;
- }
+ /* Do not continue EIR Data parsing if got incorrect length */
+ if (len > eir_len)
+ break;
+
+ data_len = field_len - 1;
switch (eir_data[1]) {
case EIR_UUID16_SOME:
case EIR_UUID16_ALL:
- eir_parse_uuid16(eir, &eir_data[2], field_len);
+ eir_parse_uuid16(eir, data, data_len);
break;
case EIR_UUID32_SOME:
case EIR_UUID32_ALL:
- eir_parse_uuid32(eir, &eir_data[2], field_len);
+ eir_parse_uuid32(eir, data, data_len);
break;
case EIR_UUID128_SOME:
case EIR_UUID128_ALL:
- eir_parse_uuid128(eir, &eir_data[2], field_len);
+ eir_parse_uuid128(eir, data, data_len);
break;
case EIR_FLAGS:
- eir->flags = eir_data[2];
+ if (data_len > 0)
+ eir->flags = *data;
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 (data_len > 0 && data[data_len - 1] == '\0')
+ data_len--;
- while (name_len > 0 && eir_data[name_len - 1] == '\0')
- name_len--;
-
- if (!g_utf8_validate((char *) &eir_data[2],
- name_len, NULL))
+ if (!g_utf8_validate((char *) data, data_len, NULL))
break;
g_free(eir->name);
- eir->name = g_strndup((char *) &eir_data[2],
- field_len - 1);
+ eir->name = g_strndup((char *) data, data_len);
eir->name_complete = eir_data[1] == EIR_NAME_COMPLETE;
break;
+
+ case EIR_CLASS_OF_DEV:
+ if (data_len < 3)
+ break;
+ memcpy(eir->dev_class, data, 3);
+ break;
+
+ case EIR_GAP_APPEARANCE:
+ if (data_len < 2)
+ break;
+ eir->appearance = bt_get_le16(data);
+ break;
}
eir_data += field_len + 1;
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)
+ uint16_t did_source, GSList *uuids, uint8_t *data)
{
GSList *l;
uint8_t *ptr = data;
}
if (did_vendor != 0x0000) {
- uint16_t source = 0x0002;
*ptr++ = 9;
*ptr++ = EIR_DEVICE_ID;
- *ptr++ = (source & 0x00ff);
- *ptr++ = (source & 0xff00) >> 8;
+ *ptr++ = (did_source & 0x00ff);
+ *ptr++ = (did_source & 0xff00) >> 8;
*ptr++ = (did_vendor & 0x00ff);
*ptr++ = (did_vendor & 0xff00) >> 8;
*ptr++ = (did_product & 0x00ff);
eir_generate_uuid128(uuids, ptr, &eir_len);
}
-gboolean eir_has_complete_name(uint8_t *data, size_t len)
+gboolean eir_has_data_type(uint8_t *data, size_t len, uint8_t type)
{
uint8_t field_len;
- size_t parsed;
+ size_t parsed = 0;
- for (parsed = 0; parsed < len - 1; parsed += field_len) {
+ while (parsed < len - 1) {
field_len = data[0];
if (field_len == 0)
if (parsed > len)
break;
- if (data[1] == EIR_NAME_COMPLETE)
+ if (data[1] == type)
return TRUE;
data += field_len + 1;
return FALSE;
}
+
+size_t eir_append_data(uint8_t *eir, size_t eir_len, uint8_t type,
+ uint8_t *data, size_t data_len)
+{
+ eir[eir_len++] = sizeof(type) + data_len;
+ eir[eir_len++] = type;
+ memcpy(&eir[eir_len], data, data_len);
+ eir_len += data_len;
+
+ return eir_len;
+}
+
+size_t eir_length(uint8_t *eir, size_t maxlen)
+{
+ uint8_t field_len;
+ size_t parsed = 0, length = 0;
+
+ while (parsed < maxlen - 1) {
+ field_len = eir[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed += field_len + 1;
+
+ if (parsed > maxlen)
+ break;
+
+ length = parsed;
+ eir += field_len + 1;
+ }
+
+ return length;
+}
*
*/
+#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_CLASS_OF_DEV 0x0D /* Class of Device */
+#define EIR_DEVICE_ID 0x10 /* device ID */
+#define EIR_GAP_APPEARANCE 0x19 /* GAP appearance */
+
struct uuid_info {
uuid_t uuid;
uint8_t svc_hint;
GSList *services;
int flags;
char *name;
+ uint8_t dev_class[3];
+ uint16_t appearance;
gboolean name_complete;
};
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);
+ uint16_t did_source, GSList *uuids, uint8_t *data);
+
+gboolean eir_has_data_type(uint8_t *data, size_t len, uint8_t type);
-gboolean eir_has_complete_name(uint8_t *data, size_t len);
+size_t eir_append_data(uint8_t *eir, size_t eir_len, uint8_t type,
+ uint8_t *data, size_t data_len);
+size_t eir_length(uint8_t *eir, size_t maxlen);
struct btd_device *device;
char pin[17];
ssize_t pinlen;
+ gboolean display = FALSE;
if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
return -ENODEV;
memset(pin, 0, sizeof(pin));
- pinlen = btd_adapter_get_pin(adapter, device, pin);
+ pinlen = btd_adapter_get_pin(adapter, device, pin, &display);
if (pinlen > 0 && (!secure || pinlen == 16)) {
+ if (display && device_is_bonding(device, NULL))
+ return device_request_authentication(device,
+ AUTH_TYPE_NOTIFY_PINCODE, pin,
+ secure, pincode_cb);
+
btd_adapter_pincode_reply(adapter, dba, pin, pinlen);
return 0;
}
- return device_request_authentication(device, AUTH_TYPE_PINCODE, 0,
+ return device_request_authentication(device, AUTH_TYPE_PINCODE, NULL,
secure, pincode_cb);
}
struct btd_device *device, gboolean success)
{
bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
- device_get_address(device, &bdaddr, NULL);
+ device_get_address(device, &bdaddr, &bdaddr_type);
- return btd_adapter_confirm_reply(adapter, &bdaddr, success);
+ return btd_adapter_confirm_reply(adapter, &bdaddr, bdaddr_type,
+ success);
}
static void confirm_cb(struct agent *agent, DBusError *err, void *user_data)
struct btd_device *device = user_data;
struct btd_adapter *adapter = device_get_adapter(device);
bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
- device_get_address(device, &bdaddr, NULL);
+ device_get_address(device, &bdaddr, &bdaddr_type);
if (err)
passkey = INVALID_PASSKEY;
- btd_adapter_passkey_reply(adapter, &bdaddr, passkey);
+ btd_adapter_passkey_reply(adapter, &bdaddr, bdaddr_type, passkey);
}
int btd_event_user_confirm(bdaddr_t *sba, bdaddr_t *dba, uint32_t passkey)
return -ENODEV;
return device_request_authentication(device, AUTH_TYPE_CONFIRM,
- passkey, FALSE, confirm_cb);
+ &passkey, FALSE, confirm_cb);
}
int btd_event_user_passkey(bdaddr_t *sba, bdaddr_t *dba)
if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
return -ENODEV;
- return device_request_authentication(device, AUTH_TYPE_PASSKEY, 0,
+ return device_request_authentication(device, AUTH_TYPE_PASSKEY, NULL,
FALSE, passkey_cb);
}
if (!get_adapter_and_device(sba, dba, &adapter, &device, TRUE))
return -ENODEV;
- return device_request_authentication(device, AUTH_TYPE_NOTIFY, passkey,
- FALSE, NULL);
+ return device_request_authentication(device, AUTH_TYPE_NOTIFY_PASSKEY,
+ &passkey, FALSE, NULL);
}
void btd_event_simple_pairing_complete(bdaddr_t *local, bdaddr_t *peer,
write_lastused_info(sba, dba, tm);
}
-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)
+void btd_event_device_found(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ int8_t rssi, uint8_t confirm_name,
+ uint8_t *data, uint8_t data_len)
{
struct btd_adapter *adapter;
}
update_lastseen(local, peer);
- write_remote_class(local, peer, class);
if (data)
- write_remote_eir(local, peer, data);
+ write_remote_eir(local, peer, data, data_len);
- adapter_update_found_devices(adapter, peer, type, class, rssi,
+ adapter_update_found_devices(adapter, peer, bdaddr_type, rssi,
confirm_name, data, data_len);
}
void btd_event_remote_name(bdaddr_t *local, bdaddr_t *peer, char *name)
{
struct btd_adapter *adapter;
- char srcaddr[18];
struct btd_device *device;
struct remote_dev_info *dev_info;
if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
return;
- ba2str(local, srcaddr);
-
dev_info = adapter_search_found_devices(adapter, peer);
if (dev_info) {
g_free(dev_info->name);
device_set_name(device, name);
}
+static char *buf2str(uint8_t *data, int datalen)
+{
+ char *buf;
+ int i;
+
+ buf = g_try_new0(char, (datalen * 2) + 1);
+ if (buf == NULL)
+ return NULL;
+
+ for (i = 0; i < datalen; i++)
+ sprintf(buf + (i * 2), "%2.2x", data[i]);
+
+ return buf;
+}
+
+static int store_longtermkey(bdaddr_t *local, bdaddr_t *peer,
+ uint8_t bdaddr_type, unsigned char *key,
+ uint8_t master, uint8_t authenticated,
+ uint8_t enc_size, uint16_t ediv, uint8_t rand[8])
+{
+ GString *newkey;
+ char *val, *str;
+ int err;
+
+ val = buf2str(key, 16);
+ if (val == NULL)
+ return -ENOMEM;
+
+ newkey = g_string_new(val);
+ g_free(val);
+
+ g_string_append_printf(newkey, " %d %d %d %d ", authenticated, master,
+ enc_size, ediv);
+
+ str = buf2str(rand, 8);
+ if (str == NULL) {
+ g_string_free(newkey, TRUE);
+ return -ENOMEM;
+ }
+
+ newkey = g_string_append(newkey, str);
+ g_free(str);
+
+ err = write_longtermkeys(local, peer, bdaddr_type, newkey->str);
+
+ g_string_free(newkey, TRUE);
+
+ return err;
+}
+
int btd_event_link_key_notify(bdaddr_t *local, bdaddr_t *peer,
uint8_t *key, uint8_t key_type,
uint8_t pin_length)
return ret;
}
-void btd_event_conn_complete(bdaddr_t *local, bdaddr_t *peer)
+int btd_event_ltk_notify(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ uint8_t *key, uint8_t master,
+ uint8_t authenticated, uint8_t enc_size,
+ uint16_t ediv, uint8_t rand[8])
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ int ret;
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, TRUE))
+ return -ENODEV;
+
+ ret = store_longtermkey(local, peer, bdaddr_type, key, master,
+ authenticated, enc_size, ediv, rand);
+ if (ret == 0) {
+ device_set_bonded(device, TRUE);
+
+ if (device_is_temporary(device))
+ device_set_temporary(device, FALSE);
+ }
+
+ return ret;
+}
+
+void btd_event_conn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ char *name, uint8_t *dev_class)
{
struct btd_adapter *adapter;
struct btd_device *device;
update_lastused(local, peer);
+ if (dev_class != NULL) {
+ uint32_t class = dev_class[0] | (dev_class[1] << 8) |
+ (dev_class[2] << 16);
+
+ if (class != 0)
+ write_remote_class(local, peer, class);
+ }
+
+ device_set_addr_type(device, bdaddr_type);
+
adapter_add_connection(adapter, device);
+
+ if (name != NULL)
+ btd_event_remote_name(local, peer, name);
}
void btd_event_conn_failed(bdaddr_t *local, bdaddr_t *peer, uint8_t status)
if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
return;
- device_block(conn, device, TRUE);
+ if (device)
+ device_block(conn, device, TRUE);
}
void btd_event_device_unblocked(bdaddr_t *local, bdaddr_t *peer)
if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
return;
- device_unblock(conn, device, FALSE, TRUE);
+ if (device)
+ device_unblock(conn, device, FALSE, TRUE);
+}
+
+void btd_event_device_unpaired(bdaddr_t *local, bdaddr_t *peer)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ DBusConnection *conn = get_dbus_connection();
+
+ if (!get_adapter_and_device(local, peer, &adapter, &device, FALSE))
+ return;
+
+ device_set_temporary(device, TRUE);
+
+ if (device_is_connected(device))
+ device_request_disconnect(device, NULL);
+ else
+ adapter_remove_device(conn, adapter, device, TRUE);
}
/* Section reserved to device HCI callbacks */
*/
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_device_found(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ 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, char *name);
-void btd_event_conn_complete(bdaddr_t *local, bdaddr_t *peer);
+void btd_event_conn_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ char *name, uint8_t *dev_class);
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_simple_pairing_complete(bdaddr_t *local, bdaddr_t *peer, uint8_t status);
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);
+void btd_event_device_unpaired(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);
+int btd_event_ltk_notify(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ uint8_t *key, uint8_t master,
+ uint8_t authenticated, uint8_t enc_size,
+ uint16_t ediv, uint8_t rand[8]);
#include <glib.h>
#include "btio.h"
-#include "glib-compat.h"
#include "glib-helper.h"
char *bt_uuid2string(uuid_t *uuid)
gboolean reverse_sdp;
gboolean name_resolv;
gboolean debug_keys;
- gboolean attrib_server;
+ gboolean gatt_enabled;
uint8_t mode;
- uint8_t discov_interval;
- char deviceid[15]; /* FIXME: */
+
+ uint16_t did_source;
+ uint16_t did_vendor;
+ uint16_t did_product;
+ uint16_t did_version;
};
enum {
- HCID_SET_NAME,
- HCID_SET_CLASS,
HCID_SET_PAGETO,
- HCID_SET_DISCOVTO,
};
extern struct main_opts main_opts;
va_end(ap);
}
+void warn(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vsyslog(LOG_WARNING, format, ap);
+
+ va_end(ap);
+}
+
void error(const char *format, ...)
{
va_list ap;
*/
void info(const char *format, ...) __attribute__((format(printf, 1, 2)));
+void warn(const char *format, ...) __attribute__((format(printf, 1, 2)));
void error(const char *format, ...) __attribute__((format(printf, 1, 2)));
void btd_debug(const char *format, ...) __attribute__((format(printf, 1, 2)));
if (__btd_debug_desc.flags & BTD_DEBUG_FLAG_PRINT) \
btd_debug("%s:%s() " fmt, __FILE__, __FUNCTION__ , ## arg); \
} while (0)
-
#include "agent.h"
#include "manager.h"
-#ifdef HAVE_CAPNG
-#include <cap-ng.h>
-#endif
-
#define BLUEZ_NAME "org.bluez"
#define LAST_ADAPTER_EXIT_TIMEOUT 30
return keyfile;
}
+static void parse_did(const char *did)
+{
+ int result;
+ uint16_t vendor, product, version , source;
+
+ /* version and source are optional */
+ version = 0x0000;
+ source = 0x0002;
+
+ result = sscanf(did, "bluetooth:%4hx:%4hx:%4hx", &vendor, &product, &version);
+ if (result != EOF && result >= 2) {
+ source = 0x0001;
+ goto done;
+ }
+
+ result = sscanf(did, "usb:%4hx:%4hx:%4hx", &vendor, &product, &version);
+ if (result != EOF && result >= 2)
+ goto done;
+
+ result = sscanf(did, "%4hx:%4hx:%4hx", &vendor, &product, &version);
+ if (result == EOF || result < 2)
+ return;
+
+done:
+ main_opts.did_source = source;
+ main_opts.did_vendor = vendor;
+ main_opts.did_product = product;
+ main_opts.did_version = version;
+}
+
static void parse_config(GKeyFile *config)
{
GError *err = NULL;
} else {
DBG("discovto=%d", val);
main_opts.discovto = val;
- main_opts.flags |= 1 << HCID_SET_DISCOVTO;
}
val = g_key_file_get_integer(config, "General",
DBG("name=%s", str);
g_free(main_opts.name);
main_opts.name = g_strdup(str);
- main_opts.flags |= 1 << HCID_SET_NAME;
g_free(str);
}
} else {
DBG("class=%s", str);
main_opts.class = strtol(str, NULL, 16);
- main_opts.flags |= 1 << HCID_SET_CLASS;
g_free(str);
}
- val = g_key_file_get_integer(config, "General",
- "DiscoverSchedulerInterval", &err);
- if (err) {
- DBG("%s", err->message);
- g_clear_error(&err);
- } else {
- DBG("discov_interval=%d", val);
- main_opts.discov_interval = val;
- }
-
boolean = g_key_file_get_boolean(config, "General",
"InitiallyPowered", &err);
if (err) {
g_clear_error(&err);
} else {
DBG("deviceid=%s", str);
- strncpy(main_opts.deviceid, str,
- sizeof(main_opts.deviceid) - 1);
+ parse_did(str);
g_free(str);
}
main_opts.debug_keys = boolean;
boolean = g_key_file_get_boolean(config, "General",
- "AttributeServer", &err);
+ "EnableGatt", &err);
if (err)
g_clear_error(&err);
else
- main_opts.attrib_server = boolean;
+ main_opts.gatt_enabled = boolean;
main_opts.link_mode = HCI_LM_ACCEPT;
init_defaults();
-#ifdef HAVE_CAPNG
- /* Drop capabilities */
- capng_clear(CAPNG_SELECT_BOTH);
- capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
- CAP_NET_BIND_SERVICE, CAP_NET_ADMIN,
- CAP_NET_RAW, CAP_IPC_LOCK, -1);
- capng_apply(CAPNG_SELECT_BOTH);
-#endif
-
context = g_option_context_new(NULL);
g_option_context_add_main_entries(context, options, NULL);
}
}
- start_sdp_server(mtu, main_opts.deviceid, SDP_SERVER_COMPAT);
+ start_sdp_server(mtu, SDP_SERVER_COMPAT);
/* 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
# which is 16384 (10 seconds).
PageTimeout = 8192
-# Discover scheduler interval used in Adapter.DiscoverDevices
-# 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
InitiallyPowered = true
# Remember the previously stored Powered state when initializing adapters
-RememberPowered = true
+#ifdef __TIZEN_PATCH__
+RememberPowered = false
+#else
+#RememberPowered = true
+#endif
-# Use vendor, product and version information for DID profile support.
-# The values are separated by ":" and VID, PID and version.
-#DeviceID = 1234:5678:abcd
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
# Do reverse service discovery for previously unknown devices that connect to
# us. This option is really only needed for qualification since the BITE tester
# that they were created for.
DebugKeys = false
-# Enable the GATT Attribute Server. Default is false, because it is only
-# useful for testing.
-AttributeServer = false
+# Enable the GATT functionality. Default is false
+EnableGatt = false
return reply;
}
-static GDBusMethodTable manager_methods[] = {
- { "GetProperties", "", "a{sv}",get_properties },
- { "DefaultAdapter", "", "o", default_adapter },
- { "FindAdapter", "s", "o", find_adapter },
- { "ListAdapters", "", "ao", list_adapters,
- G_DBUS_METHOD_FLAG_DEPRECATED},
+static const GDBusMethodTable manager_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { GDBUS_METHOD("DefaultAdapter",
+ NULL, GDBUS_ARGS({ "adapter", "o" }),
+ default_adapter) },
+ { GDBUS_METHOD("FindAdapter",
+ GDBUS_ARGS({ "pattern", "s" }),
+ GDBUS_ARGS({ "adapter", "o" }),
+ find_adapter) },
+ { GDBUS_ASYNC_METHOD("ListAdapters",
+ NULL, GDBUS_ARGS({ "adapters", "ao" }),
+ list_adapters) },
{ }
};
-static GDBusSignalTable manager_signals[] = {
- { "PropertyChanged", "sv" },
- { "AdapterAdded", "o" },
- { "AdapterRemoved", "o" },
- { "DefaultAdapterChanged", "o" },
+static const GDBusSignalTable manager_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { GDBUS_SIGNAL("AdapterAdded",
+ GDBUS_ARGS({ "adapter", "o" })) },
+ { GDBUS_SIGNAL("AdapterRemoved",
+ GDBUS_ARGS({ "adapter", "o" })) },
+ { GDBUS_SIGNAL("DefaultAdapterChanged",
+ GDBUS_ARGS({ "adapter", "o" })) },
{ }
};
btd_stop_exit_timer();
}
-struct btd_adapter *btd_manager_register_adapter(int id)
+struct btd_adapter *btd_manager_register_adapter(int id, gboolean up)
{
struct btd_adapter *adapter;
const char *path;
adapters = g_slist_append(adapters, adapter);
- if (!adapter_init(adapter)) {
+ if (!adapter_init(adapter, up)) {
adapters = g_slist_remove(adapters, adapter);
btd_adapter_unref(adapter);
return NULL;
if (default_adapter_id < 0)
manager_set_default_adapter(id);
+ if (main_opts.did_source)
+ btd_adapter_set_did(adapter, main_opts.did_vendor,
+ main_opts.did_product,
+ main_opts.did_version,
+ main_opts.did_source);
+
DBG("Adapter %s registered", path);
return btd_adapter_ref(adapter);
return 0;
}
-
-void btd_manager_set_did(uint16_t vendor, uint16_t product, uint16_t version)
-{
- GSList *l;
-
- for (l = adapters; l != NULL; l = g_slist_next(l)) {
- struct btd_adapter *adapter = l->data;
-
- btd_adapter_set_did(adapter, vendor, product, version);
- }
-}
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);
+struct btd_adapter *btd_manager_register_adapter(int id, gboolean up);
int btd_manager_unregister_adapter(int id);
void manager_add_adapter(const char *path);
-void btd_manager_set_did(uint16_t vendor, uint16_t product, uint16_t version);
--- /dev/null
+[D-BUS Service]
+Name=org.bluez
+Exec=/bin/false
+User=root
+SystemdService=dbus-org.bluez.service
return FALSE;
}
-static sdp_session_t *get_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
+static sdp_session_t *get_cached_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
{
GSList *l;
return session;
}
+ return NULL;
+}
+
+static sdp_session_t *get_sdp_session(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ sdp_session_t *session;
+
+ session = get_cached_sdp_session(src, dst);
+ if (session)
+ return session;
+
return sdp_connect(src, dst, SDP_NON_BLOCKING);
}
return 0;
}
+void bt_clear_cached_session(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ sdp_session_t *session;
+
+ session = get_cached_sdp_session(src, dst);
+ if (session)
+ sdp_close(session);
+}
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);
+void bt_clear_cached_session(const bdaddr_t *src, const bdaddr_t *dst);
cstate_size = sdp_set_cstate_pdu(buf, &newState);
} else {
if (buf->data_size == 0)
- sdp_append_to_buf(buf, 0, 0);
+ sdp_append_to_buf(buf, NULL, 0);
cstate_size = sdp_set_cstate_pdu(buf, NULL);
}
}
if (send(req->sock, rsp.data, rsp.data_size, 0) < 0)
error("send: %s (%d)", strerror(errno), errno);
- SDPDBG("Bytes Sent : %d", sent);
+ SDPDBG("Bytes Sent : %d", rsp.data_size);
free(rsp.data);
free(req->buf);
#include <glib.h>
+#include "hcid.h"
#include "log.h"
#include "sdpd.h"
return TRUE;
}
-int start_sdp_server(uint16_t mtu, const char *did, uint32_t flags)
+int start_sdp_server(uint16_t mtu, uint32_t flags)
{
int compat = flags & SDP_SERVER_COMPAT;
int master = flags & SDP_SERVER_MASTER;
return -1;
}
- if (did && strlen(did) > 0) {
- const char *ptr = did;
- uint16_t vid = 0x0000, pid = 0x0000, ver = 0x0000;
-
- vid = (uint16_t) strtol(ptr, NULL, 16);
- ptr = strchr(ptr, ':');
- if (ptr) {
- pid = (uint16_t) strtol(ptr + 1, NULL, 16);
- ptr = strchr(ptr + 1, ':');
- if (ptr)
- ver = (uint16_t) strtol(ptr + 1, NULL, 16);
- register_device_id(vid, pid, ver);
- }
- }
+ if (main_opts.did_source > 0)
+ register_device_id();
io = g_io_channel_unix_new(l2cap_sock);
g_io_channel_set_close_on_unref(io, TRUE);
#include <glib.h>
#include <dbus/dbus.h>
+#include "hcid.h"
#include "sdpd.h"
#include "log.h"
#include "adapter.h"
update_db_timestamp();
}
-void register_device_id(const uint16_t vendor, const uint16_t product,
- const uint16_t version)
+void register_device_id(void)
{
- const uint16_t spec = 0x0102, source = 0x0002;
+ const uint16_t spec = 0x0103;
const uint8_t primary = 1;
sdp_list_t *class_list, *group_list, *profile_list;
uuid_t class_uuid, group_uuid;
sdp_profile_desc_t profile;
sdp_record_t *record = sdp_record_alloc();
- info("Adding device id record for %04x:%04x", vendor, product);
-
- btd_manager_set_did(vendor, product, version);
+ info("Adding device id record for %04x:%04x:%04x:%04x",
+ main_opts.did_source, main_opts.did_vendor,
+ main_opts.did_product, main_opts.did_version);
record->handle = sdp_next_handle();
spec_data = sdp_data_alloc(SDP_UINT16, &spec);
sdp_attr_add(record, 0x0200, spec_data);
- vendor_data = sdp_data_alloc(SDP_UINT16, &vendor);
+ vendor_data = sdp_data_alloc(SDP_UINT16, &main_opts.did_vendor);
sdp_attr_add(record, 0x0201, vendor_data);
- product_data = sdp_data_alloc(SDP_UINT16, &product);
+ product_data = sdp_data_alloc(SDP_UINT16, &main_opts.did_product);
sdp_attr_add(record, 0x0202, product_data);
- version_data = sdp_data_alloc(SDP_UINT16, &version);
+ version_data = sdp_data_alloc(SDP_UINT16, &main_opts.did_version);
sdp_attr_add(record, 0x0203, version_data);
primary_data = sdp_data_alloc(SDP_BOOL, &primary);
sdp_attr_add(record, 0x0204, primary_data);
- source_data = sdp_data_alloc(SDP_UINT16, &source);
+ source_data = sdp_data_alloc(SDP_UINT16, &main_opts.did_source);
sdp_attr_add(record, 0x0205, source_data);
update_db_timestamp();
{
sdp_record_t *rec;
+ /* Refuse to remove the server's own record */
+ if (handle == SDP_SERVER_RECORD_HANDLE)
+ return -EINVAL;
+
DBG("Removing record with handle 0x%05x", handle);
rec = sdp_record_find(handle);
void register_public_browse_group(void);
void register_server_service(void);
-void register_device_id(const uint16_t vendor, const uint16_t product,
- const uint16_t version);
+void register_device_id(void);
int record_sort(const void *r1, const void *r2);
void sdp_svcdb_reset(void);
#define SDP_SERVER_COMPAT (1 << 0)
#define SDP_SERVER_MASTER (1 << 1)
-int start_sdp_server(uint16_t mtu, const char *did, uint32_t flags);
+int start_sdp_server(uint16_t mtu, uint32_t flags);
void stop_sdp_server(void);
int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec);
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include <bluetooth/uuid.h>
#include "textfile.h"
-#include "glib-compat.h"
#include "glib-helper.h"
#include "storage.h"
return 0;
}
+int read_remote_appearance(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ uint16_t *appearance)
+{
+ char filename[PATH_MAX + 1], key[20], *str;
+
+ create_filename(filename, PATH_MAX, local, "appearances");
+
+ ba2str(peer, key);
+ sprintf(&key[17], "#%hhu", bdaddr_type);
+
+ str = textfile_get(filename, key);
+ if (!str)
+ return -ENOENT;
+
+ if (sscanf(str, "%hx", appearance) != 1) {
+ free(str);
+ return -ENOENT;
+ }
+
+ free(str);
+
+ return 0;
+}
+
+int write_remote_appearance(bdaddr_t *local, bdaddr_t *peer,
+ uint8_t bdaddr_type, uint16_t appearance)
+{
+ char filename[PATH_MAX + 1], key[20], str[7];
+
+ create_filename(filename, PATH_MAX, local, "appearances");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(peer, key);
+ sprintf(&key[17], "#%hhu", bdaddr_type);
+
+ sprintf(str, "0x%4.4x", appearance);
+
+ return textfile_put(filename, key, str);
+}
+
int write_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class)
{
char filename[PATH_MAX + 1], addr[18], str[9];
return 0;
}
-int write_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data)
+int write_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data,
+ uint8_t data_len)
{
char filename[PATH_MAX + 1], addr[18], str[481];
int i;
memset(str, 0, sizeof(str));
- for (i = 0; i < HCI_MAX_EIR_LENGTH; i++)
+ for (i = 0; i < data_len; i++)
sprintf(str + (i * 2), "%2.2X", data[i]);
create_filename(filename, PATH_MAX, local, "eir");
else {
char *new_str = service_list_to_string(services);
ret = textfile_caseput(filename, addr, new_str);
- free(new_str);
+ g_free(new_str);
}
g_slist_free(services);
err = textfile_put(filename, key, str);
free(buf.data);
- free(str);
+ g_free(str);
return err;
}
}
rec = sdp_extract_pdu(pdata, size, &len);
- free(pdata);
+ g_free(pdata);
return rec;
}
}
int write_device_services(const bdaddr_t *sba, const bdaddr_t *dba,
- const char *services)
+ uint8_t bdaddr_type, const char *services)
{
- char filename[PATH_MAX + 1], addr[18];
+ char filename[PATH_MAX + 1], key[20];
- create_filename(filename, PATH_MAX, sba, "primary");
+ create_filename(filename, PATH_MAX, sba, "primaries");
create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
- ba2str(dba, addr);
+ ba2str(dba, key);
+ sprintf(&key[17], "#%hhu", bdaddr_type);
- return textfile_put(filename, addr, services);
+ return textfile_put(filename, key, services);
}
static void filter_keys(char *key, char *value, void *data)
g_slist_free_full(match.keys, g_free);
}
-int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba)
+int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint8_t bdaddr_type)
{
- char filename[PATH_MAX + 1], address[18];
+ char filename[PATH_MAX + 1], key[20];
+
+ memset(key, 0, sizeof(key));
- memset(address, 0, sizeof(address));
- ba2str(dba, address);
+ ba2str(dba, key);
+ sprintf(&key[17], "#%hhu", bdaddr_type);
- /* Deleting all characteristics of a given address */
- create_filename(filename, PATH_MAX, sba, "characteristic");
- delete_by_pattern(filename, address);
+ /* Deleting all characteristics of a given key */
+ create_filename(filename, PATH_MAX, sba, "characteristics");
+ delete_by_pattern(filename, key);
- /* Deleting all attributes values of a given address */
+ /* Deleting all attributes values of a given key */
create_filename(filename, PATH_MAX, sba, "attributes");
- delete_by_pattern(filename, address);
+ delete_by_pattern(filename, key);
- /* Deleting all CCC values of a given address */
+ /* Deleting all CCC values of a given key */
create_filename(filename, PATH_MAX, sba, "ccc");
- delete_by_pattern(filename, address);
+ delete_by_pattern(filename, key);
+
+ create_filename(filename, PATH_MAX, sba, "primaries");
- create_filename(filename, PATH_MAX, sba, "primary");
- return textfile_del(filename, address);
+ return textfile_del(filename, key);
}
-char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba)
+char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint8_t bdaddr_type)
{
- char filename[PATH_MAX + 1], addr[18];
+ char filename[PATH_MAX + 1], key[20];
- create_filename(filename, PATH_MAX, sba, "primary");
+ create_filename(filename, PATH_MAX, sba, "primaries");
- ba2str(dba, addr);
+ ba2str(dba, key);
+ sprintf(&key[17], "#%hhu", bdaddr_type);
- return textfile_caseget(filename, addr);
+ return textfile_caseget(filename, key);
}
int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
- uint16_t handle, const char *chars)
+ uint8_t bdaddr_type, uint16_t handle,
+ const char *chars)
{
- char filename[PATH_MAX + 1], addr[18], key[23];
+ char filename[PATH_MAX + 1], addr[18], key[25];
- create_filename(filename, PATH_MAX, sba, "characteristic");
+ create_filename(filename, PATH_MAX, sba, "characteristics");
create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
ba2str(dba, addr);
-
- snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
+ snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
return textfile_put(filename, key, chars);
}
char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
- uint16_t handle)
+ uint8_t bdaddr_type, uint16_t handle)
{
- char filename[PATH_MAX + 1], addr[18], key[23];
+ char filename[PATH_MAX + 1], addr[18], key[25];
- create_filename(filename, PATH_MAX, sba, "characteristic");
+ create_filename(filename, PATH_MAX, sba, "characteristics");
ba2str(dba, addr);
-
- snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
+ snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
return textfile_caseget(filename, key);
}
int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
- uint16_t handle, const char *chars)
+ uint8_t bdaddr_type, uint16_t handle,
+ const char *chars)
{
- char filename[PATH_MAX + 1], addr[18], key[23];
+ char filename[PATH_MAX + 1], addr[18], key[25];
create_filename(filename, PATH_MAX, sba, "attributes");
create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
ba2str(dba, addr);
-
- snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
+ snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
return textfile_put(filename, key, chars);
}
return textfile_foreach(filename, func, data);
}
-int read_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint16_t handle,
- uint16_t *value)
+int read_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ uint16_t handle, uint16_t *value)
{
- char filename[PATH_MAX + 1], addr[18], key[23];
+ char filename[PATH_MAX + 1], addr[18], key[25];
char *str;
unsigned int config;
int err = 0;
create_filename(filename, PATH_MAX, local, "ccc");
ba2str(peer, addr);
- snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
+ snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
str = textfile_caseget(filename, key);
if (str == NULL)
return err;
}
-int write_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint16_t handle,
- uint16_t value)
+int write_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ uint16_t handle, uint16_t value)
{
- char filename[PATH_MAX + 1], addr[18], key[23], config[5];
+ char filename[PATH_MAX + 1], addr[18], key[25], config[5];
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#%hhu#%04X", addr, bdaddr_type, handle);
- snprintf(key, sizeof(key), "%17s#%04X", addr, handle);
snprintf(config, sizeof(config), "%04X", value);
return textfile_put(filename, key, config);
create_filename(filename, PATH_MAX, local, "ccc");
delete_by_pattern(filename, addr);
}
+
+int write_longtermkeys(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ const char *key)
+{
+ char filename[PATH_MAX + 1], addr[20];
+
+ if (!key)
+ return -EINVAL;
+
+ create_filename(filename, PATH_MAX, local, "longtermkeys");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(peer, addr);
+ sprintf(&addr[17], "#%hhu", bdaddr_type);
+
+ return textfile_put(filename, addr, key);
+}
+
+gboolean has_longtermkeys(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type)
+{
+ char filename[PATH_MAX + 1], key[20], *str;
+
+ create_filename(filename, PATH_MAX, local, "longtermkeys");
+
+ ba2str(peer, key);
+ sprintf(&key[17], "#%hhu", bdaddr_type);
+
+ str = textfile_caseget(filename, key);
+ if (str) {
+ free(str);
+ return TRUE;
+ }
+
+ return FALSE;
+}
int read_local_name(bdaddr_t *bdaddr, char *name);
int write_local_class(bdaddr_t *bdaddr, uint8_t *class);
int read_local_class(bdaddr_t *bdaddr, uint8_t *class);
+int write_remote_appearance(bdaddr_t *local, bdaddr_t *peer,
+ uint8_t bdaddr_type, uint16_t appearance);
+int read_remote_appearance(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ uint16_t *appearance);
int write_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class);
int read_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t *class);
int write_device_name(bdaddr_t *local, bdaddr_t *peer, char *name);
int read_device_name(const char *src, const char *dst, char *name);
-int write_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data);
+int write_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data,
+ uint8_t data_len);
int read_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data);
int write_version_info(bdaddr_t *local, bdaddr_t *peer, uint16_t manufacturer, uint8_t lmp_ver, uint16_t lmp_subver);
int write_features_info(bdaddr_t *local, bdaddr_t *peer, unsigned char *page1, unsigned char *page2);
int write_blocked(const bdaddr_t *local, const bdaddr_t *remote,
gboolean blocked);
int write_device_services(const bdaddr_t *sba, const bdaddr_t *dba,
- const char *services);
-int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba);
-char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba);
+ uint8_t bdaddr_type, const char *services);
+int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint8_t bdaddr_type);
+char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint8_t bdaddr_type);
int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
- uint16_t handle, const char *chars);
+ uint8_t bdaddr_type, uint16_t handle,
+ const char *chars);
char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
- uint16_t handle);
+ uint8_t bdaddr_type, uint16_t handle);
int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
- uint16_t handle, const char *chars);
+ uint8_t bdaddr_type, uint16_t handle,
+ const char *chars);
int read_device_attributes(const bdaddr_t *sba, textfile_cb func, void *data);
-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);
+int read_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ uint16_t handle, uint16_t *value);
+int write_device_ccc(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ uint16_t handle, uint16_t value);
void delete_device_ccc(bdaddr_t *local, bdaddr_t *peer);
-
-#define PNP_UUID "00001200-0000-1000-8000-00805f9b34fb"
-
+int write_longtermkeys(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type,
+ const char *key);
+gboolean has_longtermkeys(bdaddr_t *local, bdaddr_t *peer, uint8_t bdaddr_type);
len = strlen(key);
off = find_key(map, size, key, len, icase);
if (!off) {
+ munmap(map, size);
if (value) {
- munmap(map, size);
lseek(fd, size, SEEK_SET);
err = write_key_value(fd, key, value);
}
/*
* Keys and buttons
*
- * Most of the keys/buttons are modeled after USB HUT 1.12
+ * Most of the keys/buttons are modelled after USB HUT 1.12
* (see http://www.usb.org/developers/hidpage).
* Abbreviations in the comments:
* AC - Application Control
+++ /dev/null
-#!/usr/bin/env python
-
-import dbus
-import dbus.decorators
-import dbus.glib
-import gobject
-import sys
-import getopt
-from signal import *
-
-mgr_cmds = [ "InterfaceVersion", "ListAdapters", "DefaultAdapter" ]
-mgr_signals = [ "AdapterAdded", "AdapterRemoved" ]
-
-dev_cmds = [ "GetAddress",
- "GetVersion",
- "GetRevision",
- "GetManufacturer",
- "GetCompany",
- "GetMode",
- "SetMode",
- "GetDiscoverableTimeout",
- "SetDiscoverableTimeout",
- "IsConnectable",
- "IsDiscoverable",
- "IsConnected",
- "ListConnections",
- "GetMajorClass",
- "ListAvailableMinorClasses",
- "GetMinorClass",
- "SetMinorClass",
- "GetServiceClasses",
- "GetName",
- "SetName",
- "GetRemoteVersion",
- "GetRemoteRevision",
- "GetRemoteManufacturer",
- "GetRemoteCompany",
- "GetRemoteMajorClass",
- "GetRemoteMinorClass",
- "GetRemoteServiceClasses",
- "GetRemoteClass",
- "GetRemoteName",
- "GetRemoteAlias",
- "SetRemoteAlias",
- "ClearRemoteAlias",
- "LastSeen",
- "LastUsed",
- "DisconnectRemoteDevice",
- "CreateBonding",
- "CancelBondingProcess",
- "RemoveBonding",
- "HasBonding",
- "ListBondings",
- "GetPinCodeLength",
- "GetEncryptionKeySize",
- "DiscoverDevices",
- "DiscoverDevicesWithoutNameResolving",
- "CancelDiscovery",
- "ListRemoteDevices",
- "ListRecentRemoteDevices" ]
-dev_signals = [ "ModeChanged",
- "NameChanged",
- "MinorClassChanged",
- "DiscoveryStarted",
- "DiscoveryCompleted",
- "RemoteDeviceFound",
- "RemoteNameUpdated",
- "RemoteNameFailed",
- "RemoteAliasChanged"
- "RemoteAliasCleared",
- "RemoteDeviceConnected",
- "RemoteDeviceDisconnectRequested",
- "RemoteDeviceDisconnected",
- "BondingCreated",
- "BondingRemoved" ]
-
-dev_signals_filter = [ "/org/bluez/hci0", "/org/bluez/hci1",
- "/org/bluez/hci2", "/org/bluez/hci3",
- "/org/bluez/hci4", "/org/bluez/hci5",
- "/org/bluez/hci6", "/org/bluez/hci7" ]
-
-class Tester:
- exit_events = []
- dev_path = None
- need_dev = False
- listen = False
- at_interrupt = None
-
- def __init__(self, argv):
- self.name = argv[0]
-
- self.parse_args(argv[1:])
-
- try:
- self.dbus_setup()
- except dbus.DBusException, e:
- print 'Failed to do D-Bus setup: %s' % e
- sys.exit(1)
-
- def parse_args(self, argv):
- try:
- opts, args = getopt.getopt(argv, "hli:")
- except getopt.GetoptError:
- self.usage()
- sys.exit(1)
-
- for o, a in opts:
- if o == "-h":
- self.usage()
- sys.exit()
- elif o == "-l":
- self.listen = True
- elif o == "-i":
- if a[0] == '/':
- self.dev_path = a
- else:
- self.dev_path = '/org/bluez/%s' % a
-
- if not (args or self.listen):
- self.usage()
- sys.exit(1)
-
- if args:
- self.cmd = args[0]
- self.cmd_args = args[1:]
-
- def dbus_dev_setup(self):
- if not self.dev_path:
- try:
- self.dbus_mgr_setup()
- self.dev_path = self.manager.DefaultAdapter()
- except dbus.DBusException, e:
- print 'Failed to get default device: %s' % e
- sys.exit(1)
- try:
- obj = self.bus.get_object('org.bluez', self.dev_path)
- self.device = dbus.Interface(obj, 'org.bluez.Adapter')
- except dbus.DBusException, e:
- print 'Failed to setup device path: %s' % e
- sys.exit(1)
-
- def dbus_dev_sig_setup(self):
- try:
- for signal in dev_signals:
- for path in dev_signals_filter:
- self.bus.add_signal_receiver(self.dev_signal_handler,
- signal, 'org.bluez.Adapter',
- 'org.bluez', path,
- message_keyword='dbus_message')
- except dbus.DBusException, e:
- print 'Failed to setup signal handler for device path: %s' % e
- sys.exit(1)
-
- def dbus_mgr_sig_setup(self):
- try:
- for signal in mgr_signals:
- self.bus.add_signal_receiver(self.mgr_signal_handler,
- signal,'org.bluez.Manager',
- 'org.bluez', '/org/bluez')
- except dbus.DBusException, e:
- print 'Failed to setup signal handler for manager path: %s' % e
- sys.exit(1)
-
- def dbus_mgr_setup(self):
- self.manager_obj = self.bus.get_object('org.bluez', '/org/bluez')
- self.manager = dbus.Interface(self.manager_obj, 'org.bluez.Manager')
-
- def dbus_setup(self):
- self.bus = dbus.SystemBus()
-
- def usage(self):
- print 'Usage: %s [-i <dev>] [-l] [-h] <cmd> [arg1..]' % self.name
- print ' -i <dev> Specify device (e.g. "hci0" or "/org/bluez/hci0")'
- print ' -l Listen for events (no command required)'
- print ' -h Show this help'
- print 'Manager commands:'
- for cmd in mgr_cmds:
- print '\t%s' % cmd
- print 'Adapter commands:'
- for cmd in dev_cmds:
- print '\t%s' % cmd
-
- #@dbus.decorators.explicitly_pass_message
- def dev_signal_handler(*args, **keywords):
- dbus_message = keywords["dbus_message"]
- print '%s - %s: ' % (dbus_message.get_member(), dbus_message.get_path()),
- for arg in args[1:]:
- print '%s ' % arg,
- print
-
- #@dbus.decorators.explicitly_pass_message
- def mgr_signal_handler(*args, **keywords):
- dbus_message = keywords["dbus_message"]
- print '%s: ' % dbus_message.get_member()
- for arg in args[1:]:
- print '%s ' % arg,
- print
-
- def signal_cb(self, sig, frame):
- print 'Caught signal, exiting'
- if self.at_interrupt:
- self.at_interrupt()
- self.main_loop.quit()
-
- def call_mgr_dbus_func(self):
- if self.cmd == 'InterfaceVersion':
- try:
- print self.manager.InterfaceVersion()
- except dbus.DBusException, e:
- print 'Sending %s failed: %s' % (self.cmd, e)
- if self.cmd == 'ListAdapters':
- try:
- devices = self.manager.ListAdapters()
- except dbus.DBusException, e:
- print 'Sending %s failed: %s' % (self.cmd, e)
- sys.exit(1)
- for device in devices:
- print device
- elif self.cmd == 'DefaultAdapter':
- try:
- print self.manager.DefaultAdapter()
- except dbus.DBusException, e:
- print 'Sending %s failed: %s' % (self.cmd, e)
- sys.exit(1)
-
- def call_dev_dbus_func(self):
- try:
- if self.cmd == 'GetAddress':
- print self.device.GetAddress()
- elif self.cmd == 'GetManufacturer':
- print self.device.GetManufacturer()
- elif self.cmd == 'GetVersion':
- print self.device.GetVersion()
- elif self.cmd == 'GetRevision':
- print self.device.GetRevision()
- elif self.cmd == 'GetCompany':
- print self.device.GetCompany()
- elif self.cmd == 'GetMode':
- print self.device.GetMode()
- elif self.cmd == 'SetMode':
- if len(self.cmd_args) == 1:
- self.device.SetMode(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> SetMode scan_mode' % self.name
- elif self.cmd == 'GetDiscoverableTimeout':
- print '%u' % (self.device.GetDiscoverableTimeout())
- elif self.cmd == 'SetDiscoverableTimeout':
- if len(self.cmd_args) == 1:
- self.device.SetDiscoverableTimeout(dbus.UInt32(self.cmd_args[0]))
- else:
- print 'Usage: %s -i <dev> SetDiscoverableTimeout timeout' % self.name
- elif self.cmd == 'IsConnectable':
- print self.device.IsConnectable()
- elif self.cmd == 'IsDiscoverable':
- print self.device.IsDiscoverable()
- elif self.cmd == 'IsConnected':
- if len(self.cmd_args) == 1:
- print self.device.IsConnected(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> IsConnected address' % self.name
- elif self.cmd == 'ListConnections':
- print self.device.ListConnections()
- elif self.cmd == 'GetMajorClass':
- print self.device.GetMajorClass()
- elif self.cmd == 'ListAvailableMinorClasses':
- print self.device.ListAvailableMinorClasses()
- elif self.cmd == 'GetMinorClass':
- print self.device.GetMinorClass()
- elif self.cmd == 'SetMinorClass':
- if len(self.cmd_args) == 1:
- self.device.SetMinorClass(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> SetMinorClass minor' % self.name
- elif self.cmd == 'GetServiceClasses':
- classes = self.device.GetServiceClasses()
- for clas in classes:
- print clas,
- elif self.cmd == 'GetName':
- print self.device.GetName()
- elif self.cmd == 'SetName':
- if len(self.cmd_args) == 1:
- self.device.SetName(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> SetName newname' % self.name
- elif self.cmd == 'GetRemoteName':
- if len(self.cmd_args) == 1:
- print self.device.GetRemoteName(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetRemoteName address' % self.name
- elif self.cmd == 'GetRemoteVersion':
- if len(self.cmd_args) == 1:
- print self.device.GetRemoteVersion(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetRemoteVersion address' % self.name
- elif self.cmd == 'GetRemoteRevision':
- if len(self.cmd_args) == 1:
- print self.device.GetRemoteRevision(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetRemoteRevision address' % self.name
- elif self.cmd == 'GetRemoteManufacturer':
- if len(self.cmd_args) == 1:
- print self.device.GetRemoteManufacturer(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetRemoteManufacturer address' % self.name
- elif self.cmd == 'GetRemoteCompany':
- if len(self.cmd_args) == 1:
- print self.device.GetRemoteCompany(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetRemoteCompany address' % self.name
- elif self.cmd == 'GetRemoteAlias':
- if len(self.cmd_args) == 1:
- print self.device.GetRemoteAlias(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetRemoteAlias address' % self.name
- elif self.cmd == 'GetRemoteMajorClass':
- if len(self.cmd_args) == 1:
- print self.device.GetRemoteMajorClass(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetRemoteMajorClass address' % self.name
- elif self.cmd == 'GetRemoteMinorClass':
- if len(self.cmd_args) == 1:
- print self.device.GetRemoteMinorClass(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetRemoteMinorClass address' % self.name
- elif self.cmd == 'GetRemoteServiceClasses':
- if len(self.cmd_args) == 1:
- print self.device.GetRemoteServiceClasses(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetRemoteServiceClasses address' % self.name
- elif self.cmd == 'SetRemoteAlias':
- if len(self.cmd_args) == 2:
- self.device.SetRemoteAlias(self.cmd_args[0], self.cmd_args[1])
- else:
- print 'Usage: %s -i <dev> SetRemoteAlias address alias' % self.name
- elif self.cmd == 'ClearRemoteAlias':
- if len(self.cmd_args) == 1:
- print self.device.ClearRemoteAlias(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> ClearRemoteAlias address' % self.name
- elif self.cmd == 'LastSeen':
- if len(self.cmd_args) == 1:
- print self.device.LastSeen(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> LastSeen address' % self.name
- elif self.cmd == 'LastUsed':
- if len(self.cmd_args) == 1:
- print self.device.LastUsed(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> LastUsed address' % self.name
- elif self.cmd == 'DisconnectRemoteDevice':
- if len(self.cmd_args) == 1:
- print self.device.LastUsed(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> DisconnectRemoteDevice address' % self.name
- elif self.cmd == 'CreateBonding':
- if len(self.cmd_args) == 1:
- print self.device.CreateBonding(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> CreateBonding address' % self.name
- elif self.cmd == 'RemoveBonding':
- if len(self.cmd_args) == 1:
- print self.device.RemoveBonding(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> RemoveBonding address' % self.name
- elif self.cmd == 'CancelBondingProcess':
- if len(self.cmd_args) == 1:
- print self.device.CancelBondingProcess(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> CancelBondingProcess address' % self.name
- elif self.cmd == 'HasBonding':
- if len(self.cmd_args) == 1:
- print self.device.HasBonding(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> HasBonding address' % self.name
- elif self.cmd == 'ListBondings':
- bondings = self.device.ListBondings()
- for bond in bondings:
- print bond,
- elif self.cmd == 'GetPinCodeLength':
- if len(self.cmd_args) == 1:
- print self.device.GetPinCodeLength(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetPinCodeLength address' % self.name
- elif self.cmd == 'GetEncryptionKeySize':
- if len(self.cmd_args) == 1:
- print self.device.GetEncryptionKeySize(self.cmd_args[0])
- else:
- print 'Usage: %s -i <dev> GetEncryptionKeySize address' % self.name
- elif self.cmd == 'DiscoverDevices':
- print self.device.DiscoverDevices()
- elif self.cmd == 'DiscoverDevicesWithoutNameResolving':
- print self.device.DiscoverDevicesWithoutNameResolving()
- elif self.cmd == 'ListRemoteDevices':
- devices = self.device.ListRemoteDevices()
- for device in devices:
- print device,
- elif self.cmd == 'ListRecentRemoteDevices':
- if len(self.cmd_args) == 1:
- devices = self.device.ListRecentRemoteDevices(self.cmd_args[0])
- for device in devices:
- print device,
- else:
- print 'Usage: %s -i <dev> ListRecentRemoteDevices date' % self.name
- else:
- # FIXME: remove at future version
- 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)
-
- def run(self):
- # Manager methods
- if self.listen:
- self.dbus_mgr_sig_setup()
- self.dbus_dev_sig_setup()
- print 'Listening for events...'
-
- if self.cmd in mgr_cmds:
- try:
- self.dbus_mgr_setup()
- except dbus.DBusException, e:
- print 'Failed to setup manager interface: %s' % e
- sys.exit(1)
- self.call_mgr_dbus_func()
- elif self.cmd in dev_cmds:
- try:
- self.dbus_dev_setup()
- except dbus.DBusException, e:
- print 'Failed to setup device interface: %s' % e
- sys.exit(1)
- self.call_dev_dbus_func()
- elif not self.listen:
- print 'Unknown command: %s' % self.cmd
- self.usage()
- sys.exit(1)
-
- if self.listen:
- signal(SIGINT, self.signal_cb)
- signal(SIGTERM, self.signal_cb)
- self.main_loop = gobject.MainLoop()
- self.main_loop.run()
-
-if __name__ == '__main__':
- gobject.threads_init()
- dbus.glib.init_threads()
-
- tester = Tester(sys.argv)
- tester.run()
#include "btio.h"
#define DEFAULT_ACCEPT_TIMEOUT 2
+static gint opt_update_sec = 0;
struct io_data {
guint ref;
return FALSE;
}
+static void update_sec_level(struct io_data *data)
+{
+ GError *err = NULL;
+ int sec_level;
+
+ if (!bt_io_get(data->io, data->type, &err,
+ BT_IO_OPT_SEC_LEVEL, &sec_level,
+ BT_IO_OPT_INVALID)) {
+ printf("bt_io_get(OPT_SEC_LEVEL): %s\n", err->message);
+ g_clear_error(&err);
+ return;
+ }
+
+ printf("sec_level=%d\n", sec_level);
+
+ if (opt_update_sec == sec_level)
+ return;
+
+ if (!bt_io_set(data->io, data->type, &err,
+ BT_IO_OPT_SEC_LEVEL, opt_update_sec,
+ BT_IO_OPT_INVALID)) {
+ printf("bt_io_set(OPT_SEC_LEVEL): %s\n", err->message);
+ g_clear_error(&err);
+ }
+}
+
static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
{
struct io_data *data = user_data;
printf("imtu=%u, omtu=%u\n", imtu, omtu);
}
+ if (data->type == BT_IO_L2CAP) {
+ uint8_t key_size;
+
+ if (!bt_io_get(io, data->type, &err,
+ BT_IO_OPT_KEY_SIZE, &key_size,
+ BT_IO_OPT_INVALID)) {
+ printf("Unable to get L2CAP Key size: %s\n",
+ err->message);
+ g_clear_error(&err);
+ } else
+ printf("key_size=%u\n", key_size);
+ }
+
if (data->disconn == 0) {
g_io_channel_shutdown(io, TRUE, NULL);
printf("Disconnected\n");
io_data_ref(data);
+
+ if (opt_update_sec > 0)
+ update_sec_level(data);
+
cond = G_IO_NVAL | G_IO_HUP | G_IO_ERR;
g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, io_watch, data,
(GDestroyNotify) io_data_unref);
io_data_ref(data);
+ if (opt_update_sec > 0)
+ update_sec_level(data);
+
if (!bt_io_accept(data->io, connect_cb, data,
(GDestroyNotify) io_data_unref, NULL)) {
printf("bt_io_accept() failed\n");
}
static void l2cap_connect(const char *src, const char *dst, uint16_t psm,
- gint disconn, gint sec,
- gint prio)
+ uint16_t cid, gint disconn,
+ gint sec, gint prio)
{
struct io_data *data;
GError *err = NULL;
BT_IO_OPT_SOURCE, src,
BT_IO_OPT_DEST, dst,
BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_CID, cid,
BT_IO_OPT_SEC_LEVEL, sec,
BT_IO_OPT_PRIORITY, prio,
BT_IO_OPT_INVALID);
&err,
BT_IO_OPT_DEST, dst,
BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_CID, cid,
BT_IO_OPT_SEC_LEVEL, sec,
BT_IO_OPT_PRIORITY, prio,
BT_IO_OPT_INVALID);
static gint opt_sec = 0;
static gboolean opt_master = FALSE;
static gint opt_priority = 0;
+static gint opt_cid = 0;
static GMainLoop *main_loop;
"RFCOMM channel" },
{ "psm", 'p', 0, G_OPTION_ARG_INT, &opt_psm,
"L2CAP PSM" },
+ { "cid", 'j', 0, G_OPTION_ARG_INT, &opt_cid,
+ "L2CAP CID" },
{ "sco", 's', 0, G_OPTION_ARG_NONE, &opt_sco,
"Use SCO" },
{ "defer", 'd', 0, G_OPTION_ARG_NONE, &opt_defer,
"Use DEFER_SETUP for incoming connections" },
{ "sec-level", 'S', 0, G_OPTION_ARG_INT, &opt_sec,
"Security level" },
+ { "update-sec-level", 'U', 0, G_OPTION_ARG_INT, &opt_update_sec,
+ "Update security level" },
{ "dev", 'i', 0, G_OPTION_ARG_STRING, &opt_dev,
"Which HCI device to use" },
{ "reject", 'r', 0, G_OPTION_ARG_INT, &opt_reject,
g_option_context_free(context);
- 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);
+ printf("accept=%d, reject=%d, discon=%d, defer=%d, sec=%d,"
+ " update_sec=%d, prio=%d\n", opt_accept, opt_reject,
+ opt_disconn, opt_defer, opt_sec, opt_update_sec, opt_priority);
- if (opt_psm) {
+ if (opt_psm || opt_cid) {
if (argc > 1)
- l2cap_connect(opt_dev, argv[1], opt_psm,
- opt_disconn, opt_sec,
- opt_priority);
+ l2cap_connect(opt_dev, argv[1], opt_psm, opt_cid,
+ opt_disconn, opt_sec, opt_priority);
else
l2cap_listen(opt_dev, opt_psm, opt_defer, opt_reject,
opt_disconn, opt_accept, opt_sec,
uint8_t features[8];
uint8_t name[248];
uint8_t dev_class[3];
+ uint8_t scan_enable;
+ uint8_t ssp_mode;
uint8_t inq_mode;
uint8_t eir_fec;
uint8_t eir_data[HCI_MAX_EIR_LENGTH];
+ uint8_t le_mode;
+ uint8_t le_simul;
uint16_t acl_cnt;
bdaddr_t bdaddr;
int dev_fd;
return NULL;
}
+static void reset_vdev(void)
+{
+ /* Device settings */
+ vdev.features[0] = 0xff;
+ vdev.features[1] = 0xff;
+ vdev.features[2] = 0x8f;
+ vdev.features[3] = 0xfe;
+ vdev.features[4] = 0x9b;
+ vdev.features[5] = 0xf9;
+ vdev.features[6] = 0x00;
+ vdev.features[7] = 0x80;
+
+ vdev.features[4] |= 0x40; /* LE Supported */
+ vdev.features[6] |= 0x01; /* Extended Inquiry Response */
+ vdev.features[6] |= 0x02; /* BR/EDR and LE */
+ vdev.features[6] |= 0x08; /* Secure Simple Pairing */
+
+ memset(vdev.name, 0, sizeof(vdev.name));
+ strncpy((char *) vdev.name, "BlueZ (Virtual HCI)",
+ sizeof(vdev.name) - 1);
+
+ vdev.dev_class[0] = 0x00;
+ vdev.dev_class[1] = 0x00;
+ vdev.dev_class[2] = 0x00;
+
+ vdev.scan_enable = 0x00;
+ vdev.ssp_mode = 0x00;
+ vdev.inq_mode = 0x00;
+ vdev.eir_fec = 0x00;
+ memset(vdev.eir_data, 0, sizeof(vdev.eir_data));
+ vdev.le_mode = 0x00;
+ vdev.le_simul = 0x00;
+}
+
static void command_status(uint16_t ogf, uint16_t ocf, uint8_t status)
{
uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
strerror(errno), errno);
}
-static int scan_enable(uint8_t *data)
+static uint8_t scan_enable(uint8_t *data)
{
+#if 0
struct epoll_event scan_event;
struct sockaddr_in sa;
bdaddr_t ba;
failed:
close(sk);
return 1;
+#endif
+
+ return data[0];
}
static void accept_connection(uint8_t *data)
static void hci_link_control(uint16_t ocf, int plen, uint8_t *data)
{
- uint8_t status;
-
const uint16_t ogf = OGF_LINK_CTL;
switch (ocf) {
break;
default:
- status = 0x01;
- command_complete(ogf, ocf, 1, &status);
+ command_status(ogf, ocf, 0x01);
break;
}
}
static void hci_link_policy(uint16_t ocf, int plen, uint8_t *data)
{
- uint8_t status;
-
const uint16_t ogf = OGF_INFO_PARAM;
switch (ocf) {
default:
- status = 0x01;
- command_complete(ogf, ocf, 1, &status);
+ command_status(ogf, ocf, 0x01);
break;
}
}
static void hci_host_control(uint16_t ocf, int plen, uint8_t *data)
{
+ read_scan_enable_rp se;
read_local_name_rp ln;
read_class_of_dev_rp cd;
read_inquiry_mode_rp im;
read_ext_inquiry_response_rp ir;
+ read_simple_pairing_mode_rp pm;
+ read_le_host_supported_rp hs;
uint8_t status;
const uint16_t ogf = OGF_HOST_CTL;
switch (ocf) {
case OCF_RESET:
status = 0x00;
+ reset_vdev();
command_complete(ogf, ocf, 1, &status);
break;
command_complete(ogf, ocf, 1, &status);
break;
+ case OCF_READ_SCAN_ENABLE:
+ se.status = 0x00;
+ se.enable = vdev.scan_enable;
+ command_complete(ogf, ocf, sizeof(se), &se);
+ break;
+
case OCF_WRITE_SCAN_ENABLE:
- status = scan_enable(data);
+ status = 0x00;
+ vdev.scan_enable = scan_enable(data);
command_complete(ogf, ocf, 1, &status);
break;
command_complete(ogf, ocf, 1, &status);
break;
- default:
- status = 0x01;
+ case OCF_READ_SIMPLE_PAIRING_MODE:
+ pm.status = 0x00;
+ pm.mode = vdev.ssp_mode;
+ command_complete(ogf, ocf, sizeof(pm), &pm);
+ break;
+
+ case OCF_WRITE_SIMPLE_PAIRING_MODE:
+ status = 0x00;
+ vdev.ssp_mode = data[0];
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ case OCF_READ_LE_HOST_SUPPORTED:
+ hs.status = 0x00;
+ hs.le = vdev.le_mode;
+ hs.simul = vdev.le_simul;
+ command_complete(ogf, ocf, sizeof(hs), &hs);
+ break;
+
+ case OCF_WRITE_LE_HOST_SUPPORTED:
+ status = 0x00;
+ vdev.le_mode = data[0];
+ vdev.le_simul = data[1];
command_complete(ogf, ocf, 1, &status);
break;
+
+ default:
+ command_status(ogf, ocf, 0x01);
+ break;
}
}
read_local_ext_features_rp ef;
read_buffer_size_rp bs;
read_bd_addr_rp ba;
- uint8_t status;
const uint16_t ogf = OGF_INFO_PARAM;
switch (ocf) {
case OCF_READ_LOCAL_VERSION:
lv.status = 0x00;
- lv.hci_ver = 0x03;
+ lv.hci_ver = 0x06;
lv.hci_rev = htobs(0x0000);
- lv.lmp_ver = 0x03;
- lv.manufacturer = htobs(29);
+ lv.lmp_ver = 0x06;
+ lv.manufacturer = htobs(63);
lv.lmp_subver = htobs(0x0000);
command_complete(ogf, ocf, sizeof(lv), &lv);
break;
ef.status = 0x00;
if (*data == 0) {
ef.page_num = 0;
- ef.max_page_num = 0;
+ ef.max_page_num = 1;
memcpy(ef.features, vdev.features, 8);
+ } else if (*data == 1) {
+ ef.page_num = 1;
+ ef.max_page_num = 1;
+ memset(ef.features, 0, 8);
+ ef.features[0] |= (!!vdev.ssp_mode << 0);
+ ef.features[0] |= (!!vdev.le_mode << 1);
+ ef.features[0] |= (!!vdev.le_simul << 2);
} else {
ef.page_num = *data;
ef.max_page_num = 0;
break;
default:
- status = 0x01;
- command_complete(ogf, ocf, 1, &status);
+ command_status(ogf, ocf, 0x01);
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;
break;
default:
- status = 0x01;
- command_complete(ogf, ocf, 1, &status);
+ command_status(ogf, ocf, 0x01);
+ break;
+ }
+}
+
+static void hci_le_control(uint16_t ocf, int plen, uint8_t *data)
+{
+ le_read_buffer_size_rp bs;
+
+ const uint16_t ogf = OGF_LE_CTL;
+
+ switch (ocf) {
+ case OCF_LE_READ_BUFFER_SIZE:
+ bs.status = 0;
+ bs.pkt_len = htobs(VHCI_ACL_MTU);
+ bs.max_pkt = htobs(VHCI_ACL_MAX_PKT);
+ command_complete(ogf, ocf, sizeof(bs), &bs);
+ break;
+
+ default:
+ command_status(ogf, ocf, 0x01);
break;
}
}
case OGF_STATUS_PARAM:
hci_status_param(ocf, ch->plen, ptr);
break;
+
+ case OGF_LE_CTL:
+ hci_le_control(ocf, ch->plen, ptr);
+ break;
+
+ default:
+ command_status(ogf, ocf, 0x01);
+ break;
}
}
if (n == 0) {
/* loopback port */
in_addr_t addr = INADDR_LOOPBACK;
+ uint16_t be16 = htons(atoi(str));
bdaddr_t b;
memcpy(&b, &addr, 4);
- *(uint16_t *) (&b.b[4]) = htons(atoi(str));
+ memcpy(&b.b[4], &be16, sizeof(be16));
baswap(ba, &b);
return 0;
goto close_device;
}
- /* Device settings */
- vdev.features[0] = 0xff;
- vdev.features[1] = 0xff;
- vdev.features[2] = 0x8f;
- vdev.features[3] = 0xfe;
- vdev.features[4] = 0x9b;
- vdev.features[5] = 0xf9;
- vdev.features[6] = 0x01;
- vdev.features[7] = 0x80;
-
- memset(vdev.name, 0, sizeof(vdev.name));
- strncpy((char *) vdev.name, "BlueZ (Virtual HCI)",
- sizeof(vdev.name) - 1);
-
- vdev.dev_class[0] = 0x00;
- vdev.dev_class[1] = 0x00;
- vdev.dev_class[2] = 0x00;
-
- vdev.inq_mode = 0x00;
- vdev.eir_fec = 0x00;
- memset(vdev.eir_data, 0, sizeof(vdev.eir_data));
+ reset_vdev();
vdev.dev_fd = device_fd;
vdev.dd = dd;
static int defer_setup = 0;
static int priority = -1;
static int rcvbuf = 0;
+static int chan_policy = -1;
+static int bdaddr_type = 0;
-static struct {
+struct lookup_table {
char *name;
int flag;
-} l2cap_modes[] = {
+};
+
+static struct lookup_table l2cap_modes[] = {
{ "basic", L2CAP_MODE_BASIC },
/* Not implemented
{ "flowctl", L2CAP_MODE_FLOWCTL },
{ 0 }
};
-static void list_l2cap_modes(void)
+static struct lookup_table chan_policies[] = {
+ { "bredr", BT_CHANNEL_POLICY_BREDR_ONLY },
+ { "bredr_pref", BT_CHANNEL_POLICY_BREDR_PREFERRED },
+ { "amp_pref", BT_CHANNEL_POLICY_AMP_PREFERRED },
+ { NULL, 0 },
+};
+
+static struct lookup_table bdaddr_types[] = {
+ { "bredr", BDADDR_BREDR },
+ { "le_public", BDADDR_LE_PUBLIC },
+ { "le_random", BDADDR_LE_RANDOM },
+ { NULL, 0 },
+};
+
+static int get_lookup_flag(struct lookup_table *table, char *name)
{
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);
+ for (i = 0; table[i].name; i++)
+ if (!strcasecmp(table[i].name, name))
+ return table[i].flag;
+
+ return -1;
+}
+
+static void print_lookup_values(struct lookup_table *table, char *header)
+{
+ int i;
+
+ printf("%s\n", header);
+
+ for (i = 0; table[i].name; i++)
+ printf("\t%s\n", table[i].name);
}
static float tv2fl(struct timeval tv)
}
#endif
+ if (chan_policy != -1) {
+ if (setsockopt(sk, SOL_BLUETOOTH, BT_CHANNEL_POLICY,
+ &chan_policy, sizeof(chan_policy)) < 0) {
+ syslog(LOG_ERR, "Can't enable chan policy : %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+ }
+
/* Enable SO_LINGER */
if (linger) {
struct linger l = { .l_onoff = 1, .l_linger = linger };
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
str2ba(svr, &addr.l2_bdaddr);
+ addr.l2_bdaddr_type = bdaddr_type;
if (cid)
addr.l2_cid = htobs(cid);
else if (psm)
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
str2ba(svr, &addr.l2_bdaddr);
+ addr.l2_bdaddr_type = bdaddr_type;
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
perror("Can't connect socket");
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
str2ba(svr, &addr.l2_bdaddr);
+ addr.l2_bdaddr_type = bdaddr_type;
if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
perror("Can't connect socket");
"\t[-D milliseconds] delay after sending num frames (default = 0)\n"
"\t[-K milliseconds] delay before receiving (default = 0)\n"
"\t[-X mode] l2cap mode (help for list, default = basic)\n"
+ "\t[-a policy] chan policy (help for list, default = bredr)\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[-E] request encryption\n"
"\t[-S] secure connection\n"
"\t[-M] become master\n"
- "\t[-T] enable timestamps\n");
+ "\t[-T] enable timestamps\n"
+ "\t[-V type] address type (help for list, default = bredr)\n");
}
int main(int argc, char *argv[])
{
struct sigaction sa;
- int opt, sk, i, mode = RECV, need_addr = 0;
+ int opt, sk, 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:Y:H:K:RUGAESMT")) != EOF) {
- switch(opt) {
+ while ((opt = getopt(argc, argv, "rdscuwmntqxyzpb:a:"
+ "i:P:I:O:J:B:N:L:W:C:D:X:F:Q:Z:Y:H:K:V:RUGAESMT")) != EOF) {
+ switch (opt) {
case 'r':
mode = RECV;
break;
break;
case 'X':
- rfcmode = -1;
+ rfcmode = get_lookup_flag(l2cap_modes, optarg);
- for (i = 0; l2cap_modes[i].name; i++)
- if (!strcasecmp(l2cap_modes[i].name, optarg))
- rfcmode = l2cap_modes[i].flag;
+ if (rfcmode == -1) {
+ print_lookup_values(l2cap_modes,
+ "List L2CAP modes:");
+ exit(1);
+ }
+
+ break;
- if (!strcasecmp(optarg, "help") || rfcmode == -1) {
- list_l2cap_modes();
+ case 'a':
+ chan_policy = get_lookup_flag(chan_policies, optarg);
+
+ if (chan_policy == -1) {
+ print_lookup_values(chan_policies,
+ "List L2CAP chan policies:");
exit(1);
}
rcvbuf = atoi(optarg);
break;
+ case 'V':
+ bdaddr_type = get_lookup_flag(bdaddr_types, optarg);
+
+ if (bdaddr_type == -1) {
+ print_lookup_values(bdaddr_types,
+ "List Address types:");
+ exit(1);
+ }
+
+ break;
+
default:
usage();
exit(1);
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import dbus
bus = dbus.SystemBus()
for i in adapter_list:
adapter = dbus.Interface(bus.get_object("org.bluez", i),
"org.bluez.Adapter")
- print "[ " + i + " ]"
+ print("[ " + i + " ]")
properties = adapter.GetProperties()
for key in properties.keys():
value = properties[key]
if (key == "Devices"):
list = extract_objects(value)
- print " %s = %s" % (key, list)
+ print(" %s = %s" % (key, list))
elif (key == "UUIDs"):
list = extract_uuids(value)
- print " %s = %s" % (key, list)
+ print(" %s = %s" % (key, list))
else:
- print " %s = %s" % (key, value)
+ print(" %s = %s" % (key, value))
try:
device_list = properties["Devices"]
for n in device_list:
device = dbus.Interface(bus.get_object("org.bluez", n),
"org.bluez.Device")
- print " [ " + n + " ]"
+ print(" [ " + n + " ]")
properties = device.GetProperties()
for key in properties.keys():
value = properties[key]
if (key == "Nodes"):
list = extract_objects(value)
- print " %s = %s" % (key, list)
+ print(" %s = %s" % (key, list))
elif (key == "UUIDs"):
list = extract_uuids(value)
- print " %s = %s" % (key, list)
+ print(" %s = %s" % (key, list))
elif (key == "Class"):
- print " %s = 0x%06x" % (key, value)
+ print(" %s = 0x%06x" % (key, value))
elif (key == "Vendor"):
- print " %s = 0x%04x" % (key, value)
+ print(" %s = 0x%04x" % (key, value))
elif (key == "Product"):
- print " %s = 0x%04x" % (key, value)
+ print(" %s = 0x%04x" % (key, value))
elif (key == "Version"):
- print " %s = 0x%04x" % (key, value)
+ print(" %s = 0x%04x" % (key, value))
else:
- print " %s = %s" % (key, value)
+ print(" %s = %s" % (key, value))
try:
node_list = properties["Nodes"]
for x in node_list:
node = dbus.Interface(bus.get_object("org.bluez", x),
"org.bluez.Node")
- print " [ " + x + " ]"
+ print(" [ " + x + " ]")
properties = node.GetProperties()
for key in properties.keys():
- print " %s = %s" % (key, properties[key])
+ print(" %s = %s" % (key, properties[key]))
- print
+ print("")
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import gobject
import dbus
def property_changed(name, value, path, interface):
iface = interface[interface.rfind(".") + 1:]
val = str(value)
- print "{%s.PropertyChanged} [%s] %s = %s" % (iface, path, name, val)
+ print("{%s.PropertyChanged} [%s] %s = %s" % (iface, path, name, val))
def object_signal(value, path, interface, member):
iface = interface[interface.rfind(".") + 1:]
val = str(value)
- print "{%s.%s} [%s] Path = %s" % (iface, member, path, val)
+ print("{%s.%s} [%s] Path = %s" % (iface, member, path, val))
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
#!/usr/bin/python
-import gobject
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
import sys
import dbus
import dbus.mainloop.glib
from optparse import OptionParser
+def ask(prompt):
+ try:
+ return raw_input(prompt)
+ except:
+ return input(prompt)
+
class Rejected(dbus.DBusException):
_dbus_error_name = "org.bluez.Error.Rejected"
@dbus.service.method("org.bluez.Agent",
in_signature="", out_signature="")
def Release(self):
- print "Release"
+ print("Release")
if self.exit_on_release:
mainloop.quit()
@dbus.service.method("org.bluez.Agent",
in_signature="os", out_signature="")
def Authorize(self, device, uuid):
- print "Authorize (%s, %s)" % (device, uuid)
- authorize = raw_input("Authorize connection (yes/no): ")
+ print("Authorize (%s, %s)" % (device, uuid))
+ authorize = ask("Authorize connection (yes/no): ")
if (authorize == "yes"):
return
raise Rejected("Connection rejected by user")
@dbus.service.method("org.bluez.Agent",
in_signature="o", out_signature="s")
def RequestPinCode(self, device):
- print "RequestPinCode (%s)" % (device)
- return raw_input("Enter PIN Code: ")
+ print("RequestPinCode (%s)" % (device))
+ return ask("Enter PIN Code: ")
@dbus.service.method("org.bluez.Agent",
in_signature="o", out_signature="u")
def RequestPasskey(self, device):
- print "RequestPasskey (%s)" % (device)
- passkey = raw_input("Enter passkey: ")
+ print("RequestPasskey (%s)" % (device))
+ passkey = ask("Enter passkey: ")
return dbus.UInt32(passkey)
@dbus.service.method("org.bluez.Agent",
in_signature="ou", out_signature="")
def DisplayPasskey(self, device, passkey):
- print "DisplayPasskey (%s, %d)" % (device, passkey)
+ print("DisplayPasskey (%s, %06d)" % (device, passkey))
+
+ @dbus.service.method("org.bluez.Agent",
+ in_signature="os", out_signature="")
+ def DisplayPinCode(self, device, pincode):
+ print("DisplayPinCode (%s, %s)" % (device, pincode))
@dbus.service.method("org.bluez.Agent",
in_signature="ou", out_signature="")
def RequestConfirmation(self, device, passkey):
- print "RequestConfirmation (%s, %d)" % (device, passkey)
- confirm = raw_input("Confirm passkey (yes/no): ")
+ print("RequestConfirmation (%s, %06d)" % (device, passkey))
+ confirm = ask("Confirm passkey (yes/no): ")
if (confirm == "yes"):
return
raise Rejected("Passkey doesn't match")
@dbus.service.method("org.bluez.Agent",
in_signature="s", out_signature="")
def ConfirmModeChange(self, mode):
- print "ConfirmModeChange (%s)" % (mode)
- authorize = raw_input("Authorize mode change (yes/no): ")
+ print("ConfirmModeChange (%s)" % (mode))
+ authorize = ask("Authorize mode change (yes/no): ")
if (authorize == "yes"):
return
raise Rejected("Mode change by user")
@dbus.service.method("org.bluez.Agent",
in_signature="", out_signature="")
def Cancel(self):
- print "Cancel"
+ print("Cancel")
def create_device_reply(device):
- print "New device (%s)" % (device)
+ print("New device (%s)" % (device))
mainloop.quit()
def create_device_error(error):
- print "Creating device failed: %s" % (error)
+ print("Creating device failed: %s" % (error))
mainloop.quit()
if __name__ == '__main__':
manager = dbus.Interface(bus.get_object("org.bluez", "/"),
"org.bluez.Manager")
- capability = "DisplayYesNo"
+ capability = "KeyboardDisplay"
parser = OptionParser()
parser.add_option("-c", "--capability", action="store",
path = "/test/agent"
agent = Agent(bus, path)
- mainloop = gobject.MainLoop()
+ mainloop = GObject.MainLoop()
if len(args) > 1:
if len(args) > 2:
agent.set_exit_on_release(False)
adapter.CreatePairedDevice(args[1], path, capability,
+ timeout=60000,
reply_handler=create_device_reply,
error_handler=create_device_error)
else:
adapter.RegisterAgent(path, capability)
- print "Agent registered"
+ print("Agent registered")
mainloop.run()
#adapter.UnregisterAgent(path)
- #print "Agent unregistered"
+ #print("Agent unregistered")
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import dbus
import dbus.service
@dbus.service.method("org.bluez.MediaEndpoint",
in_signature="", out_signature="")
def Release(self):
- print "Release"
+ print("Release")
if self.exit_on_release:
mainloop.quit()
@dbus.service.method("org.bluez.MediaEndpoint",
in_signature="", out_signature="")
def ClearConfiguration(self):
- print "ClearConfiguration"
+ print("ClearConfiguration")
@dbus.service.method("org.bluez.MediaEndpoint",
in_signature="oay", out_signature="")
def SetConfiguration(self, transport, config):
- print "SetConfiguration (%s, %s)" % (transport, config)
+ print("SetConfiguration (%s, %s)" % (transport, config))
return
@dbus.service.method("org.bluez.MediaEndpoint",
in_signature="ay", out_signature="ay")
def SelectConfiguration(self, caps):
- print "SelectConfiguration (%s)" % (caps)
+ print("SelectConfiguration (%s)" % (caps))
return self.configuration
if __name__ == '__main__':
"Capabilities" : PCM_CONFIGURATION })
endpoint.default_configuration(dbus.Array([]))
- print properties
+ print(properties)
media.RegisterEndpoint(path, properties)
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import time
import dbus
handle = service.AddRecord(xml)
-print "Service record with handle 0x%04x added" % (handle)
+print("Service record with handle 0x%04x added" % (handle))
-print "Press CTRL-C to remove service record"
+print("Press CTRL-C to remove service record")
try:
time.sleep(1000)
- print "Terminating session"
+ print("Terminating session")
except:
pass
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import dbus
import time
"org.bluez.Adapter")
if (len(args) < 1):
- print "Usage: %s <command>" % (sys.argv[0])
- print ""
- print " address"
- print " list"
- print " name [name]"
- print " powered [on/off]"
- print " pairable [on/off]"
- print " pairabletimeout [timeout]"
- print " discoverable [on/off]"
- print " discoverabletimeout [timeout]"
- print " discovering"
+ print("Usage: %s <command>" % (sys.argv[0]))
+ print("")
+ print(" address")
+ print(" list")
+ print(" name [name]")
+ print(" powered [on/off]")
+ print(" pairable [on/off]")
+ print(" pairabletimeout [timeout]")
+ print(" discoverable [on/off]")
+ print(" discoverabletimeout [timeout]")
+ print(" discovering")
sys.exit(1)
if (args[0] == "address"):
properties = adapter.GetProperties()
- print properties["Address"]
+ print(properties["Address"])
sys.exit(0)
if (args[0] == "name"):
if (len(args) < 2):
properties = adapter.GetProperties()
- print properties["Name"]
+ print(properties["Name"])
else:
adapter.SetProperty("Name", args[1])
sys.exit(0)
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():
+ print(" [ %s ]" % (adapter_path))
+ for (key, value) in prop.items():
if (key == "Class"):
- print " %s = 0x%06x" % (key, value)
+ print(" %s = 0x%06x" % (key, value))
else:
- print " %s = %s" % (key, value)
- print
+ print(" %s = %s" % (key, value))
+ print()
sys.exit(0)
if (args[0] == "powered"):
if (len(args) < 2):
properties = adapter.GetProperties()
- print properties["Powered"]
+ print(properties["Powered"])
else:
if (args[1] == "on"):
value = dbus.Boolean(1)
if (args[0] == "pairable"):
if (len(args) < 2):
properties = adapter.GetProperties()
- print properties["Pairable"]
+ print(properties["Pairable"])
else:
if (args[1] == "on"):
value = dbus.Boolean(1)
if (args[0] == "pairabletimeout"):
if (len(args) < 2):
properties = adapter.GetProperties()
- print properties["PairableTimeout"]
+ print(properties["PairableTimeout"])
else:
timeout = dbus.UInt32(args[1])
adapter.SetProperty("PairableTimeout", timeout)
if (args[0] == "discoverable"):
if (len(args) < 2):
properties = adapter.GetProperties()
- print properties["Discoverable"]
+ print(properties["Discoverable"])
else:
if (args[1] == "on"):
value = dbus.Boolean(1)
if (args[0] == "discoverabletimeout"):
if (len(args) < 2):
properties = adapter.GetProperties()
- print properties["DiscoverableTimeout"]
+ print(properties["DiscoverableTimeout"])
else:
timeout = dbus.UInt32(args[1])
adapter.SetProperty("DiscoverableTimeout", timeout)
if (args[0] == "discovering"):
properties = adapter.GetProperties()
- print properties["Discovering"]
+ print(properties["Discovering"])
sys.exit(0)
-print "Unknown command"
+print("Unknown command")
sys.exit(1)
#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
# Script for testing the Attribute D-Bus API
import sys
"org.bluez.Adapter")
if (len(args) < 1):
- print "Usage: %s <command>" % (sys.argv[0])
- print ""
- print " list"
- print " services <address>"
- print " discover <service path>"
- print " chars <service path>"
+ print("Usage: %s <command>" % (sys.argv[0]))
+ print("")
+ print(" list")
+ print(" services <address>")
+ print(" discover <service path>")
+ print(" chars <service path>")
sys.exit(1)
if (args[0] == "list"):
device = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Device")
devprop = device.GetProperties()
- print "[ %s ]" % devprop["Address"]
+ print("[ %s ]" % devprop["Address"])
for path in devprop["Services"]:
service = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Characteristic")
srvprop = service.GetProperties()
- print " * %s" % (path)
- print " UUID: %s" % srvprop["UUID"]
- print " Chars: ",
+ print(" * %s" % (path))
+ print(" UUID: %s" % srvprop["UUID"])
+ print(" Chars: ",)
for char in srvprop["Characteristics"]:
- print "%s " % char,
- print
- print
- print
+ print("%s " % char,)
+ print()
+ print()
+ print()
sys.exit(0)
if (args[0] == "services"):
if (len(args) < 2):
- print "Need address parameter"
+ print("Need address parameter")
else:
path = adapter.FindDevice(args[1])
device = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Device")
properties = device.GetProperties()
for path in properties["Services"]:
- print path
+ print(path)
sys.exit(0)
if (args[0] == "discover"):
if (len(args) < 2):
- print "Need service path parameter"
+ print("Need service path parameter")
else:
service = dbus.Interface(bus.get_object("org.bluez", args[1]),
"org.bluez.Characteristic")
for path in service.DiscoverCharacteristics():
- print path
+ print(path)
sys.exit(0)
if (args[0] == "chars"):
if (len(args) < 2):
- print "Need service path parameter"
+ print("Need service path parameter")
else:
service = dbus.Interface(bus.get_object("org.bluez", args[1]),
"org.bluez.Characteristic")
srvprop = service.GetProperties()
for path in srvprop["Characteristics"]:
- print "[ %s ]" % (path)
+ print("[ %s ]" % (path))
char = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Characteristic")
charprop = char.GetProperties()
- print " Name: %s" % charprop["Name"]
- print " UUID: %s" % charprop["UUID"]
- print
- print
+ print(" Name: %s" % charprop["Name"])
+ print(" UUID: %s" % charprop["UUID"])
+ print()
+ print()
sys.exit(0)
-print "Unknown command"
+print("Unknown command")
sys.exit(1)
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import dbus
from optparse import OptionParser, make_option
"org.bluez.Adapter")
if len(args) < 2:
- print """Usage: %s <command>
+ print("""Usage: %s <command>
connect <bdaddr>
disconnect <bdaddr>
- """ % sys.argv[0]
+ """ % sys.argv[0])
sys.exit(1)
device = adapter.FindDevice(args[1])
elif args[0] == "disconnect":
audio.Disconnect()
else:
- print "Unknown command"
+ print("Unknown command")
sys.exit(1)
#!/usr/bin/python
-import gobject
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
import sys
import dbus
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
-mainloop = gobject.MainLoop()
+mainloop = GObject.MainLoop()
manager = dbus.Interface(bus.get_object("org.bluez", "/"), "org.bluez.Manager")
"org.bluez.Adapter")
if (len(args) < 1):
- print "Usage: %s <command>" % (sys.argv[0])
- print ""
- print " list"
- print " services <address>"
- print " create <address>"
- print " remove <address|path>"
- print " disconnect <address>"
- print " discover <address> [pattern]"
- print " class <address>"
- print " name <address>"
- print " alias <address> [alias]"
- print " trusted <address> [yes/no]"
- print " blocked <address> [yes/no]"
+ print("Usage: %s <command>" % (sys.argv[0]))
+ print("")
+ print(" list")
+ print(" services <address>")
+ print(" create <address>")
+ print(" remove <address|path>")
+ print(" disconnect <address>")
+ print(" discover <address> [pattern]")
+ print(" class <address>")
+ print(" name <address>")
+ print(" alias <address> [alias]")
+ print(" trusted <address> [yes/no]")
+ print(" blocked <address> [yes/no]")
sys.exit(1)
if (args[0] == "list"):
device = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Device")
properties = device.GetProperties()
- print "%s %s" % (properties["Address"], properties["Alias"])
+ print("%s %s" % (properties["Address"], properties["Alias"]))
sys.exit(0)
def create_device_reply(device):
- print "New device (%s)" % device
+ print("New device (%s)" % device)
mainloop.quit()
sys.exit(0)
def create_device_error(error):
- print "Creating device failed: %s" % error
+ print("Creating device failed: %s" % error)
mainloop.quit()
sys.exit(1)
if (args[0] == "create"):
if (len(args) < 2):
- print "Need address parameter"
+ print("Need address parameter")
else:
adapter.CreateDevice(args[1],
reply_handler=create_device_reply,
if (args[0] == "remove"):
if (len(args) < 2):
- print "Need address or object path parameter"
+ print("Need address or object path parameter")
else:
try:
path = adapter.FindDevice(args[1])
if (args[0] == "disconnect"):
if (len(args) < 2):
- print "Need address parameter"
+ print("Need address parameter")
else:
path = adapter.FindDevice(args[1])
device = dbus.Interface(bus.get_object("org.bluez", path),
if (args[0] == "discover"):
if (len(args) < 2):
- print "Need address parameter"
+ print("Need address parameter")
else:
path = adapter.FindDevice(args[1])
device = dbus.Interface(bus.get_object("org.bluez", path),
for key in services.keys():
p = re.compile(">.*?<")
xml = p.sub("><", services[key].replace("\n", ""))
- print "[ 0x%5x ]" % (key)
- print xml
- print
+ print("[ 0x%5x ]" % (key))
+ print(xml)
+ print()
sys.exit(0)
if (args[0] == "class"):
if (len(args) < 2):
- print "Need address parameter"
+ print("Need address parameter")
else:
path = adapter.FindDevice(args[1])
device = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Device")
properties = device.GetProperties()
- print "0x%06x" % (properties["Class"])
+ print("0x%06x" % (properties["Class"]))
sys.exit(0)
if (args[0] == "name"):
if (len(args) < 2):
- print "Need address parameter"
+ print("Need address parameter")
else:
path = adapter.FindDevice(args[1])
device = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Device")
properties = device.GetProperties()
- print properties["Name"]
+ print(properties["Name"])
sys.exit(0)
if (args[0] == "alias"):
if (len(args) < 2):
- print "Need address parameter"
+ print("Need address parameter")
else:
path = adapter.FindDevice(args[1])
device = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Device")
if (len(args) < 3):
properties = device.GetProperties()
- print properties["Alias"]
+ print(properties["Alias"])
else:
device.SetProperty("Alias", args[2])
sys.exit(0)
if (args[0] == "trusted"):
if (len(args) < 2):
- print "Need address parameter"
+ print("Need address parameter")
else:
path = adapter.FindDevice(args[1])
device = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Device")
if (len(args) < 3):
properties = device.GetProperties()
- print properties["Trusted"]
+ print(properties["Trusted"])
else:
if (args[2] == "yes"):
value = dbus.Boolean(1)
if (args[0] == "blocked"):
if (len(args) < 2):
- print "Need address parameter"
+ print("Need address parameter")
else:
path = adapter.FindDevice(args[1])
device = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Device")
if (len(args) < 3):
properties = device.GetProperties()
- print properties["Blocked"]
+ print(properties["Blocked"])
else:
if (args[2] == "yes"):
value = dbus.Boolean(1)
if (args[0] == "services"):
if (len(args) < 2):
- print "Need address parameter"
+ print("Need address parameter")
else:
path = adapter.FindDevice(args[1])
device = dbus.Interface(bus.get_object("org.bluez", path),
"org.bluez.Device")
properties = device.GetProperties()
for path in properties["Services"]:
- print path
+ print(path)
sys.exit(0)
-print "Unknown command"
+print("Unknown command")
sys.exit(1)
#!/usr/bin/python
-import gobject
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
import dbus
import dbus.mainloop.glib
from optparse import OptionParser, make_option
def device_found(address, properties):
- print "[ " + address + " ]"
+ print("[ " + address + " ]")
for key in properties.keys():
value = properties[key]
+ if type(value) is dbus.String:
+ value = unicode(value).encode('ascii', 'replace')
if (key == "Class"):
- print " %s = 0x%06x" % (key, value)
+ print(" %s = 0x%06x" % (key, value))
else:
- print " %s = %s" % (key, value)
+ print(" %s = %s" % (key, value))
+
+ print()
def property_changed(name, value):
if (name == "Discovering" and not value):
adapter.StartDiscovery()
- mainloop = gobject.MainLoop()
+ mainloop = GObject.MainLoop()
mainloop.run()
#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
# -*- coding: utf-8 -*-
import dbus
return;
sig_name = kwargs["member"]
path = kwargs["path"]
- print sig_name
- print path
+ print(sig_name)
+ print(path)
if sig_name == "PropertyChanged":
k, v = args
- print k
- print v
+ print(k)
+ print(v)
else:
ob = args[0]
- print ob
+ print(ob)
def enter_mainloop():
interface_keyword="interface")
try:
- print "Entering main lopp, push Ctrl+C for finish"
+ print("Entering main lopp, push Ctrl+C for finish")
mainloop = gobject.MainLoop()
mainloop.run()
except KeyboardInterrupt:
pass
finally:
- print "Exiting, bye"
+ 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: ",
+ print("Select 1. source or 2. sink: ",)
try:
sel = int(sys.stdin.readline())
if sel == 1:
else:
raise ValueError
except (TypeError, ValueError):
- print "Wrong selection, try again: ",
+ print("Wrong selection, try again: ",)
except KeyboardInterrupt:
sys.exit()
dtype = None
while dtype == None:
- print "Select a data type: ",
+ 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: ",
+ print("Wrong selection, try again: ",)
except KeyboardInterrupt:
sys.exit()
if role == "Source":
while pref == None:
try:
- print "Select a preferred data channel type 1.",
- print "reliable 2. streaming: ",
+ print("Select a preferred data channel type 1.",)
+ print("reliable 2. streaming: ",)
sel = int(sys.stdin.readline())
if sel == 1:
pref = "Reliable"
raise ValueError
except (TypeError, ValueError):
- print "Wrong selection, try again"
+ print("Wrong selection, try again")
except KeyboardInterrupt:
sys.exit()
"Description": "Test sink",
"Role": role})
-print "New application created:", app_path
+print("New application created:", app_path)
con = None
while con == None:
try:
- print "Connect to a remote device (y/n)? ",
+ 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."
+ print("Wrong selection, try again.")
except KeyboardInterrupt:
sys.exit()
i = 1
for ad in adapters:
- print "%d. %s" % (i, ad)
+ print("%d. %s" % (i, ad))
i = i + 1
-print "Select an adapter: ",
+print("Select an adapter: ",)
select = None
while select == None:
try:
raise TypeError
select = adapters[pos]
except (TypeError, IndexError, ValueError):
- print "Wrong selection, try again: ",
+ print("Wrong selection, try again: ",)
except KeyboardInterrupt:
sys.exit()
devices = adapter.ListDevices()
if len(devices) == 0:
- print "No devices available"
+ print("No devices available")
sys.exit()
i = 1
for dev in devices:
- print "%d. %s" % (i, dev)
+ print("%d. %s" % (i, dev))
i = i + 1
-print "Select a device: ",
+print("Select a device: ",)
select = None
while select == None:
try:
raise TypeError
select = devices[pos]
except (TypeError, IndexError, ValueError):
- print "Wrong selection, try again: ",
+ print("Wrong selection, try again: ",)
except KeyboardInterrupt:
sys.exit()
echo = None
while echo == None:
try:
- print "Perform an echo (y/n)? ",
+ 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."
+ print("Wrong selection, try again.")
except KeyboardInterrupt:
sys.exit()
if echo:
if device.Echo():
- print "Echo was ok"
+ print("Echo was ok")
else:
- print "Echo war wrong, exiting"
+ print("Echo war wrong, exiting")
sys.exit()
-print "Connecting to device %s" % (select)
+print("Connecting to device %s" % (select))
if role == "Source":
chan = device.CreateChannel(app_path, "Reliable")
else:
chan = device.CreateChannel(app_path, "Any")
-print chan
+print(chan)
enter_mainloop()
#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
# -*- coding: utf-8 -*-
import dbus
app_path = hdp_manager.CreateApplication({"DataType": dbus.types.UInt16(4103),
"Role": "sink"})
-print app_path
+print(app_path)
manager = dbus.Interface(bus.get_object("org.bluez", "/"),
"org.bluez.Manager")
i = 1
for ad in adapters:
- print "%d. %s" % (i, ad)
+ print("%d. %s" % (i, ad))
i = i + 1
-print "Select an adapter: ",
+print("Select an adapter: ",)
select = None
while select == None:
try:
raise TypeError
select = adapters[pos]
except (TypeError, IndexError, ValueError):
- print "Wrong selection, try again: ",
+ print("Wrong selection, try again: ",)
except KeyboardInterrupt:
sys.exit()
devices = adapter.ListDevices()
if len(devices) == 0:
- print "No devices available"
+ print("No devices available")
sys.exit()
i = 1
for dev in devices:
- print "%d. %s" % (i, dev)
+ print("%d. %s" % (i, dev))
i = i + 1
-print "Select a device: ",
+print("Select a device: ",)
select = None
while select == None:
try:
raise TypeError
select = devices[pos]
except (TypeError, IndexError, ValueError):
- print "Wrong selection, try again: ",
+ print("Wrong selection, try again: ",)
except KeyboardInterrupt:
sys.exit()
-print "Connecting to %s" % (select)
+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(chan)
-print "Push Enter for finishing"
+print("Push Enter for finishing")
sys.stdin.readline()
hdp_manager.DestroyApplication(app_path)
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import dbus
from optparse import OptionParser, make_option
"org.bluez.Adapter")
if len(args) < 2:
- print """Usage: %s <command>
+ print("""Usage: %s <command>
connect <bdaddr>
disconnect <bdaddr>
- """ % sys.argv[0]
+ """ % sys.argv[0])
sys.exit(1)
device = adapter.FindDevice(args[1])
elif args[0] == "disconnect":
input.Disconnect()
else:
- print "Unknown command"
+ print("Unknown command")
sys.exit(1)
#!/usr/bin/python
-import gobject
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
import dbus
import dbus.mainloop.glib
def adapter_added(path):
- print "Adapter with path %s added" % (path)
+ print("Adapter with path %s added" % (path))
def adapter_removed(path):
- print "Adapter with path %s removed" % (path)
+ print("Adapter with path %s removed" % (path))
def default_changed(path):
- print "Default adapter is now at path %s" % (path)
+ print("Default adapter is now at path %s" % (path))
if __name__ == "__main__":
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
except:
pass
- mainloop = gobject.MainLoop()
+ mainloop = GObject.MainLoop()
mainloop.run()
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import time
import dbus
server.Register(service, bridge)
-print "Server for %s registered for %s" % (service, bridge)
+print("Server for %s registered for %s" % (service, bridge))
-print "Press CTRL-C to disconnect"
+print("Press CTRL-C to disconnect")
try:
time.sleep(1000)
- print "Terminating connection"
+ print("Terminating connection")
except:
pass
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import time
import dbus
"org.bluez.Adapter")
if (len(args) < 1):
- print "Usage: %s <address> [service]" % (sys.argv[0])
+ print("Usage: %s <address> [service]" % (sys.argv[0]))
sys.exit(1)
address = args[0]
iface = network.Connect(service)
-print "Connected %s to %s" % (device, address)
+print("Connected %s to %s" % (device, address))
-print "Press CTRL-C to disconnect"
+print("Press CTRL-C to disconnect")
try:
time.sleep(1000)
- print "Terminating connection"
+ print("Terminating connection")
except:
pass
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import gobject
import dbus.mainloop.glib
def create_device_reply(device):
- print "Pairing succeed!"
+ print("Pairing succeed!")
mainloop.quit()
def create_device_error(error):
- print "Pairing failed."
+ print("Pairing failed.")
mainloop.quit()
if __name__ == '__main__':
adapter0_address = adapter0.GetProperties()["Address"]
adapter1_address = adapter1.GetProperties()["Address"]
- print "Adapters:"
- print " hci0: " + adapter0_address
- print " hci1: " + adapter1_address
- print
+ print("Adapters:")
+ print(" hci0: " + adapter0_address)
+ print(" hci1: " + adapter1_address)
+ print()
- print "Removing any existing bond..."
+ print("Removing any existing bond...")
try:
device = adapter0.FindDevice(adapter1_address)
except:
pass
- print "Done."
- print
- print "Reading local Out of Band data..."
+ print("Done.")
+ print()
+ print("Reading local Out of Band data...")
oob_adapter0 = dbus.Interface(bus.get_object("org.bluez",
adapter0_path), "org.bluez.OutOfBand")
oob0 = oob_adapter0.ReadLocalData()
oob1 = oob_adapter1.ReadLocalData()
- print "Done."
- print
- print "Exchanging Out of Band data..."
+ 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."
+ print("Done.")
+ print()
+ print("Starting to pair.")
adapter1.CreatePairedDevice(adapter0_address, "/test/agent_oob",
"DisplayYesNo",
reply_handler=create_device_reply,
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
'''
Proximity Monitor test script
'''
def property_changed(name, value):
- print "PropertyChanged('%s', '%s')" % (name, value)
+ print("PropertyChanged('%s', '%s')" % (name, value))
mainloop.quit()
if __name__ == "__main__":
"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>"
+ 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",
+ dbus_interface="org.bluez.ProximityMonitor",
signal_name="PropertyChanged")
proximity = dbus.Interface(bus.get_object("org.bluez",
- device_path), "org.bluez.Proximity")
+ device_path), "org.bluez.ProximityMonitor")
- print "Proximity SetProperty('%s', '%s')" % (args[0], args[1])
+ print("Proximity SetProperty('%s', '%s')" % (args[0], args[1]))
proximity.SetProperty(args[0], args[1])
mainloop = gobject.MainLoop()
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
from sap import *
import time
def connect_disconnect_by_client(sap):
- print "[Test] Connect - Disconnect by client \n"
+ print("[Test] Connect - Disconnect by client \n")
try:
if not sap.isConnected():
if sap.proc_connect():
if sap.proc_disconnectByClient():
- print "OK"
+ print("OK")
return 0
- print "NOT OK"
+ print("NOT OK")
return 1
- except BluetoothError , e:
- print "Error " + str(e)
+ except BluetoothError as e:
+ print("Error " + str(e))
def connect_disconnect_by_server_gracefully(sap, timeout=0):
- print "[Test] Connect - Disconnect by server with timer \n"
+ print("[Test] Connect - Disconnect by server with timer \n")
try:
if not sap.isConnected():
if sap.proc_connect():
if sap.proc_disconnectByServer(timeout):
- print "OK"
+ print("OK")
return 0
- print "NOT OK"
+ print("NOT OK")
return 1
- except BluetoothError , e:
- print "Error " + str(e)
+ except BluetoothError as e:
+ print("Error " + str(e))
def connect_txAPDU_disconnect_by_client(sap):
- print "[Test] Connect - TX APDU - Disconnect by client \n"
+ print("[Test] Connect - TX APDU - Disconnect by client \n")
try:
if not sap.isConnected():
if sap.proc_connect():
if not sap.proc_transferAPDU():
- print "NOT OK 1"
+ print("NOT OK 1")
return 1
if not sap.proc_transferAPDU():
- print "NOT OK 2"
+ print("NOT OK 2")
return 1
if not sap.proc_transferAPDU():
- print "NOT OK 3"
+ print("NOT OK 3")
return 1
if not sap.proc_transferAPDU():
- print "NOT OK 4"
+ print("NOT OK 4")
return 1
if sap.proc_disconnectByClient():
- print "OK"
+ print("OK")
return 0
- print "NOT OK"
+ print("NOT OK")
return 1
- except BluetoothError , e:
- print "Error " + str(e)
+ except BluetoothError as 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"
+ print("[Test] Connect rfcomm only - Disconnect by server timeout \n")
if not sap.isConnected():
sap.connect()
time.sleep(40)
- print "OK"
+ print("OK")
def power_sim_off_on(sap):
- print "[Test] Powe sim off \n"
+ print("[Test] Powe sim off \n")
try:
if not sap.isConnected():
if sap.proc_connect():
if not sap.proc_resetSim():
- print "NOT OK"
+ print("NOT OK")
return 1
if not sap.proc_powerSimOff():
- print "NOT OK"
+ print("NOT OK")
return 1
if not sap.proc_powerSimOn():
- print "NOT OK"
+ print("NOT OK")
return 1
if sap.proc_disconnectByClient():
- print "OK"
+ print("OK")
return 0
- print "NOT OK"
+ print("NOT OK")
return 1
- except BluetoothError , e:
- print "Error " + str(e)
+ except BluetoothError as e:
+ print("Error " + str(e))
if __name__ == "__main__":
try:
s = SAPClient(host, port)
- except BluetoothError , e:
- print "Error " + str(e)
+ except BluetoothError as e:
+ print("Error " + str(e))
connect_disconnect_by_client(s)
connect_disconnect_by_server_gracefully(s)
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import time
import dbus
"org.bluez.Adapter")
if (len(args) < 1):
- print "Usage: %s <address> [service]" % (sys.argv[0])
+ print("Usage: %s <address> [service]" % (sys.argv[0]))
sys.exit(1)
address = args[0]
node = serial.Connect(service)
-print "Connected %s to %s" % (node, address)
+print("Connected %s to %s" % (node, address))
-print "Press CTRL-C to disconnect"
+print("Press CTRL-C to disconnect")
try:
time.sleep(1000)
- print "Terminating connection"
+ print("Terminating connection")
except:
pass
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import time
import dbus
"org.bluez.Adapter")
if (len(args) < 1):
- print "Usage: %s <socket_name> [service]" % (sys.argv[0])
+ print("Usage: %s <socket_name> [service]" % (sys.argv[0]))
sys.exit(1)
socket_name = args[0]
conn, addr = sk.accept()
-print "Waiting for message"
+print("Waiting for message")
while 1:
data = conn.recv(1024)
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import dbus
import time
"org.bluez.Service")
if (len(args) < 1):
- print "Usage: %s <command>" % (sys.argv[0])
- print ""
- print " addrecord <file>"
+ print("Usage: %s <command>" % (sys.argv[0]))
+ print("")
+ print(" addrecord <file>")
sys.exit(1)
if (args[0] == "addrecord"):
if (len(args) < 2):
- print "Need file parameter"
+ print("Need file parameter")
else:
f = open(args[1])
record = f.read()
f.close()
handle = service.AddRecord(record)
- print "0x%x" % (handle)
+ print("0x%x" % (handle))
time.sleep(120)
sys.exit(0)
-print "Unknown command"
+print("Unknown command")
sys.exit(1)
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
import sys
import dbus
from optparse import OptionParser, make_option
"org.bluez.TelephonyTest")
if len(args) < 1:
- print """Usage: %s <command>
+ print("""Usage: %s <command>
connect <bdaddr>
disconnect <bdaddr>
microphonegain <bdaddr> [level]
play <bdaddr>
stop <bdaddr>
- """ % sys.argv[0]
+ """ % sys.argv[0])
sys.exit(1)
if args[0] == "connect":
if len(args) < 2:
- print "Need device address parameter"
+ print("Need device address parameter")
sys.exit(1)
device = adapter.FindDevice(args[1])
headset = dbus.Interface(bus.get_object("org.bluez", device),
if args[0] == "disconnect":
if len(args) < 2:
- print "Need device address parameter"
+ print("Need device address parameter")
sys.exit(1)
device = adapter.FindDevice(args[1])
headset = dbus.Interface(bus.get_object("org.bluez", device),
if args[0] == "speakergain":
if len(args) < 2:
- print "Need device address parameter"
+ print("Need device address parameter")
sys.exit(1)
device = adapter.FindDevice(args[1])
headset = dbus.Interface(bus.get_object("org.bluez", device),
headset.SetProperty('SpeakerGain', dbus.UInt16(args[2]))
else:
props = headset.GetProperties()
- print props['SpeakerGain']
+ print(props['SpeakerGain'])
sys.exit(0)
if args[0] == "microphonegain":
if len(args) < 2:
- print "Need device address parameter"
+ print("Need device address parameter")
sys.exit(1)
device = adapter.FindDevice(args[1])
headset = dbus.Interface(bus.get_object("org.bluez", device),
headset.SetProperty('MicrophoneGain', dbus.UInt16(args[2]))
else:
props = headset.GetProperties()
- print props['MicrophoneGain']
+ print(props['MicrophoneGain'])
sys.exit(0)
if args[0] == "play":
if len(args) < 2:
- print "Need device address parameter"
+ print("Need device address parameter")
sys.exit(1)
device = adapter.FindDevice(args[1])
headset = dbus.Interface(bus.get_object("org.bluez", device),
if args[0] == "stop":
if len(args) < 2:
- print "Need device address parameter"
+ print("Need device address parameter")
sys.exit(1)
device = adapter.FindDevice(args[1])
headset = dbus.Interface(bus.get_object("org.bluez", device),
if len(args) > 1:
test.OutgoingCall(args[1])
else:
- print "Need number parameter"
+ print("Need number parameter")
sys.exit(0)
if args[0] == "incoming":
if len(args) > 1:
test.IncomingCall(args[1])
else:
- print "Need number parameter"
+ print("Need number parameter")
sys.exit(0)
if args[0] == "cancel":
if len(args) > 1:
test.SignalStrength(args[1])
else:
- print "Need signal strength parameter"
+ print("Need signal strength parameter")
sys.exit(0)
if args[0] == "battery":
if len(args) > 1:
test.BatteryLevel(args[1])
else:
- print "Need battery level parameter"
+ print("Need battery level parameter")
sys.exit(0)
if args[0] == "roaming":
if len(args) > 1:
test.RoamingStatus(args[1] == "yes" or False)
else:
- print "Need yes/no parameter"
+ print("Need yes/no parameter")
sys.exit(0)
if args[0] == "registration":
if len(args) > 1:
test.RegistrationStatus(args[1] == "yes" or False)
else:
- print "Need yes/no parameter"
+ print("Need yes/no parameter")
sys.exit(0)
if args[0] == "subscriber":
if len(args) > 1:
test.SetSubscriberNumber(args[1])
else:
- print "Need number parameter"
+ print("Need number parameter")
sys.exit(0)
-print "Unknown command"
+print("Unknown command")
sys.exit(1)
#!/usr/bin/python
+from __future__ import absolute_import, print_function, unicode_literals
+
'''
Thermometer test script
'''
@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"]
+ 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"]
+ if "Time" in measure:
+ print("Time: ", measure["Time"])
- print "Type: ", measure["Type"]
+ if "Type" in measure:
+ print("Type: ", measure["Type"])
def property_changed(name, value):
- print "PropertyChanged('%s', '%s')" % (name, value)
+ print("PropertyChanged('%s', '%s')" % (name, value))
if __name__ == "__main__":
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
(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"
+ print("Usage: %s [-i <adapter>] -b <bdaddr> [command]" % (sys.argv[0]))
+ print("Possible commands:")
+ print("\tEnableIntermediateMeasurement")
sys.exit(1)
if options.adapter:
if args[0] == "EnableIntermediateMeasurement":
thermometer.EnableIntermediateMeasurement(path)
else:
- print "unknown command"
+ print("unknown command")
sys.exit(1)
mainloop = gobject.MainLoop()
#include <config.h>
#endif
+#include <stdint.h>
#include <glib.h>
#include <errno.h>
#include <gdbus.h>
#include "plugin.h"
#include "manager.h"
+#include "hcid.h"
+#include "log.h"
static DBusConnection *connection = NULL;
static int thermometer_init(void)
{
+ if (!main_opts.gatt_enabled) {
+ DBG("GATT is disabled");
+ return -ENOTSUP;
+ }
+
connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
if (connection == NULL)
return -EIO;
static void thermometer_exit(void)
{
+ if (!main_opts.gatt_enabled)
+ return;
+
thermometer_manager_exit();
dbus_connection_unref(connection);
#include "adapter.h"
#include "device.h"
#include "att.h"
+#include "gattrib.h"
+#include "gatt.h"
#include "thermometer.h"
#include "manager.h"
-#define HEALTH_THERMOMETER_UUID "00001809-0000-1000-8000-00805f9b34fb"
-
static DBusConnection *connection = NULL;
+static gint primary_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct gatt_primary *prim = a;
+ const char *uuid = b;
+
+ return g_strcmp0(prim->uuid, uuid);
+}
+
static int thermometer_driver_probe(struct btd_device *device, GSList *uuids)
{
- struct att_primary *tattr;
- GSList *list;
+ struct gatt_primary *tattr;
+ GSList *primaries, *l;
+
+ primaries = btd_device_get_primaries(device);
- list = device_services_from_record(device, uuids);
- if (list == NULL)
+ l = g_slist_find_custom(primaries, HEALTH_THERMOMETER_UUID,
+ primary_uuid_cmp);
+ if (l == NULL)
return -EINVAL;
- tattr = list->data;
+ tattr = l->data;
return thermometer_register(connection, device, tattr);
}
#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
};
struct characteristic {
- struct att_char attr; /* Characteristic */
+ struct gatt_char attr; /* Characteristic */
GSList *desc; /* Descriptors */
struct thermometer *t; /* Thermometer where the char belongs */
};
{
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 remove_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_dbus_remove_watch(watcher->t->conn, watcher->id);
+}
+
static void destroy_char(gpointer user_data)
{
struct characteristic *c = user_data;
g_slist_free_full(t->chars, destroy_char);
if (t->fwatchers != NULL)
- g_slist_free_full(t->fwatchers, destroy_watcher);
+ g_slist_free_full(t->fwatchers, remove_watcher);
dbus_connection_unref(t->conn);
btd_device_unref(t->dev);
change_property(desc->ch->t, "Minimum", &min);
}
+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 process_thermometer_desc(struct descriptor *desc)
{
struct characteristic *ch = desc->ch;
if (bt_uuid_cmp(&desc->uuid, &btuuid) == 0) {
uint8_t atval[2];
uint16_t val;
+ char *msg;
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;
+ val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
+ msg = g_strdup("Enable Temperature Measurement "
+ "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;
+ val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
+ msg = g_strdup("Enable Intermediate Temperature "
+ "notification");
} else if (g_strcmp0(ch->attr.uuid,
- MEASUREMENT_INTERVAL_UUID) == 0)
- val = ATT_CLIENT_CHAR_CONF_INDICATION;
- else
+ MEASUREMENT_INTERVAL_UUID) == 0) {
+ val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
+ msg = g_strdup("Enable Measurement Interval "
+ "indication");
+ } else
goto done;
att_put_u16(val, atval);
gatt_write_char(ch->t->attrib, desc->handle, atval, 2,
- NULL, NULL);
+ measurement_cb, msg);
return;
}
}
for (l = characteristics; l; l = l->next) {
- struct att_char *c = l->data;
+ struct gatt_char *c = l->data;
struct characteristic *ch;
uint16_t start, end;
start = c->value_handle + 1;
if (l->next != NULL) {
- struct att_char *c = l->next->data;
+ struct gatt_char *c = l->next->data;
if (start == c->handle)
continue;
end = c->handle - 1;
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;
remove_int_watcher(t, watcher);
t->fwatchers = g_slist_remove(t->fwatchers, watcher);
- watcher->id = 0;
+ g_dbus_remove_watch(watcher->t->conn, watcher->id);
if (g_slist_length(t->fwatchers) == 0)
disable_final_measurement(t);
remove_int_watcher(t, watcher);
t->fwatchers = g_slist_remove(t->fwatchers, watcher);
- destroy_watcher(watcher);
+ g_dbus_remove_watch(watcher->t->conn, watcher->id);
if (g_slist_length(t->fwatchers) == 0)
disable_final_measurement(t);
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 const GDBusMethodTable thermometer_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { GDBUS_ASYNC_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
+ set_property) },
+ { GDBUS_METHOD("RegisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ register_watcher) },
+ { GDBUS_METHOD("UnregisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ unregister_watcher) },
+ { GDBUS_METHOD("EnableIntermediateMeasurement",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ enable_intermediate) },
+ { GDBUS_METHOD("DisableIntermediateMeasurement",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ disable_intermediate) },
{ }
};
-static GDBusSignalTable thermometer_signals[] = {
- { "PropertyChanged", "sv" },
+static const GDBusSignalTable thermometer_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ }
};
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;
+ else
+ type = NULL;
- m.type = g_strdup(type);
+ m.type = type ? g_strdup(type) : NULL;
m.value = final ? "Final" : "Intermediate";
recv_measurement(t, &m);
}
int thermometer_register(DBusConnection *connection, struct btd_device *device,
- struct att_primary *tattr)
+ struct gatt_primary *tattr)
{
const gchar *path = device_get_path(device);
struct thermometer *t;
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;
+ t->svc_range->start = tattr->range.start;
+ t->svc_range->end = tattr->range.end;
if (!g_dbus_register_interface(t->conn, path, THERMOMETER_INTERFACE,
thermometer_methods, thermometer_signals,
*/
int thermometer_register(DBusConnection *connection, struct btd_device *device,
- struct att_primary *tattr);
+ struct gatt_primary *tattr);
void thermometer_unregister(struct btd_device *device);
#include <stdint.h>
#include <glib.h>
+#include <errno.h>
#include "plugin.h"
#include "hcid.h"
static int time_init(void)
{
- if (!main_opts.attrib_server) {
- DBG("Attribute server is disabled");
- return -1;
+ if (!main_opts.gatt_enabled) {
+ DBG("GATT is disabled");
+ return -ENOTSUP;
}
return time_server_init();
static void time_exit(void)
{
- if (!main_opts.attrib_server)
+ if (!main_opts.gatt_enabled)
return;
time_server_exit();
#include <bluetooth/uuid.h>
#include <adapter.h>
-#include "att.h"
#include "gattrib.h"
+#include "att.h"
+#include "gatt.h"
+#include "att-database.h"
#include "attrib-server.h"
#include "gatt-service.h"
#include "log.h"
return 0;
}
-static uint8_t current_time_read(struct attribute *a, gpointer user_data)
+static uint8_t current_time_read(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
{
uint8_t value[10];
return 0;
}
-static uint8_t local_time_info_read(struct attribute *a, gpointer user_data)
+static uint8_t local_time_info_read(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
{
uint8_t value[2];
static void register_current_time_service(void)
{
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, CURRENT_TIME_SVC_UUID);
+
/* Current Time service */
/* FIXME: Provide the adapter in next function */
- gatt_service_add(NULL, GATT_PRIM_SVC_UUID, CURRENT_TIME_SVC_UUID,
+ gatt_service_add(NULL, GATT_PRIM_SVC_UUID, &uuid,
/* CT Time characteristic */
GATT_OPT_CHR_UUID, CT_TIME_CHR_UUID,
GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ |
while (1) {
delay = ubcsp_poll(&activity);
- if (activity & UBCSP_PACKET_RECEIVED)
+ if (activity & UBCSP_PACKET_SENT)
break;
if (delay) {
.TP
.B ath3k
Atheros AR300x based serial Bluetooth device
+.TP
+.B intel
+Intel Bluetooth device
.RE
Supported IDs are (manufacturer id, product id)
#include <sys/poll.h>
#include <sys/param.h>
#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
case 3500000:
return B3500000;
#endif
+#ifdef B3710000
+ case 3710000
+ return B3710000;
+#endif
#ifdef B4000000
case 4000000:
return B4000000;
return qualcomm_init(fd, u->speed, ti, u->bdaddr);
}
+static int intel(int fd, struct uart_t *u, struct termios *ti)
+{
+ return intel_init(fd, u->init_speed, &u->speed, ti);
+}
+
static int read_check(int fd, void *buf, int count)
{
int res;
{ "qualcomm", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
FLOW_CTL, DISABLE_PM, NULL, qualcomm, NULL },
+ /* Intel Bluetooth Module */
+ { "intel", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
+ FLOW_CTL, DISABLE_PM, NULL, intel, NULL },
+
+ /* Three-wire UART */
+ { "3wire", 0x0000, 0x0000, HCI_UART_3WIRE, 115200, 115200,
+ 0, DISABLE_PM, NULL, NULL, NULL },
+
{ NULL, 0 }
};
printpid = 0;
raw = 0;
#ifdef __TI_PATCH__
- while ((opt=getopt(argc, argv, "bnprft:g:s:l")) != EOF)
+ while ((opt=getopt(argc, argv, "bnprft:g:s:l")) != EOF) {
#else
- while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF)
+ while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF) {
#endif
- {
switch(opt) {
case 'b':
send_break = 1;
#define HCI_UART_ATH3K 5
#define HCI_UART_RAW_DEVICE 0
+#define HCI_UART_RESET_ON_INIT 1
+#define HCI_UART_CREATE_AMP 2
int read_hci_event(int fd, unsigned char* buf, int size);
int set_speed(int fd, struct termios *ti, int speed);
struct termios *ti);
int ath3k_post(int fd, int pm);
int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr);
+int intel_init(int fd, int init_speed, int *speed, struct termios *ti);
}
/* send vendor specific command with Sleep feature Enabled */
- if (hci_send_cmd(dd, OGF_VENDOR_CMD, HCI_SLEEP_CMD_OCF, 1, &pm) < 0)
+ if (hci_send_cmd(dd, OGF_VENDOR_CMD, HCI_SLEEP_CMD_OCF, 1, &pm) < 0)
perror("PM command failed, power management Disabled");
nanosleep(&tm, NULL);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <time.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+
+#ifdef INTEL_DEBUG
+#define DBGPRINT(fmt, args...) printf("DBG: " fmt "\n", ## args)
+#define PRINT_PACKET(buf, len, msg) { \
+ int i; \
+ printf("%s\n", msg); \
+ for (i = 0; i < len; i++) \
+ printf("%02X ", buf[i]); \
+ printf("\n"); \
+ }
+#else
+#define DBGPRINT(fmt, args...)
+#define PRINT_PACKET(buf, len, msg)
+#endif
+
+#define PATCH_SEQ_EXT ".bseq"
+#define PATCH_FILE_PATH "/lib/firmware/intel/"
+#define PATCH_MAX_LEN 260
+#define PATCH_TYPE_CMD 1
+#define PATCH_TYPE_EVT 2
+
+#define INTEL_VER_PARAM_LEN 9
+#define INTEL_MFG_PARAM_LEN 2
+
+/**
+ * A data structure for a patch entry.
+ */
+struct patch_entry {
+ int type;
+ int len;
+ unsigned char data[PATCH_MAX_LEN];
+};
+
+/**
+ * A structure for patch context
+ */
+struct patch_ctx {
+ int dev;
+ int fd;
+ int patch_error;
+ int reset_enable_patch;
+};
+
+/**
+ * Send HCI command to the controller
+ */
+static int intel_write_cmd(int dev, unsigned char *buf, int len)
+{
+ int ret;
+
+ PRINT_PACKET(buf, len, "<----- SEND CMD: ");
+
+ ret = write(dev, buf, len);
+ if (ret < 0)
+ return -errno;
+
+ if (ret != len)
+ return -1;
+
+ return ret;
+}
+
+/**
+ * Read the event from the controller
+ */
+static int intel_read_evt(int dev, unsigned char *buf, int len)
+{
+ int ret;
+
+ ret = read_hci_event(dev, buf, len);
+ if (ret < 0)
+ return -1;
+
+ PRINT_PACKET(buf, ret, "-----> READ EVT: ");
+
+ return ret;
+}
+
+/**
+ * Validate HCI events
+ */
+static int validate_events(struct patch_entry *event,
+ struct patch_entry *entry)
+{
+ if (event == NULL || entry == NULL) {
+ DBGPRINT("invalid patch entry parameters");
+ return -1;
+ }
+
+ if (event->len != entry->len) {
+ DBGPRINT("lengths are mismatched:[%d|%d]",
+ event->len, entry->len);
+ return -1;
+ }
+
+ if (memcmp(event->data, entry->data, event->len)) {
+ DBGPRINT("data is mismatched");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Read the next patch entry one line at a time
+ */
+static int get_next_patch_entry(int fd, struct patch_entry *entry)
+{
+ int len, size;
+ char rb;
+
+ if (read(fd, &rb, 1) <= 0)
+ return 0;
+
+ entry->type = rb;
+ len = 0;
+
+ switch (entry->type) {
+ case PATCH_TYPE_CMD:
+ entry->data[0] = HCI_COMMAND_PKT;
+
+ if (read(fd, &entry->data[1], 3) < 0)
+ return -1;
+
+ size = (int)entry->data[3];
+
+ if (read(fd, &entry->data[4], size) < 0)
+ return -1;
+
+ entry->len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + size;
+
+ break;
+
+ case PATCH_TYPE_EVT:
+ entry->data[0] = HCI_EVENT_PKT;
+
+ if (read(fd, &entry->data[len], 2) < 0)
+ return -1;
+
+ size = (int)entry->data[2];
+
+ if (read(fd, &entry->data[3], size) < 0)
+ return -1;
+
+ entry->len = HCI_TYPE_LEN + HCI_EVENT_HDR_SIZE + size;
+
+ break;
+
+ default:
+ fprintf(stderr, "invalid patch entry(%d)\n", entry->type);
+ return -1;
+ }
+
+ return len;
+}
+
+/**
+ * Download the patch set to the controller and verify the event
+ */
+static int intel_download_patch(struct patch_ctx *ctx)
+{
+ int ret;
+ struct patch_entry entry;
+ struct patch_entry event;
+
+ DBGPRINT("start patch downloading");
+
+ do {
+ ret = get_next_patch_entry(ctx->fd, &entry);
+ if (ret <= 0) {
+ ctx->patch_error = 1;
+ break;
+ }
+
+ switch (entry.type) {
+ case PATCH_TYPE_CMD:
+ ret = intel_write_cmd(ctx->dev,
+ entry.data,
+ entry.len);
+ if (ret <= 0) {
+ fprintf(stderr, "failed to send cmd(%d)\n",
+ ret);
+ return ret;
+ }
+ break;
+
+ case PATCH_TYPE_EVT:
+ ret = intel_read_evt(ctx->dev, event.data,
+ sizeof(event.data));
+ if (ret <= 0) {
+ fprintf(stderr, "failed to read evt(%d)\n",
+ ret);
+ return ret;
+ }
+ event.len = ret;
+
+ if (validate_events(&event, &entry) < 0) {
+ DBGPRINT("events are mismatched");
+ ctx->patch_error = 1;
+ return -1;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "unknown patch type(%d)\n",
+ entry.type);
+ return -1;
+ }
+ } while (1);
+
+ return ret;
+}
+
+static int open_patch_file(struct patch_ctx *ctx, char *fw_ver)
+{
+ char patch_file[PATH_MAX];
+
+ snprintf(patch_file, PATH_MAX, "%s%s%s", PATCH_FILE_PATH,
+ fw_ver, PATCH_SEQ_EXT);
+ DBGPRINT("PATCH_FILE: %s", patch_file);
+
+ ctx->fd = open(patch_file, O_RDONLY);
+ if (ctx->fd < 0) {
+ DBGPRINT("cannot open patch file. go to post patch");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Prepare the controller for patching.
+ */
+static int pre_patch(struct patch_ctx *ctx)
+{
+ int ret, i;
+ struct patch_entry entry;
+ char fw_ver[INTEL_VER_PARAM_LEN * 2];
+
+ DBGPRINT("start pre_patch");
+
+ entry.data[0] = HCI_COMMAND_PKT;
+ entry.data[1] = 0x11;
+ entry.data[2] = 0xFC;
+ entry.data[3] = 0x02;
+ entry.data[4] = 0x01;
+ entry.data[5] = 0x00;
+ entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + INTEL_MFG_PARAM_LEN;
+
+ ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+ if (ret < 0) {
+ fprintf(stderr, "failed to send cmd(%d)\n", ret);
+ return ret;
+ }
+
+ ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+ if (ret < 0) {
+ fprintf(stderr, "failed to read evt(%d)\n", ret);
+ return ret;
+ }
+ entry.len = ret;
+
+ if (entry.data[6] != 0x00) {
+ DBGPRINT("command failed. status=%02x", entry.data[6]);
+ ctx->patch_error = 1;
+ return -1;
+ }
+
+ entry.data[0] = HCI_COMMAND_PKT;
+ entry.data[1] = 0x05;
+ entry.data[2] = 0xFC;
+ entry.data[3] = 0x00;
+ entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE;
+
+ ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+ if (ret < 0) {
+ fprintf(stderr, "failed to send cmd(%d)\n", ret);
+ return ret;
+ }
+
+ ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+ if (ret < 0) {
+ fprintf(stderr, "failed to read evt(%d)\n", ret);
+ return ret;
+ }
+ entry.len = ret;
+
+ if (entry.data[6] != 0x00) {
+ DBGPRINT("command failed. status=%02x", entry.data[6]);
+ ctx->patch_error = 1;
+ return -1;
+ }
+
+ for (i = 0; i < INTEL_VER_PARAM_LEN; i++)
+ sprintf(&fw_ver[i*2], "%02x", entry.data[7+i]);
+
+ if (open_patch_file(ctx, fw_ver) < 0) {
+ ctx->patch_error = 1;
+ return -1;
+ }
+
+ return ret;
+}
+
+/*
+ * check the event is startup event
+ */
+static int is_startup_evt(unsigned char *buf)
+{
+ if (buf[1] == 0xFF && buf[2] == 0x01 && buf[3] == 0x00)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * Finalize the patch process and reset the controller
+ */
+static int post_patch(struct patch_ctx *ctx)
+{
+ int ret;
+ struct patch_entry entry;
+
+ DBGPRINT("start post_patch");
+
+ entry.data[0] = HCI_COMMAND_PKT;
+ entry.data[1] = 0x11;
+ entry.data[2] = 0xFC;
+ entry.data[3] = 0x02;
+ entry.data[4] = 0x00;
+ if (ctx->reset_enable_patch)
+ entry.data[5] = 0x02;
+ else
+ entry.data[5] = 0x01;
+
+ entry.len = HCI_TYPE_LEN + HCI_COMMAND_HDR_SIZE + INTEL_MFG_PARAM_LEN;
+
+ ret = intel_write_cmd(ctx->dev, entry.data, entry.len);
+ if (ret < 0) {
+ fprintf(stderr, "failed to send cmd(%d)\n", ret);
+ return ret;
+ }
+
+ ret = intel_read_evt(ctx->dev, entry.data, sizeof(entry.data));
+ if (ret < 0) {
+ fprintf(stderr, "failed to read evt(%d)\n", ret);
+ return ret;
+ }
+ entry.len = ret;
+
+ if (entry.data[6] != 0x00) {
+ fprintf(stderr, "cmd failed. st=%02x\n", entry.data[6]);
+ return -1;
+ }
+
+ do {
+ ret = intel_read_evt(ctx->dev, entry.data,
+ sizeof(entry.data));
+ if (ret < 0) {
+ fprintf(stderr, "failed to read cmd(%d)\n", ret);
+ return ret;
+ }
+ entry.len = ret;
+ } while (!is_startup_evt(entry.data));
+
+ return ret;
+}
+
+/**
+ * Main routine that handles the device patching process.
+ */
+static int intel_patch_device(struct patch_ctx *ctx)
+{
+ int ret;
+
+ ret = pre_patch(ctx);
+ if (ret < 0) {
+ if (!ctx->patch_error) {
+ fprintf(stderr, "I/O error: pre_patch failed\n");
+ return ret;
+ }
+
+ DBGPRINT("patch failed. proceed to post patch");
+ goto post_patch;
+ }
+
+ ret = intel_download_patch(ctx);
+ if (ret < 0) {
+ if (!ctx->patch_error) {
+ fprintf(stderr, "I/O error: download_patch failed\n");
+ close(ctx->fd);
+ return ret;
+ }
+ } else {
+ DBGPRINT("patch done");
+ ctx->reset_enable_patch = 1;
+ }
+
+ close(ctx->fd);
+
+post_patch:
+ ret = post_patch(ctx);
+ if (ret < 0) {
+ fprintf(stderr, "post_patch failed(%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int set_rts(int dev, int rtsval)
+{
+ int arg;
+
+ if (ioctl(dev, TIOCMGET, &arg) < 0) {
+ perror("cannot get TIOCMGET");
+ return -errno;
+ }
+ if (rtsval)
+ arg |= TIOCM_RTS;
+ else
+ arg &= ~TIOCM_RTS;
+
+ if (ioctl(dev, TIOCMSET, &arg) == -1) {
+ perror("cannot set TIOCMGET");
+ return -errno;
+ }
+
+ return 0;
+}
+
+static unsigned char get_intel_speed(int speed)
+{
+ switch (speed) {
+ case 9600:
+ return 0x00;
+ case 19200:
+ return 0x01;
+ case 38400:
+ return 0x02;
+ case 57600:
+ return 0x03;
+ case 115200:
+ return 0x04;
+ case 230400:
+ return 0x05;
+ case 460800:
+ return 0x06;
+ case 921600:
+ return 0x07;
+ case 1843200:
+ return 0x08;
+ case 3250000:
+ return 0x09;
+ case 2000000:
+ return 0x0A;
+ case 3000000:
+ return 0x0B;
+ default:
+ return 0xFF;
+ }
+}
+
+/**
+ * if it failed to change to new baudrate, it will rollback
+ * to initial baudrate
+ */
+static int change_baudrate(int dev, int init_speed, int *speed,
+ struct termios *ti)
+{
+ int ret;
+ unsigned char br;
+ unsigned char cmd[5];
+ unsigned char evt[7];
+
+ DBGPRINT("start baudrate change");
+
+ ret = set_rts(dev, 0);
+ if (ret < 0) {
+ fprintf(stderr, "failed to clear RTS\n");
+ return ret;
+ }
+
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x06;
+ cmd[2] = 0xFC;
+ cmd[3] = 0x01;
+
+ br = get_intel_speed(*speed);
+ if (br == 0xFF) {
+ fprintf(stderr, "speed %d is not supported\n", *speed);
+ return -1;
+ }
+ cmd[4] = br;
+
+ ret = intel_write_cmd(dev, cmd, sizeof(cmd));
+ if (ret < 0) {
+ fprintf(stderr, "failed to send cmd(%d)\n", ret);
+ return ret;
+ }
+
+ /*
+ * wait for buffer to be consumed by the controller
+ */
+ usleep(300000);
+
+ if (set_speed(dev, ti, *speed) < 0) {
+ fprintf(stderr, "can't set to new baud rate\n");
+ return -1;
+ }
+
+ ret = set_rts(dev, 1);
+ if (ret < 0) {
+ fprintf(stderr, "failed to set RTS\n");
+ return ret;
+ }
+
+ ret = intel_read_evt(dev, evt, sizeof(evt));
+ if (ret < 0) {
+ fprintf(stderr, "failed to read evt(%d)\n", ret);
+ return ret;
+ }
+
+ if (evt[4] != 0x00) {
+ fprintf(stderr,
+ "failed to change speed. use default speed %d\n",
+ init_speed);
+ *speed = init_speed;
+ }
+
+ return 0;
+}
+
+/**
+ * An entry point for Intel specific initialization
+ */
+int intel_init(int dev, int init_speed, int *speed, struct termios *ti)
+{
+ int ret = 0;
+ struct patch_ctx ctx;
+
+ if (change_baudrate(dev, init_speed, speed, ti) < 0)
+ return -1;
+
+ ctx.dev = dev;
+ ctx.patch_error = 0;
+ ctx.reset_enable_patch = 0;
+
+ ret = intel_patch_device(&ctx);
+ if (ret < 0)
+ fprintf(stderr, "failed to initialize the device");
+
+ return ret;
+}
#include <sys/poll.h>
#include <sys/param.h>
#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <sys/poll.h>
#include <sys/param.h>
#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
static void cmd_aclmtu(int ctl, int hdev, char *opt)
{
- struct hci_dev_req dr = { dev_id: hdev };
+ struct hci_dev_req dr = { .dev_id = hdev };
uint16_t mtu, mpkt;
if (!opt)
static void cmd_scomtu(int ctl, int hdev, char *opt)
{
- struct hci_dev_req dr = { dev_id: hdev };
+ struct hci_dev_req dr = { .dev_id = hdev };
uint16_t mtu, mpkt;
if (!opt)
printf("\t%s service classes:",
type == 0x02 ? "Shortened" : "Complete");
for (i = 0; i < (len - 1) / 2; i++) {
- uint16_t val = btohs(bt_get_unaligned((uint16_t *) (ptr + (i * 2))));
+ uint16_t val = bt_get_le16((ptr + (i * 2)));
printf(" 0x%4.4x", val);
}
printf("\n");
static int dev_info(int s, int dev_id, long arg)
{
- struct hci_dev_info di = { dev_id: dev_id };
+ struct hci_dev_info di = { .dev_id = dev_id };
char addr[18];
if (ioctl(s, HCIGETDEVINFO, (void *) &di))
if (udev == NULL)
goto exit;
- snprintf(syspath, sizeof(syspath), "%s/%s", udev_get_sys_path(udev), devpath);
+ snprintf(syspath, sizeof(syspath), "/sys/%s", devpath);
udev_dev = udev_device_new_from_syspath(udev, syspath);
if (udev_dev == NULL) {
fprintf(stderr, "error: could not find '%s'\n", devpath);
if (strcmp(argv[0], "all") == 0)
print_dev_list(ctl, 0);
else {
- struct rfcomm_dev_info di = { id: atoi(argv[0]) };
+ struct rfcomm_dev_info di = { .id = atoi(argv[0]) };
if (ioctl(ctl, RFCOMMGETDEVINFO, &di) < 0) {
perror("Get info failed");
exit(1);
{ 0x302, "Remote audio volume control", NULL, 0 },
};
+/* Name of the various GOEP attributes. See BT assigned numbers */
+static struct attrib_def goep_attrib_names[] = {
+ { 0x200, "GoepL2capPsm", NULL, 0 },
+};
+
/* Same for the UUIDs. See BT assigned numbers */
static struct uuid_def uuid16_names[] = {
/* -- Protocols -- */
{ 0x1102, "LANAccessUsingPPP", NULL, 0 },
{ 0x1103, "DialupNetworking (DUN)", NULL, 0 },
{ 0x1104, "IrMCSync", NULL, 0 },
- { 0x1105, "OBEXObjectPush", NULL, 0 },
- { 0x1106, "OBEXFileTransfer", NULL, 0 },
+ { 0x1105, "OBEXObjectPush",
+ goep_attrib_names, sizeof(goep_attrib_names)/sizeof(struct attrib_def) },
+ { 0x1106, "OBEXFileTransfer",
+ goep_attrib_names, sizeof(goep_attrib_names)/sizeof(struct attrib_def) },
{ 0x1107, "IrMCSyncCommand", NULL, 0 },
{ 0x1108, "Headset",
audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
uint8_t network;
} svc_info_t;
-static void add_lang_attr(sdp_record_t *r)
-{
- sdp_lang_attr_t base_lang;
- sdp_list_t *langs = 0;
-
- /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
- base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
- base_lang.encoding = 106;
- base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
- langs = sdp_list_append(0, &base_lang);
- sdp_set_lang_attr(r, langs);
- sdp_list_free(langs, 0);
-}
-
static int add_sp(sdp_session_t *session, svc_info_t *si)
{
sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
aproto = sdp_list_append(0, apseq);
sdp_set_access_protos(&record, aproto);
- add_lang_attr(&record);
+ sdp_add_lang_attr(&record);
sdp_set_info_attr(&record, "Serial Port", "BlueZ", "COM Port");
root = sdp_list_append(0, &root_uuid);
sdp_set_browse_groups(&record, root);
- add_lang_attr(&record);
+ sdp_add_lang_attr(&record);
sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
svclass_id = sdp_list_append(0, &hidkb_uuid);
aproto = sdp_list_append(0, apseq);
sdp_set_add_access_protos(&record, aproto);
- add_lang_attr(&record);
+ sdp_add_lang_attr(&record);
sdp_set_info_attr(&record, "Nintendo RVL-CNT-01",
"Nintendo", "Nintendo RVL-CNT-01");
ret = sdp_device_record_register(session, &interface, &record,
SDP_RECORD_PERSIST);
- if (ret < 0)
+ if (ret < 0)
printf("Service Record registration failed\n");
else
printf("Generic Attribute Profile Service registered\n");
+++ /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 <string.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <syslog.h>
-
-#include <glib.h>
-
-#ifdef HAVE_CAPNG
-#include <cap-ng.h>
-#endif
-
-static GMainLoop *event_loop;
-
-static void sig_term(int sig)
-{
- g_main_loop_quit(event_loop);
-}
-
-static gboolean option_detach = TRUE;
-static gboolean option_debug = FALSE;
-
-static GOptionEntry options[] = {
- { "nodaemon", 'n', G_OPTION_FLAG_REVERSE,
- G_OPTION_ARG_NONE, &option_detach,
- "Don't run as daemon in background" },
- { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_debug,
- "Enable debug information output" },
- { NULL },
-};
-
-static void debug(const char *format, ...)
-{
- va_list ap;
-
- if (!option_debug)
- return;
-
- va_start(ap, format);
-
- vsyslog(LOG_DEBUG, format, ap);
-
- va_end(ap);
-}
-
-static void sig_debug(int sig)
-{
- option_debug = !option_debug;
-}
-
-int main(int argc, char *argv[])
-{
- GOptionContext *context;
- GError *err = NULL;
- struct sigaction sa;
-
-#ifdef HAVE_CAPNG
- /* Drop capabilities */
- capng_clear(CAPNG_SELECT_BOTH);
- capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED,
- CAP_NET_BIND_SERVICE, CAP_NET_ADMIN,
- CAP_NET_RAW, CAP_IPC_LOCK, -1);
- capng_apply(CAPNG_SELECT_BOTH);
-#endif
-
- context = g_option_context_new(NULL);
- g_option_context_add_main_entries(context, options, NULL);
-
- if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
- if (err != NULL) {
- g_printerr("%s\n", err->message);
- g_error_free(err);
- } else
- g_printerr("An unknown error occurred\n");
- exit(1);
- }
-
- g_option_context_free(context);
-
- if (option_detach == TRUE) {
- if (daemon(0, 0)) {
- perror("Can't start daemon");
- exit(1);
- }
- }
-
- umask(0077);
-
- openlog("hcitrace", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
-
- syslog(LOG_INFO, "HCI trace daemon %s", VERSION);
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_flags = SA_NOCLDSTOP;
- sa.sa_handler = sig_term;
- sigaction(SIGTERM, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
-
- sa.sa_handler = sig_debug;
- sigaction(SIGUSR2, &sa, NULL);
-
- sa.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sa, NULL);
-
- if (option_debug == TRUE) {
- syslog(LOG_INFO, "Enabling debug information");
- }
-
- event_loop = g_main_loop_new(NULL, FALSE);
-
- debug("Entering main loop");
-
- g_main_loop_run(event_loop);
-
- g_main_loop_unref(event_loop);
-
- syslog(LOG_INFO, "Exit");
-
- closelog();
-
- return 0;
-}